decidim-core 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/assets/config/decidim_core_manifest.js +1 -0
  4. data/app/assets/javascripts/decidim.js.es6 +3 -2
  5. data/app/assets/javascripts/decidim/append_elements.js.es6 +8 -0
  6. data/app/assets/javascripts/decidim/filters.js.es6 +22 -0
  7. data/app/assets/javascripts/decidim/form_filter.component.js.es6 +176 -0
  8. data/app/assets/javascripts/decidim/form_filter.component.test.js +151 -0
  9. data/app/assets/javascripts/decidim/inline_svg.js.es6 +12 -0
  10. data/app/assets/javascripts/decidim/user_registrations.js.es6 +22 -0
  11. data/app/assets/stylesheets/decidim/_variables.scss +1 -1
  12. data/app/assets/stylesheets/decidim/modules/_comments.scss +3 -6
  13. data/app/assets/stylesheets/decidim/modules/_cookie-bar.scss +22 -0
  14. data/app/assets/stylesheets/decidim/modules/_layout.scss +9 -1
  15. data/app/assets/stylesheets/decidim/modules/_navbar.scss +8 -5
  16. data/app/assets/stylesheets/decidim/modules/_order-by.scss +0 -1
  17. data/app/assets/stylesheets/decidim/modules/_process-phase.scss +4 -0
  18. data/app/assets/stylesheets/decidim/modules/_timeline.scss +62 -80
  19. data/app/assets/stylesheets/decidim/modules/_typography.scss +20 -31
  20. data/app/assets/stylesheets/decidim/utils/_fontface.scss +4 -16
  21. data/app/assets/stylesheets/decidim/utils/_settings.scss +29 -26
  22. data/app/assets/stylesheets/decidim/utils/_variables.scss +22 -0
  23. data/app/commands/decidim/authorize_user.rb +17 -1
  24. data/app/commands/decidim/create_omniauth_registration.rb +92 -0
  25. data/app/commands/decidim/create_registration.rb +50 -0
  26. data/app/commands/decidim/invite_user.rb +50 -0
  27. data/app/commands/decidim/invite_user_again.rb +25 -0
  28. data/app/commands/decidim/remove_user_role.rb +26 -0
  29. data/app/controllers/concerns/decidim/feature_settings.rb +27 -0
  30. data/app/controllers/concerns/decidim/filter_resource.rb +71 -0
  31. data/app/controllers/concerns/decidim/form_factory.rb +21 -18
  32. data/app/controllers/concerns/decidim/needs_participatory_process.rb +2 -3
  33. data/app/controllers/decidim/devise/confirmations_controller.rb +2 -0
  34. data/app/controllers/decidim/devise/invitations_controller.rb +4 -0
  35. data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +96 -0
  36. data/app/controllers/decidim/devise/passwords_controller.rb +2 -0
  37. data/app/controllers/decidim/devise/registrations_controller.rb +34 -0
  38. data/app/controllers/decidim/devise/sessions_controller.rb +1 -0
  39. data/app/controllers/decidim/pages_controller.rb +10 -2
  40. data/app/controllers/decidim/participatory_process_steps_controller.rb +16 -0
  41. data/app/controllers/decidim/participatory_processes_controller.rb +6 -11
  42. data/app/forms/decidim/form.rb +6 -6
  43. data/app/forms/decidim/invite_admin_form.rb +37 -0
  44. data/app/forms/decidim/omniauth_registration_form.rb +24 -0
  45. data/app/forms/decidim/registration_form.rb +41 -0
  46. data/app/helpers/decidim/filters_helper.rb +21 -0
  47. data/app/helpers/decidim/layout_helper.rb +17 -1
  48. data/app/helpers/decidim/omniauth_helper.rb +23 -0
  49. data/app/helpers/decidim/paginate_helper.rb +21 -0
  50. data/app/helpers/decidim/participatory_process_helper.rb +14 -0
  51. data/app/helpers/decidim/participatory_process_steps_helper.rb +17 -0
  52. data/app/mailers/decidim/decidim_devise_mailer.rb +2 -2
  53. data/app/models/decidim/feature.rb +40 -0
  54. data/app/models/decidim/identity.rb +12 -0
  55. data/app/models/decidim/organization.rb +3 -10
  56. data/app/models/decidim/user.rb +3 -1
  57. data/app/models/decidim/user_group.rb +18 -0
  58. data/app/models/decidim/user_group_membership.rb +9 -0
  59. data/app/services/decidim/authorization_handler.rb +13 -13
  60. data/app/services/decidim/resource_search.rb +52 -0
  61. data/app/uploaders/decidim/organization_logo_uploader.rb +1 -1
  62. data/app/views/decidim/authorizations/new.html.erb +3 -1
  63. data/app/views/decidim/devise/omniauth_registrations/new.html.erb +40 -0
  64. data/app/views/decidim/devise/registrations/new.html.erb +21 -1
  65. data/app/views/decidim/devise/sessions/new.html.erb +1 -0
  66. data/app/views/decidim/devise/shared/_links.html.erb +0 -8
  67. data/app/views/decidim/devise/shared/_omniauth_buttons.html.erb +21 -0
  68. data/app/views/decidim/participatory_process_steps/_participatory_process_step.html.erb +16 -0
  69. data/app/views/decidim/participatory_process_steps/_timeline.html.erb +7 -0
  70. data/app/views/decidim/participatory_process_steps/index.html.erb +12 -0
  71. data/app/views/decidim/participatory_processes/show.html.erb +5 -5
  72. data/app/views/decidim/shared/_login_modal.html.erb +30 -0
  73. data/app/views/devise/mailer/invite_admin.html.erb +17 -0
  74. data/app/views/devise/mailer/invite_admin.text.erb +15 -0
  75. data/app/views/devise/mailer/organization_admin_invitation_instructions.html.erb +1 -1
  76. data/app/views/devise/mailer/organization_admin_invitation_instructions.text.erb +1 -1
  77. data/app/views/layouts/decidim/_application.html.erb +1 -0
  78. data/app/views/layouts/decidim/_header.html.erb +1 -1
  79. data/app/views/layouts/decidim/_meta.html.erb +1 -0
  80. data/app/views/layouts/decidim/_process_header.html.erb +12 -1
  81. data/app/views/layouts/decidim/_process_header_steps.html.erb +1 -0
  82. data/app/views/layouts/decidim/_social_meta.html.erb +11 -0
  83. data/app/views/layouts/decidim/participatory_process.html.erb +4 -0
  84. data/app/views/pages/home.html.erb +29 -2
  85. data/config/i18n-tasks.yml +1 -0
  86. data/config/initializers/devise.rb +10 -1
  87. data/config/locales/ca.yml +45 -3
  88. data/config/locales/en.yml +45 -3
  89. data/config/locales/es.yml +45 -3
  90. data/config/routes.rb +9 -2
  91. data/config/secrets.yml +36 -0
  92. data/db/migrate/20170110133113_add_configuration_to_features.rb +7 -0
  93. data/db/migrate/20170110153807_add_handler_to_organization.rb +5 -0
  94. data/db/migrate/20170116110851_create_identities.rb +11 -0
  95. data/db/migrate/20170116135237_loosen_step_requirements.rb +6 -0
  96. data/db/migrate/20170117142904_add_uniqueness_field_to_authorizations.rb +7 -0
  97. data/db/migrate/20170119145359_create_user_groups.rb +11 -0
  98. data/db/migrate/20170119150255_create_user_group_memberships.rb +12 -0
  99. data/db/migrate/20170119150649_add_show_statistics_to_organization.rb +5 -0
  100. data/db/migrate/20170120120733_add_user_groups_verified.rb +5 -0
  101. data/db/seeds.rb +45 -79
  102. data/lib/decidim/core.rb +8 -0
  103. data/lib/decidim/core/engine.rb +8 -0
  104. data/lib/decidim/core/test/factories.rb +188 -0
  105. data/lib/decidim/core/version.rb +1 -1
  106. data/lib/decidim/devise_failure_app.rb +1 -0
  107. data/lib/decidim/feature_manifest.rb +27 -0
  108. data/lib/decidim/features/base_controller.rb +8 -3
  109. data/lib/decidim/features/settings_manifest.rb +94 -0
  110. data/lib/decidim/filter_form_builder.rb +56 -0
  111. data/lib/decidim/form_builder.rb +4 -7
  112. data/vendor/assets/javascripts/svg-injector.js +464 -0
  113. metadata +142 -26
  114. data/app/assets/stylesheets/decidim/modules/_owl-carousel.scss +0 -72
  115. data/app/assets/stylesheets/decidim/modules/_phase-nav.scss +0 -177
  116. data/app/assets/stylesheets/decidim/utils/_helpers.sass +0 -21
  117. data/app/assets/stylesheets/decidim/utils/_keyframes.sass +0 -13
  118. data/app/assets/stylesheets/decidim/utils/_mixins.sass +0 -33
