beyond_canvas 0.15.2.pre → 0.17.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 %>