has_many ui has been implemented

This commit is contained in:
did 2011-05-19 17:44:04 +02:00
parent 94528ca94f
commit d2f6cc0c53
14 changed files with 489 additions and 215 deletions

View File

@ -20,7 +20,8 @@ gem 'inherited_resources', '~> 1.1.2'
gem 'rmagick', '2.12.2'
gem 'locomotive_carrierwave', '0.5.0.1.beta3', :require => 'carrierwave'
gem 'custom_fields', '1.0.0.beta.11'
# gem 'custom_fields', '1.0.0.beta.11'
gem 'custom_fields', :path => '../gems/custom_fields'
gem 'fog', '0.3.7'
gem 'mimetype-fu'
gem 'actionmailer-with-request', :require => 'actionmailer_with_request'

View File

@ -11,6 +11,14 @@ GIT
xpath (0.1.2)
nokogiri (~> 1.4)
PATH
remote: ../gems/custom_fields
specs:
custom_fields (1.0.0.beta.11)
activesupport (>= 3.0.7)
locomotive_carrierwave
mongoid (~> 2.0.1)
GEM
remote: http://rubygems.org/
specs:
@ -51,7 +59,7 @@ GEM
activesupport (= 3.0.7)
activesupport (3.0.7)
archive-tar-minitar (0.5.2)
arel (2.0.9)
arel (2.0.10)
autotest (4.4.6)
ZenTest (>= 4.4.1)
bcrypt-ruby (2.1.4)
@ -87,10 +95,6 @@ GEM
cucumber-rails (0.3.2)
cucumber (>= 0.8.0)
culerity (0.2.15)
custom_fields (1.0.0.beta.11)
activesupport (>= 3.0.7)
locomotive_carrierwave
mongoid (~> 2.0.1)
daemons (1.1.3)
database_cleaner (0.6.7)
delayed_job (2.1.4)
@ -275,7 +279,7 @@ DEPENDENCIES
capybara
cucumber (= 0.8.5)
cucumber-rails
custom_fields (= 1.0.0.beta.11)
custom_fields!
database_cleaner
delayed_job (= 2.1.4)
delayed_job_mongoid (= 1.0.2)

View File

@ -7,7 +7,7 @@ module Admin::ContentTypesHelper
@content_types = current_site.content_types.ordered.
limit(:contents => Locomotive.config.lastest_items_nb).
only(:name, :slug, :highlighted_field_name, :content_custom_fields_version).to_a
only(:name, :slug, :highlighted_field_name, :content_custom_fields_version, :order_by).to_a
if @content_type && @content_type.persisted? && @content_types.index(@content_type) >= MAX_DISPLAYED_CONTENTS
@content_types.delete(@content_type)

View File

@ -1,7 +1,7 @@
module Admin::CustomFieldsHelper
def options_for_field_kind
%w(string text category boolean date file).map do |kind|
%w(string text category boolean date file has_one has_many).map do |kind|
[t("custom_fields.kind.#{kind}"), kind]
end
end
@ -39,4 +39,19 @@ module Admin::CustomFieldsHelper
end
end
def options_for_association_target
current_site.content_types.collect { |c| [c.name, c.content_klass.to_s] }
end
def options_for_has_one(field)
target_contents_from_field(field).collect { |c| [c._label, c._id] }
end
alias :options_for_has_many :options_for_has_one
def target_contents_from_field(field)
content_type = field.target.constantize._parent
content_type.ordered_contents
end
end

View File

@ -40,9 +40,11 @@ class ContentInstance
end
def highlighted_field_value
self.send(self.content_type.highlighted_field._name)
self.send(self.content_type.highlighted_field_name)
end
alias :_label :highlighted_field_value
def visible?
self._visible || self._visible.nil?
end

View File

