rails_admin 1.0.0.rc → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rails_admin might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/README.md +1 -14
- data/app/assets/javascripts/rails_admin/ra.filtering-multiselect.js +15 -10
- data/app/assets/javascripts/rails_admin/ra.filtering-select.js +205 -110
- data/app/assets/javascripts/rails_admin/ra.remote-form.js +1 -4
- data/app/controllers/rails_admin/application_controller.rb +5 -5
- data/app/controllers/rails_admin/main_controller.rb +2 -2
- data/app/helpers/rails_admin/application_helper.rb +2 -2
- data/app/helpers/rails_admin/form_builder.rb +9 -2
- data/app/helpers/rails_admin/main_helper.rb +1 -1
- data/app/views/layouts/rails_admin/application.html.haml +1 -0
- data/app/views/rails_admin/main/_delete_notice.html.haml +14 -9
- data/app/views/rails_admin/main/index.html.haml +2 -2
- data/config/initializers/active_record_extensions.rb +2 -2
- data/config/locales/rails_admin.en.yml +1 -0
- data/lib/rails_admin/abstract_model.rb +6 -7
- data/lib/rails_admin/adapters/active_record.rb +30 -11
- data/lib/rails_admin/adapters/mongoid.rb +2 -2
- data/lib/rails_admin/adapters/mongoid/association.rb +2 -2
- data/lib/rails_admin/adapters/mongoid/extension.rb +1 -1
- data/lib/rails_admin/adapters/mongoid/property.rb +1 -1
- data/lib/rails_admin/bootstrap-sass.rb +1 -3
- data/lib/rails_admin/bootstrap-sass/compass_functions.rb +1 -1
- data/lib/rails_admin/config.rb +12 -8
- data/lib/rails_admin/config/actions.rb +1 -1
- data/lib/rails_admin/config/actions/base.rb +3 -4
- data/lib/rails_admin/config/actions/bulk_delete.rb +1 -3
- data/lib/rails_admin/config/actions/index.rb +3 -5
- data/lib/rails_admin/config/configurable.rb +4 -6
- data/lib/rails_admin/config/fields/association.rb +1 -1
- data/lib/rails_admin/config/fields/base.rb +15 -26
- data/lib/rails_admin/config/fields/factories/carrierwave.rb +1 -1
- data/lib/rails_admin/config/fields/types.rb +1 -1
- data/lib/rails_admin/config/fields/types/active_record_enum.rb +17 -2
- data/lib/rails_admin/config/fields/types/file_upload.rb +1 -1
- data/lib/rails_admin/config/fields/types/has_many_association.rb +1 -1
- data/lib/rails_admin/config/fields/types/polymorphic_association.rb +1 -1
- data/lib/rails_admin/config/fields/types/string.rb +6 -5
- data/lib/rails_admin/config/fields/types/text.rb +3 -3
- data/lib/rails_admin/config/hideable.rb +1 -1
- data/lib/rails_admin/config/inspectable.rb +39 -0
- data/lib/rails_admin/config/model.rb +4 -17
- data/lib/rails_admin/config/sections/base.rb +4 -17
- data/lib/rails_admin/engine.rb +3 -2
- data/lib/rails_admin/extension.rb +4 -4
- data/lib/rails_admin/extensions/paper_trail/auditing_adapter.rb +10 -2
- data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +11 -1
- data/lib/rails_admin/support/datetime.rb +1 -1
- data/lib/rails_admin/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5f7c910bef240049b818e4189a241c41e68e180
|
4
|
+
data.tar.gz: ea59f46605ddef6defbe19c6846d3d9ee6419166
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9c9d4bc637f60e21978c7ce73f693987de692177f79a61ab27757cd75b3ee531575d5bff729361d57c2fad35e82eb48763b5dc4a0c8615394569af7a2a5da5f
|
7
|
+
data.tar.gz: 9f16f21be6393621ee9e3b3de88fa3c4c11e75d2d972b88fe411acc04625568adba1c580ede46ae97c012b637b1d75730b49515d3e0379ea8949806e78f851b2
|
data/Gemfile
CHANGED
@@ -2,7 +2,6 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gem 'appraisal', '>= 2.0'
|
4
4
|
gem 'devise'
|
5
|
-
gem 'remotipart', github: 'mshibuya/remotipart'
|
6
5
|
|
7
6
|
group :active_record do
|
8
7
|
platforms :jruby do
|
@@ -43,7 +42,7 @@ group :test do
|
|
43
42
|
gem 'pundit'
|
44
43
|
gem 'rack-cache', require: 'rack/cache'
|
45
44
|
gem 'rspec-rails', '>= 2.14'
|
46
|
-
gem 'rubocop', '~> 0.
|
45
|
+
gem 'rubocop', '~> 0.41.2'
|
47
46
|
gem 'simplecov', '>= 0.9', require: false
|
48
47
|
gem 'timecop', '>= 0.5'
|
49
48
|
|
data/README.md
CHANGED
@@ -33,20 +33,7 @@ RailsAdmin is a Rails engine that provides an easy-to-use interface for managing
|
|
33
33
|
|
34
34
|
## Installation
|
35
35
|
|
36
|
-
1. On your gemfile:
|
37
|
-
- For Rails 5 project:
|
38
|
-
|
39
|
-
```
|
40
|
-
gem 'remotipart', github: 'mshibuya/remotipart'
|
41
|
-
gem 'rails_admin', '>= 1.0.0.rc'
|
42
|
-
```
|
43
|
-
|
44
|
-
- For Rails 4 project:
|
45
|
-
|
46
|
-
```
|
47
|
-
gem 'rails_admin'
|
48
|
-
```
|
49
|
-
|
36
|
+
1. On your gemfile: `gem 'rails_admin', '~> 1.0'`
|
50
37
|
2. Run `bundle install`
|
51
38
|
3. Run `rails g rails_admin:install`
|
52
39
|
4. Provide a namespace for the routes when asked
|
@@ -175,22 +175,25 @@
|
|
175
175
|
_queryFilter: function(val) {
|
176
176
|
var widget = this;
|
177
177
|
widget._query(val, function(matches) {
|
178
|
-
|
178
|
+
|
179
179
|
var filtered = [];
|
180
|
-
|
181
|
-
|
180
|
+
var i;
|
181
|
+
|
182
|
+
for (i = 0; i < matches.length; i++) {
|
183
|
+
if (!widget.selected(matches[i].id)) {
|
182
184
|
filtered.push(i);
|
183
185
|
}
|
184
186
|
}
|
185
187
|
if (filtered.length > 0) {
|
186
|
-
widget.collection.
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
);
|
188
|
+
widget.collection[0].innerHTML = '';
|
189
|
+
var filteredContainer = [];
|
190
|
+
for (i = 0; i < filtered.length; i++) {
|
191
|
+
var newOptions = '<option value="'+matches[filtered[i]].id+'" title="'+matches[filtered[i]].label+'">'+matches[filtered[i]].label+'</option>';
|
192
|
+
filteredContainer.push(newOptions);
|
191
193
|
}
|
194
|
+
widget.collection[0].innerHTML = filteredContainer.join("");
|
192
195
|
} else {
|
193
|
-
widget.collection.
|
196
|
+
widget.collection[0].innerHTML = widget.noObjectsPlaceholder;
|
194
197
|
}
|
195
198
|
});
|
196
199
|
},
|
@@ -314,7 +317,9 @@
|
|
314
317
|
},
|
315
318
|
|
316
319
|
selected: function(value) {
|
317
|
-
|
320
|
+
if (this.selection[0].querySelectorAll('option[value="' + value + '"]')[0]) {
|
321
|
+
return true;
|
322
|
+
}
|
318
323
|
},
|
319
324
|
|
320
325
|
destroy: function() {
|
@@ -14,7 +14,9 @@
|
|
14
14
|
* jquery.ui.autocomplete.js
|
15
15
|
*/
|
16
16
|
(function($) {
|
17
|
-
|
17
|
+
'use strict';
|
18
|
+
|
19
|
+
$.widget('ra.filteringSelect', {
|
18
20
|
options: {
|
19
21
|
createQuery: function(query) {
|
20
22
|
return { query: query };
|
@@ -26,135 +28,90 @@
|
|
26
28
|
xhr: false
|
27
29
|
},
|
28
30
|
|
31
|
+
button: null,
|
32
|
+
input: null,
|
33
|
+
select: null,
|
34
|
+
|
29
35
|
_create: function() {
|
30
|
-
var
|
31
|
-
select = this.element.hide(),
|
32
|
-
selected = select.children(":selected"),
|
33
|
-
value = selected.val() ? selected.text() : "";
|
36
|
+
var filtering_select;
|
34
37
|
|
35
|
-
|
36
|
-
|
38
|
+
// When using the browser back and forward buttons, it is possible that
|
39
|
+
// the autocomplete field will be cached which causes duplicate fields
|
40
|
+
// to be generated.
|
41
|
+
if (this.element.is(':visible')) {
|
42
|
+
this.element.hide();
|
43
|
+
filtering_select = this._inputGroup(this.element.attr('id'));
|
44
|
+
this.input = this._inputField();
|
45
|
+
this.button = this._buttonField();
|
37
46
|
} else {
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
filtering_select = this.element.siblings(
|
48
|
+
'[data-input-for="' + this.element.attr('id') + '"]'
|
49
|
+
);
|
50
|
+
this.input = filtering_select.children('input');
|
51
|
+
this.button = filtering_select.children('.input-group-btn');
|
41
52
|
}
|
42
|
-
var filtering_select = $('<div class="input-group filtering-select col-sm-2" style="float:left"></div>')
|
43
|
-
var input = this.input = $('<input type="text">')
|
44
|
-
.val(value)
|
45
|
-
.addClass("form-control ra-filtering-select-input")
|
46
|
-
.attr('style', select.attr('style'))
|
47
|
-
.show()
|
48
|
-
.autocomplete({
|
49
|
-
delay: this.options.searchDelay,
|
50
|
-
minLength: this.options.minLength,
|
51
|
-
source: this._getSourceFunction(this.options.source),
|
52
|
-
select: function(event, ui) {
|
53
|
-
var option = $('<option></option>').attr('value', ui.item.id).attr('selected', 'selected').text(ui.item.value);
|
54
|
-
select.html(option);
|
55
|
-
select.trigger("change", ui.item.id);
|
56
|
-
self._trigger("selected", event, {
|
57
|
-
item: option
|
58
|
-
});
|
59
|
-
$(self.element.parents('.controls')[0]).find('.update').removeClass('disabled');
|
60
|
-
},
|
61
|
-
change: function(event, ui) {
|
62
|
-
if (!ui.item) {
|
63
|
-
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val()) + "$", "i"),
|
64
|
-
valid = false;
|
65
|
-
select.children("option").each(function() {
|
66
|
-
if ($(this).text().match(matcher)) {
|
67
|
-
this.selected = valid = true;
|
68
|
-
return false;
|
69
|
-
}
|
70
|
-
});
|
71
|
-
if (!valid || $(this).val() == '') {
|
72
|
-
// remove invalid value, as it didn't match anything
|
73
|
-
$(this).val(null);
|
74
|
-
select.html($('<option value="" selected="selected"></option>'));
|
75
|
-
input.data("ui-autocomplete").term = "";
|
76
|
-
$(self.element.parents('.controls')[0]).find('.update').addClass('disabled');
|
77
|
-
return false;
|
78
|
-
}
|
79
|
-
|
80
|
-
}
|
81
|
-
}
|
82
|
-
})
|
83
|
-
.keyup(function() {
|
84
|
-
/* Clear select options and trigger change if selected item is deleted */
|
85
|
-
if ($(this).val().length == 0) {
|
86
|
-
select.html($('<option value="" selected="selected"></option>'));
|
87
|
-
select.trigger("change");
|
88
|
-
}
|
89
|
-
})
|
90
|
-
|
91
|
-
if(select.attr('placeholder'))
|
92
|
-
input.attr('placeholder', select.attr('placeholder'))
|
93
|
-
|
94
|
-
input.data("ui-autocomplete")._renderItem = function(ul, item) {
|
95
|
-
return $("<li></li>")
|
96
|
-
.data("ui-autocomplete-item", item)
|
97
|
-
.append( $( "<a></a>" ).html( item.html || item.id ) )
|
98
|
-
.appendTo(ul);
|
99
|
-
};
|
100
|
-
|
101
|
-
var button = this.button = $('<span class="input-group-btn"><label class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="Show All Items" role="button"><span class="caret"></span><span class="ui-button-text"> </span></label></span>')
|
102
|
-
.click(function() {
|
103
|
-
// close if already visible
|
104
|
-
if (input.autocomplete("widget").is(":visible")) {
|
105
|
-
input.autocomplete("close");
|
106
|
-
return;
|
107
|
-
}
|
108
|
-
|
109
|
-
// pass empty string as value to search for, displaying all results
|
110
|
-
input.autocomplete("search", "");
|
111
|
-
input.focus();
|
112
|
-
});
|
113
|
-
|
114
|
-
filtering_select.append(input).append(button).insertAfter(select);
|
115
53
|
|
54
|
+
this._setOptionsSource();
|
55
|
+
this._initAutocomplete();
|
56
|
+
this._initKeyEvent();
|
57
|
+
this._overloadRenderItem();
|
58
|
+
this._autocompleteDropdownEvent(this.button);
|
116
59
|
|
60
|
+
return filtering_select.append(this.input)
|
61
|
+
.append(this.button)
|
62
|
+
.insertAfter(this.element);
|
117
63
|
},
|
118
64
|
|
119
65
|
_getResultSet: function(request, data, xhr) {
|
120
|
-
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term),
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
return $('<span></span>').text(el).html();
|
125
|
-
}).join($('<strong></strong>').text(word)[0].outerHTML);
|
126
|
-
}else{
|
127
|
-
return $('<span></span>').text(label).html();
|
128
|
-
}
|
66
|
+
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), 'i');
|
67
|
+
|
68
|
+
var spannedContent = function(content) {
|
69
|
+
return $('<span>').text(content).html();
|
129
70
|
};
|
130
71
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
72
|
+
var highlighter = function(label, word) {
|
73
|
+
if(word.length) {
|
74
|
+
return $.map(
|
75
|
+
label.split(word),
|
76
|
+
function(el) {
|
77
|
+
return spannedContent(el);
|
78
|
+
})
|
79
|
+
.join($('<strong>')
|
80
|
+
.text(word)[0]
|
81
|
+
.outerHTML
|
82
|
+
);
|
83
|
+
} else {
|
84
|
+
return spannedContent(label);
|
139
85
|
}
|
86
|
+
};
|
87
|
+
|
88
|
+
return $.map(
|
89
|
+
data,
|
90
|
+
function(el) {
|
91
|
+
var id = el.id || el.value;
|
92
|
+
var value = el.label || el.id;
|
93
|
+
// match regexp only for local requests, remote ones are already
|
94
|
+
// filtered, and label may not contain filtered term.
|
95
|
+
if (id && (xhr || matcher.test(el.label))) {
|
96
|
+
return {
|
97
|
+
html: highlighter(value, request.term),
|
98
|
+
value: value,
|
99
|
+
id: id
|
100
|
+
};
|
101
|
+
}
|
140
102
|
});
|
141
103
|
},
|
142
104
|
|
143
105
|
_getSourceFunction: function(source) {
|
144
|
-
|
145
|
-
var
|
146
|
-
requestIndex = 0;
|
106
|
+
var self = this;
|
107
|
+
var requestIndex = 0;
|
147
108
|
|
148
109
|
if ($.isArray(source)) {
|
149
|
-
|
150
110
|
return function(request, response) {
|
151
111
|
response(self._getResultSet(request, source, false));
|
152
112
|
};
|
153
|
-
|
154
|
-
} else if (typeof source === "string") {
|
155
|
-
|
113
|
+
} else if (typeof source === 'string') {
|
156
114
|
return function(request, response) {
|
157
|
-
|
158
115
|
if (this.xhr) {
|
159
116
|
this.xhr.abort();
|
160
117
|
}
|
@@ -162,7 +119,7 @@
|
|
162
119
|
this.xhr = $.ajax({
|
163
120
|
url: source,
|
164
121
|
data: self.options.createQuery(request.term),
|
165
|
-
dataType:
|
122
|
+
dataType: 'json',
|
166
123
|
autocompleteRequest: ++requestIndex,
|
167
124
|
success: function(data, status) {
|
168
125
|
if (this.autocompleteRequest === requestIndex) {
|
@@ -176,11 +133,149 @@
|
|
176
133
|
}
|
177
134
|
});
|
178
135
|
};
|
136
|
+
} else {
|
137
|
+
return source;
|
138
|
+
}
|
139
|
+
},
|
179
140
|
|
141
|
+
_setOptionsSource: function() {
|
142
|
+
if (this.options.xhr) {
|
143
|
+
this.options.source = this.options.remote_source;
|
180
144
|
} else {
|
145
|
+
this.options.source = this.element.children('option').map(function() {
|
146
|
+
return { label: $(this).text(), value: this.value };
|
147
|
+
}).toArray();
|
148
|
+
}
|
149
|
+
},
|
181
150
|
|
182
|
-
|
151
|
+
_buttonField: function() {
|
152
|
+
return $(
|
153
|
+
'<span class="input-group-btn">' +
|
154
|
+
'<label class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="Show All Items" role="button">' +
|
155
|
+
'<span class="caret"></span>' +
|
156
|
+
'<span class="ui-button-text"> </span>' +
|
157
|
+
'</label>' +
|
158
|
+
'</span>'
|
159
|
+
);
|
160
|
+
},
|
161
|
+
|
162
|
+
_autocompleteDropdownEvent: function(element) {
|
163
|
+
var self = this;
|
164
|
+
|
165
|
+
return element.click(function() {
|
166
|
+
// close if already visible
|
167
|
+
if (self.input.autocomplete('widget').is(':visible')) {
|
168
|
+
self.input.autocomplete('close');
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
|
172
|
+
// pass empty string as value to search for, displaying all results
|
173
|
+
self.input.autocomplete('search', '');
|
174
|
+
self.input.focus();
|
175
|
+
});
|
176
|
+
},
|
177
|
+
|
178
|
+
_inputField: function() {
|
179
|
+
var input;
|
180
|
+
var selected = this.element.children(':selected');
|
181
|
+
var value = selected.val() ? selected.text() : '';
|
182
|
+
|
183
|
+
input = $('<input type="text">')
|
184
|
+
.val(value)
|
185
|
+
.addClass('form-control ra-filtering-select-input')
|
186
|
+
.attr('style', this.element.attr('style'))
|
187
|
+
.show();
|
188
|
+
|
189
|
+
if (this.element.attr('placeholder')) {
|
190
|
+
input.attr('placeholder', this.element.attr('placeholder'));
|
183
191
|
}
|
192
|
+
|
193
|
+
return input;
|
194
|
+
},
|
195
|
+
|
196
|
+
_inputGroup: function(inputFor) {
|
197
|
+
return $('<div>')
|
198
|
+
.addClass('input-group filtering-select col-sm-2')
|
199
|
+
.attr('data-input-for', inputFor)
|
200
|
+
.css('float', 'left');
|
201
|
+
},
|
202
|
+
|
203
|
+
_initAutocomplete: function() {
|
204
|
+
var self = this;
|
205
|
+
|
206
|
+
return this.input.autocomplete({
|
207
|
+
delay: this.options.searchDelay,
|
208
|
+
minLength: this.options.minLength,
|
209
|
+
source: this._getSourceFunction(this.options.source),
|
210
|
+
select: function(event, ui) {
|
211
|
+
var option = $('<option>')
|
212
|
+
.attr('value', ui.item.id)
|
213
|
+
.attr('selected', 'selected')
|
214
|
+
.text(ui.item.value);
|
215
|
+
self.element.html(option)
|
216
|
+
.trigger('change', ui.item.id);
|
217
|
+
self._trigger('selected', event, {
|
218
|
+
item: option
|
219
|
+
});
|
220
|
+
$(self.element.parents('.controls')[0])
|
221
|
+
.find('.update')
|
222
|
+
.removeClass('disabled');
|
223
|
+
},
|
224
|
+
change: function(event, ui) {
|
225
|
+
if (ui.item) {
|
226
|
+
return;
|
227
|
+
}
|
228
|
+
|
229
|
+
var matcher = new RegExp('^' + $.ui.autocomplete.escapeRegex($(this).val()) + '$', 'i');
|
230
|
+
var valid = false;
|
231
|
+
|
232
|
+
self.element.children('option')
|
233
|
+
.each(function() {
|
234
|
+
if ($(this).text().match(matcher)) {
|
235
|
+
valid = true;
|
236
|
+
return false;
|
237
|
+
}
|
238
|
+
});
|
239
|
+
|
240
|
+
if (valid || $(this).val() !== '') {
|
241
|
+
return;
|
242
|
+
}
|
243
|
+
|
244
|
+
// remove invalid value, as it didn't match anything
|
245
|
+
$(this).val(null);
|
246
|
+
self.element.html($('<option value="" selected="selected"></option>'));
|
247
|
+
self.input.data('ui-autocomplete').term = '';
|
248
|
+
$(self.element.parents('.controls')[0])
|
249
|
+
.find('.update')
|
250
|
+
.addClass('disabled');
|
251
|
+
return false;
|
252
|
+
}
|
253
|
+
});
|
254
|
+
},
|
255
|
+
|
256
|
+
_initKeyEvent: function() {
|
257
|
+
var self = this;
|
258
|
+
|
259
|
+
return this.input.keyup(function() {
|
260
|
+
if ($(this).val().length) {
|
261
|
+
return;
|
262
|
+
}
|
263
|
+
|
264
|
+
/* Clear select options and trigger change if selected item is deleted */
|
265
|
+
return self.element
|
266
|
+
.html($('<option value="" selected="selected"></option>'))
|
267
|
+
.trigger('change');
|
268
|
+
});
|
269
|
+
},
|
270
|
+
|
271
|
+
_overloadRenderItem: function() {
|
272
|
+
this.input.data('ui-autocomplete')._renderItem = function(ul, item) {
|
273
|
+
return $('<li></li>')
|
274
|
+
.data('ui-autocomplete-item', item)
|
275
|
+
.append($('<a></a>')
|
276
|
+
.html(item.html || item.id))
|
277
|
+
.appendTo(ul);
|
278
|
+
};
|
184
279
|
},
|
185
280
|
|
186
281
|
destroy: function() {
|