@@ -19,14 +19,13 @@ module Decidim
19
19
  #
20
20
  # Returns the current ParticipatoryProcess.
21
21
  def current_participatory_process
22
- @current_participatory_process ||=
23
- current_organization.participatory_processes.find_by(id: params[:participatory_process_id])
22
+ @current_participatory_process ||= current_organization.participatory_processes.find_by(id: params[:participatory_process_id] || params[:id])
24
23
  end
25
24
 
26
25
  private
27
26
 
28
27
  def verify_participatory_process
29
- raise ActionController::RoutingError, "Not Found" unless current_participatory_process
28
+ raise ActionController::RoutingError, "Participatory process not found." unless current_participatory_process
30
29
  end
31
30
  end
32
31
  end
@@ -5,6 +5,8 @@ module Decidim
5
5
  class ConfirmationsController < ::Devise::ConfirmationsController
6
6
  include Decidim::NeedsOrganization
7
7
  include Decidim::LocaleSwitcher
8
+ helper Decidim::TranslationsHelper
9
+
8
10
  layout "layouts/decidim/application"
9
11
  end
10
12
  end
@@ -3,6 +3,10 @@ module Decidim
3
3
  module Devise
4
4
  # This controller customizes the behaviour of Devise::Invitiable.
5
5
  class InvitationsController < ::Devise::InvitationsController
