Add the reverse has_many field feature (mainly based on pull request #142)
This commit is contained in:
parent
333934c022
commit
67d7b13f4f
2
Gemfile
2
Gemfile
@ -26,7 +26,7 @@ gem 'carrierwave', '~> 0.5.5'
|
|||||||
gem 'dragonfly', '~> 0.9.1'
|
gem 'dragonfly', '~> 0.9.1'
|
||||||
gem 'rack-cache', :require => 'rack/cache'
|
gem 'rack-cache', :require => 'rack/cache'
|
||||||
|
|
||||||
gem 'custom_fields', '1.0.0.beta.21'
|
gem 'custom_fields', '1.0.0.beta.22'
|
||||||
gem 'cancan'
|
gem 'cancan'
|
||||||
gem 'fog', '0.8.2'
|
gem 'fog', '0.8.2'
|
||||||
gem 'mimetype-fu'
|
gem 'mimetype-fu'
|
||||||
|
@ -87,7 +87,7 @@ GEM
|
|||||||
capybara (>= 1.0.0)
|
capybara (>= 1.0.0)
|
||||||
cucumber (~> 1.0.0)
|
cucumber (~> 1.0.0)
|
||||||
nokogiri (>= 1.4.6)
|
nokogiri (>= 1.4.6)
|
||||||
custom_fields (1.0.0.beta.21)
|
custom_fields (1.0.0.beta.22)
|
||||||
activesupport (~> 3.0.9)
|
activesupport (~> 3.0.9)
|
||||||
mongoid (= 2.0.2)
|
mongoid (= 2.0.2)
|
||||||
daemons (1.1.4)
|
daemons (1.1.4)
|
||||||
@ -296,7 +296,7 @@ DEPENDENCIES
|
|||||||
carrierwave (~> 0.5.5)
|
carrierwave (~> 0.5.5)
|
||||||
cells
|
cells
|
||||||
cucumber-rails (= 1.0.2)
|
cucumber-rails (= 1.0.2)
|
||||||
custom_fields (= 1.0.0.beta.21)
|
custom_fields (= 1.0.0.beta.22)
|
||||||
database_cleaner
|
database_cleaner
|
||||||
delayed_job (= 2.1.4)
|
delayed_job (= 2.1.4)
|
||||||
delayed_job_mongoid (= 1.0.2)
|
delayed_job_mongoid (= 1.0.2)
|
||||||
|
@ -11,16 +11,26 @@ module Admin
|
|||||||
|
|
||||||
before_filter :authorize_content
|
before_filter :authorize_content
|
||||||
|
|
||||||
|
helper_method :breadcrumb_root, :breadcrumb_url, :back_url
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@contents = @content_type.list_or_group_contents
|
@contents = @content_type.list_or_group_contents
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
new! { @content.attributes = params[:content] }
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
create! { edit_admin_content_url(@content_type.slug, @content.id) }
|
create! { after_create_or_update_url }
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
edit! { @content.attributes = params[:content] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
update! { edit_admin_content_url(@content_type.slug, @content.id) }
|
update! { after_create_or_update_url }
|
||||||
end
|
end
|
||||||
|
|
||||||
def sort
|
def sort
|
||||||
@ -43,9 +53,31 @@ module Admin
|
|||||||
set_content_type
|
set_content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_create_or_update_url
|
||||||
|
if params[:breadcrumb_alias].blank?
|
||||||
|
edit_admin_content_url(@content_type.slug, @content.id)
|
||||||
|
else
|
||||||
|
self.breadcrumb_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def authorize_content
|
def authorize_content
|
||||||
authorize! params[:action].to_sym, ContentInstance
|
authorize! params[:action].to_sym, ContentInstance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def breadcrumb_root
|
||||||
|
return nil if params[:breadcrumb_alias].blank?
|
||||||
|
|
||||||
|
@breadcrumb_root ||= resource.send(params[:breadcrumb_alias].to_sym)
|
||||||
|
end
|
||||||
|
|
||||||
|
def breadcrumb_url
|
||||||
|
edit_admin_content_url(self.breadcrumb_root._parent.slug, self.breadcrumb_root)
|
||||||
|
end
|
||||||
|
|
||||||
|
def back_url
|
||||||
|
self.breadcrumb_root ? self.breadcrumb_url : admin_contents_url(@content_type.slug)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -45,6 +45,30 @@ module Admin::CustomFieldsHelper
|
|||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def options_for_reverse_lookups(my_content_type)
|
||||||
|
klass_name = my_content_type.content_klass.to_s
|
||||||
|
|
||||||
|
[].tap do |options|
|
||||||
|
ContentType.where(:'content_custom_fields.kind' => 'has_one', :'content_custom_fields.target' => klass_name).each do |content_type|
|
||||||
|
content_type.content_custom_fields.find_all { |f| f.has_one? && f.target == klass_name }.each do |field|
|
||||||
|
options << {
|
||||||
|
:klass => content_type.content_klass.to_s,
|
||||||
|
:label => field.label,
|
||||||
|
:name => field._name
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_options_for_reverse_has_many(contents, reverse_lookup, object)
|
||||||
|
# Only display items which don't belong to a different object
|
||||||
|
contents.reject do |c|
|
||||||
|
owner = c.send(reverse_lookup.to_sym)
|
||||||
|
!(owner.nil? || owner == object._id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def options_for_has_one(field, value)
|
def options_for_has_one(field, value)
|
||||||
self.options_for_has_one_or_has_many(field) do |groups|
|
self.options_for_has_one_or_has_many(field) do |groups|
|
||||||
grouped_options_for_select(groups.collect do |g|
|
grouped_options_for_select(groups.collect do |g|
|
||||||
@ -57,16 +81,20 @@ module Admin::CustomFieldsHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def options_for_has_many(field)
|
def options_for_has_many(field, content = nil)
|
||||||
self.options_for_has_one_or_has_many(field)
|
self.options_for_has_one_or_has_many(field, content)
|
||||||
end
|
end
|
||||||
|
|
||||||
def options_for_has_one_or_has_many(field, &block)
|
def options_for_has_one_or_has_many(field, content = nil, &block)
|
||||||
content_type = field.target.constantize._parent
|
content_type = field.target.constantize._parent
|
||||||
|
|
||||||
if content_type.groupable?
|
if content_type.groupable?
|
||||||
grouped_contents = content_type.list_or_group_contents
|
grouped_contents = content_type.list_or_group_contents
|
||||||
|
|
||||||
|
grouped_contents.each do |g|
|
||||||
|
g[:items] = filter_options_for_reverse_has_many(g[:items], field.reverse_lookup, content)
|
||||||
|
end if field.reverse_has_many?
|
||||||
|
|
||||||
if block_given?
|
if block_given?
|
||||||
block.call(grouped_contents)
|
block.call(grouped_contents)
|
||||||
else
|
else
|
||||||
@ -80,8 +108,36 @@ module Admin::CustomFieldsHelper
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
contents = content_type.ordered_contents
|
contents = content_type.ordered_contents
|
||||||
|
|
||||||
|
if field.reverse_has_many?
|
||||||
|
contents = filter_options_for_reverse_has_many(contents, field.reverse_lookup, content)
|
||||||
|
end
|
||||||
|
|
||||||
contents.collect { |c| [c._label, c._id] }
|
contents.collect { |c| [c._label, c._id] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_many_data_to_js(field, content)
|
||||||
|
options = {
|
||||||
|
:taken_ids => content.send(field._alias.to_sym).ids
|
||||||
|
}
|
||||||
|
|
||||||
|
if !content.new_record? && field.reverse_has_many?
|
||||||
|
url_options = {
|
||||||
|
:breadcrumb_alias => field.reverse_lookup_alias,
|
||||||
|
"content[#{field.reverse_lookup_alias}]" => content._id
|
||||||
|
}
|
||||||
|
|
||||||
|
options.merge!(
|
||||||
|
:new_item => {
|
||||||
|
:label => t('admin.contents.form.has_many.new_item'),
|
||||||
|
:url => new_admin_content_url(field.target_klass._parent.slug, url_options)
|
||||||
|
},
|
||||||
|
:edit_item_url => edit_admin_content_url(field.target_klass._parent.slug, 42, url_options)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
collection_to_js(options_for_has_many(field, content), options)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,6 @@ class Site
|
|||||||
## Extensions ##
|
## Extensions ##
|
||||||
extend Extensions::Site::SubdomainDomains
|
extend Extensions::Site::SubdomainDomains
|
||||||
extend Extensions::Site::FirstInstallation
|
extend Extensions::Site::FirstInstallation
|
||||||
extend Extensions::Site::FirstInstallation
|
|
||||||
include Extensions::Shared::Seo
|
include Extensions::Shared::Seo
|
||||||
|
|
||||||
## fields ##
|
## fields ##
|
||||||
|
@ -18,4 +18,4 @@
|
|||||||
|
|
||||||
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug_was), :button_label => :update
|
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug_was), :button_label => :update
|
||||||
|
|
||||||
= render 'admin/custom_fields/edit_field'
|
= render 'admin/custom_fields/edit_field', :content_type => @content_type
|
@ -14,4 +14,4 @@
|
|||||||
|
|
||||||
= render 'admin/shared/form_actions', :back_url => admin_pages_url, :button_label => :create
|
= render 'admin/shared/form_actions', :back_url => admin_pages_url, :button_label => :create
|
||||||
|
|
||||||
= render 'admin/custom_fields/edit_field'
|
= render 'admin/custom_fields/edit_field', :content_type => @content_type
|
@ -1,4 +1,7 @@
|
|||||||
- title t('.title', :type => @content_type.name.capitalize)
|
- if breadcrumb_root
|
||||||
|
- title t('.title.breadcrumb', :root => link_to(breadcrumb_root._label, breadcrumb_url), :type => @content_type.name.capitalize)
|
||||||
|
- else
|
||||||
|
- title t('.title.default', :type => @content_type.name.capitalize)
|
||||||
|
|
||||||
- content_for :submenu do
|
- content_for :submenu do
|
||||||
= render 'admin/shared/menu/contents'
|
= render 'admin/shared/menu/contents'
|
||||||
@ -18,4 +21,4 @@
|
|||||||
|
|
||||||
= render 'form', :f => form
|
= render 'form', :f => form
|
||||||
|
|
||||||
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update
|
= render 'admin/shared/form_actions', :back_url => back_url, :button_label => :update
|
@ -1,4 +1,7 @@
|
|||||||
- title t('.title', :type => @content_type.name.capitalize)
|
- if breadcrumb_root
|
||||||
|
- title t('.title.breadcrumb', :root => link_to(breadcrumb_root._label, breadcrumb_url), :type => @content_type.name.capitalize)
|
||||||
|
- else
|
||||||
|
- title t('.title.default', :type => @content_type.name.capitalize)
|
||||||
|
|
||||||
- content_for :submenu do
|
- content_for :submenu do
|
||||||
= render 'admin/shared/menu/contents'
|
= render 'admin/shared/menu/contents'
|
||||||
@ -16,4 +19,4 @@
|
|||||||
|
|
||||||
= render 'form', :f => form
|
= render 'form', :f => form
|
||||||
|
|
||||||
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :create
|
= render 'admin/shared/form_actions', :back_url => back_url, :button_label => :create
|
@ -9,8 +9,12 @@
|
|||||||
= g.input :hint
|
= g.input :hint
|
||||||
= g.input :text_formatting, :as => 'select', :collection => options_for_text_formatting, :include_blank => false, :wrapper_html => { :style => 'display: none' }
|
= 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' }
|
= g.input :target, :as => 'select', :collection => options_for_association_target, :include_blank => false, :wrapper_html => { :style => 'display: none' }
|
||||||
|
= g.input :reverse_lookup, :as => 'select', :collection => [], :include_blank => true, :wrapper_html => { :style => 'display: none' }
|
||||||
|
|
||||||
.popup-actions
|
.popup-actions
|
||||||
%p
|
%p
|
||||||
%button.button.light{ :type => 'submit' }
|
%button.button.light{ :type => 'submit' }
|
||||||
%span= t('admin.shared.form_actions.update')
|
%span= t('admin.shared.form_actions.update')
|
||||||
|
|
||||||
|
%script{ :type => 'text/javascript', :name => 'reverse_lookups' }
|
||||||
|
!= collection_to_js(options_for_reverse_lookups(content_type))
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
%input{ :name => '{{base_name}}[target]', :value => '{{{target}}}', :type => 'hidden', :'data-field' => 'target' }
|
%input{ :name => '{{base_name}}[target]', :value => '{{{target}}}', :type => 'hidden', :'data-field' => 'target' }
|
||||||
|
|
||||||
|
%input{ :name => '{{base_name}}[reverse_lookup]', :value => '{{{reverse_lookup}}}', :type => 'hidden', :'data-field' => 'reverse_lookup' }
|
||||||
|
|
||||||
%input{ :name => '{{base_name}}[label]', :value => '{{{label}}}', :type => 'text', :'data-field' => 'label' }
|
%input{ :name => '{{base_name}}[label]', :value => '{{{label}}}', :type => 'text', :'data-field' => 'label' }
|
||||||
|
|
||||||
—
|
—
|
||||||
|
@ -25,9 +25,11 @@
|
|||||||
{{/if_template}}
|
{{/if_template}}
|
||||||
|
|
||||||
%span.actions
|
%span.actions
|
||||||
|
- if field.reverse_lookup?
|
||||||
|
= link_to image_tag('admin/form/pen.png'), '#', :class => 'edit first'
|
||||||
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove'
|
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove'
|
||||||
%button{ :class => 'button light mini add', :type => 'button' }
|
%button{ :class => 'button light mini add', :type => 'button' }
|
||||||
%span!= t('admin.buttons.new_item')
|
%span!= t('admin.buttons.new_item')
|
||||||
|
|
||||||
%script{ :type => 'text/javascript', :name => 'data' }
|
%script{ :type => 'text/javascript', :name => 'data' }
|
||||||
!= collection_to_js(options_for_has_many(field), :taken_ids => form.object.send(field._alias.to_sym).ids)
|
!= has_many_data_to_js(field, form.object)
|
@ -1,6 +1,14 @@
|
|||||||
- field.target.constantize.reload_parent! # to make sure all the contents from the parent are loaded
|
- field.target.constantize.reload_parent! # to make sure all the contents from the parent are loaded
|
||||||
|
|
||||||
- selected_id = form.object.send(field._alias.to_sym).try(:_id)
|
- if breadcrumb_root && params[:breadcrumb_alias] == field._alias
|
||||||
|
|
||||||
= 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_id), :selected => selected_id, :required => required
|
= form.hidden_field field._name.to_sym
|
||||||
|
|
||||||
|
= hidden_field_tag 'breadcrumb_alias', field._alias
|
||||||
|
|
||||||
|
- else
|
||||||
|
|
||||||
|
- selected_id = form.object.send(field._alias.to_sym).try(:_id)
|
||||||
|
|
||||||
|
= 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_id), :selected => selected_id, :required => required
|
||||||
|
|
||||||
|
@ -237,7 +237,6 @@ de:
|
|||||||
asc: Aufsteigend
|
asc: Aufsteigend
|
||||||
desc: Absteigend
|
desc: Absteigend
|
||||||
|
|
||||||
|
|
||||||
contents:
|
contents:
|
||||||
index:
|
index:
|
||||||
title: '"%{type}" anzeigen'
|
title: '"%{type}" anzeigen'
|
||||||
@ -251,9 +250,16 @@ de:
|
|||||||
list:
|
list:
|
||||||
no_items: "Momentan gibt es keine Elemente. Klicke einfach <a href='%{url}'>hier</a>, um das erste Element zu erstellen."
|
no_items: "Momentan gibt es keine Elemente. Klicke einfach <a href='%{url}'>hier</a>, um das erste Element zu erstellen."
|
||||||
new:
|
new:
|
||||||
title: '%{type} — neues Element'
|
title:
|
||||||
|
default: '%{type} — neues Element'
|
||||||
|
breadcrumb: '%{root} » %{type} — Element bearbeiten'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — Element bearbeiten'
|
title:
|
||||||
|
default: '%{type} — editing item'
|
||||||
|
breadcrumb: '%{root} » %{type} — Element bearbeiten'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: Neues Element
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Füge ein Bild in den Code ein
|
link: Füge ein Bild in den Code ein
|
||||||
|
@ -71,6 +71,8 @@ en:
|
|||||||
edit_categories: Edit options
|
edit_categories: Edit options
|
||||||
file:
|
file:
|
||||||
delete_file: Delete file
|
delete_file: Delete file
|
||||||
|
has_many:
|
||||||
|
empty: Empty
|
||||||
index:
|
index:
|
||||||
is_required: is required
|
is_required: is required
|
||||||
default_label: Field name
|
default_label: Field name
|
||||||
@ -246,9 +248,16 @@ en:
|
|||||||
list:
|
list:
|
||||||
no_items: "There are no items for now. Just click <a href=\"%{url}\">here</a> to create the first one."
|
no_items: "There are no items for now. Just click <a href=\"%{url}\">here</a> to create the first one."
|
||||||
new:
|
new:
|
||||||
title: '%{type} — new item'
|
title:
|
||||||
|
default: '%{type} — new item'
|
||||||
|
breadcrumb: '%{root} » %{type} — new item'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — editing item'
|
title:
|
||||||
|
default: '%{type} — editing item'
|
||||||
|
breadcrumb: '%{root} » %{type} — editing item'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: New item
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Insert an image into the code
|
link: Insert an image into the code
|
||||||
|
@ -232,9 +232,16 @@ es:
|
|||||||
list:
|
list:
|
||||||
no_items: "No hay ningún elemento. Haga click <a href=\"%{url}\">aquí</a> para crear el primero."
|
no_items: "No hay ningún elemento. Haga click <a href=\"%{url}\">aquí</a> para crear el primero."
|
||||||
new:
|
new:
|
||||||
title: '%{type} — nuevo elemento'
|
title:
|
||||||
|
default: '%{type} — nuevo elemento'
|
||||||
|
breadcrumb: '%{root} » %{type} — nuevo elemento'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — editando elemento'
|
title:
|
||||||
|
default: '%{type} — editando elemento'
|
||||||
|
breadcrumb: '%{root} » %{type} — editando elemento'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: Nuevo elemento
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Insertar una imagen el el código
|
link: Insertar una imagen el el código
|
||||||
|
@ -71,6 +71,8 @@ fr:
|
|||||||
edit_categories: Editer options
|
edit_categories: Editer options
|
||||||
file:
|
file:
|
||||||
delete_file: Supprimer fichier
|
delete_file: Supprimer fichier
|
||||||
|
has_many:
|
||||||
|
empty: Vide
|
||||||
index:
|
index:
|
||||||
is_required: est obligatoire
|
is_required: est obligatoire
|
||||||
default_label: Nom du champ
|
default_label: Nom du champ
|
||||||
@ -248,9 +250,16 @@ fr:
|
|||||||
list:
|
list:
|
||||||
no_items: "Il n'existe pas d'éléments. Vous pouvez commencer par créer un <a href='%{url}'>ici</a>"
|
no_items: "Il n'existe pas d'éléments. Vous pouvez commencer par créer un <a href='%{url}'>ici</a>"
|
||||||
new:
|
new:
|
||||||
title: '%{type} — nouvel élément'
|
title:
|
||||||
|
default: '%{type} — nouvel élément'
|
||||||
|
breadcrumb: '%{root} » %{type} — nouvel élément'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — édition élément'
|
title:
|
||||||
|
default: '%{type} — édition élément'
|
||||||
|
breadcrumb: '%{root} » %{type} — édition élément'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: Nouvel élément
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Insérer une image dans le code
|
link: Insérer une image dans le code
|
||||||
|
@ -245,9 +245,16 @@ it:
|
|||||||
list:
|
list:
|
||||||
no_items: "Per ora non ci sono elementi. Clicca <a href=\"%{url}\">qui</a> per creare il primo."
|
no_items: "Per ora non ci sono elementi. Clicca <a href=\"%{url}\">qui</a> per creare il primo."
|
||||||
new:
|
new:
|
||||||
title: '%{type} — nuovo elemento'
|
title:
|
||||||
|
default: '%{type} — nuovo elemento'
|
||||||
|
breadcrumb: '%{root} » %{type} — nuovo elemento'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — modifica elemento'
|
title:
|
||||||
|
default: '%{type} — modifica elemento'
|
||||||
|
breadcrumb: '%{root} » %{type} — modifica elemento'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: Nuovo elemento
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Inserisci un immagine nel codice
|
link: Inserisci un immagine nel codice
|
||||||
|
@ -230,9 +230,16 @@ nl:
|
|||||||
list:
|
list:
|
||||||
no_items: "Er zijn momenteel geen items. Klik hier <a href=\"%{url}\">here</a> om de eerste te maken."
|
no_items: "Er zijn momenteel geen items. Klik hier <a href=\"%{url}\">here</a> om de eerste te maken."
|
||||||
new:
|
new:
|
||||||
title: '%{type} — nieuw item'
|
title:
|
||||||
|
default: '%{type} — nieuw item'
|
||||||
|
breadcrumb: '%{root} » %{type} — nieuw item'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — wijzig item'
|
title:
|
||||||
|
default: '%{type} — wijzig item'
|
||||||
|
breadcrumb: '%{root} » %{type} — wijzig item'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: Nieuw item
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Voeg een afbeelding toe aan de code
|
link: Voeg een afbeelding toe aan de code
|
||||||
|
@ -226,9 +226,16 @@ pt-BR:
|
|||||||
list:
|
list:
|
||||||
no_items: "Não existem itens ainda. Clique <a href=\"%{url}\">aqui</a> para criar o primeiro."
|
no_items: "Não existem itens ainda. Clique <a href=\"%{url}\">aqui</a> para criar o primeiro."
|
||||||
new:
|
new:
|
||||||
title: '%{type} — novo item'
|
title:
|
||||||
|
default: '%{type} — novo item'
|
||||||
|
breadcrumb: '%{root} » %{type} — novo item'
|
||||||
edit:
|
edit:
|
||||||
title: '%{type} — editando item'
|
title:
|
||||||
|
default: '%{type} — editando item'
|
||||||
|
breadcrumb: '%{root} » %{type} — editando item'
|
||||||
|
form:
|
||||||
|
has_many:
|
||||||
|
new_item: Novo item
|
||||||
|
|
||||||
image_picker:
|
image_picker:
|
||||||
link: Insira uma imagem no código
|
link: Insira uma imagem no código
|
||||||
|
2
doc/TODO
2
doc/TODO
@ -21,7 +21,7 @@ BACKLOG:
|
|||||||
- sync data
|
- sync data
|
||||||
- import only theme assets
|
- import only theme assets
|
||||||
- endless pagination
|
- endless pagination
|
||||||
- overide sort for contents
|
- override sort for contents
|
||||||
- tooltip to explain the difference between 1.) Admin 2.) Author 3.) Designer?
|
- tooltip to explain the difference between 1.) Admin 2.) Author 3.) Designer?
|
||||||
- [bushido] guiders / welcome page / devise cas authentication (SSO)
|
- [bushido] guiders / welcome page / devise cas authentication (SSO)
|
||||||
|
|
||||||
|
61
features/admin/content_types/has_many_reverse.feature
Normal file
61
features/admin/content_types/has_many_reverse.feature
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
Feature: Set up a has many reverse relationship
|
||||||
|
In order to have a true 1:N relationship between 2 content types
|
||||||
|
As an administrator
|
||||||
|
I want to set up a reverse a has many relationship
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given I have the site: "test site" set up
|
||||||
|
And I have a custom model named "Clients" with
|
||||||
|
| label | kind | required |
|
||||||
|
| Name | string | true |
|
||||||
|
| Description | string | false |
|
||||||
|
And I have a custom model named "Projects" with
|
||||||
|
| label | kind | required | target |
|
||||||
|
| Name | string | true | |
|
||||||
|
| Description | text | false | |
|
||||||
|
| Client | has_one | false | Clients |
|
||||||
|
And I set up a reverse has_many relationship between "Clients" and "Projects"
|
||||||
|
And I have entries for "Clients" with
|
||||||
|
| name | description |
|
||||||
|
| Apple Inc | Lorem ipsum |
|
||||||
|
| NoCoffee | Lorem ipsum... |
|
||||||
|
And I have entries for "Projects" with
|
||||||
|
| name | description |
|
||||||
|
| My sexy project | Lorem ipsum |
|
||||||
|
| Foo project | Lorem ipsum... |
|
||||||
|
| Bar project | Lorem ipsum... |
|
||||||
|
|
||||||
|
And I am an authenticated user
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: I do not see the "Add Item" button for new parent
|
||||||
|
When I go to the "Clients" model creation page
|
||||||
|
Then "New item" should not be an option for "label"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: I attach already created items for an existing parent and save it
|
||||||
|
When I go to the "Clients" model list page
|
||||||
|
And I follow "Apple Inc"
|
||||||
|
And I wait until ".has-many-selector ul li.template" is visible
|
||||||
|
Then "My sexy project" should be an option for "label"
|
||||||
|
When I select "My sexy project" from "label"
|
||||||
|
And I press "+ add"
|
||||||
|
And "My sexy project" should not be an option for "label"
|
||||||
|
When I press "Save"
|
||||||
|
And I wait until ".has-many-selector ul li.template" is visible
|
||||||
|
Then "My sexy project" should not be an option for "label"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: I create a new item and attach it
|
||||||
|
When I go to the "Clients" model list page
|
||||||
|
And I follow "Apple Inc"
|
||||||
|
And I wait until ".has-many-selector ul li.template" is visible
|
||||||
|
And I press "+ add"
|
||||||
|
Then I should see "Apple Inc » Projects — new item"
|
||||||
|
And I should not see "Client" within the main form
|
||||||
|
When I fill in "Name" with "iPad"
|
||||||
|
And I press "Create"
|
||||||
|
Then I should see "Content was successfully created."
|
||||||
|
When I wait until ".has-many-selector ul li.template" is visible
|
||||||
|
Then I should see "iPad"
|
||||||
|
And "iPad" should not be an option for "label"
|
@ -2,12 +2,33 @@ Given %r{^I have a custom model named "([^"]*)" with$} do |name, fields|
|
|||||||
site = Site.first
|
site = Site.first
|
||||||
content_type = Factory.build(:content_type, :site => site, :name => name)
|
content_type = Factory.build(:content_type, :site => site, :name => name)
|
||||||
fields.hashes.each do |field|
|
fields.hashes.each do |field|
|
||||||
f = content_type.content_custom_fields.build field
|
if (target_name = field.delete('target')).present?
|
||||||
|
target_content_type = site.content_types.where(:name => target_name).first
|
||||||
|
field['target'] = target_content_type.content_klass.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
content_type.content_custom_fields.build field
|
||||||
end
|
end
|
||||||
content_type.valid?
|
content_type.valid?
|
||||||
content_type.save.should be_true
|
content_type.save.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Given /^I set up a reverse has_many relationship between "([^"]*)" and "([^"]*)"$/ do |name_1, name_2|
|
||||||
|
site = Site.first
|
||||||
|
|
||||||
|
content_type_1 = site.content_types.where(:name => name_1).first
|
||||||
|
content_type_2 = site.content_types.where(:name => name_2).first
|
||||||
|
|
||||||
|
content_type_1.content_custom_fields.build({
|
||||||
|
:label => name_2,
|
||||||
|
:kind => 'has_many',
|
||||||
|
:target => content_type_2.content_klass.to_s,
|
||||||
|
:reverse_lookup => content_type_2.content_klass.custom_field_alias_to_name(name_1.downcase.singularize)
|
||||||
|
})
|
||||||
|
|
||||||
|
content_type_1.save.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
Given %r{^I have "([^"]*)" as "([^"]*)" values of the "([^"]*)" model$} do |values, field, name|
|
Given %r{^I have "([^"]*)" as "([^"]*)" values of the "([^"]*)" model$} do |values, field, name|
|
||||||
content_type = ContentType.where(:name => name).first
|
content_type = ContentType.where(:name => name).first
|
||||||
field = content_type.content_custom_fields.detect { |f| f.label == field }
|
field = content_type.content_custom_fields.detect { |f| f.label == field }
|
||||||
@ -45,3 +66,4 @@ end
|
|||||||
Then %r{^I should see once the "([^"]*)" field$} do |field|
|
Then %r{^I should see once the "([^"]*)" field$} do |field|
|
||||||
page.all(:css, "#content_#{field.underscore.downcase}_input").size.should == 1
|
page.all(:css, "#content_#{field.underscore.downcase}_input").size.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,3 +5,14 @@ end
|
|||||||
Then /^I should get a download with the filename "([^\"]*)"$/ do |filename|
|
Then /^I should get a download with the filename "([^\"]*)"$/ do |filename|
|
||||||
page.response_headers['Content-Disposition'].should include("filename=\"#{filename}\"")
|
page.response_headers['Content-Disposition'].should include("filename=\"#{filename}\"")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
When /^I wait until "([^"]*)" is visible$/ do |selector|
|
||||||
|
page.has_css?("#{selector}", :visible => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^"([^"]*)" should( not)? be an option for "([^"]*)"(?: within "([^\"]*)")?$/ do |value, negate, field, selector|
|
||||||
|
with_scope(selector) do
|
||||||
|
expectation = negate ? :should_not : :should
|
||||||
|
field_labeled(field).first(:xpath, ".//option[text() = '#{value}']").send(expectation, be_present)
|
||||||
|
end
|
||||||
|
end
|
15
features/step_definitions/within_steps.rb
Normal file
15
features/step_definitions/within_steps.rb
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# http://mislav.uniqpath.com/2010/09/cuking-it-right/
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
# 'as a movie title in the results' => 'ol.movies h1',
|
||||||
|
# 'in a button' => 'button, input[type=submit]',
|
||||||
|
# 'in the navigation' => 'nav'
|
||||||
|
}.
|
||||||
|
each do |within, selector|
|
||||||
|
Then /^(.+) #{within}$/ do |step|
|
||||||
|
with_scope(selector) do
|
||||||
|
Then step
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -34,6 +34,9 @@ module NavigationHelpers
|
|||||||
when /the "(.*)" model list page/
|
when /the "(.*)" model list page/
|
||||||
content_type = Site.first.content_types.where(:name => $1).first
|
content_type = Site.first.content_types.where(:name => $1).first
|
||||||
admin_contents_path(content_type.slug)
|
admin_contents_path(content_type.slug)
|
||||||
|
when /the "(.*)" model creation page/
|
||||||
|
content_type = Site.first.content_types.where(:name => $1).first
|
||||||
|
new_admin_content_path(content_type.slug)
|
||||||
when /the "(.*)" model edition page/
|
when /the "(.*)" model edition page/
|
||||||
content_type = Site.first.content_types.where(:name => $1).first
|
content_type = Site.first.content_types.where(:name => $1).first
|
||||||
edit_admin_content_type_path(content_type)
|
edit_admin_content_type_path(content_type)
|
||||||
|
@ -11,6 +11,9 @@ module HtmlSelectorsHelpers
|
|||||||
when "the page"
|
when "the page"
|
||||||
"html > body"
|
"html > body"
|
||||||
|
|
||||||
|
when "the main form"
|
||||||
|
"form.formtastic"
|
||||||
|
|
||||||
# Add more mappings here.
|
# Add more mappings here.
|
||||||
# Here is an example that pulls values out of the Regexp:
|
# Here is an example that pulls values out of the Regexp:
|
||||||
#
|
#
|
||||||
|
@ -42,7 +42,7 @@ Gem::Specification.new do |s|
|
|||||||
s.add_dependency 'dragonfly', '~> 0.9.1'
|
s.add_dependency 'dragonfly', '~> 0.9.1'
|
||||||
s.add_dependency 'rack-cache'
|
s.add_dependency 'rack-cache'
|
||||||
|
|
||||||
s.add_dependency 'custom_fields', '1.0.0.beta.21'
|
s.add_dependency 'custom_fields', '1.0.0.beta.22'
|
||||||
s.add_dependency 'cancan', '~> 1.6.0'
|
s.add_dependency 'cancan', '~> 1.6.0'
|
||||||
s.add_dependency 'fog', '0.8.2'
|
s.add_dependency 'fog', '0.8.2'
|
||||||
s.add_dependency 'mimetype-fu'
|
s.add_dependency 'mimetype-fu'
|
||||||
|
@ -5,6 +5,7 @@ $(document).ready(function() {
|
|||||||
var template = wrapper.find('script[name=template]').html();
|
var template = wrapper.find('script[name=template]').html();
|
||||||
var baseInputName = wrapper.find('script[name=template]').attr('data-base-input-name');
|
var baseInputName = wrapper.find('script[name=template]').attr('data-base-input-name');
|
||||||
var data = eval(wrapper.find('script[name=data]').html());
|
var data = eval(wrapper.find('script[name=data]').html());
|
||||||
|
var reverseLookupData = eval($('script[name=reverse_lookups]').html());
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
var domFieldVal = function(domField, fieldName, val) {
|
var domFieldVal = function(domField, fieldName, val) {
|
||||||
@ -20,6 +21,39 @@ $(document).ready(function() {
|
|||||||
return (typeof(val) == 'undefined' ? domBoxAttr(fieldName).val() : domBoxAttr(fieldName).val(val));
|
return (typeof(val) == 'undefined' ? domBoxAttr(fieldName).val() : domBoxAttr(fieldName).val(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var setupReverseLookupDropdown = function(currentVal) {
|
||||||
|
var dropdown = $('#fancybox-inner #edit-custom-field #custom_fields_field_reverse_lookup');
|
||||||
|
|
||||||
|
// Get the target content_type
|
||||||
|
var targetClassEl = $('#fancybox-inner #edit-custom-field #custom_fields_field_target');
|
||||||
|
|
||||||
|
var callback = function() {
|
||||||
|
// Clear the reverse_lookup dropdown
|
||||||
|
dropdown.find('option').remove();
|
||||||
|
|
||||||
|
// Add the initial blank entry
|
||||||
|
dropdown.append($('<option>'));
|
||||||
|
|
||||||
|
// Go through the collection and add the appropriate elements
|
||||||
|
collection = reverseLookupData['collection'];
|
||||||
|
for (var i = 0; i < collection.length; i++) {
|
||||||
|
element = collection[i];
|
||||||
|
if (element.klass === targetClassEl.val()) {
|
||||||
|
var option = $('<option>', { value: element.name });
|
||||||
|
option.text(element.label);
|
||||||
|
dropdown.append(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(); // first call
|
||||||
|
|
||||||
|
targetClassEl.change(callback);
|
||||||
|
|
||||||
|
// Set initial value
|
||||||
|
dropdown.val(currentVal);
|
||||||
|
}
|
||||||
|
|
||||||
/* ___ Register all the different events when a field is added (destroy, edit details, ...etc) ___ */
|
/* ___ Register all the different events when a field is added (destroy, edit details, ...etc) ___ */
|
||||||
|
|
||||||
var registerFieldEvents = function(field, domField) {
|
var registerFieldEvents = function(field, domField) {
|
||||||
@ -50,7 +84,7 @@ $(document).ready(function() {
|
|||||||
// edit
|
// edit
|
||||||
domField.find('a.edit').click(function(e) {
|
domField.find('a.edit').click(function(e) {
|
||||||
var link = $(this);
|
var link = $(this);
|
||||||
var attributes = ['_alias', 'hint', 'text_formatting', 'target'];
|
var attributes = ['_alias', 'hint', 'text_formatting', 'target', 'reverse_lookup'];
|
||||||
|
|
||||||
$.fancybox({
|
$.fancybox({
|
||||||
titleShow: false,
|
titleShow: false,
|
||||||
@ -61,7 +95,7 @@ $(document).ready(function() {
|
|||||||
$.each(attributes, function(index, name) {
|
$.each(attributes, function(index, name) {
|
||||||
try {
|
try {
|
||||||
var val = domBoxAttrVal(name).trim();
|
var val = domBoxAttrVal(name).trim();
|
||||||
if (val != '') domFieldVal(domField, name, val);
|
if (val != '' || name == 'reverse_lookup') domFieldVal(domField, name, val);
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
});
|
});
|
||||||
domBoxAttr('text_formatting').parent().hide();
|
domBoxAttr('text_formatting').parent().hide();
|
||||||
@ -70,16 +104,28 @@ $(document).ready(function() {
|
|||||||
e.preventDefault(); e.stopPropagation();
|
e.preventDefault(); e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var reverseLookupInitialVal = null;
|
||||||
|
|
||||||
// copy current val to the form in the box
|
// copy current val to the form in the box
|
||||||
$.each(attributes, function(index, name) {
|
$.each(attributes, function(index, name) {
|
||||||
var val = domFieldVal(domField, name).trim();
|
var val = domFieldVal(domField, name).trim();
|
||||||
if (val == '' && name == '_alias') val = makeSlug(domFieldVal(domField, 'label'));
|
if (val == '' && name == '_alias') val = makeSlug(domFieldVal(domField, 'label'));
|
||||||
|
|
||||||
|
if (name == 'reverse_lookup') reverseLookupInitialVal = val;
|
||||||
|
|
||||||
domBoxAttrVal(name, val);
|
domBoxAttrVal(name, val);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (domFieldVal(domField, 'kind').toLowerCase() == 'text') domBoxAttr('text_formatting').parents('li').show();
|
if (domFieldVal(domField, 'kind').toLowerCase() == 'text') domBoxAttr('text_formatting').parents('li').show();
|
||||||
if (domFieldVal(domField, 'kind').toLowerCase() == 'has_one' ||
|
if (domFieldVal(domField, 'kind').toLowerCase() == 'has_one' ||
|
||||||
domFieldVal(domField, 'kind').toLowerCase() == 'has_many') domBoxAttr('target').parents('li').show();
|
domFieldVal(domField, 'kind').toLowerCase() == 'has_many') domBoxAttr('target').parents('li').show();
|
||||||
|
if (domFieldVal(domField, 'kind').toLowerCase() == 'has_many')
|
||||||
|
domBoxAttr('reverse_lookup').parents('li').show();
|
||||||
|
|
||||||
|
// Configure the reverse_lookup dropdown and populate it
|
||||||
|
setupReverseLookupDropdown(reverseLookupInitialVal);
|
||||||
|
|
||||||
|
setTimeout($.fancybox.resize, 1500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e.preventDefault(); e.stopPropagation();
|
e.preventDefault(); e.stopPropagation();
|
||||||
|
@ -9,6 +9,14 @@ $(document).ready(function() {
|
|||||||
var populateSelect = function(context) {
|
var populateSelect = function(context) {
|
||||||
context.select.find('optgroup, option').remove();
|
context.select.find('optgroup, option').remove();
|
||||||
|
|
||||||
|
if (context.data.new_item) {
|
||||||
|
var newItemInfo = context.data.new_item;
|
||||||
|
var option = new Option(newItemInfo.label, newItemInfo.url, true, true);
|
||||||
|
context.select.append(option);
|
||||||
|
|
||||||
|
context.select.append(new Option('-'.repeat(newItemInfo.label.length), '', false, false));
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < context.data.collection.length; i++) {
|
for (var i = 0; i < context.data.collection.length; i++) {
|
||||||
var obj = context.data.collection[i];
|
var obj = context.data.collection[i];
|
||||||
|
|
||||||
@ -19,7 +27,7 @@ $(document).ready(function() {
|
|||||||
for (var j = 0; j < obj.items.length; j++) {
|
for (var j = 0; j < obj.items.length; j++) {
|
||||||
var innerObj = obj.items[j];
|
var innerObj = obj.items[j];
|
||||||
if ($.inArray(innerObj[1], context.data.taken_ids) == -1) {
|
if ($.inArray(innerObj[1], context.data.taken_ids) == -1) {
|
||||||
optgroup.append(new Option(innerObj[0], innerObj[1], true, true));
|
optgroup.append(new Option(innerObj[0], innerObj[1], false, false));
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +36,7 @@ $(document).ready(function() {
|
|||||||
} else {
|
} else {
|
||||||
if ($.inArray(obj[1], context.data.taken_ids) == -1)
|
if ($.inArray(obj[1], context.data.taken_ids) == -1)
|
||||||
{
|
{
|
||||||
var option = new Option("", obj[1], true, true);
|
var option = new Option("", obj[1], false, false);
|
||||||
$(option).text(obj[0]);
|
$(option).text(obj[0]);
|
||||||
context.select.append(option);
|
context.select.append(option);
|
||||||
}
|
}
|
||||||
@ -71,6 +79,15 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var registerElementEvents = function(context, data, domElement) {
|
var registerElementEvents = function(context, data, domElement) {
|
||||||
|
// edit
|
||||||
|
domElement.find('a.edit').click(function(e) {
|
||||||
|
var url = context.data.edit_item_url.replace(/\/42\//, '/' + data.id + '/');
|
||||||
|
|
||||||
|
window.location.href = url;
|
||||||
|
|
||||||
|
e.preventDefault(); e.stopPropagation();
|
||||||
|
})
|
||||||
|
|
||||||
// remove
|
// remove
|
||||||
domElement.find('a.remove').click(function(e) {
|
domElement.find('a.remove').click(function(e) {
|
||||||
domElement.remove();
|
domElement.remove();
|
||||||
@ -91,6 +108,14 @@ $(document).ready(function() {
|
|||||||
label: context.select.find('option:selected').text()
|
label: context.select.find('option:selected').text()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (newElement.id == '') return;
|
||||||
|
|
||||||
|
if (newElement.id.match(/^http:\/\//)) {
|
||||||
|
window.location.href = newElement.id;
|
||||||
|
e.preventDefault(); e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
addId(context, newElement.id);
|
addId(context, newElement.id);
|
||||||
|
|
||||||
addElement(context, newElement, { refreshPosition: true });
|
addElement(context, newElement, { refreshPosition: true });
|
||||||
|
@ -17,6 +17,11 @@ function makeSlug(val, sep) { // code largely inspired by http://www.thewebsitet
|
|||||||
String.prototype.trim = function() {
|
String.prototype.trim = function() {
|
||||||
return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String.prototype.repeat = function(num) {
|
||||||
|
for (var i = 0, buf = ""; i < num; i++) buf += this;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
Object.size = function(obj) {
|
Object.size = function(obj) {
|
||||||
|
@ -87,9 +87,10 @@ div.asset-picker ul li .more { top: 8px; }
|
|||||||
/* ___ form action ___ */
|
/* ___ form action ___ */
|
||||||
|
|
||||||
#fancybox-inner .popup-actions {
|
#fancybox-inner .popup-actions {
|
||||||
position: absolute;
|
/* position: absolute;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
|
*/
|
||||||
height: 61px;
|
height: 61px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #8b8d9a;
|
background: #8b8d9a;
|
||||||
|
@ -492,6 +492,7 @@ form.formtastic fieldset ol li.has-many ul li.template span.actions {
|
|||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.formtastic fieldset ol li.has-many ul li.template span.actions a.edit,
|
||||||
form.formtastic fieldset ol li.has-many ul li.template span.actions a.remove {
|
form.formtastic fieldset ol li.has-many ul li.template span.actions a.remove {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,17 @@ body {
|
|||||||
border-bottom: 1px dotted #bbbbbd;
|
border-bottom: 1px dotted #bbbbbd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#content div.inner h2 a {
|
||||||
|
color: #1F82BC;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content div.inner h2 a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
#content div.inner h2 a.editable {
|
#content div.inner h2 a.editable {
|
||||||
padding: 2px 25px 2px 6px;
|
padding: 2px 25px 2px 6px;
|
||||||
text-decoration: none;
|
|
||||||
color: #1e1f26;
|
color: #1e1f26;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
@ -90,6 +98,7 @@ body {
|
|||||||
#content div.inner h2 a.editable:hover {
|
#content div.inner h2 a.editable:hover {
|
||||||
background: #fffbe5 url(/images/admin/form/pen.png) no-repeat right 5px;
|
background: #fffbe5 url(/images/admin/form/pen.png) no-repeat right 5px;
|
||||||
border-bottom: 1px dotted #efe4a5;
|
border-bottom: 1px dotted #efe4a5;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content div.inner h3 {
|
#content div.inner h3 {
|
||||||
|
Loading…
Reference in New Issue
Block a user