beyond_canvas 0.15.2.pre → 0.17.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -6
  3. data/Rakefile +3 -3
  4. data/app/assets/config/beyond_canvas_manifest.js +1 -0
  5. data/app/assets/images/icons/arrow_right.svg +1 -0
  6. data/app/assets/images/icons/close.svg +1 -0
  7. data/app/assets/images/icons/home.svg +1 -0
  8. data/app/assets/javascripts/beyond_canvas/base.js +120 -246
  9. data/app/assets/stylesheets/beyond_canvas/base.scss +8 -1
  10. data/app/assets/stylesheets/beyond_canvas/components/_action_bar.scss +28 -0
  11. data/app/assets/stylesheets/beyond_canvas/components/_breadcrumbs.scss +29 -0
  12. data/app/assets/stylesheets/beyond_canvas/components/_buttons.scss +1 -1
  13. data/app/assets/stylesheets/beyond_canvas/components/_debug.scss +10 -0
  14. data/app/assets/stylesheets/beyond_canvas/components/_forms.scss +11 -14
  15. data/app/assets/stylesheets/beyond_canvas/components/_main.scss +12 -13
  16. data/app/assets/stylesheets/beyond_canvas/components/_margins.scss +8 -0
  17. data/app/assets/stylesheets/beyond_canvas/components/_menu.scss +40 -0
  18. data/app/assets/stylesheets/beyond_canvas/components/_modals.scss +27 -0
  19. data/app/assets/stylesheets/beyond_canvas/components/_sidebar.scss +53 -0
  20. data/app/assets/stylesheets/beyond_canvas/components/_titles.scss +7 -0
  21. data/app/assets/stylesheets/beyond_canvas/settings/_breakpoints.scss +6 -6
  22. data/app/assets/stylesheets/beyond_canvas/settings/_variables.scss +77 -22
  23. data/app/controllers/beyond_canvas/application_controller.rb +2 -0
  24. data/app/controllers/beyond_canvas/authentications_controller.rb +69 -0
  25. data/app/controllers/concerns/beyond_canvas/authentication.rb +13 -0
  26. data/app/controllers/concerns/beyond_canvas/locale_management.rb +5 -4
  27. data/app/controllers/concerns/beyond_canvas/request_validation.rb +1 -1
  28. data/app/helpers/beyond_canvas/authentications_helper.rb +28 -0
  29. data/app/helpers/beyond_canvas/cockpit_app_helper.rb +17 -0
  30. data/app/helpers/beyond_canvas/debug_helper.rb +9 -0
  31. data/app/helpers/beyond_canvas/locale_switch_helper.rb +5 -1
  32. data/app/javascript/beyond_canvas/base.js +3 -0
  33. data/app/javascript/beyond_canvas/initializers/buttons.js +65 -19
  34. data/app/javascript/beyond_canvas/initializers/flash.js +9 -2
  35. data/app/javascript/beyond_canvas/initializers/inputs.js +4 -1
  36. data/app/javascript/beyond_canvas/initializers/modals.js +14 -0
  37. data/app/views/beyond_canvas/authentications/new.html.erb +27 -0
  38. data/app/views/beyond_canvas/shared/_action_bar.html.erb +15 -0
  39. data/app/views/beyond_canvas/shared/_breadcrumbs.html.erb +14 -0
  40. data/app/views/beyond_canvas/shared/_flash.html.erb +22 -12
  41. data/app/views/beyond_canvas/shared/_locales.html.erb +8 -0
  42. data/app/views/beyond_canvas/shared/_menu.html.erb +31 -0
  43. data/app/views/beyond_canvas/shared/_modal.html.erb +6 -0
  44. data/app/views/beyond_canvas/shared/_sidebar.html.erb +16 -0
  45. data/app/views/layouts/beyond_canvas/application.html.erb +31 -0
  46. data/app/views/layouts/beyond_canvas/public.html.erb +10 -1
  47. data/config/locales/en.yml +9 -0
  48. data/config/routes.rb +8 -1
  49. data/lib/beyond_canvas.rb +18 -2
  50. data/lib/beyond_canvas/configuration.rb +11 -3
  51. data/lib/beyond_canvas/engine.rb +6 -0
  52. data/lib/beyond_canvas/menu_item_registration.rb +19 -0
  53. data/lib/beyond_canvas/parameter_sanitizer.rb +43 -0
  54. data/lib/beyond_canvas/rails/routes.rb +22 -0
  55. data/lib/beyond_canvas/version.rb +1 -1
  56. data/lib/generators/beyond_canvas/controller/controller_generator.rb +19 -0
  57. data/lib/generators/beyond_canvas/controller/templates/controller.erb +20 -0
  58. data/lib/generators/beyond_canvas/custom_menu/custom_menu_generator.rb +13 -0
  59. data/lib/generators/beyond_canvas/custom_menu/templates/beyond_canvas_custom_menu.html.erb +32 -0
  60. data/lib/generators/beyond_canvas/custom_styles/custom_styles_generator.rb +1 -1
  61. data/lib/generators/beyond_canvas/custom_styles/templates/beyond_canvas_custom_styles.scss +199 -0
  62. data/lib/generators/beyond_canvas/install/install_generator.rb +13 -5
  63. data/lib/generators/beyond_canvas/install/templates/beyond_canvas.rb.erb +55 -6
  64. data/lib/generators/beyond_canvas/model/model_generator.rb +49 -0
  65. data/lib/generators/beyond_canvas/model/templates/migration.erb +18 -0
  66. data/lib/generators/beyond_canvas/model/templates/model.erb +5 -0
  67. data/lib/generators/beyond_canvas/views/views_generator.rb +17 -0
  68. data/lib/models/concerns/authentication.rb +57 -0
  69. data/lib/models/concerns/utils.rb +79 -0
  70. data/lib/models/shop.rb +12 -0
  71. metadata +95 -12
  72. data/app/javascript/beyond_canvas/initializers/functions.js +0 -41
  73. data/app/views/beyond_canvas/locales/_edit.html.erb +0 -8
  74. data/lib/generators/beyond_canvas/custom_styles/templates/beyond_canvas_custom_styles.sass +0 -123