6
+ include Decidim::NeedsOrganization
7
+ include Decidim::LocaleSwitcher
8
+ helper Decidim::TranslationsHelper
9
+
6
10
  # We don't users to create invitations, so we just redirect them to the
7
11
  # homepage.
8
12
  def authenticate_inviter!
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Devise
4
+ # This controller customizes the behaviour of Devise::Omniauthable.
5
+ class OmniauthRegistrationsController < ::Devise::OmniauthCallbacksController
6
+ include FormFactory
7
+
8
+ include Decidim::NeedsOrganization
9
+ include Decidim::LocaleSwitcher
10
+ helper Decidim::TranslationsHelper
11
+
12
+ layout "layouts/decidim/application"
13
+
14
+ def new
15
+ @form = form(OmniauthRegistrationForm).from_params(params[:user])
16
+ end
17
+
18
+ def create
19
+ form_params = user_params_from_oauth_hash || params[:user]
20
+ @form = form(OmniauthRegistrationForm).from_params(form_params)
21
+ @form.email ||= verified_email
22
+
23
+ CreateOmniauthRegistration.call(@form, verified_email) do
24
+ on(:ok) do |user|
25
+ if user.active_for_authentication?
26
+ sign_in_and_redirect user, event: :authentication
27
+ set_flash_message :notice, :success, kind: @form.provider.capitalize
28
+ else
29
+ expire_data_after_sign_in!
30
+ redirect_to root_path
31
+ flash[:notice] = t("devise.registrations.signed_up_but_unconfirmed")
32
+ end
33
+ end
34
+
35
+ on(:invalid) do
36
+ set_flash_message :notice, :success, kind: @form.provider.capitalize
37
+ render :new
38
+ end
39
+
40
+ on(:error) do
41
+ redirect_to decidim.new_user_registration_path
42
+ set_flash_message :alert, :failure, kind: @form.provider, reason: t("decidim.devise.omniauth_registrations.create.email_already_exists")
43
+ end
44
+ end
45
+ end
46
+
47
+ def after_sign_in_path_for(user)
48
+ if !pending_redirect?(user) && first_login_and_not_authorized?(user)
49
+ authorizations_path
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # Calling the `stored_location_for` method removes the key, so in order
56
+ # to check if there's any pending redirect after login I need to call
57
+ # this method and use the value to set a pending redirect. This is the
58
+ # only way to do this without checking the session directly.
59
+ def pending_redirect?(user)
60
+ store_location_for(user, stored_location_for(user))
61
+ end
62
+
63
+ def first_login_and_not_authorized?(user)
64
+ user.is_a?(User) && user.sign_in_count == 1 && Decidim.authorization_handlers.any?
65
+ end
66
+
67
+ def action_missing(action_name)
68
+ return send(:create) if devise_mapping.omniauthable? && User.omniauth_providers.include?(action_name.to_sym)
69
+ raise AbstractController::ActionNotFound, "The action '#{action_name}' could not be found for Decidim::Devise::OmniauthCallbacksController"
70
+ end
71
+
72
+ private
73
+
74
+ def oauth_data
75
+ return {} unless request.env["omniauth.auth"]
76
+ @oauth_data ||= request.env["omniauth.auth"].slice(:provider, :uid, :info)
77
+ end
78
+
79
+ # Private: Create form params from omniauth hash
80
+ # Since we are using trusted omniauth data we are generating a valid signature.
81
+ def user_params_from_oauth_hash
82
+ return nil unless request.env["omniauth.auth"]
83
+ {
84
+ provider: oauth_data[:provider],
85
+ uid: oauth_data[:uid],
86
+ name: oauth_data[:info][:name],
87
+ oauth_signature: OmniauthRegistrationForm.create_signature(oauth_data[:provider], oauth_data[:uid])
88
+ }
89
+ end
90
+
91
+ def verified_email
92
+ @verified_email ||= oauth_data.dig(:info, :email)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -5,6 +5,8 @@ module Decidim
5
5
  class PasswordsController < ::Devise::PasswordsController