@ -35,4 +35,40 @@
!= t('.delete_file')
= form.check_box :"remove_#{field._name}"
- elsif field.has_one?
= form.input field._alias.to_sym, :label => field.label, :hint => field.hint, :input_html => { :class => 'has_one' }, :as => :select, :collection => options_for_has_one(field), :selected => form.object.send(field._alias.to_sym).try(:_id)
- elsif field.has_many?
= form.custom_input field._alias.to_sym, :label => field.label, :hint => field.hint, :css => 'has-many' do
.has-many-selector
%p{ :style => form.object.send(field._alias.to_sym).empty? ? '' : 'display: none' }
= t('.empty')
%ul
%script{ :type => 'text/x-mustache-template', :name => 'template', :'data-base-input-name' => "content[#{field._alias.to_sym}]" }
%li{ :class => "item {{behaviour_flag}}" }
%span.handle
= image_tag 'admin/form/icons/drag.png'
{{^if_template}}
%input{ :name => '{{base_name}}[]', :value => '{{{id}}}', :type => 'hidden', :'data-field' => 'id' }
{{/if_template}}
%strong {{label}}
{{#if_template}}
= select_tag 'label', ''
{{/if_template}}
%span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove'
%button{ :class => 'button light mini add', :type => 'button' }
%span!= t('admin.buttons.new_item')
%script{ :type => 'text/javascript', :name => 'data' }
!= collection_to_js(options_for_has_many(field), :taken_ids => form.object.send(field._alias.to_sym).ids)
= render '/admin/custom_fields/category_tmpl'

View File

@ -8,6 +8,7 @@
= g.input :_alias
= g.input :hint
= g.input :text_formatting, :as => 'select', :collection => options_for_text_formatting, :include_blank => false, :wrapper_html => { :style => 'display: none' }
= g.input :target, :as => 'select', :collection => options_for_association_target, :include_blank => false, :wrapper_html => { :style => 'display: none' }
.popup-actions
%p

View File

@ -23,6 +23,8 @@
%input{ :name => '{{base_name}}[text_formatting]', :value => '{{{text_formatting}}}', :type => 'hidden', :'data-field' => 'text_formatting' }
%input{ :name => '{{base_name}}[target]', :value => '{{{target}}}', :type => 'hidden', :'data-field' => 'target' }
%input{ :name => '{{base_name}}[label]', :value => '{{{label}}}', :type => 'text', :'data-field' => 'label' }
—

View File

@ -34,6 +34,7 @@ javascripts:
- public/javascripts/admin/plugins/fancybox.js
- public/javascripts/admin/plugins/mustache.js
- public/javascripts/admin/custom_fields/category.js
- public/javascripts/admin/custom_fields/has_many.js
asset_collections:
- public/javascripts/admin/plugins/fancybox.js
- public/javascripts/admin/plugins/mustache.js

View File

@ -50,7 +50,7 @@ $(document).ready(function() {
// edit
domField.find('a.edit').click(function(e) {
var link = $(this);
var attributes = ['_alias', 'hint', 'text_formatting'];
var attributes = ['_alias', 'hint', 'text_formatting', 'target'];
$.fancybox({
titleShow: false,
@ -76,6 +76,8 @@ $(document).ready(function() {
domBoxAttrVal(name, val);
});
if (domFieldVal(domField, 'kind').toLowerCase() == 'text') domBoxAttr('text_formatting').parents('li').show();
if (domFieldVal(domField, 'kind').toLowerCase() == 'has_one' ||
domFieldVal(domField, 'kind').toLowerCase() == 'has_many') domBoxAttr('target').parents('li').show();
}
});
e.preventDefault(); e.stopPropagation();
@ -200,161 +202,5 @@ $(document).ready(function() {
}
}
setup(); // <- let's the show begin
setup(); // <- let the show begin
});
// $(document).ready(function() {
//
// $('fieldset.fields').parents('form').submit(function() {
// $('fieldset.fields li.template input, fieldset.fields li.template select').attr('disabled', 'disabled');
// });
//
// var defaultValue = $('fieldset.fields li.template input[type=text]').val();
// var selectOnChange = function(select) {
// select.hide();
// select.prev()
// .show()
// .html(select[0].options[select[0].options.selectedIndex].text);
// }
//
// var refreshPosition = function() {
// jQuery.each($('fieldset.fields li.added input.position'), function(index) {
// $(this).val(index);
// });
// }
//
// /* __ fields ___ */
// $('fieldset.fields li.added select').each(function() {
// var select = $(this)
// .hover(function() {
// clearTimeout(select.attr('timer'));
// }, function() {
// select.attr('timer', setTimeout(function() {
// select.hide();
// select.prev().show();
// }, 1000));
// })
// .change(function() { selectOnChange(select); });
//
// select.prev().click(function() {
// $(this).hide();
// select.show();
// });
// });
//
// $('fieldset.fields li.template input[type=text]').focus(function() {
// if ($(this).hasClass('void') && $(this).parents('li').hasClass('template'))
// $(this).val('').removeClass('void');
// });
//
// $('fieldset.fields li.template button').click(function() {
// var lastRow = $(this).parents('li.template');
//
// var label = lastRow.find('input.label').val().trim();
// if (label == '' || label == defaultValue) return false;
//
// var newRow = lastRow.clone(true).removeClass('template').addClass('added new').insertBefore(lastRow);
//
// var dateFragment = '[' + new Date().getTime() + ']';
// newRow.find('input, select').each(function(index) {
// $(this).attr('name', $(this).attr('name').replace('[-1]', dateFragment));
// });
//
// var select = newRow.find('select')
// .val(lastRow.find('select').val())
// .change(function() { selectOnChange(select); })
// .hover(function() {
// clearTimeout(select.attr('timer'));
// }, function() {
// select.attr('timer', setTimeout(function() {
// select.hide();
// select.prev().show();
// }, 1000));
// });
// select.prev()
// .html(select[0].options[select[0].options.selectedIndex].text)
// .click(function() {
// $(this).hide();
// select.show();
// });
//
// // then "reset" the form
// lastRow.find('input.label').val(defaultValue).addClass('void');
//
// // warn the sortable widget about the new row
// $("fieldset.fields ol").sortable('refresh');
//
// refreshPosition();
// });
//
// $('fieldset.fields li a.remove').click(function(e) {
// if (confirm($(this).attr('data-confirm'))) {
// var parent = $(this).parents('li');
//
// if (parent.hasClass('new'))
// parent.remove();
// else {
// var field = parent.find('input.position')
// field.attr('name', field.attr('name').replace('[position]', '[_destroy]'));
//
// parent.hide().removeClass('added')
// }
//
// refreshPosition();
// }
//
// e.preventDefault();
// e.stopPropagation();
// });
//
// // sortable list
// $("fieldset.fields ol").sortable({
// handle: 'span.handle',
// items: 'li:not(.template)',
// axis: 'y',
// update: refreshPosition
// });
//
// // edit in depth custom field
// $('fieldset.fields li.item span.actions a.edit').click(function() {
// var link = $(this);
// $.fancybox({
// titleShow: false,
// content: $(link.attr('href')).parent().html(),
// onComplete: function() {
// $('#fancybox-wrap form').submit(function(e) {
// $.fancybox.close();
// e.preventDefault();
// e.stopPropagation();
// });
//
// var parent = link.parent();
//
// if (parent.prevAll('select').val() == 'Text') {
// var formatting = parent.prevAll('.text-formatting').val();
// $('#fancybox-wrap #custom_fields_field_text_formatting').val(formatting);
// $('#fancybox-wrap #custom_fields_field_text_formatting_input').show();
// } else {
// $('#fancybox-wrap #custom_fields_field_text_formatting_input').hide();
// }
//
// var alias = parent.prevAll('.alias').val().trim();
// if (alias == '') alias = makeSlug(link.parent().prevAll('.label').val());
// $('#fancybox-wrap #custom_fields_field__alias').val(alias);
//
// var hint = parent.prevAll('.hint').val();
// $('#fancybox-wrap #custom_fields_field_hint').val(hint);
// },
// onCleanup: function() {
// var parent = link.parent();
//
// var alias = $('#fancybox-wrap #custom_fields_field__alias').val().trim();
// if (alias != '') parent.prevAll('.alias').val(alias);
// var hint = $('#fancybox-wrap #custom_fields_field_hint').val().trim();
// if (hint != '') parent.prevAll('.hint').val(hint);
// var formatting = $('#fancybox-wrap #custom_fields_field_text_formatting').val();
// parent.prevAll('.text-formatting').val(formatting);
// }
// })
// });
// });

View File

@ -0,0 +1,155 @@
$(document).ready(function() {
// add/remove/sort items in a has_many relationship
$('.has-many-selector').hasManySelector();
});
(function($){
$.fn.hasManySelector = function(options) {
var populateSelect = function(context) {
context.select.find('option').remove();
for (var i = 0; i < context.data.collection.length; i++) {
var obj = context.data.collection[i];
if ($.inArray(obj[1], context.data.taken_ids) == -1)
context.select.append(new Option(obj[0], obj[1], true, true));
}
if (context.select.find('option').size() == 0)
context.list.find('li.template').hide();
else
context.list.find('li.template').show();
}
var addId = function(context, id) {
context.data.taken_ids.push(id);
populateSelect(context);
if (context.data.taken_ids.length > 0) {
context.empty.hide();
context.list.next('input[type=hidden]').remove();
}
if (context.data.taken_ids.length == context.data.collection.length)
context.sep.hide();
}
var removeId = function(context, id) {
context.data.taken_ids = jQuery.grep(context.data.taken_ids, function(value) {
return value != id;
});
populateSelect(context);
if (context.data.taken_ids.length == 0) {
context.empty.show();
context.list.after('<input type="hidden" name="' + context.baseInputName + '" value="" />');
}
context.sep.show();
}
var registerElementEvents = function(context, data, domElement) {
// remove
domElement.find('a.remove').click(function(e) {
domElement.remove();
removeId(context, data.id);
context.list.sortable('refresh');
e.preventDefault(); e.stopPropagation();
});
}
var registerElementTemplateEvents = function(context, domElement) {
// bind the "Add field" button
domElement.find('button').click(function(e) {
var newElement = {
id: context.select.val(),
label: context.select.find('option:selected').text()
};
addId(context, newElement.id);
addElement(context, newElement, { refreshPosition: true });
context.list.sortable('refresh');
e.preventDefault(); e.stopPropagation();
});
}
/* ___ Add an element into the list ___ */
var addElement = function(context, data, options) {
options = $.extend({
'is_template': false,
'refreshPosition': false
}, options);
data = $.extend({
behaviour_flag: function() { return options.is_template ? 'template' : 'added' },
base_name: function() { return options.is_template ? '' : context.baseInputName },
if_template: function() { return options.is_template }
}, data);
var html = Mustache.to_html(context.template, data);
var domElement = null;
if (options.is_template) {
domElement = context.list.append('<li class="sep">&nbsp;</li>').append(html).find('.template');
context.sep = context.list.find('.sep');
registerElementTemplateEvents(context, domElement);
}
else {
domElement = context.list.find('> .sep').before(html).prev('li');
registerElementEvents(context, data, domElement);
context.list.sortable('refresh');
}
}
return this.each(function() {
var wrapper = $(this);
var context = {
list: wrapper.find('ul'),
empty: wrapper.find('p'),
template: wrapper.find('script[name=template]').html(),
baseInputName: wrapper.find('script[name=template]').attr('data-base-input-name'),
data: eval(wrapper.find('script[name=data]').html())
};
// sortable list
context.list.sortable({
handle: 'span.handle',
items: 'li:not(.template)',
axis: 'y'
});
// add the template element used to insert the new ones
addElement(context, null, { is_template: true });
context.select = wrapper.find('select[name=label]');
populateSelect(context);
for (var i = 0; i < context.data.taken_ids.length; i++) {
var data = { id: context.data.taken_ids[i], label: null };
for (var j = 0; j < context.data.collection.length; j++) {
var current = context.data.collection[j];
if (data.id == current[1])
data.label = current[0];
}
addElement(context, data);
}
});
};
})(jQuery);

View File

@ -1,3 +1,7 @@
/* ___ AUTOMATICALLY GENERATED: see admin/button.scss for the source file */
/* ___ rounded ___ */
/* ___ box shadow ___ */
/* ___ others ___ */
.button {
display: inline-block;
background: transparent url(/images/admin/buttons/dark-gray-left.png) no-repeat 0 0;
@ -7,49 +11,51 @@
cursor: pointer;
border: none;
height: 31px;
outline: none;
}
.button span {
display: inline-block;
background: transparent url(/images/admin/buttons/dark-gray-right.png) no-repeat right top;
position: relative;
top: -1px;
padding: 3px 9px 9px 4px;
line-height: 21px;
text-shadow: 1px 1px 1px #000;
outline: none;
}
.button.light {
background-image: url(/images/admin/buttons/light-gray-left.png);
color: #787a89;
}
.button.light span {
background-image: url(/images/admin/buttons/light-gray-right.png);
text-shadow: 1px 1px 1px #fff;
}
.button.small {
background: #ebedf4;
outline: none;
-moz-border-radius : 10px;
-webkit-border-radius: 10px;
height: 20px;
font-size: 0.7em;
padding: 0px 12px 0px 12px;
color: #8B8D9A !important;
text-decoration: none;
text-shadow: 1px 1px 1px #fff;
}
.button.small.add {
}
.button.remove {
color: #ff092c !important;
font-size: 1.1em;
}
.button.remove:hover { text-decoration: underline; }
outline: none; }
.button span {
display: inline-block;
background: transparent url(/images/admin/buttons/dark-gray-right.png) no-repeat right top;
position: relative;
top: -1px;
padding: 3px 9px 9px 4px;
line-height: 21px;
text-shadow: 1px 1px 1px #000;
outline: none; }
.button.light {
background-image: url(/images/admin/buttons/light-gray-left.png);
color: #787a89; }
.button.light span {
background-image: url(/images/admin/buttons/light-gray-right.png);
text-shadow: 1px 1px 1px #fff; }
.button.small {
background: #ebedf4;
outline: none;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
height: 20px;
font-size: 0.7em;
padding: 0px 12px 0px 12px;
color: #8B8D9A !important;
text-decoration: none;
text-shadow: 1px 1px 1px #fff; }
.button.mini.add {
background: transparent;
height: 20px;
background: #e1e4ed;
background: -moz-linear-gradient(0% 100% 90deg, #d7dbe7, #ebedf4);
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ebedf4), to(#d7dbe7));
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4);
-webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4); }
.button.mini.add span {
background: none;
line-height: 10px;
padding: 0px 5px 0 0; }
.button.remove {
color: #ff092c !important;
font-size: 1.1em; }
.button.remove:hover {
text-decoration: underline; }

View File

@ -434,6 +434,85 @@ form.formtastic fieldset ol li.file p.remove a {
form.formtastic fieldset ol li.file p.remove a:hover { text-decoration: underline; }
form.formtastic fieldset ol li.has-many p {
font-style: italic;
margin-bottom: 5px;
}
form.formtastic fieldset ol li.has-many ul {
width: 736px;
margin-left: 20%;
}
form.formtastic fieldset ol li.has-many ul li.item {
background: transparent url(/images/admin/form/item-popup.png) no-repeat 0 0;
width: 413px;
margin: 0 0 10px 0;
}
form.formtastic fieldset ol li.has-many ul li.item strong {
font-size: 0.9em;
font-weight: bold;
color: #17171d;
position: relative;
top: -1px;
left: 10px;
}
form.formtastic fieldset ol li.has-many ul li.item span.handle {
cursor: move;
position: relative;
top: 1px;
}
form.formtastic fieldset ol li.has-many ul li.added span.actions a.remove {
display: inline;
}
form.formtastic fieldset ol li.has-many ul li.added span.actions button {
display: none;
}
form.formtastic fieldset ol li.has-many ul li.sep {
border-top: 1px solid #ccc;
height: 2px;
margin: 5px 0 7px;
width: 433px;
}
form.formtastic fieldset ol li.has-many ul li.template {}
form.formtastic fieldset ol li.has-many ul li.template select {
display: inline;
top: -1px;
}
form.formtastic fieldset ol li.has-many ul li.template span.handle {
display: none;
}
form.formtastic fieldset ol li.has-many ul li.template span.actions {
width: auto;
top: 0px;
}
form.formtastic fieldset ol li.has-many ul li.template span.actions a.remove {
display: none;
}
form.formtastic fieldset ol li.has-many ul li.template span.actions button {
display: inline;
position: relative;
top: 1px;
right: 3px;
}
form.formtastic fieldset ol li.has-many ul li.template span.actions button span {
font-size: 0.8em;
}
/* ___ my account ___ */
form.formtastic fieldset.language li.full span {

View File

@ -0,0 +1,126 @@
/* ___ AUTOMATICALLY GENERATED: see admin/button.scss for the source file */
@import "helpers";
.button {
display: inline-block;
background: transparent url(/images/admin/buttons/dark-gray-left.png) no-repeat 0 0;
padding: 0px 0px 0px 2px;
font-size: 0.9em;
color: white;
cursor: pointer;
border: none;
height: 31px;
outline: none;
span {
display: inline-block;
background: transparent url(/images/admin/buttons/dark-gray-right.png) no-repeat right top;
position: relative;
top: -1px;
padding: 3px 9px 9px 4px;
line-height: 21px;
text-shadow: 1px 1px 1px #000;
outline: none;
}
&.light {
background-image: url(/images/admin/buttons/light-gray-left.png);
color: #787a89;
span {
background-image: url(/images/admin/buttons/light-gray-right.png);
text-shadow: 1px 1px 1px #fff;
}
}
&.small {
background: #ebedf4;
outline: none;
-moz-border-radius : 10px;
-webkit-border-radius: 10px;
height: 20px;
font-size: 0.7em;
padding: 0px 12px 0px 12px;
color: #8B8D9A !important;
text-decoration: none;
text-shadow: 1px 1px 1px #fff;
}
&.mini.add {
background: transparent;
height: 20px;
@include linear-background-gradient(#ebedf4, #d7dbe7);
@include full-rounded(4px);
@include box-shadow(1px, 1px, 1px, rgba(0, 0, 0, 0.4));
span {
background: none;
line-height: 10px;
padding: 0px 5px 0 0;
}
}
&.remove {
color: #ff092c !important;
font-size: 1.1em;
&:hover { text-decoration: underline; }
}
}
// .button {
// display: inline-block;
// background: transparent url(/images/admin/buttons/dark-gray-left.png) no-repeat 0 0;
// padding: 0px 0px 0px 2px;
// font-size: 0.9em;
// color: white;
// cursor: pointer;
// border: none;
// height: 31px;
// outline: none;
// }
//
// .button span {
// display: inline-block;
// background: transparent url(/images/admin/buttons/dark-gray-right.png) no-repeat right top;
// position: relative;
// top: -1px;
// padding: 3px 9px 9px 4px;
// line-height: 21px;
// text-shadow: 1px 1px 1px #000;
// outline: none;
// }
//
// .button.light {
// background-image: url(/images/admin/buttons/light-gray-left.png);
// color: #787a89;
// }
//
// .button.light span {
// background-image: url(/images/admin/buttons/light-gray-right.png);
// text-shadow: 1px 1px 1px #fff;
// }
//
// .button.small {
// background: #ebedf4;
// outline: none;
// -moz-border-radius : 10px;
// -webkit-border-radius: 10px;
// height: 20px;
// font-size: 0.7em;
// padding: 0px 12px 0px 12px;
// color: #8B8D9A !important;
// text-decoration: none;
// text-shadow: 1px 1px 1px #fff;
// }
//
// .button.small.add {
// }
//
// .button.remove {
// color: #ff092c !important;
// font-size: 1.1em;
// }
//
// .button.remove:hover { text-decoration: underline; }