@@ -5,5 +5,7 @@ module BeyondCanvas
5
5
  protect_from_forgery with: :exception
6
6
 
7
7
  include ::BeyondCanvas::StatusCodes
8
+ include ::BeyondCanvas::AuthenticationsHelper
9
+ include ::BeyondCanvas::DebugHelper
8
10
  end
9
11
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'beyond_canvas/application_controller'
4
+
5
+ module BeyondCanvas
6
+ class AuthenticationsController < ApplicationController # :nodoc:
7
+ layout 'beyond_canvas/public'
8
+
9
+ include ::BeyondCanvas::Authentication
10
+
11
+ before_action :validate_app_installation_request!, only: :new
12
+
13
+ def new
14
+ @shop = Shop.find_or_initialize_by(beyond_api_url: params[:api_url])
15
+
16
+ if @shop&.authenticated?
17
+ open_app(@shop)
18
+ elsif BeyondCanvas.configuration.preinstalled
19
+ preinstall
20
+ end
21
+ end
22
+
23
+ def install
24
+ @shop = Shop.create_with(shop_params).create_or_find_by(beyond_api_url: params[:shop][:api_url])
25
+
26
+ @shop.assign_attributes(shop_params)
27
+
28
+ if @shop.save
29
+ @shop.authenticate(params[:shop][:code])
30
+
31
+ redirect_to after_installation_path
32
+ else
33
+ render :new
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def shop_params
40
+ beyond_canvas_parameter_sanitizer.sanitize
41
+ end
42
+
43
+ def after_preinstallation_path
44
+ params[:return_url]
45
+ end
46
+
47
+ def after_installation_path
48
+ params[:shop][:return_url]
49
+ end
50
+
51
+ def after_sign_in_path
52
+ BeyondCanvas.configuration.open_app_url
53
+ end
54
+
55
+ def preinstall
56
+ @shop = Shop.create_or_find_by(beyond_api_url: params[:api_url])
57
+ @shop.authenticate(params[:code])
58
+
59
+ redirect_to after_preinstallation_path
60
+ end
61
+
62
+ def open_app(shop)
63
+ reset_session
64
+ log_in shop
65
+
66
+ redirect_to after_sign_in_path
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BeyondCanvas
4
+ module Authentication # :nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def beyond_canvas_parameter_sanitizer
10
+ @beyond_canvas_parameter_sanitizer ||= BeyondCanvas::ParameterSanitizer.new(:shop, params)
11
+ end
12
+ end
13
+ end
@@ -15,16 +15,13 @@ module BeyondCanvas
15
15
  # browser compatible locale, sets the value to the cookie and set that locale as default locale.
16
16
  #
17
17
  def switch_locale(&action)