6
6
  include Decidim::NeedsOrganization
7
7
  include Decidim::LocaleSwitcher
8
+ helper Decidim::TranslationsHelper
9
+
8
10
  layout "layouts/decidim/application"
9
11
  end
10
12
  end
@@ -6,9 +6,43 @@ module Decidim
6
6
  class RegistrationsController < ::Devise::RegistrationsController
7
7
  include Decidim::NeedsOrganization
8
8
  include Decidim::LocaleSwitcher
9
+ include FormFactory
10
+ helper Decidim::TranslationsHelper
11
+ helper Decidim::OmniauthHelper
12
+
9
13
  layout "layouts/decidim/application"
10
14
  before_action :configure_permitted_parameters
11
15
 
16
+ def new
17
+ @form = form(RegistrationForm).from_params(
18
+ user: {
19
+ sign_up_as: "user"
20
+ }
21
+ )
22
+ end
23
+
24
+ def create
25
+ @form = form(RegistrationForm).from_params(params[:user])
26
+
27
+ CreateRegistration.call(@form) do
28
+ on(:ok) do |user|
29
+ if user.active_for_authentication?
30
+ set_flash_message! :notice, :signed_up
31
+ sign_up(:user, user)
32
+ respond_with user, location: after_sign_up_path_for(user)
33
+ else
34
+ set_flash_message! :notice, :"signed_up_but_#{user.inactive_message}"
35
+ expire_data_after_sign_in!
36
+ respond_with user, location: after_inactive_sign_up_path_for(user)
37
+ end
38
+ end
39
+
40
+ on(:invalid) do
41
+ render :new
42
+ end
43
+ end
44
+ end
45
+
12
46
  protected
13
47
 
14
48
  def configure_permitted_parameters
@@ -6,6 +6,7 @@ module Decidim
6
6
  include Decidim::NeedsOrganization
7
7
  include Decidim::LocaleSwitcher
8
8
  helper Decidim::TranslationsHelper
9
+ helper Decidim::OmniauthHelper
9
10
 
10
11
  layout "layouts/decidim/application"
11
12
 
@@ -11,14 +11,22 @@ module Decidim
11
11
 
12
12
  authorize_resource :public_pages, class: false
13
13
  delegate :page, to: :page_finder
14
- helper_method :page, :participatory_processes
14
+ helper_method :page, :participatory_processes, :promoted_participatory_processes, :users
15
15
 
