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.
- checksums.yaml +4 -4
- data/README.md +0 -6
- data/Rakefile +3 -3
- data/app/assets/config/beyond_canvas_manifest.js +1 -0
- data/app/assets/images/icons/arrow_right.svg +1 -0
- data/app/assets/images/icons/close.svg +1 -0
- data/app/assets/images/icons/home.svg +1 -0
- data/app/assets/javascripts/beyond_canvas/base.js +120 -246
- data/app/assets/stylesheets/beyond_canvas/base.scss +8 -1
- data/app/assets/stylesheets/beyond_canvas/components/_action_bar.scss +28 -0
- data/app/assets/stylesheets/beyond_canvas/components/_breadcrumbs.scss +29 -0
- data/app/assets/stylesheets/beyond_canvas/components/_buttons.scss +1 -1
- data/app/assets/stylesheets/beyond_canvas/components/_debug.scss +10 -0
- data/app/assets/stylesheets/beyond_canvas/components/_forms.scss +11 -14
- data/app/assets/stylesheets/beyond_canvas/components/_main.scss +12 -13
- data/app/assets/stylesheets/beyond_canvas/components/_margins.scss +8 -0
- data/app/assets/stylesheets/beyond_canvas/components/_menu.scss +40 -0
- data/app/assets/stylesheets/beyond_canvas/components/_modals.scss +27 -0
- data/app/assets/stylesheets/beyond_canvas/components/_sidebar.scss +53 -0
- data/app/assets/stylesheets/beyond_canvas/components/_titles.scss +7 -0
- data/app/assets/stylesheets/beyond_canvas/settings/_breakpoints.scss +6 -6
- data/app/assets/stylesheets/beyond_canvas/settings/_variables.scss +77 -22
- data/app/controllers/beyond_canvas/application_controller.rb +2 -0
- data/app/controllers/beyond_canvas/authentications_controller.rb +69 -0
- data/app/controllers/concerns/beyond_canvas/authentication.rb +13 -0
- data/app/controllers/concerns/beyond_canvas/locale_management.rb +5 -4
- data/app/controllers/concerns/beyond_canvas/request_validation.rb +1 -1
- data/app/helpers/beyond_canvas/authentications_helper.rb +28 -0
- data/app/helpers/beyond_canvas/cockpit_app_helper.rb +17 -0
- data/app/helpers/beyond_canvas/debug_helper.rb +9 -0
- data/app/helpers/beyond_canvas/locale_switch_helper.rb +5 -1
- data/app/javascript/beyond_canvas/base.js +3 -0
- data/app/javascript/beyond_canvas/initializers/buttons.js +65 -19
- data/app/javascript/beyond_canvas/initializers/flash.js +9 -2
- data/app/javascript/beyond_canvas/initializers/inputs.js +4 -1
- data/app/javascript/beyond_canvas/initializers/modals.js +14 -0
- data/app/views/beyond_canvas/authentications/new.html.erb +27 -0
- data/app/views/beyond_canvas/shared/_action_bar.html.erb +15 -0
- data/app/views/beyond_canvas/shared/_breadcrumbs.html.erb +14 -0
- data/app/views/beyond_canvas/shared/_flash.html.erb +22 -12
- data/app/views/beyond_canvas/shared/_locales.html.erb +8 -0
- data/app/views/beyond_canvas/shared/_menu.html.erb +31 -0
- data/app/views/beyond_canvas/shared/_modal.html.erb +6 -0
- data/app/views/beyond_canvas/shared/_sidebar.html.erb +16 -0
- data/app/views/layouts/beyond_canvas/application.html.erb +31 -0
- data/app/views/layouts/beyond_canvas/public.html.erb +10 -1
- data/config/locales/en.yml +9 -0
- data/config/routes.rb +8 -1
- data/lib/beyond_canvas.rb +18 -2
- data/lib/beyond_canvas/configuration.rb +11 -3
- data/lib/beyond_canvas/engine.rb +6 -0
- data/lib/beyond_canvas/menu_item_registration.rb +19 -0
- data/lib/beyond_canvas/parameter_sanitizer.rb +43 -0
- data/lib/beyond_canvas/rails/routes.rb +22 -0
- data/lib/beyond_canvas/version.rb +1 -1
- data/lib/generators/beyond_canvas/controller/controller_generator.rb +19 -0
- data/lib/generators/beyond_canvas/controller/templates/controller.erb +20 -0
- data/lib/generators/beyond_canvas/custom_menu/custom_menu_generator.rb +13 -0
- data/lib/generators/beyond_canvas/custom_menu/templates/beyond_canvas_custom_menu.html.erb +32 -0
- data/lib/generators/beyond_canvas/custom_styles/custom_styles_generator.rb +1 -1
- data/lib/generators/beyond_canvas/custom_styles/templates/beyond_canvas_custom_styles.scss +199 -0
- data/lib/generators/beyond_canvas/install/install_generator.rb +13 -5
- data/lib/generators/beyond_canvas/install/templates/beyond_canvas.rb.erb +55 -6
- data/lib/generators/beyond_canvas/model/model_generator.rb +49 -0
- data/lib/generators/beyond_canvas/model/templates/migration.erb +18 -0
- data/lib/generators/beyond_canvas/model/templates/model.erb +5 -0
- data/lib/generators/beyond_canvas/views/views_generator.rb +17 -0
- data/lib/models/concerns/authentication.rb +57 -0
- data/lib/models/concerns/utils.rb +79 -0
- data/lib/models/shop.rb +12 -0
- metadata +95 -12
- data/app/javascript/beyond_canvas/initializers/functions.js +0 -41
- data/app/views/beyond_canvas/locales/_edit.html.erb +0 -8
- data/lib/generators/beyond_canvas/custom_styles/templates/beyond_canvas_custom_styles.sass +0 -123
@@ -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
|
@@ -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,8 +1,11 @@
|
|
1
|
-
|
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(
|
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
|
-
|
15
|
-
enableActionElements();
|
17
|
+
$.restoreActionElements();
|
16
18
|
});
|
17
19
|
});
|
18
20
|
|
19
|
-
$(
|
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.
|
28
|
-
|
29
|
-
<div class="
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
39
|
-
enableActionElements();
|
42
|
+
$.restoreActionElements();
|
40
43
|
})
|
41
44
|
.on('ajax:error', function () {
|
42
|
-
|
43
|
-
enableActionElements();
|
45
|
+
$.restoreActionElements();
|
44
46
|
});
|
45
47
|
});
|
46
48
|
};
|
47
49
|
|
48
|
-
$(document).on('
|
49
|
-
|
50
|
-
|
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(
|
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 %>
|