18
- # NOTE: Check the HTTP_ACCEPT_LANGUAGE header to identify if the request comes from a browser or a server
19
- return I18n.with_locale(I18n.default_locale, &action) if request.headers['HTTP_ACCEPT_LANGUAGE'].blank?
20
-
21
18
  unless valid_locale?(cookies[:locale])
22
19
  cookies[:locale] = { value: browser_compatible_locale, expires: 1.day.from_now }
23
20
  end
24
21
 
25
22
  I18n.with_locale(cookies[:locale], &action)
26
23
 
27
- logger.debug "[BeyondCanvas] Locale set to: #{cookies[:locale]}".yellow
24
+ logger.debug "[BeyondCanvas] Locale set to: #{cookies[:locale]}".yellow if debug_mode?
28
25
  end
29
26
 
30
27
  #
@@ -36,12 +33,16 @@ module BeyondCanvas
36
33
  # +I18n.default_locale+. (e.g. +'en-GB'+)
37
34
  #
38
35
  def browser_compatible_locale
36
+ return I18n.default_locale if request.headers['HTTP_ACCEPT_LANGUAGE'].blank?
37
+
39
38
  browser_locales = HTTP::Accept::Languages.parse(request.headers['HTTP_ACCEPT_LANGUAGE'])
40
39
  available_locales = HTTP::Accept::Languages::Locales.new(I18n.available_locales.map(&:to_s))
41
40
 
42
41
  locales = available_locales & browser_locales
43
42
 
44
43
  locales.empty? ? I18n.default_locale : locales.first
44
+ rescue
45
+ I18n.default_locale
45
46
  end
46
47
 
47
48
  #
@@ -31,7 +31,7 @@ module BeyondCanvas
31
31
  def valid_signature?(signature, data, secret)
32
32
  digest = OpenSSL::Digest.new('SHA1')
33
33
  hmac = OpenSSL::HMAC.digest(digest, secret, data)
34
- signature == Base64.encode64(hmac).chop
34
+ URI.decode(signature) == Base64.encode64(hmac).chop
35
35
  end
36
36
  end
37
37
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BeyondCanvas
4
+ module AuthenticationsHelper # :nodoc:
5
+ #
6
+ # Logs in the given shop
7
+ #
8
+ def log_in(shop)
9
+ session[:shop_id] = shop.id
10
+ end
11
+
12
+ #
13
+ # Returns the current logged-in shop (if any)
14
+ #
15
+ def current_shop
16
+ if session[:shop_id]
17
+ @current_shop ||= Shop.find_by(id: session[:shop_id])
18
+ end
19
+ end
20
+
21
+ #
22
+ # Returns true if the shop is logged in, false otherwise
23
+ #
24
+ def logged_in?
25
+ !current_shop.nil?
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BeyondCanvas
4
+ module CockpitAppHelper # :nodoc:
5
+ def is_cockpit_app?
6
+ BeyondCanvas.configuration.cockpit_app == true
7
+ end
8
+
9
+ def action_bar_content?
10
+ content_for?(:action_bar_left) || content_for?(:action_bar_right)
11
+ end
12
+
13
+ def menu_content?
14
+ BeyondCanvas.configuration.menu_items.any?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BeyondCanvas
4
+ module DebugHelper # :nodoc:
5
+ def debug_mode?
6
+ Rails.env.development? && BeyondCanvas.configuration.debug_mode
7
+ end
8
+ end
9
+ end
@@ -2,11 +2,15 @@
2
2
 
3
3
  module BeyondCanvas
4
4
  module LocaleSwitchHelper # :nodoc:
5
+ def show_locale_switch?
6
+ defined?(I18n) && I18n.available_locales.count > 1
7
+ end
8
+
5
9
  def translate_locale(locale)
6
10
  if I18n.exists?("locales.#{locale}")
7
11
  I18n.t("locales.#{locale}")
8
12
  else
9
- logger.debug "[BeyondCanvas] Missing translation: #{I18n.locale}.locales.#{locale}".yellow
13
+ logger.debug "[BeyondCanvas] Missing translation: #{I18n.locale}.locales.#{locale}".yellow if debug_mode?
10
14
  locale
11
15
  end
12
16
  end
@@ -1,3 +1,6 @@
1
+ import 'jquery';
2
+
1
3
  import './initializers/buttons';
2
4
  import './initializers/flash';
3
5
  import './initializers/inputs';