16
16
  def page_finder
17
17
  @page_finder ||= Decidim::PageFinder.new(params[:id], current_organization)
18
18
  end
19
19
 
20
+ def promoted_participatory_processes
21
+ @promoted_participatory_processes ||= participatory_processes.promoted
22
+ end
23
+
20
24
  def participatory_processes
21
- @participatory_processes ||= current_organization.participatory_processes.includes(:active_step).published.promoted
25
+ @participatory_processes ||= current_organization.participatory_processes.includes(:active_step).published
26
+ end
27
+
28
+ def users
29
+ @users ||= Decidim::User.where(organization: current_organization)
22
30
  end
23
31
  end
24
32
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require_dependency "decidim/application_controller"
3
+
4
+ module Decidim
5
+ # A controller that holds the logic to show ParticipatoryProcessSteps in a
6
+ # public layout.
7
+ class ParticipatoryProcessStepsController < ApplicationController
8
+ helper_method :participatory_process, :current_participatory_process
9
+ layout "layouts/decidim/participatory_process", only: [:index]
10
+ include NeedsParticipatoryProcess
11
+
12
+ def index
13
+ authorize! :read, ParticipatoryProcess
14
+ end
15
+ end
16
+ end
@@ -5,29 +5,24 @@ module Decidim
5
5
  # A controller that holds the logic to show ParticipatoryProcesses in a
6
6
  # public layout.
7
7
  class ParticipatoryProcessesController < ApplicationController
8
- helper_method :participatory_processes, :participatory_process, :promoted_processes
9
- helper_method :current_participatory_process
8
+ include NeedsParticipatoryProcess
10
9
 
11
10
  layout "layouts/decidim/participatory_process", only: [:show]
12
11
 
12
+ helper_method :participatory_processes, :promoted_processes
13
+
14
+ skip_after_action :verify_participatory_process , only: [:index]
15
+
13
16
  def index
14
17
  authorize! :read, ParticipatoryProcess
15
18
  end
16
19
 
17
20
  def show
18
- authorize! :read, participatory_process
21
+ authorize! :read, current_participatory_process
19
22
  end
20
23
 
21
24
  private
22
25
 
23
- def current_participatory_process
24
- participatory_process
25
- end
26
-
27
- def participatory_process
28
- @participatory_process ||= ParticipatoryProcess.find(params[:id])
29
- end
30
-
31
26
  def participatory_processes
32
27
  @participatory_processes ||= current_organization.participatory_processes.includes(:active_step).published
33
28
  end
@@ -3,13 +3,13 @@ module Decidim
3
3
  # A base form object to hold common logic, like automatically adding as
4
4
  # public method the params sent as context by the `FormFactory` concern.
5
5
  class Form < Rectify::Form
6
- attr_reader :current_organization, :current_user, :current_feature
6
+ delegate :current_organization,
7
+ :current_user,
8
+ :current_feature,
9
+ to: :context, prefix: false, allow_nil: true
7
10
 
