tb_core 1.4.3.1 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -54
  3. data/app/assets/javascripts/admin/core/application.js +8 -4
  4. data/app/assets/javascripts/admin/core/dashboard.js +2 -3
  5. data/app/assets/javascripts/admin/core/date_picker.js +2 -2
  6. data/app/assets/javascripts/admin/core/editor.js +2 -2
  7. data/app/assets/javascripts/admin/core/modal.js +1 -1
  8. data/app/assets/javascripts/admin/core/users.js +5 -5
  9. data/app/assets/javascripts/tb_core.js +8 -0
  10. data/app/assets/libs/sortable/sortable.js +152 -91
  11. data/app/controllers/admin/application_controller.rb +1 -2
  12. data/app/controllers/admin/dashboard_controller.rb +2 -2
  13. data/app/controllers/admin/settings_controller.rb +2 -2
  14. data/app/controllers/admin/users_controller.rb +4 -7
  15. data/app/controllers/concerns/tb_core/error_handling.rb +16 -7
  16. data/app/controllers/concerns/tb_core/user_authentication.rb +5 -5
  17. data/app/controllers/tb_core/application_controller.rb +1 -1
  18. data/app/controllers/user_sessions_controller.rb +2 -2
  19. data/app/helpers/tb_core/application_helper.rb +4 -36
  20. data/app/mailers/tb_core_mailer.rb +3 -3
  21. data/app/models/concerns/tb_core/user_model.rb +131 -0
  22. data/app/models/spud/spud_user_model.rb +6 -138
  23. data/app/models/spud_permission.rb +4 -4
  24. data/app/models/spud_role.rb +1 -1
  25. data/app/models/spud_user.rb +2 -1
  26. data/app/views/admin/dashboard/index.html.erb +1 -1
  27. data/app/views/admin/settings/_form.html.erb +1 -1
  28. data/app/views/admin/settings/edit.html.erb +1 -1
  29. data/app/views/admin/setup/new.html.erb +1 -1
  30. data/app/views/admin/users/_form.html.erb +1 -1
  31. data/app/views/admin/users/index.html.erb +2 -2
  32. data/app/views/admin/users/show.html.erb +1 -1
  33. data/app/views/layouts/admin/application.html.erb +6 -6
  34. data/app/views/layouts/admin/login.html.erb +1 -1
  35. data/app/views/tb_core_mailer/forgot_password_notification.html.erb +2 -2
  36. data/app/views/tb_core_mailer/user_credentials.html.erb +2 -2
  37. data/app/views/user_sessions/_form.html.erb +1 -1
  38. data/app/views/user_sessions/new.html.erb +1 -1
  39. data/lib/generators/spud/module_generator.rb +7 -7
  40. data/lib/generators/spud/setup_generator.rb +6 -7
  41. data/lib/generators/spud/templates/assets/admin/application.js +1 -1
  42. data/lib/generators/spud/templates/assets/application.js +3 -4
  43. data/lib/generators/spud/templates/controller_spec.rb.erb +5 -5
  44. data/lib/generators/spud/templates/views/admin/_form.html.erb +1 -1
  45. data/lib/generators/spud/templates/views/admin/index.html.erb +1 -1
  46. data/lib/generators/spud/templates/views/admin/show.html.erb +1 -1
  47. data/lib/generators/spud/templates/views/frontend/index.html.erb +1 -1
  48. data/lib/generators/spud/templates/views/frontend/show.html.erb +1 -1
  49. data/lib/tb_core.rb +3 -5
  50. data/lib/tb_core/belongs_to_app.rb +47 -46
  51. data/lib/tb_core/catch_all_route.rb +3 -0
  52. data/lib/tb_core/configuration.rb +28 -0
  53. data/lib/tb_core/engine.rb +61 -0
  54. data/lib/tb_core/errors.rb +50 -0
  55. data/lib/tb_core/responder.rb +0 -26
  56. data/lib/{spud_core → tb_core}/searchable.rb +4 -1
  57. data/lib/tb_core/spud_core.rb +23 -0
  58. data/lib/{spud_core → tb_core}/test_files.rb +0 -0
  59. data/lib/tb_core/test_helper.rb +3 -3
  60. data/lib/tb_core/version.rb +3 -0
  61. data/spec/controllers/admin/application_controller_spec.rb +5 -5
  62. data/spec/controllers/admin/dashboard_controller_spec.rb +5 -5
  63. data/spec/controllers/admin/password_reset_controller_spec.rb +1 -1
  64. data/spec/controllers/admin/settings_controller_spec.rb +1 -1
  65. data/spec/controllers/admin/setup_controller_spec.rb +2 -2
  66. data/spec/controllers/admin/user_sessions_controller_spec.rb +2 -2
  67. data/spec/controllers/admin/users_controller_spec.rb +17 -17
  68. data/spec/dummy/app/assets/javascripts/admin/application.js +1 -1
  69. data/spec/dummy/app/assets/javascripts/application.js +1 -1
  70. data/spec/dummy/config/application.rb +1 -1
  71. data/spec/factories/spud_admin_permission_factories.rb +2 -2
  72. data/spec/factories/spud_role_factories.rb +2 -2
  73. data/spec/factories/spud_user_factories.rb +6 -6
  74. data/spec/helpers/spud/admin/application_helper_spec.rb +1 -1
  75. data/spec/helpers/tb_core/application_helper_spec.rb +0 -35
  76. data/spec/lib/spud_core/configuration_spec.rb +2 -2
  77. data/spec/lib/tb_core/belongs_to_app_spec.rb +4 -3
  78. data/spec/models/spud_role_spec.rb +2 -2
  79. data/spec/models/spud_user_spec.rb +13 -13
  80. data/spec/rails_helper.rb +1 -1
  81. metadata +59 -67
  82. data/app/assets/javascripts/tb_core.js.erb +0 -9
  83. data/app/assets/javascripts/tb_core/configuration.js.erb +0 -9
  84. data/app/assets/javascripts/tb_core/console.js +0 -12
  85. data/app/assets/javascripts/tb_core/remote.js +0 -237
  86. data/app/assets/javascripts/tb_core/utility.js +0 -55
  87. data/app/controllers/concerns/tb_core/sortable_params.rb +0 -80
  88. data/app/controllers/spud/application_controller.rb +0 -10
  89. data/lib/generators/spud/templates/assets/bootstrap.js +0 -14
  90. data/lib/spud_core/catch_all_route.rb +0 -3
  91. data/lib/spud_core/configuration.rb +0 -56
  92. data/lib/spud_core/engine.rb +0 -76
  93. data/lib/spud_core/errors.rb +0 -47
  94. data/lib/spud_core/version.rb +0 -5
  95. data/lib/tb_core/table_header.rb +0 -92
  96. data/spec/controllers/tb_core/sortable_params_spec.rb +0 -64