6
+ import './initializers/modals';
@@ -1,8 +1,11 @@
1
- import { disableActionElements, enableActionElements, hideSpinner, showSpinner } from './functions';
1
+ const SPINNER_ANIMATION_TIMEOUT = 125;
2
+ const BUTTON_SELECTORS = '[class^="button"]';
2
3
 
3
4
  (function ($) {
4
- const onDOMReady = function () {
5
- const inputs = $('input, textarea, select').not(':input[type=button], :input[type=submit], :input[type=reset]');
5
+ const onDOMReady = function (e) {
6
+ const inputs = $('input, textarea, select').not(
7
+ ':input[type=button], :input[type=submit], :input[type=reset]'
8
+ );
6
9
 
7
10
  inputs.each(function () {
8
11
  var input = $(this);
@@ -11,12 +14,11 @@ import { disableActionElements, enableActionElements, hideSpinner, showSpinner }
11
14
  if ($(input).is(':hidden')) {
12
15
  e.preventDefault();
13
16
  }
14
- hideSpinner();
15
- enableActionElements();
17
+ $.restoreActionElements();
16
18
  });
17
19
  });
18
20
 
19
- $('button[class^="button"]').each(function () {
21
+ $(BUTTON_SELECTORS).each(function () {
20
22
  var button = $(this);
21
23
 
22
24
  // Add width attribute and save old width
@@ -24,31 +26,75 @@ import { disableActionElements, enableActionElements, hideSpinner, showSpinner }
24
26
  button.data('oldWidth', button.width());
25
27
 
26
28
  // Add the spinner
27
- button.prepend(`
28
- <div class="spinner">
29
- <div class="bounce1"></div>
30
- <div class="bounce2"></div>
31
- <div class="bounce3"></div>
32
- </div>`);
29
+ if (button.find('.spinner').length == 0) {
30
+ button.prepend(`
31
+ <div class="spinner">
32
+ <div class="bounce1"></div>
33
+ <div class="bounce2"></div>
34
+ <div class="bounce3"></div>
35
+ </div>`);
36
+ }
33
37
 
34
38
  // Bind ajax:success and ajax:error to the form the button belongs to
35
39
  button
36
40
  .closest('form')
37
41
  .on('ajax:success', function () {
38
- hideSpinner();
39
- enableActionElements();
42
+ $.restoreActionElements();
40
43
  })
41
44
  .on('ajax:error', function () {
42
- hideSpinner();
43
- enableActionElements();
45
+ $.restoreActionElements();
44
46
  });
45
47
  });
46
48
  };
47
49
 
48
- $(document).on('click', '[class^="button"]', function () {
49
- disableActionElements();
50
- showSpinner($(this));
50
+ $(document).on('confirm:complete', function () {
51
+ $.restoreActionElements();
52
+ });
53
+
54
+ $(document).on('click', BUTTON_SELECTORS, function () {
55
+ $.disableActionElements();
56
+ $(this).showSpinner();
51
57
  });
52
58
 
53
59
  $(document).on('ready page:load turbolinks:load', onDOMReady);
54
60
  })(jQuery);
61
+
62
+ $.extend({
63
+ restoreActionElements: function () {
64
+ // Hide spinners
65
+ $(BUTTON_SELECTORS).each(function (_, button) {
66
+ setTimeout(function () {
67
+ // Hide the spinner
68
+ $(button).find('.spinner').hide();
69
+ // Adjust the width of the button
70
+ $(button).width($(button).data('oldWidth'));
71
+ }, SPINNER_ANIMATION_TIMEOUT);
72
+ });
73
+
74
+ // Enable action elements
75
+ $('a, input[type="submit"], input[type="button"], input[type="reset"], button').each(function () {
76
+ $(this).removeClass('actions--disabled');
77
+ });
78
+ },
79
+ disableActionElements: function () {
80
+ $('a, input[type="submit"], input[type="button"], input[type="reset"], button').each(function () {
81
+ $(this).addClass('actions--disabled');
82
+ });
83
+ }
84
+ });
85
+
86
+ $.fn.extend({
87
+ showSpinner: function () {
88
+ var button = $(this);
89
+
90
+ // Adjust the width of the button
91
+ button.width(
92
+ button.width() + $('.spinner').outerWidth(true)
93
+ );
94
+
95
+ // Show the spinner
96
+ setTimeout(function () {
97
+ button.find('.spinner').css('display', 'inline-flex');
98
+ }, SPINNER_ANIMATION_TIMEOUT);
99
+ }
100
+ });
@@ -1,5 +1,3 @@
1
- import { closeAlert } from './functions';
2
-
3
1
  (function ($) {
4
2
  const onDOMReady = function () {
5
3
  $('.flash').each(function () {
@@ -17,3 +15,12 @@ import { closeAlert } from './functions';
17
15
 
18
16
  $(document).on('ready page:load turbolinks:load', onDOMReady);
19
17
  })(jQuery);
18
+
19
+ function closeAlert() {
20
+ $('.flash')
21
+ .removeClass('flash--shown')
22
+ .delay(700)
23
+ .queue(function () {
24
+ $(this).remove();
25
+ });
26
+ }
@@ -9,7 +9,10 @@
9
9
  var fileName = '';
10
10
 
11
11
  if (this.files && this.files.length > 1)
12
- fileName = (this.getAttribute('data-multiple-caption') || '').replace('{count}', this.files.length);
12
+ fileName = (this.getAttribute('data-multiple-caption') || '').replace(
13
+ '{count}',
14
+ this.files.length
15
+ );
13
16
  else if (e.target.value) fileName = e.target.value.split('\\').pop();
14
17
 
15
18
  if (fileName)
@@ -0,0 +1,14 @@
1
+ $.extend({
2
+ displayModal: function (content, options = {}) {
3
+ $('#modal').find('#modal__content').html(content);
4
+ $('#modal').css('display', 'flex');
5
+ $.restoreActionElements();
6
+ $(document).trigger('modal:opened', options['extraEventParameters']);
7
+ },
8
+ closeModal: function () {
9
+ $('#modal').find('#modal__content').empty();
10
+ $('#modal').css('display', 'none');
11
+ $.restoreActionElements();
12
+ $(document).trigger('modal:closed');
13
+ }
14
+ });
@@ -0,0 +1,27 @@
1
+ <% app_name = BeyondCanvas.configuration.site_title %>
2
+ <% i18n_scope = 'beyond_canvas.authentications.new' %>
3
+
4
+ <div class="card card--padding">
5
+
6
+ <%= form_with(url: callback_path, method: :post, scope: :shop, model: @shop, local: true, builder: BeyondCanvas::FormBuilder) do |f| %>
7
+
8
+ <h2 class="card__headline"><%= I18n.t('headline', app_name: app_name, scope: i18n_scope) %></h2>
9
+
10
+ <p class="margin--bottom"><%= I18n.t('body', app_name: app_name, scope: i18n_scope) %></p>
11
+
12
+ <%# Don't delete or modify any ot these hidden fields. They are used on shop identification and authentication %>
13
+
14
+ <%= f.hidden_field :code, value: params[:code] || @shop.code %>
15
+ <%= f.hidden_field :signature, value: params[:signature] || @shop.signature %>
16
+ <%= f.hidden_field :return_url, value: params[:return_url] || @shop.return_url %>
17
+ <%= f.hidden_field :api_url, value: params[:api_url] || @shop.api_url %>
18
+ <%= f.hidden_field :access_token_url, value: params[:access_token_url] || @shop.access_token_url %>
19
+
20
+ <%# Add HERE your custom shop fields %>
21
+
22
+ <div class="form__actions--spaced">
23
+ <%= f.button I18n.t('actions.install', scope: i18n_scope), type: :submit, class: 'button__solid--primary' %>
24
+ </div>
25
+
26
+ <% end %>
27
+ </div>
@@ -0,0 +1,15 @@
1
+ <% if action_bar_content? %>
2
+
3
+ <div class="action_bar<% if menu_content? %> action_bar--menu<% end %>">
4
+
5
+ <div class="action_bar--left">
6
+ <%= yield :action_bar_left %>
7
+ </div>
8
+
9
+ <div class="action_bar--right">
10
+ <%= yield :action_bar_right %>
11
+ </div>
12
+
13
+ </div>
14
+
15
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <% if is_cockpit_app? %>
2
+
3
+ <div class='breadcrumbs'>
4
+
5
+ <%= inline_svg_tag 'icons/home.svg' %>
6
+
7
+ <% breadcrumb_trail do |crumb| %>
8
+ <%= link_to crumb.name, crumb.url, class: "breadcrumb__item#{ '--current' if crumb.current? }" %>
9
+ <% unless crumb.current? %><%= inline_svg_tag 'icons/arrow_right.svg' %><% end %>
10
+ <% end %>
11
+
12
+ </div>
13
+
14
+ <% end %>