tb_core 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +101 -51
- data/app/assets/javascripts/admin/core/application.js +1 -5
- data/app/assets/javascripts/tb_core/remote.js +179 -0
- data/app/assets/libs/jquery-ui/jquery-ui.scss +16 -16
- data/app/assets/stylesheets/admin/core/{application.css.scss → application.scss} +22 -0
- data/app/assets/stylesheets/admin/core/{login.css.scss → login.scss} +0 -0
- data/app/controllers/admin/application_controller.rb +6 -0
- data/app/controllers/admin/users_controller.rb +4 -10
- data/app/controllers/spud/application_controller.rb +8 -2
- data/app/controllers/user_sessions_controller.rb +1 -1
- data/app/helpers/tb_core/application_helper.rb +88 -0
- data/app/views/admin/users/_form.html.erb +21 -17
- data/app/views/admin/users/edit.html.erb +1 -3
- data/app/views/admin/users/new.html.erb +1 -3
- data/app/views/layouts/admin/_search.html.erb +3 -0
- data/app/views/layouts/admin/detail.html.erb +1 -1
- data/app/views/layouts/admin/error_page.html.erb +10 -12
- data/config/locales/en.yml +8 -0
- data/lib/generators/spud/controller_spec_generator.rb +34 -0
- data/lib/generators/spud/module_generator.rb +37 -0
- data/lib/generators/spud/setup_generator.rb +5 -0
- data/lib/generators/spud/templates/admin_controller.rb.erb +2 -7
- data/lib/generators/spud/templates/assets/application.scss +1 -0
- data/lib/generators/spud/templates/controller.rb.erb +1 -5
- data/lib/generators/spud/templates/controller_spec.rb.erb +79 -0
- data/lib/generators/spud/templates/views/admin/_form.html.erb +2 -2
- data/lib/generators/spud/templates/views/layouts/application.html.erb +2 -3
- data/lib/spud_core/belongs_to_app.rb +1 -1
- data/lib/spud_core/engine.rb +2 -0
- data/lib/spud_core/errors.rb +23 -14
- data/lib/spud_core/version.rb +1 -1
- data/lib/tb_core/form_builder.rb +11 -1
- data/lib/tb_core/responder.rb +32 -0
- data/lib/tb_core/test_helper.rb +42 -0
- data/spec/controllers/admin/dashboard_controller_spec.rb +3 -7
- data/spec/controllers/admin/settings_controller_spec.rb +1 -4
- data/spec/controllers/admin/user_sessions_controller_spec.rb +1 -2
- data/spec/controllers/admin/users_controller_spec.rb +1 -4
- data/spec/dummy/app/assets/stylesheets/admin/{application.css.scss → application.scss} +0 -0
- data/spec/dummy/app/assets/stylesheets/{application.css.scss → application.scss} +0 -0
- data/spec/rails_helper.rb +2 -1
- metadata +14 -14
- data/app/helpers/spud/application_helper.rb +0 -36
- data/app/helpers/twice_baked/application_helper.rb +0 -42
- data/spec/authlogic_helper.rb +0 -2
- data/spec/helpers/spud/application_helper_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71aea9ddb3c9316dc739b01f90a9d4fc691de43f
|
4
|
+
data.tar.gz: 2c6513d8c1deca712ffee16191abc4a60d1d0ace
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eab1674d55e9a78198f6b740e1e3ba26bb62805e0dde9382c68f110794b81db84bc70865d26c4a3e18a1ca98d4761aa9d71759552ceb6d04e0e43a1747428147
|
7
|
+
data.tar.gz: cd40971446fe52fdeafedfa2e653701ced4d23843b0592902c2e2d1f56a771585db6b413f5b08e7fc66bc363710b4caefbcfe8720f18f83eb2e774d5d3273687
|
data/README.md
CHANGED
@@ -52,11 +52,19 @@ The core engine provides various configuration options.
|
|
52
52
|
Adding Apps to the Dashboard
|
53
53
|
----------------------------
|
54
54
|
|
55
|
-
|
55
|
+
### Using the Module Generator
|
56
|
+
|
57
|
+
The fastest way to add an app to the dashboard is via the geneator. The generator is best suited for situations where you are also creating a new ActiveRecord model, and you want to create a standard set of CRUD actions to manage it.
|
56
58
|
|
57
59
|
rails g spud:module Cars make:string model:string year:integer classic:boolean notes:text
|
58
60
|
|
59
|
-
|
61
|
+
Running the example above would create an ActiveRecord model, migration, admin controller with CRUD actions, and a set of front end views.
|
62
|
+
|
63
|
+
### Adding Manually
|
64
|
+
|
65
|
+
Sometimes you want to create a dashboard app without all the magic described above. Start by building your controllers and views under the `admin` namespace. Extend your controller from `Admin::ApplicationController` in order to inherit the default admin behaviors, and look at the core admin controllers for example implementations.
|
66
|
+
|
67
|
+
Then, add your module to the `Spud::Core.config.admin_applications` array. We recommend you perform this action in either an initializer or within your `application.rb` file.
|
60
68
|
|
61
69
|
Spud::Core.config.admin_applications += [{
|
62
70
|
# Required:
|
@@ -67,43 +75,7 @@ Admin apps are added via the `Spud::Core.config.admin_applications` array. We re
|
|
67
75
|
:badge => ->(user){ Client.where(:is_pending => true).count() }
|
68
76
|
}]
|
69
77
|
|
70
|
-
|
71
|
-
|
72
|
-
Roles & Permissions
|
73
|
-
-------------------
|
74
|
-
|
75
|
-
A user can be assigned to a role, which itself has one or more permissions. These permissions serve two distinct purposes: Determining what dashboard apps a user has access to (if any), and being used within your application logic to establish fine-grained user abilities.
|
76
|
-
|
77
|
-
Checking permissions is easy. Each permission has a `tag` which is a unique, namespaced identifier. Use the `has_permission?` and `has_any_permission?` methods on the user object to check for their existence. Keep in mind that these calls will always return true for users who have the `super_admin` flag set.
|
78
|
-
|
79
|
-
if !@spud_user.has_permission?('my_website.clients.my_clients')
|
80
|
-
redirect_to root_path, :notice => 'Permission Denied!'
|
81
|
-
end
|
82
|
-
|
83
|
-
Permissions are created one of two ways:
|
84
|
-
|
85
|
-
1. Every dashboard app automatically generates a "Full Access" permission, with a tag of `admin.(app name).full_access`.
|
86
|
-
2. You as the developer may append custom permissions to the `Spud::Core.permissions` array.
|
87
|
-
|
88
|
-
Create custom permissions whenever you need to permit an action that falls outside of the standard "full access to an app" use case; For example, turning on/off the ability for a user to upload an avatar.
|
89
|
-
|
90
|
-
// application.rb
|
91
|
-
Spud::Core.permissions += [
|
92
|
-
{:tag => 'my_website.profile.avatar', :name => 'Upload an avatar to my profile'}
|
93
|
-
]
|
94
|
-
// some_view.html.erb
|
95
|
-
<% if current_user.has_permission?('my_website.profile.avatar') %>
|
96
|
-
<%= link_to 'Upload Avatar', upload_avatar_path %>
|
97
|
-
<% end %>
|
98
|
-
|
99
|
-
Finally, custom permissions may optionally be tied to one or more dashboard apps. A user who has the permission shown below would have access to the the Clients and Projects dashboard apps. After that is is up to you to code your view and controller logic in accorance to what permissions the user has.
|
100
|
-
|
101
|
-
// application.rb
|
102
|
-
Spud::Core.permissions += [{
|
103
|
-
:tag => 'my_website.projects.project_management',
|
104
|
-
:name => 'Manage clients and projects, but cannot delete them or view private info',
|
105
|
-
:apps => [:clients, :projects]
|
106
|
-
}]
|
78
|
+
Restart your application, and the new module should appear on the dashboard.
|
107
79
|
|
108
80
|
Extending the User Model
|
109
81
|
------------------------
|
@@ -144,28 +116,111 @@ Create a file in your app at `app/views/admin/users/_show_additions.html.erb`.
|
|
144
116
|
</dd>
|
145
117
|
<% end %>
|
146
118
|
|
147
|
-
|
119
|
+
Error Handling
|
148
120
|
------------
|
149
121
|
|
150
|
-
The base `Spud::ApplicationController` will automatically rescue from any `Spud::NotFoundError` and `Spud::AccessDeniedError` errors by rendering out the `layouts/error_page.html` template.
|
122
|
+
The base `Spud::ApplicationController` will automatically rescue from any `Spud::NotFoundError` and `Spud::AccessDeniedError` errors by rendering out the `layouts/error_page.html` template. If you ran the `spud:setup` generator, a template of that name will have been created for you automatically.
|
151
123
|
|
152
|
-
When building your own apps you may raise a `Spud::NotFoundError` at any time to halt further execution and cause the
|
124
|
+
When building your own apps you may raise a `Spud::NotFoundError` or `Spud::AccessDeniedError` error at any time to halt further execution and cause the error page to render. For example:
|
153
125
|
|
154
126
|
class CarsController
|
155
127
|
before_action :get_record
|
156
128
|
# ... actions here
|
157
129
|
def get_record
|
158
|
-
@car = Car.
|
130
|
+
@car = Car.find_by(:id => params[:id])
|
159
131
|
if @car.blank?
|
160
|
-
raise Spud::NotFoundError.new(
|
132
|
+
raise Spud::NotFoundError.new('car')
|
161
133
|
end
|
162
134
|
end
|
163
135
|
end
|
164
136
|
|
137
|
+
Error messages also support localization. Add the following to `en.yml`, as well as any other locales you are using:
|
138
|
+
|
139
|
+
en:
|
140
|
+
tb_core:
|
141
|
+
errors:
|
142
|
+
not_found:
|
143
|
+
title: "Not Found"
|
144
|
+
message: "The %{item} you were looking for could not be found."
|
145
|
+
access_denied:
|
146
|
+
title: "Access Denied"
|
147
|
+
message: "You are not authorized to view the requested %{item}."
|
148
|
+
|
149
|
+
Form Builder
|
150
|
+
------------
|
151
|
+
|
152
|
+
The `TbCore::FormBuilder` class is designed to output a Bootstrap form in the `form-horizontal` style. Use it to quickly build simple forms with lots of fields. Validation errors will appear automatically next to their input fields.
|
153
|
+
|
154
|
+
You can specificy the `:builder` attribute in a `form_for`, or you can just use the provided `tb_form_for` helper to use it automatically.
|
155
|
+
|
156
|
+
### Example
|
157
|
+
|
158
|
+
<%= tb_form_for @widget do |f| %>
|
159
|
+
<%= f.tb_text_field :name %>
|
160
|
+
<%= f.tb_number_field :age %>
|
161
|
+
<%= f.tb_save_buttons('Widget', widgets_path) %>
|
162
|
+
<% end %>
|
163
|
+
|
164
|
+
See `form_builder.rb` for the full details.
|
165
|
+
|
166
|
+
Remote Forms
|
167
|
+
--------------
|
168
|
+
|
169
|
+
If you choose to include the `tb_core` JavaScript file in your project, we provide a couple additional features on top of the standard [jquery-ujs](https://github.com/rails/jquery-ujs) adapter. Use the following data attributes on your remote forms.
|
170
|
+
|
171
|
+
- `data-errors`: Set to `inline` for inline form errors, or `alert` for a browser alert window.
|
172
|
+
- `data-success`: Set to a path you would like to redirect to. For example, redirecting back to an index after a successful post.
|
173
|
+
|
174
|
+
This feature works with `tb_form_for` or standard `form_for` forms.
|
175
|
+
|
176
|
+
### Example
|
177
|
+
|
178
|
+
<%= tb_form_for @widget, :remote => true, :data => {:errors => :inline, :success => widgets_path} do |f| %>
|
179
|
+
<!--input fields here -->
|
180
|
+
<% end %>
|
181
|
+
|
182
|
+
See `remote.js` for the full details.
|
183
|
+
|
184
|
+
Roles & Permissions
|
185
|
+
-------------------
|
186
|
+
|
187
|
+
A user can be assigned to a role, which itself has one or more permissions. These permissions serve two distinct purposes: Determining what dashboard apps a user has access to (if any), and being used within your application logic to establish fine-grained user abilities.
|
188
|
+
|
189
|
+
Checking permissions is easy. Each permission has a `tag` which is a unique, namespaced identifier. Use the `has_permission?` and `has_any_permission?` methods on the user object to check for their existence. Keep in mind that these calls will always return true for users who have the `super_admin` flag set.
|
190
|
+
|
191
|
+
if !@spud_user.has_permission?('my_website.clients.my_clients')
|
192
|
+
redirect_to root_path, :notice => 'Permission Denied!'
|
193
|
+
end
|
194
|
+
|
195
|
+
Permissions are created one of two ways:
|
196
|
+
|
197
|
+
1. Every dashboard app automatically generates a "Full Access" permission, with a tag of `admin.(app name).full_access`.
|
198
|
+
2. You as the developer may append custom permissions to the `Spud::Core.permissions` array.
|
199
|
+
|
200
|
+
Create custom permissions whenever you need to permit an action that falls outside of the standard "full access to an app" use case; For example, turning on/off the ability for a user to upload an avatar.
|
201
|
+
|
202
|
+
// application.rb
|
203
|
+
Spud::Core.permissions += [
|
204
|
+
{:tag => 'my_website.profile.avatar', :name => 'Upload an avatar to my profile'}
|
205
|
+
]
|
206
|
+
// some_view.html.erb
|
207
|
+
<% if current_user.has_permission?('my_website.profile.avatar') %>
|
208
|
+
<%= link_to 'Upload Avatar', upload_avatar_path %>
|
209
|
+
<% end %>
|
210
|
+
|
211
|
+
Finally, custom permissions may optionally be tied to one or more dashboard apps. A user who has the permission shown below would have access to the the Clients and Projects dashboard apps. After that is is up to you to code your view and controller logic in accorance to what permissions the user has.
|
212
|
+
|
213
|
+
// application.rb
|
214
|
+
Spud::Core.permissions += [{
|
215
|
+
:tag => 'my_website.projects.project_management',
|
216
|
+
:name => 'Manage clients and projects, but cannot delete them or view private info',
|
217
|
+
:apps => [:clients, :projects]
|
218
|
+
}]
|
219
|
+
|
165
220
|
Testing
|
166
221
|
-----------------
|
167
222
|
|
168
|
-
Twice Baked uses RSpec
|
223
|
+
Twice Baked uses RSpec for testing. Get the tests running with a few short commands:
|
169
224
|
|
170
225
|
1. Create and migrate the databases:
|
171
226
|
|
@@ -182,9 +237,4 @@ Twice Baked uses RSpec, Capybara and Jasmine for testing. Get the tests running
|
|
182
237
|
|
183
238
|
After the tests have completed the current code coverage stats is available by opening `/coverage/index.html` in a browser.
|
184
239
|
|
185
|
-
|
186
|
-
|
187
|
-
For running the javascript unit tets:
|
188
|
-
|
189
|
-
rake jasmine:ci
|
190
|
-
|
240
|
+
You can include a few basic test helpers in your own twice baked applications by requiring `tb_core/test_helper` in your specs.
|
@@ -0,0 +1,179 @@
|
|
1
|
+
(function(){
|
2
|
+
|
3
|
+
/*
|
4
|
+
* jQuery UJS Handlers
|
5
|
+
*
|
6
|
+
* These handlers are designed to work with the Rails/jQuery UJS adapter. This applies
|
7
|
+
* to things like data-remote forms, data-method links, etc.
|
8
|
+
*
|
9
|
+
* For more info see the following lnks:
|
10
|
+
* - https://github.com/rails/jquery-ujs/wiki/ajax
|
11
|
+
* - http://guides.rubyonrails.org/working_with_javascript_in_rails.html
|
12
|
+
*
|
13
|
+
*/
|
14
|
+
tb.remote = {
|
15
|
+
init: function(){
|
16
|
+
$('body').on('ajax:before', 'form', onRemoteFormBefore);
|
17
|
+
$('body').on('ajax:complete', 'form', onRemoteFormComplete);
|
18
|
+
$('body').on('ajax:success', 'form[data-success]', onRemoteFormSuccess);
|
19
|
+
$('body').on('ajax:error', 'form[data-errors]', onRemoteFormErrors);
|
20
|
+
$('body').on('ajax:success', 'tr a[data-method=delete][data-remote=true]', onRemoteDeleteTableRow);
|
21
|
+
}
|
22
|
+
};
|
23
|
+
|
24
|
+
/*
|
25
|
+
* UJS likes to request application/javascript by default, but Twice Baked prefers JSON.
|
26
|
+
*/
|
27
|
+
var onRemoteFormBefore = function(xhr, settings){
|
28
|
+
if($(this).data('type') === undefined){
|
29
|
+
$(this).data('type', 'json');
|
30
|
+
}
|
31
|
+
};
|
32
|
+
|
33
|
+
/*
|
34
|
+
* Runs any time a remote form completes and assigns a true/false value
|
35
|
+
* to lastRemoteSuccess depending on if the post was succesful. This is
|
36
|
+
* referenced later on in enableFormElement
|
37
|
+
*/
|
38
|
+
var onRemoteFormComplete = function(event, jqXHR, textStatus){
|
39
|
+
var success = jqXHR.status >= 200 && jqXHR.status < 300;
|
40
|
+
$(this).data('lastRemoteSuccess', success);
|
41
|
+
};
|
42
|
+
|
43
|
+
/*
|
44
|
+
* Called when a remote form is submitted with a data-success configured
|
45
|
+
* Simply redirects to the configured path, or reloads the page if "reload" is passed
|
46
|
+
*
|
47
|
+
* ie:
|
48
|
+
* <form action="..." data-remote="true" data-success="/path/for/success/:id">
|
49
|
+
*/
|
50
|
+
var onRemoteFormSuccess = function(event, json, textStatus, jqXHR){
|
51
|
+
var successPath = $(this).data('success');
|
52
|
+
if(successPath == 'reload'){
|
53
|
+
window.location.reload();
|
54
|
+
}
|
55
|
+
else{
|
56
|
+
if(json && json.id){
|
57
|
+
successPath = successPath.replace(':id', json.id);
|
58
|
+
}
|
59
|
+
window.location = successPath;
|
60
|
+
}
|
61
|
+
};
|
62
|
+
|
63
|
+
/*
|
64
|
+
* Called when a remote form is submitted and the server has returned an error, only
|
65
|
+
* if the form has a data-errors attribute
|
66
|
+
*
|
67
|
+
* ie:
|
68
|
+
* <form action="..." data-remote="true" data-errors="inline">
|
69
|
+
* - OR -
|
70
|
+
* <form action="..." data-remote="true" data-errors="alert">
|
71
|
+
*/
|
72
|
+
var onRemoteFormErrors = function(event, jqXHR, textStatus, errorThrown){
|
73
|
+
var $form = $(this);
|
74
|
+
var errorType = $form.data('errors');
|
75
|
+
if(jqXHR.status == 422){
|
76
|
+
if(errorType == 'inline'){
|
77
|
+
displayErrorsInline($form, jqXHR.responseJSON.errors);
|
78
|
+
}
|
79
|
+
else{
|
80
|
+
displayErrorsAlert(jqXHR.responseJSON.full_messages);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
else if(jqXHR.status == 401 || jqXHR.status == 403){
|
84
|
+
window.alert('Error: Access denied.');
|
85
|
+
}
|
86
|
+
else{
|
87
|
+
window.alert('An unexpected error occurred: ' + errorThrown);
|
88
|
+
}
|
89
|
+
};
|
90
|
+
|
91
|
+
/*
|
92
|
+
* Append error text to form input fields and scroll to the first error
|
93
|
+
*/
|
94
|
+
var displayErrorsInline = function($form, errors){
|
95
|
+
$form.find('.form-error-inline, .form-error-base').remove();
|
96
|
+
for(var key in errors){
|
97
|
+
if(key == 'base'){
|
98
|
+
for(var i=0; i<errors[key].length; i++){
|
99
|
+
$form.prepend('<p class="form-error form-error-base">'+errors[key][i]+'</p>');
|
100
|
+
}
|
101
|
+
}
|
102
|
+
else{
|
103
|
+
var inputName, $input;
|
104
|
+
|
105
|
+
keyArr = key.split('.');
|
106
|
+
|
107
|
+
if (keyArr.length === 2) {
|
108
|
+
keyArr[0] += '_attributes';
|
109
|
+
$input = $("[name$='[" + keyArr.join('][') + "]']");
|
110
|
+
} else {
|
111
|
+
$input = $("[name$='[" + key + "]']");
|
112
|
+
}
|
113
|
+
|
114
|
+
if($input.length > 0){
|
115
|
+
var message = errors[key][0];
|
116
|
+
$input.after('<p class="form-error form-error-inline">'+message+'</p>');
|
117
|
+
}
|
118
|
+
else{
|
119
|
+
console.warn('Missing input field for key:', key);
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
var $firstError = $form.find('.form-error-inline, .form-error-base').first().parent('div');
|
124
|
+
if($firstError.length > 0){
|
125
|
+
$('body, html').animate({
|
126
|
+
scrollTop: $firstError.offset().top
|
127
|
+
});
|
128
|
+
}
|
129
|
+
};
|
130
|
+
|
131
|
+
/*
|
132
|
+
* Display errors in a standard window.alert dialog
|
133
|
+
*/
|
134
|
+
var displayErrorsAlert = function(errors){
|
135
|
+
var text = "Please correct the following errors:\n";
|
136
|
+
var len = errors.length;
|
137
|
+
for(var i=0; i<len; i++){
|
138
|
+
text += " - " + errors[i] + "\n";
|
139
|
+
}
|
140
|
+
window.alert(text);
|
141
|
+
};
|
142
|
+
|
143
|
+
/*
|
144
|
+
* Used for deleting table rows
|
145
|
+
* Add a link with data-remote=true and data-method=delete to your table to trigger this callback
|
146
|
+
*/
|
147
|
+
var onRemoteDeleteTableRow = function(){
|
148
|
+
var row = $(this).parents('tr');
|
149
|
+
row.fadeOut(200, function(){
|
150
|
+
row.remove();
|
151
|
+
});
|
152
|
+
};
|
153
|
+
|
154
|
+
/*
|
155
|
+
* Monkeypatch the base $.rails.enableFormElement function to add a
|
156
|
+
* delay and a success text value to the button before returning to
|
157
|
+
* default state.
|
158
|
+
*/
|
159
|
+
var originalEnableFormElement = $.rails.enableFormElement;
|
160
|
+
|
161
|
+
$.rails.enableFormElement = function(element){
|
162
|
+
var success = $(element).parents('form').data('lastRemoteSuccess');
|
163
|
+
var message = element.data('enableWith');
|
164
|
+
|
165
|
+
if(success && message){
|
166
|
+
var method = element.is('button') ? 'html' : 'val';
|
167
|
+
element[method](message);
|
168
|
+
setTimeout(function(){
|
169
|
+
originalEnableFormElement(element);
|
170
|
+
}, 1000);
|
171
|
+
}
|
172
|
+
else{
|
173
|
+
originalEnableFormElement(element);
|
174
|
+
}
|
175
|
+
};
|
176
|
+
|
177
|
+
$(document).ready(tb.remote.init);
|
178
|
+
|
179
|
+
})();
|
@@ -404,7 +404,7 @@
|
|
404
404
|
}
|
405
405
|
.ui-widget-content {
|
406
406
|
border: 1px solid #aaaaaa;
|
407
|
-
background: #ffffff image-url("jquery-ui/images
|
407
|
+
background: #ffffff image-url("jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;
|
408
408
|
color: #222222;
|
409
409
|
}
|
410
410
|
.ui-widget-content a {
|
@@ -412,7 +412,7 @@
|
|
412
412
|
}
|
413
413
|
.ui-widget-header {
|
414
414
|
border: 1px solid #aaaaaa;
|
415
|
-
background: #cccccc image-url("jquery-ui/images
|
415
|
+
background: #cccccc image-url("jquery-ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;
|
416
416
|
color: #222222;
|
417
417
|
font-weight: bold;
|
418
418
|
}
|
@@ -426,7 +426,7 @@
|
|
426
426
|
.ui-widget-content .ui-state-default,
|
427
427
|
.ui-widget-header .ui-state-default {
|
428
428
|
border: 1px solid #d3d3d3;
|
429
|
-
background: #e6e6e6 image-url("jquery-ui/images
|
429
|
+
background: #e6e6e6 image-url("jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;
|
430
430
|
font-weight: normal;
|
431
431
|
color: #555555;
|
432
432
|
}
|
@@ -443,7 +443,7 @@
|
|
443
443
|
.ui-widget-content .ui-state-focus,
|
444
444
|
.ui-widget-header .ui-state-focus {
|
445
445
|
border: 1px solid #999999;
|
446
|
-
background: #dadada image-url("jquery-ui/images
|
446
|
+
background: #dadada image-url("jquery-ui/images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;
|
447
447
|
font-weight: normal;
|
448
448
|
color: #212121;
|
449
449
|
}
|
@@ -462,7 +462,7 @@
|
|
462
462
|
.ui-widget-content .ui-state-active,
|
463
463
|
.ui-widget-header .ui-state-active {
|
464
464
|
border: 1px solid #aaaaaa;
|
465
|
-
background: #ffffff image-url("jquery-ui/images
|
465
|
+
background: #ffffff image-url("jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
|
466
466
|
font-weight: normal;
|
467
467
|
color: #212121;
|
468
468
|
}
|
@@ -479,7 +479,7 @@
|
|
479
479
|
.ui-widget-content .ui-state-highlight,
|
480
480
|
.ui-widget-header .ui-state-highlight {
|
481
481
|
border: 1px solid #fcefa1;
|
482
|
-
background: #fbf9ee image-url("jquery-ui/images
|
482
|
+
background: #fbf9ee image-url("jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
|
483
483
|
color: #363636;
|
484
484
|
}
|
485
485
|
.ui-state-highlight a,
|
@@ -491,7 +491,7 @@
|
|
491
491
|
.ui-widget-content .ui-state-error,
|
492
492
|
.ui-widget-header .ui-state-error {
|
493
493
|
border: 1px solid #cd0a0a;
|
494
|
-
background: #fef1ec image-url("jquery-ui/images
|
494
|
+
background: #fef1ec image-url("jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
|
495
495
|
color: #cd0a0a;
|
496
496
|
}
|
497
497
|
.ui-state-error a,
|
@@ -537,27 +537,27 @@
|
|
537
537
|
}
|
538
538
|
.ui-icon,
|
539
539
|
.ui-widget-content .ui-icon {
|
540
|
-
background-image: image-url("jquery-ui/images
|
540
|
+
background-image: image-url("jquery-ui/images/ui-icons_222222_256x240.png");
|
541
541
|
}
|
542
542
|
.ui-widget-header .ui-icon {
|
543
|
-
background-image: image-url("jquery-ui/images
|
543
|
+
background-image: image-url("jquery-ui/images/ui-icons_222222_256x240.png");
|
544
544
|
}
|
545
545
|
.ui-state-default .ui-icon {
|
546
|
-
background-image: image-url("jquery-ui/images
|
546
|
+
background-image: image-url("jquery-ui/images/ui-icons_888888_256x240.png");
|
547
547
|
}
|
548
548
|
.ui-state-hover .ui-icon,
|
549
549
|
.ui-state-focus .ui-icon {
|
550
|
-
background-image: image-url("jquery-ui/images
|
550
|
+
background-image: image-url("jquery-ui/images/ui-icons_454545_256x240.png");
|
551
551
|
}
|
552
552
|
.ui-state-active .ui-icon {
|
553
|
-
background-image: image-url("jquery-ui/images
|
553
|
+
background-image: image-url("jquery-ui/images/ui-icons_454545_256x240.png");
|
554
554
|
}
|
555
555
|
.ui-state-highlight .ui-icon {
|
556
|
-
background-image: image-url("jquery-ui/images
|
556
|
+
background-image: image-url("jquery-ui/images/ui-icons_2e83ff_256x240.png");
|
557
557
|
}
|
558
558
|
.ui-state-error .ui-icon,
|
559
559
|
.ui-state-error-text .ui-icon {
|
560
|
-
background-image: image-url("jquery-ui/images
|
560
|
+
background-image: image-url("jquery-ui/images/ui-icons_cd0a0a_256x240.png");
|
561
561
|
}
|
562
562
|
|
563
563
|
// positioning */
|
@@ -770,14 +770,14 @@
|
|
770
770
|
|
771
771
|
// Overlays */
|
772
772
|
.ui-widget-overlay {
|
773
|
-
background: #aaaaaa image-url("jquery-ui/images
|
773
|
+
background: #aaaaaa image-url("jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
774
774
|
opacity: .3;
|
775
775
|
filter: Alpha(Opacity=30); // support: IE8 */
|
776
776
|
}
|
777
777
|
.ui-widget-shadow {
|
778
778
|
margin: -8px 0 0 -8px;
|
779
779
|
padding: 8px;
|
780
|
-
background: #aaaaaa image-url("jquery-ui/images
|
780
|
+
background: #aaaaaa image-url("jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
781
781
|
opacity: .3;
|
782
782
|
filter: Alpha(Opacity=30); // support: IE8 */
|
783
783
|
border-radius: 8px;
|
@@ -72,6 +72,9 @@ body {
|
|
72
72
|
}
|
73
73
|
|
74
74
|
// Detail
|
75
|
+
.nav-tabs{
|
76
|
+
margin-bottom: 20px;
|
77
|
+
}
|
75
78
|
.admin-search-form{
|
76
79
|
display: inline;
|
77
80
|
}
|
@@ -102,12 +105,31 @@ body {
|
|
102
105
|
}
|
103
106
|
|
104
107
|
// Forms
|
108
|
+
|
109
|
+
// Old Errors
|
105
110
|
div.field_with_errors {
|
106
111
|
display: inline;
|
107
112
|
}
|
108
113
|
div.field_with_errors label, .form-field-error, .form-errors {
|
109
114
|
color: crimson;
|
110
115
|
}
|
116
|
+
|
117
|
+
// New (Remote) Errors
|
118
|
+
p.form-error{
|
119
|
+
color: crimson;
|
120
|
+
font-size: 12px;
|
121
|
+
font-style: italic;
|
122
|
+
line-height: normal;
|
123
|
+
margin: 4px 0 0;
|
124
|
+
}
|
125
|
+
p.form-error-base{
|
126
|
+
margin: 0 0 10px;
|
127
|
+
}
|
128
|
+
@media (min-width: $screen-sm){
|
129
|
+
.form-horizontal .form-error-base{
|
130
|
+
margin-left: 17.3%;
|
131
|
+
}
|
132
|
+
}
|
111
133
|
.date-select{
|
112
134
|
&.form-control{
|
113
135
|
display: inline-block;
|
File without changes
|
@@ -4,9 +4,15 @@ class Admin::ApplicationController < Spud::ApplicationController
|
|
4
4
|
add_breadcrumb "Dashboard", :admin_root_path
|
5
5
|
layout 'admin/detail'
|
6
6
|
respond_to :html, :json
|
7
|
+
# self.responder = TbCore::Responder
|
7
8
|
|
8
9
|
private
|
9
10
|
|
11
|
+
def handle_request_error(error)
|
12
|
+
error.template = '/layouts/admin/error_page'
|
13
|
+
super(error)
|
14
|
+
end
|
15
|
+
|
10
16
|
def require_admin_user
|
11
17
|
if current_user.blank?
|
12
18
|
flash[:notice] = "You must be logged in to access the requested page"
|
@@ -23,12 +23,8 @@ class Admin::UsersController < Admin::ApplicationController
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def create
|
26
|
-
@user = SpudUser.
|
27
|
-
|
28
|
-
render 'show', :status => 200
|
29
|
-
else
|
30
|
-
render 'new', :status => 422
|
31
|
-
end
|
26
|
+
@user = SpudUser.create(user_params)
|
27
|
+
respond_with @user, :location => admin_users_path
|
32
28
|
end
|
33
29
|
|
34
30
|
def edit
|
@@ -48,12 +44,10 @@ class Admin::UsersController < Admin::ApplicationController
|
|
48
44
|
private
|
49
45
|
|
50
46
|
def load_user
|
51
|
-
@user = SpudUser.
|
47
|
+
@user = SpudUser.find(params[:id])
|
52
48
|
if @user.blank?
|
53
|
-
|
54
|
-
redirect_to admin_users_path and return false
|
49
|
+
raise Spud::NotFoundError.new('user')
|
55
50
|
end
|
56
|
-
return true
|
57
51
|
end
|
58
52
|
|
59
53
|
# attr_accessible :login,:email,:first_name,:last_name,:password,:password_confirmation,:password_salt,:last_login_at,:last_request_at,:last_login_ip,:failed_login_count,:current_login_at,:login_count,:persistence_token,:perishable_token,:single_access_token,:crypted_password, :current_login_ip, :created_at, :updated_at,:time_zone, :as => [:default, :admin]
|
@@ -6,12 +6,13 @@ class Spud::ApplicationController < ActionController::Base
|
|
6
6
|
helper_method :current_user_session, :current_user, :current_user_id
|
7
7
|
around_filter :set_time_zone
|
8
8
|
|
9
|
-
include
|
9
|
+
include TbCore::ApplicationHelper
|
10
10
|
before_action :set_mailer_default_url
|
11
11
|
|
12
12
|
rescue_from Spud::RequestError, :with => :handle_request_error
|
13
|
+
rescue_from ActiveRecord::RecordNotFound, :with => :handle_record_not_found
|
13
14
|
rescue_from ActionController::UnknownFormat, :with => :handle_unknown_format_error
|
14
|
-
|
15
|
+
|
15
16
|
def not_found
|
16
17
|
raise Spud::NotFoundError
|
17
18
|
end
|
@@ -91,6 +92,11 @@ private
|
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
95
|
+
def handle_record_not_found(error)
|
96
|
+
error = Spud::NotFoundError.new('record')
|
97
|
+
handle_request_error(error)
|
98
|
+
end
|
99
|
+
|
94
100
|
def handle_unknown_format_error(error)
|
95
101
|
error = Spud::NotFoundError.new()
|
96
102
|
handle_request_error(error)
|