@@ -1,9 +0,0 @@
1
- //= require_self
2
- //= require_directory ./tb_core
3
-
4
- (function(){
5
- window.tb = {
6
-
7
- };
8
-
9
- })();
@@ -1,9 +0,0 @@
1
- (function(){
2
-
3
- tb.config = {
4
- env: '<%= Rails.env %>',
5
- siteName: '<%= Spud::Core.config.site_name.gsub("'"){"\\'"} %>',
6
- subUri: '<%= Rails.configuration.relative_url_root %>'
7
- };
8
-
9
- })();
@@ -1,12 +0,0 @@
1
- (function(){
2
-
3
- if(typeof(console) == 'undefined'){
4
- var noop = function(){};
5
- window.console = {
6
- log: noop,
7
- warn: noop,
8
- error: noop
9
- };
10
- }
11
-
12
- })();
@@ -1,237 +0,0 @@
1
- //= require rails-ujs
2
- //= require_self
3
-
4
- (function(){
5
-
6
- /*
7
- * jQuery UJS Handlers
8
- *
9
- * These handlers are designed to work with the Rails/jQuery UJS adapter. This applies
10
- * to things like data-remote forms, data-method links, etc.
11
- *
12
- * For more info see the following lnks:
13
- * - https://github.com/rails/jquery-ujs/wiki/ajax
14
- * - http://guides.rubyonrails.org/working_with_javascript_in_rails.html
15
- *
16
- */
17
- tb.remote = {
18
- init: function(){
19
- Rails.delegate(document, 'form', 'ajax:before', onRemoteFormBefore);
20
- Rails.delegate(document, 'form[data-success], a[data-success]', 'ajax:success', onRemoteFormSuccess);
21
- Rails.delegate(document, 'form[data-errors]', 'ajax:error', onRemoteFormErrors);
22
- Rails.delegate(document, 'tr a[data-method=delete][data-remote=true]', 'ajax:success', onRemoteDeleteTableRow);
23
- }
24
- };
25
-
26
- /*
27
- * UJS likes to request application/javascript by default, but Twice Baked prefers JSON.
28
- */
29
- var onRemoteFormBefore = function(event){
30
- if(this.getAttribute('data-type') === null){
31
- this.setAttribute('data-type', 'json');
32
- }
33
- };
34
-
35
- /*
36
- * Called when a remote form is submitted with a data-success configured
37
- * Simply redirects to the configured path, or reloads the page if "reload" is passed
38
- *
39
- * ie:
40
- * <form action="..." data-remote="true" data-success="/path/for/success/:id">
41
- */
42
- var onRemoteFormSuccess = function(event){
43
- var successPath = this.getAttribute('data-success');
44
- if(successPath === 'reload'){
45
- window.location.reload(true);
46
- }
47
- else{
48
- var json = event.detail[0];
49
- window.location = buildSuccessPath(successPath, json);
50
- }
51
- };
52
-
53
- /*
54
- * Build a path for redirection.
55
- *
56
- * - Adds a timestamp GET param for cache busting
57
- * - Inserts an ID attribute if necessary
58
- *
59
- */
60
- var buildSuccessPath = function(path, json){
61
- var timestamp = (new Date()).valueOf();
62
- if(path.indexOf('?') > -1){
63
- path += ('&t=' + timestamp);
64
- }
65
- else{
66
- path += ('?t=' + timestamp);
67
- }
68
- if(json && json.id){
69
- path = path.replace(':id', json.id);
70
- }
71
- return path;
72
- };
73
-
74
- /*
75
- * Called when a remote form is submitted and the server has returned an error, only
76
- * if the form has a data-errors attribute
77
- *
78
- * ie:
79
- * <form action="..." data-remote="true" data-errors="inline">
80
- * - OR -
81
- * <form action="..." data-remote="true" data-errors="alert">
82
- */
83
- var onRemoteFormErrors = function(event){
84
- var json = event.detail[0];
85
- var xhr = event.detail[2];
86
- var form = this;
87
- var errorType = form.getAttribute('data-errors');
88
-
89
- if(xhr.status === 422){
90
- if(errorType === 'inline'){
91
- tb.remote.displayErrorsInline(form, json);
92
- }
93
- else{
94
- tb.remote.displayErrorsAlert(json);
95
- }
96
- }
97
- else if(xhr.status === 401 || xhr.status === 403){
98
- window.alert('Error: Access denied.');
99
- }
100
- else if(json.location) {
101
- window.location = json.location;
102
- }
103
- else{
104
- var errorThrown = event.detail[1];
105
- window.alert('An unexpected error occurred: ' + errorThrown);
106
- }
107
- };
108
-
109
- function removeElement(element) {
110
- element.parentElement.removeChild(element);
111
- }
112
-
113
- function createErrorParagraph(text, base) {
114
- var p = document.createElement('p');
115
- p.classList.add('form-error');
116
- p.classList.add(base ? 'form-error-base' : 'form-error-inline');
117
- p.innerText = text;
118
- return p;
119
- }
120
-
121
- function querySelectorArray(parent, selector) {
122
- var query = parent.querySelectorAll(selector);
123
- var array = Array.prototype.slice.call(query);
124
- return array;
125
- }
126
-
127
- /*
128
- * Take a single error message and format it as if it were
129
- * a json error coming from the server
130
- */
131
- function formatStringAsErrors(errorString) {
132
- return {base: {messages: [errorString] }};
133
- }
134
-
135
- /*
136
- * Remove inline error messages
137
- */
138
- tb.remote.clearErrors = function(form) {
139
- querySelectorArray(form, '.form-error-inline, .form-error-base').forEach(removeElement);
140
- };
141
-
142
- /*
143
- * Append error text to form input fields and scroll to the first error
144
- */
145
- tb.remote.displayErrorsInline = function(form, errors){
146
- if(typeof errors === 'string') {
147
- errors = formatStringAsErrors(errors);
148
- }
149
-
150
- tb.remote.clearErrors(form);
151
-
152
- for(var key in errors){
153
- var messages = errors[key].messages;
154
- if(key === 'base'){
155
- for(var i=0; i<messages.length; i++){
156
- var p = createErrorParagraph(messages[i], true);
157
- form.insertBefore(p, form.firstChild);
158
- }
159
- }
160
- else{
161
- var inputName, input;
162
-
163
- keyArr = key.split('.');
164
-
165
- if(keyArr.length === 2){
166
- keyArr[0] += '_attributes';
167
- input = form.querySelector("[name$='[" + keyArr.join('][') + "]']");
168
- }
169
- else{
170
- var array = querySelectorArray(form, "[name$='[" + key + "]']");
171
- input = array.filter(function(input){
172
- return !input.name.match(/\]\[/); // Filter out nested attribute inputs ie model[a][b]
173
- })[0];
174
- if(input === null && !key.match(/_id$/)){
175
- input = form.querySelector("[name$='[" + key + "_id]']");
176
- }
177
- }
178
-
179
- var message = messages[0];
180
- if(input){
181
- input.parentNode.appendChild(createErrorParagraph(message));
182
- }
183
- else{
184
- console.warn('Missing input field for key:', key);
185
- form.insertBefore(
186
- createErrorParagraph(key+' '+message), form.firstChild
187
- );
188
- }
189
- }
190
- }
191
-
192
- var firstError = form.querySelector('.form-error-inline, .form-error-base')
193
- if(firstError &&
194
- !tb.util.elementIsInView(firstError) &&
195
- typeof(firstError.scrollIntoView) === 'function'){
196
- firstError.parentElement.scrollIntoView();
197
- }
198
- };
199
-
200
- /*
201
- * Display errors in a standard window.alert dialog
202
- */
203
- tb.remote.displayErrorsAlert = function(errors){
204
- if(typeof errors === 'string') {
205
- errors = formatStringAsErrors(errors);
206
- }
207
- var text = "Please correct the following errors:\n";
208
- var label, message;
209
- var index = 0;
210
- for(var key in errors){
211
- label = errors[key].label;
212
- message = errors[key].messages[0];
213
- if(key === 'base'){
214
- text += ' - ' + message + "\n"
215
- }
216
- else{
217
- text += ' - ' + label + ' ' + message + "\n"
218
- }
219
- index++;
220
- }
221
- window.alert(text);
222
- };
223
-
224
- /*
225
- * Used for deleting table rows
226
- * Add a link with data-remote=true and data-method=delete to your table to trigger this callback
227
- */
228
- var onRemoteDeleteTableRow = function(){
229
- var row = $(this).parents('tr');
230
- row.fadeOut(200, function(){
231
- row.remove();
232
- });
233
- };
234
-
235
- tb.remote.init();
236
-
237
- })();
@@ -1,55 +0,0 @@
1
- (function(){
2
-
3
- tb.util = {
4
- domainWithPort: function(){
5
- if(window.location.origin){
6
- return window.location.origin;
7
- }
8
- else{
9
- return location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
10
- }
11
- },
12
-
13
- pathFor: function(){
14
- var args = Array.prototype.slice.call(arguments, 0);
15
- var parts = [''];
16
- if(tb.config.subUri){
17
- parts.push(tb.config.subUri);
18
- }
19
- return parts.concat(args).join('/');
20
- },
21
-
22
- urlFor: function(){
23
- var args = Array.prototype.slice.call(arguments, 0);
24
- var parts = [tb.util.domainWithPort()];
25
- if(tb.config.subUri){
26
- parts.push(tb.config.subUri);
27
- }
28
- return parts.concat(args).join('/');
29
- },
30
-
31
- getCsrfToken: function(){
32
- console.warn('Call to tb.util.getCsrfToken() will be removed in the future; Use Rails.csrfToken() instead.')
33
- return Rails.csrfToken();
34
- },
35
-
36
- /*
37
- * Use top/bottom offsets to determine if the given jQuery element is within the current scroll view
38
- *
39
- * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
40
- */
41
- elementIsInView: function(el){
42
- if (typeof jQuery === "function" && el instanceof jQuery) {
43
- el = el[0];
44
- }
45
- var rect = el.getBoundingClientRect();
46
- return (
47
- rect.top >= 0 &&
48
- rect.left >= 0 &&
49
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
50
- rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
51
- );
52
- }
53
- };
54
-
55
- })();
@@ -1,80 +0,0 @@
1
- module TbCore
2
- module SortableParams
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
6
- attr_accessor :default_sort_attribute
7
- attr_accessor :sortable_mapping
8
-
9
- # Define attributes you wish to sort by
10
- #
11
- # This optional configuration method tells the the sortable
12
- # helpers how they should map attributes to queries. For
13
- # example a "name" sort might need to sort on first and last
14
- # name attributes
15
- #
16
- # The `default` argument, if passed, will determine the
17
- # attribute used when no sort param has been passed
18
- #
19
- # Usage:
20
- #
21
- # sortable_by :email, name: [:last_name, :first_name]
22
- # default: :email,
23
- #
24
- #
25
- def sortable_by(*attributes, **options)
26
- @default_sort_attribute = options.delete(:default)
27
- @sortable_mapping = options.merge(attributes.map { |att| [att, att] }.to_h)
28
- end
29
- end
30
-
31
- # Return the current sort direction
32
- #
33
- # Defaults to :asc for all values other than "desc"
34
- #
35
- def sort_direction
36
- if params[:dir].present? && params[:dir] == 'desc'
37
- :desc
38
- else
39
- :asc
40
- end
41
- end
42
-
43
- # Return the current sort param
44
- #
45
- def sort_attribute
46
- params[:sort].try(:to_sym) || self.class.default_sort_attribute
47
- end
48
-
49
- # Return a hash that can be used in an ActiveRecord order query
50
- #
51
- # Example:
52
- # MyModel.where(...).order(sortable_query).paginate(...)
53
- #
54
- def sortable_query
55
- return unless sort_attribute
56
- normalize_sort_value(
57
- sort_attribute,
58
- sort_direction
59
- )
60
- end
61
-
62
- # Translate the sort_by and direction into a sortable hash
63
- #
64
- def normalize_sort_value(sort_by, direction)
65
- mapping = self.class.sortable_mapping[sort_by]
66
- if mapping.is_a? Symbol
67
- { mapping => sort_direction }
68
- elsif mapping.is_a? String
69
- mapping.gsub(':dir', sort_direction.to_s)
70
- elsif mapping.is_a? Array
71
- mapping.map { |att| [att, direction] }.to_h
72
- elsif mapping.is_a? Proc
73
- mapping.call(direction)
74
- else
75
- logger.debug("WARNING: Sort attribute '#{sort_by}' is not recognized. Did you mean to pass it to sortable_by?")
76
- nil
77
- end
78
- end
79
- end
80
- end
@@ -1,10 +0,0 @@
1
- class Spud::ApplicationController < TbCore::ApplicationController
2
- def initialize
3
- ActiveSupport::Deprecation.warn(
4
- 'Spud::ApplicationController is deprecated and may be removed from future releases,
5
- use TbCore::ApplicationController instead.',
6
- caller
7
- )
8
- super
9
- end
10
- end
@@ -1,14 +0,0 @@
1
- // Use this file to comment out any Bootstrap JavaScript files you are not using
2
- //
3
- //= require bootstrap/affix
4
- //= require bootstrap/alert
5
- //= require bootstrap/button
6
- //= require bootstrap/carousel
7
- //= require bootstrap/collapse
8
- //= require bootstrap/dropdown
9
- //= require bootstrap/modal
10
- //= require bootstrap/scrollspy
11
- //= require bootstrap/tab
12
- //= require bootstrap/transition
13
- //= require bootstrap/tooltip
14
- //= require bootstrap/popover
@@ -1,3 +0,0 @@
1
- Rails.application.routes.draw do
2
- get '*any' => 'spud/application#not_found'
3
- end
@@ -1,56 +0,0 @@
1
- module Spud
2
- module Core
3
- include ActiveSupport::Configurable
4
- config_accessor :site_name, :admin_applications, :from_address, :site_id, :short_name,
5
- :javascripts, :stylesheets, :admin_javascripts, :admin_stylesheets,
6
- :permissions, :production_alert_domain, :use_email_as_login
7
- self.admin_applications = []
8
- self.site_name = 'Company Name'
9
- self.site_id = 0
10
- self.short_name = 'default'
11
- self.javascripts = []
12
- self.stylesheets = []
13
- self.from_address = 'no-reply@companyname.com'
14
- self.permissions = []
15
- self.admin_javascripts = ['admin/core/application', 'admin/application']
16
- self.admin_stylesheets = ['admin/core/application', 'admin/application']
17
- self.production_alert_domain = nil
18
- self.use_email_as_login = false
19
-
20
- def self.site_config_for_host(host)
21
- ActiveSupport::Deprecation.warn 'Spud::Core.site_config_for_host is deprecated and will be removed in the future'
22
- return default_site_config
23
- end
24
-
25
- def self.site_config_for_id(id)
26
- ActiveSupport::Deprecation.warn 'Spud::Core.site_config_for_id is deprecated and will be removed in the future'
27
- return default_site_config
28
- end
29
-
30
- def self.default_site_config
31
- ActiveSupport::Deprecation.warn 'Spud::Core.default_site_config is deprecated and will be removed in the future'
32
- return {
33
- site_id: Spud::Core.config.site_id,
34
- site_name: Spud::Core.config.site_name,
35
- short_name: Spud::Core.config.short_name
36
- }
37
- end
38
-
39
- def self.append_admin_javascripts(*args)
40
- if args[0].class == Array
41
- Spud::Core.config.admin_javascripts += args[0]
42
- else
43
- Spud::Core.config.admin_javascripts += args
44
- end
45
- end
46
-
47
- def self.append_admin_stylesheets(*args)
48
- if args[0].class == Array
49
- Spud::Core.config.admin_stylesheets += args[0]
50
- else
51
- Spud::Core.config.admin_stylesheets += args
52
- end
53
- end
54
-
55
- end
56
- end