8
- def initialize(attributes = {})
9
- @current_organization = attributes.delete("current_organization") || attributes.delete(:current_organization)
10
- @current_user = attributes.delete("current_user") || attributes.delete(:current_user)
11
- @current_feature = attributes.delete("current_feature") || attributes.delete(:current_feature)
12
- super
11
+ def available_locales
12
+ current_organization&.available_locales
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # A form object used to invite admins to an organization.
5
+ #
6
+ class InviteAdminForm < Form
7
+ mimic :user
8
+
9
+ attribute :email, String
10
+ attribute :name, String
11
+ attribute :invitation_instructions, String
12
+ attribute :roles, Array[String]
13
+ attribute :organization, Decidim::Organization
14
+ attribute :invited_by, Decidim::User
15
+
16
+ validates :email, :name, :organization, :invitation_instructions, :roles, presence: true
17
+ validate :admin_uniqueness
18
+
19
+ def email
20
+ super&.downcase
21
+ end
22
+
23
+ def organization
24
+ super || current_organization
25
+ end
26
+
27
+ def invited_by
28
+ super || current_user
29
+ end
30
+
31
+ private
32
+
33
+ def admin_uniqueness
34
+ errors.add(:email, :taken) if organization.admins.where(email: email).exists?
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # A form object used to fisnish signup from omniauth data
5
+ class OmniauthRegistrationForm < Form
6
+ mimic :user
7
+
8
+ attribute :email, String
9
+ attribute :name, String
10
+ attribute :provider, String
11
+ attribute :uid, String
12
+ attribute :tos_agreement, Boolean
13
+ attribute :oauth_signature, String
14
+
15
+ validates :email, presence: true
16
+ validates :name, presence: true
17
+ validates :provider, presence: true
18
+ validates :uid, presence: true
19
+
20
+ def self.create_signature(provider, uid)
21
+ Digest::MD5.hexdigest("#{provider}-#{uid}-#{Rails.application.secrets.secret_key_base}")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # A form object used to handle user registrations
5
+ class RegistrationForm < Form
6
+ mimic :user
7
+
8
+ attribute :sign_up_as, String
9
+ attribute :name, String
10
+ attribute :email, String
11
+ attribute :password, String
12
+ attribute :password_confirmation, String
13
+ attribute :tos_agreement, Boolean
14
+
15
+ attribute :user_group_name, String
16
+ attribute :user_group_document_number, String
17
+ attribute :user_group_phone, String
18
+
19
+ validates :sign_up_as, inclusion: { in: %w(user user_group) }
20
+ validates :name, presence: true
21
+ validates :email, presence: true
22
+ validates :password, presence: true, confirmation: true
23
+ validates :tos_agreement, allow_nil: false, acceptance: true
24
+
25
+ validates :user_group_name, presence: true, if: :is_user_group?
26
+ validates :user_group_document_number, presence: true, if: :is_user_group?
27
+ validates :user_group_phone, presence: true, if: :is_user_group?
28
+
29
+ validate :email_unique_in_organization
30
+
31
+ def is_user_group?
32
+ sign_up_as == "user_group"
33
+ end
34
+
35
+ private
36
+
37
+ def email_unique_in_organization
38
+ errors.add :email, :taken if User.where(email: email, organization: current_organization).first.present?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ # Helper that provides a single method to create filter resource forms
4
+ module FiltersHelper
5
+ # This method wraps everything in a div with class filters and calls
6
+ # the form_for helper with a custom builder
7
+ #
8
+ # filter - A filter object
9
+ # block - A block to be called with the form builder
10
+ #
11
+ # Returns the filter resource form wrapped in a div
12
+ def filter_form_for(filter)
13
+ filters_container = content_tag :div, class: "filters" do
14
+ form_for filter, builder: FilterFormBuilder, url: url_for, as: :filter, method: :get, remote: true do |form|
15
+ yield form
16
+ end
17
+ end
18
+ filters_container + javascript_include_tag("decidim/filters")
19
+ end
20
+ end
21
+ end
@@ -33,11 +33,27 @@ module Decidim
33
33
  html_properties["aria-label"] = options[:aria_label]
34
34
  html_properties["role"] = options[:role]
35
35
  html_properties["aria-hidden"] = options[:aria_hidden]
36
- html_properties["class"] = "icon icon--#{name} #{options[:class]}"
36
+
37
+ icon_class = (options[:remove_icon_class] ? "" : "icon")
38
+ html_properties["class"] = "icon--#{name} #{icon_class} #{options[:class]}"
37
39
 
38
40
  content_tag :svg, html_properties do
39
41
  content_tag :use, nil, "xlink:href" => "#{asset_url("decidim/icons.svg")}#icon-#{name}"
40
42
  end
41
43
  end
44
+
45
+ # Outputs a SVG icon from an external file. It apparently renders an image
46
+ # tag, but then a JS script kicks in and replaces it with an inlined SVG
47
+ # version.
48
+ #
49
+ # path - The asset's path
50
+ #
51
+ # Returns an <img /> tag with the SVG icon.
52
+ def external_icon(path)
53
+ # Ugly hack to prevent PhantomJS from freaking out with SVGs.
54
+ return content_tag(:span, "?", class: "external-svg", "data-src" => path) if Rails.env.test?
55
+
56
+ image_tag(path, class: "external-svg", style: "display: none")
57
+ end
42
58
  end
43
59
  end