fix CSRF issues with tinymce and some ajax actions + begin to work on the roles feature powered by cancan (in progress, not stable)
This commit is contained in:
parent
33a29210ba
commit
09171555a7
1
Gemfile
1
Gemfile
@ -26,6 +26,7 @@ gem 'dragonfly', '~> 0.9.1'
|
||||
gem 'rack-cache', :require => 'rack/cache'
|
||||
|
||||
gem 'custom_fields', '1.0.0.beta.19'
|
||||
gem 'cancan'
|
||||
gem 'fog', '0.8.2'
|
||||
gem 'mimetype-fu'
|
||||
gem 'actionmailer-with-request', :require => 'actionmailer_with_request'
|
||||
|
@ -62,6 +62,7 @@ GEM
|
||||
highline (>= 1.6.1)
|
||||
json (>= 1.4.6)
|
||||
rest-client (>= 1.6.1)
|
||||
cancan (1.6.0)
|
||||
capybara (0.4.1.2)
|
||||
celerity (>= 0.7.9)
|
||||
culerity (>= 0.2.4)
|
||||
@ -280,6 +281,7 @@ DEPENDENCIES
|
||||
bson_ext (~> 1.3.0)
|
||||
bushido
|
||||
bushido_stub!
|
||||
cancan
|
||||
capybara
|
||||
cucumber (= 0.8.5)
|
||||
cucumber-rails
|
||||
|
@ -15,7 +15,7 @@ module Admin
|
||||
end
|
||||
|
||||
def create
|
||||
params[:asset] = { :name => params[:name], :source => params[:file] } if params[:file]
|
||||
@asset = current_site.assets.build(:name => params[:name], :source => params[:file])
|
||||
|
||||
create! do |success, failure|
|
||||
success.json do
|
||||
|
@ -9,11 +9,13 @@ module Admin
|
||||
|
||||
before_filter :require_site
|
||||
|
||||
load_and_authorize_resource
|
||||
|
||||
before_filter :validate_site_membership
|
||||
|
||||
before_filter :set_locale
|
||||
|
||||
helper_method :sections, :current_site_url, :site_url, :page_url
|
||||
helper_method :sections, :current_site_url, :site_url, :page_url, :current_ability
|
||||
|
||||
# https://rails.lighthouseapp.com/projects/8994/tickets/1905-apphelpers-within-plugin-not-being-mixed-in
|
||||
Dir[File.dirname(__FILE__) + "/../../helpers/**/*_helper.rb"].each do |file|
|
||||
@ -26,8 +28,26 @@ module Admin
|
||||
|
||||
respond_to :html
|
||||
|
||||
rescue_from CanCan::AccessDenied do |exception|
|
||||
puts "exception = #{exception.inspect}"
|
||||
|
||||
logger.debug "[CanCan::AccessDenied] #{exception.inspect}"
|
||||
|
||||
if request.xhr?
|
||||
render :json => { :error => exception.message }
|
||||
else
|
||||
flash[:alert] = exception.message
|
||||
|
||||
redirect_to admin_pages_url
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def current_ability
|
||||
@current_ability ||= Ability.new(current_admin, current_site)
|
||||
end
|
||||
|
||||
def require_admin
|
||||
authenticate_admin!
|
||||
end
|
||||
|
@ -7,8 +7,11 @@ module Admin
|
||||
|
||||
respond_to :json, :only => :update
|
||||
|
||||
# before_filter :authorize
|
||||
|
||||
def index
|
||||
@contents = @content_type.list_or_group_contents
|
||||
authorize! :index, ContentInstance
|
||||
end
|
||||
|
||||
def create
|
||||
|
56
app/models/ability.rb
Normal file
56
app/models/ability.rb
Normal file
@ -0,0 +1,56 @@
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
ROLES = %w(admin author designer)
|
||||
|
||||
def initialize(account, site)
|
||||
@account, @site = account, site
|
||||
|
||||
alias_action :index, :show, :edit, :update, :to => :touch
|
||||
|
||||
@membership = @site.memberships.where(:account_id => @account.id).first
|
||||
|
||||
return false if @membership.blank?
|
||||
|
||||
if @membership.admin?
|
||||
setup_admin_permissions!
|
||||
else
|
||||
setup_default_permissions!
|
||||
|
||||
setup_designer_permissions! if @membership.designer?
|
||||
|
||||
setup_author_permissions! if @membership.author?
|
||||
end
|
||||
end
|
||||
|
||||
def setup_default_permissions!
|
||||
cannot :manage, :all
|
||||
end
|
||||
|
||||
def setup_author_permissions!
|
||||
can :touch, [Page, ThemeAsset]
|
||||
can :sort, Page
|
||||
|
||||
can :manage, [ContentInstance, Asset]
|
||||
end
|
||||
|
||||
def setup_designer_permissions!
|
||||
can :manage, Page
|
||||
|
||||
can :manage, ContentInstance
|
||||
|
||||
can :manage, ContentType
|
||||
|
||||
can :manage, ThemeAsset
|
||||
|
||||
can :import, Site
|
||||
|
||||
can :point, Site
|
||||
|
||||
can :manage, Membership
|
||||
end
|
||||
|
||||
def setup_admin_permissions!
|
||||
can :manage, :all
|
||||
end
|
||||
end
|
@ -25,6 +25,8 @@ class Asset
|
||||
|
||||
## methods ##
|
||||
|
||||
alias :name :source_filename
|
||||
|
||||
def extname
|
||||
return nil unless self.source?
|
||||
File.extname(self.source_filename).gsub(/^\./, '')
|
||||
|
@ -6,7 +6,7 @@ module Extensions
|
||||
def create_first_one(attributes)
|
||||
site = self.new(attributes)
|
||||
|
||||
site.memberships.build :account => Account.first, :admin => true
|
||||
site.memberships.build :account => Account.first, :role => 'admin'
|
||||
|
||||
site.save
|
||||
|
||||
|
@ -3,7 +3,8 @@ class Membership
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :admin, :type => Boolean, :default => false
|
||||
# field :admin, :type => Boolean, :default => false
|
||||
field :role, :default => 'author'
|
||||
|
||||
## associations ##
|
||||
referenced_in :account, :validate => false
|
||||
@ -12,8 +13,17 @@ class Membership
|
||||
## validations ##
|
||||
validates_presence_of :account
|
||||
|
||||
## callbacks ##
|
||||
before_save :define_role
|
||||
|
||||
## methods ##
|
||||
|
||||
Ability::ROLES.each do |_role|
|
||||
define_method("#{_role}?") do
|
||||
self.role == _role
|
||||
end
|
||||
end
|
||||
|
||||
def email; @email; end
|
||||
|
||||
def email=(email)
|
||||
@ -36,4 +46,14 @@ class Membership
|
||||
end
|
||||
end
|
||||
|
||||
def ability
|
||||
@ability ||= Ability.new(self.account, self.site)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def define_role
|
||||
self.role = Ability::ROLES.include?(role.downcase) ? role.downcase : Ability::ROLES.first
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -28,6 +28,7 @@ class Site
|
||||
|
||||
## behaviours ##
|
||||
enable_subdomain_n_domains_if_multi_sites
|
||||
accepts_nested_attributes_for :memberships
|
||||
|
||||
## methods ##
|
||||
|
||||
|
@ -41,12 +41,27 @@
|
||||
%button{ :class => 'button light add', :type => 'button' }
|
||||
%span!= t('admin.buttons.new_item')
|
||||
|
||||
= f.foldable_inputs :name => :memberships, :class => 'memberships' do
|
||||
- @site.memberships.each_with_index do |membership, index|
|
||||
- account = membership.account
|
||||
%li{ :class => "item #{'last' if index == @site.memberships.size - 1}" }
|
||||
- if current_ability.can?(:touch, Membership)
|
||||
|
||||
= f.foldable_inputs :name => :memberships, :class => 'memberships off' do
|
||||
= f.semantic_fields_for :memberships do |fm|
|
||||
|
||||
- membership, account = fm.object, fm.object.account
|
||||
|
||||
%li.item.membership{ :'data-role' => membership.role }
|
||||
%strong= account.name
|
||||
%em= account.email
|
||||
- if account != current_admin
|
||||
|
||||
%em.email= account.email
|
||||
|
||||
- if current_admin != account && current_ability.can?(:touch, Membership)
|
||||
.role
|
||||
%em.editable= t("admin.memberships.roles.#{membership.role}")
|
||||
|
||||
= fm.select :role, Ability::ROLES.collect { |r| [t("admin.memberships.roles.#{r}"), r] }, :include_blank => false
|
||||
|
||||
%span.actions
|
||||
= link_to image_tag('admin/form/icons/trash.png'), admin_membership_url(membership), :class => 'remove first', :confirm => t('admin.messages.confirm'), :method => :delete
|
||||
= link_to image_tag('admin/form/icons/trash.png'), admin_membership_url(membership), :class => 'remove first', :confirm =>t('admin.messages.confirm'), :method => :delete
|
||||
|
||||
- else
|
||||
.role
|
||||
%em.locked= t("admin.memberships.roles.#{membership.role}")
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
—
|
||||
|
||||
%em {{kind_name}}
|
||||
%em.editable {{kind_name}}
|
||||
|
||||
= select_tag '{{base_name}}[kind]', options_for_select(options_for_field_kind), :'data-field' => 'kind'
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
= include_javascripts :image_picker, :edit_page
|
||||
= include_stylesheets :editable_elements, :fancybox
|
||||
|
||||
= f.foldable_inputs :name => :information do
|
||||
- if can?(:manage, @page)
|
||||
|
||||
= f.foldable_inputs :name => :information do
|
||||
|
||||
= f.input :title
|
||||
|
||||
@ -11,13 +13,17 @@
|
||||
|
||||
= f.input :slug, :required => false, :hint => @page.slug.blank? ? ' ' : page_url(@page), :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}; height: 50px" }
|
||||
|
||||
= render 'editable_elements', :page => @page
|
||||
|
||||
= f.foldable_inputs :name => :seo do
|
||||
|
||||
= f.input :seo_title
|
||||
= f.input :meta_keywords
|
||||
= f.input :meta_description
|
||||
|
||||
= f.foldable_inputs :name => :advanced_options do
|
||||
- if can?(:manage, @page)
|
||||
|
||||
= f.foldable_inputs :name => :advanced_options do
|
||||
|
||||
= f.input :content_type_id, :as => :select, :collection => current_site.content_types.all.to_a, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}; height: 50px" }
|
||||
|
||||
@ -37,9 +43,8 @@
|
||||
|
||||
= f.input :redirect_url, :required => true, :wrapper_html => { :style => "#{'display: none' unless @page.redirect?}" }
|
||||
|
||||
= render 'editable_elements', :page => @page
|
||||
|
||||
= f.foldable_inputs :name => :raw_template do
|
||||
= f.foldable_inputs :name => :raw_template do
|
||||
= f.custom_input :value, :css => 'code full', :with_label => false do
|
||||
= f.label :raw_template
|
||||
%code{ :class => 'html' }
|
||||
|
@ -1,5 +1,8 @@
|
||||
%li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? } #{'templatized' if page.templatized?}"}
|
||||
- with_children = !page.children.empty?
|
||||
|
||||
- children = can?(:manage, page) ? page.children : page.children.find_all { |p| !p.templatized? }
|
||||
|
||||
- with_children = !children.empty?
|
||||
|
||||
- if not page.index? and with_children
|
||||
= image_tag 'admin/list/icons/node_closed.png', :class => 'toggler'
|
||||
@ -10,9 +13,10 @@
|
||||
%span!= t('.updated_at')
|
||||
= l page.updated_at, :format => :short
|
||||
|
||||
- if not page.index? and not page.not_found?
|
||||
- if !page.index_or_not_found? && can?(:manage, page)
|
||||
= link_to image_tag('admin/list/icons/trash.png'), admin_page_url(page), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
|
||||
|
||||
- if with_children
|
||||
%ul{ :id => "folder-#{page._id}", :class => "folder depth-#{(page.depth || 0) + 1}", :data_url => sort_admin_page_url(page), :style => "display: #{cookies["folder-#{page._id}"] || 'block'}" }
|
||||
= render page.children
|
||||
|
||||
= render children
|
@ -9,7 +9,8 @@
|
||||
- content_for :actions do
|
||||
= render 'admin/shared/actions/contents'
|
||||
|
||||
- content_for :buttons do
|
||||
- if can? :create, Page
|
||||
- content_for :buttons do
|
||||
= admin_button_tag :new, new_admin_page_url, :class => 'new'
|
||||
|
||||
%p!= t('.help')
|
||||
|
@ -1 +1,2 @@
|
||||
= link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url
|
||||
- if can? :manage, ContentType
|
||||
= link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url
|
@ -1,4 +1,5 @@
|
||||
= admin_submenu_item 'pages', admin_pages_url do
|
||||
- if can? :manage, @page
|
||||
.header
|
||||
%p= link_to t('admin.pages.index.new'), new_admin_page_url
|
||||
.inner
|
||||
@ -12,7 +13,10 @@
|
||||
- each_content_type_menu_item do |content_type|
|
||||
.header
|
||||
%p= link_to t('admin.contents.index.new'), new_admin_content_url(content_type.slug)
|
||||
|
||||
- if can? :manage, content_type
|
||||
%p.edit= link_to t('admin.contents.index.edit'), edit_admin_content_type_url(content_type)
|
||||
|
||||
.inner
|
||||
%h2!= t('admin.contents.index.lastest_items')
|
||||
%ul
|
||||
|
@ -138,10 +138,14 @@ en:
|
||||
edit:
|
||||
import: import
|
||||
new_membership: add account
|
||||
help: "The site name can be updated by clicking it."
|
||||
help: "The site name can be updated by clicking it. To apply your changes, click on the \"Update\" button."
|
||||
ask_for_name: "Please type the new site name"
|
||||
|
||||
memberships:
|
||||
roles:
|
||||
admin: Administrator
|
||||
designer: Designer
|
||||
author: Author
|
||||
new:
|
||||
title: New membership
|
||||
help: "Please give the account email to add. If it does not exist, you will be redirected to the account creation form."
|
||||
@ -153,7 +157,7 @@ en:
|
||||
|
||||
my_accounts:
|
||||
edit:
|
||||
help: "Your name can be updated by clicking it."
|
||||
help: "Your name can be updated by clicking it. To apply your changes, click on the \"Update\" button."
|
||||
new_site: new site
|
||||
en: English
|
||||
de: German
|
||||
|
@ -138,7 +138,7 @@ fr:
|
||||
edit:
|
||||
import: importer
|
||||
new_membership: ajouter compte
|
||||
help: "Le nom du site est modifiable en cliquant dessus."
|
||||
help: "Le nom du site est modifiable en cliquant dessus. Pour appliquer votre modification, cliquez après sur le bouton \"Modifier\""
|
||||
ask_for_name: "Veuillez entrer le nouveau nom"
|
||||
|
||||
memberships:
|
||||
@ -153,7 +153,7 @@ fr:
|
||||
|
||||
my_accounts:
|
||||
edit:
|
||||
help: "Votre nom est modifiable en cliquant dessus."
|
||||
help: "Votre nom est modifiable en cliquant dessus. Pour appliquer votre modification, cliquez après sur le bouton \"Modifier\""
|
||||
new_site: nouveau site
|
||||
en: en Anglais
|
||||
de: en Allemand
|
||||
|
17
doc/TODO
17
doc/TODO
@ -31,9 +31,22 @@ x Has_one => group by in the select
|
||||
x better hints:
|
||||
x notify the user that after changing the page title, they still have to click "update" for the change to be saved
|
||||
x created_by ASC => "Creation date ascending"
|
||||
- cancan: authors / designers
|
||||
- cancan: (authors / designers / admin)
|
||||
x model
|
||||
x ui
|
||||
- controllers / views:
|
||||
- page
|
||||
- asset
|
||||
- content type
|
||||
- site
|
||||
- account
|
||||
- snippet
|
||||
- theme asset
|
||||
- better ui: increase text field length + refactor error message
|
||||
- convert existing templates (the 2 of the themes section)
|
||||
- bug heroku: unable to upload a new file
|
||||
- bugs
|
||||
- heroku: unable to upload a new file
|
||||
- import
|
||||
|
||||
BACKLOG:
|
||||
|
||||
|
@ -13,6 +13,22 @@ namespace :locomotive do
|
||||
|
||||
namespace :upgrade do
|
||||
|
||||
desc 'Set roles to the existing users'
|
||||
task :set_roles => :environment do
|
||||
Site.all.each do |site|
|
||||
site.memberships.each do |membership|
|
||||
if membership.attributes['admin'] == true
|
||||
puts "...[#{site.name}] #{membership.account.name} has now the admin role"
|
||||
membership.role = 'admin'
|
||||
else
|
||||
puts "...[#{site.name}] #{membership.account.name} has now the author role"
|
||||
membership.role = 'author'
|
||||
end
|
||||
end
|
||||
site.save
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Remove asset collections and convert them into content types'
|
||||
task :remove_asset_collections => :environment do
|
||||
puts "Processing #{AssetCollection.count} asset collection(s)..."
|
||||
|
BIN
public/images/admin/icons/membership_edit.png
Normal file
BIN
public/images/admin/icons/membership_edit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 302 B |
BIN
public/images/admin/icons/membership_lock.png
Normal file
BIN
public/images/admin/icons/membership_lock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 219 B |
@ -24,6 +24,7 @@ $(document).ready(function() {
|
||||
'update': function(event, ui) {
|
||||
var params = $(this).sortable('serialize', { 'key': 'children[]' });
|
||||
params += '&_method=put';
|
||||
params += '&' + $('meta[name=csrf-param]').attr('content') + '=' + $('meta[name=csrf-token]').attr('content');
|
||||
|
||||
$.post($(this).attr('data_url'), params, function(data) {
|
||||
var error = typeof(data.error) != 'undefined';
|
||||
|
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="#" class="remove" data-confirm="{#locomedia_dlg.confirm}" data-method="delete" rel="nofollow">
|
||||
<a href="#" class="remove" data-remote="true" data-confirm="{#locomedia_dlg.confirm}" data-method="delete" rel="nofollow">
|
||||
<img src="/images/admin/list/icons/cross.png">
|
||||
</a>
|
||||
</div>
|
||||
|
@ -1 +1 @@
|
||||
(function(){tinymce.create('tinymce.plugins.LocoMediaPlugin',{init:function(ed,url){ed.addCommand('locoMedia',function(){ed.windowManager.open({file:url+'/dialog.htm',width:645,height:650,inline:1},{plugin_url:url})});ed.addButton('locomedia',{title:'locomedia.image_desc',cmd:'locoMedia'})},getInfo:function(){return{longname:'Locomotive Media File',author:'Didier Lafforgue',authorurl:'http://www.locomotivecms.com',infourl:'http://www.locomotivecms.com',version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add('locomedia',tinymce.plugins.LocoMediaPlugin)})();
|
||||
(function(){tinymce.create('tinymce.plugins.LocoMediaPlugin',{init:function(ed,url){ed.addCommand('locoMedia',function(){ed.windowManager.open({file:url+'/dialog.htm?7',width:645,height:650,inline:1},{plugin_url:url})});ed.addButton('locomedia',{title:'locomedia.image_desc',cmd:'locoMedia'})},getInfo:function(){return{longname:'Locomotive Media File',author:'Didier Lafforgue',authorurl:'http://www.locomotivecms.com',infourl:'http://www.locomotivecms.com',version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add('locomedia',tinymce.plugins.LocoMediaPlugin)})();
|
@ -15,6 +15,13 @@ var MediafileDialog = {
|
||||
init : function(ed) {
|
||||
var self = this;
|
||||
|
||||
with(window.parent) {
|
||||
var csrf_token = $('meta[name=csrf-token]').attr('content'),
|
||||
csrf_param = $('meta[name=csrf-param]').attr('content');
|
||||
}
|
||||
|
||||
$.fn.setCsrfSettings(csrf_token, csrf_param);
|
||||
|
||||
formElement = $(document.forms[0]);
|
||||
|
||||
listElement = formElement.find('ul');
|
||||
@ -153,13 +160,6 @@ var MediafileDialog = {
|
||||
|
||||
asset.find('.actions a')
|
||||
.attr('href', data.destroy_url)
|
||||
.bind('click', function(e) {
|
||||
if (confirm($(this).attr('data-confirm'))) {
|
||||
self.showSpinner('destroying');
|
||||
$(this).callRemote();
|
||||
}
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
})
|
||||
.bind('ajax:success', function(event, data) {
|
||||
self._destroyAsset(asset);
|
||||
});
|
||||
|
@ -3,6 +3,12 @@ jQuery(function ($) {
|
||||
csrf_param = $('meta[name=csrf-param]').attr('content');
|
||||
|
||||
$.fn.extend({
|
||||
|
||||
setCsrfSettings: function(token, param) {
|
||||
csrf_token = token;
|
||||
csrf_param = param;
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers a custom event on an element and returns the event result
|
||||
* this is used to get around not being able to ensure callbacks are placed
|
||||
@ -32,6 +38,10 @@ jQuery(function ($) {
|
||||
} else {
|
||||
if (el.triggerAndReturn('ajax:before')) {
|
||||
var data = el.is('form') ? el.serializeArray() : [];
|
||||
|
||||
if (!el.is('form') && method != 'GET')
|
||||
data.push({ 'name': csrf_param, 'value': csrf_token });
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
data: data,
|
||||
|
@ -46,4 +46,29 @@ $(document).ready(function() {
|
||||
$('#header h1 a span.ui-selectmenu-status').html(value);
|
||||
$('#site-selector-menu li.ui-selectmenu-item-selected a').html(value);
|
||||
}, []);
|
||||
|
||||
// account roles
|
||||
$('.membership .role em.editable').click(function() {
|
||||
$(this).hide();
|
||||
$(this).next().show();
|
||||
});
|
||||
|
||||
$('.membership .role select').each(function() {
|
||||
var select = $(this);
|
||||
select.hover(function() {
|
||||
clearTimeout($.data(select, 'timer'));
|
||||
},
|
||||
function() {
|
||||
$.data(select, 'timer', setTimeout(function() {
|
||||
select.hide();
|
||||
select.prev().show();
|
||||
}, 1000));
|
||||
}).change(function() {
|
||||
select.hide().prev()
|
||||
.show()
|
||||
.html(select[0].options[select[0].options.selectedIndex].text);
|
||||
});
|
||||
}).hide();
|
||||
|
||||
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ form.formtastic legend span {
|
||||
color: #1e1f26;
|
||||
font-size: 0.7em;
|
||||
padding: 4px 0 0 20px;
|
||||
text-shadow: #fff 0px 1px;
|
||||
}
|
||||
|
||||
form.formtastic legend span small {
|
||||
@ -246,6 +247,28 @@ form.formtastic fieldset ol li.item em {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li em.editable {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
color: #8b8d9a;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-left: 3px;
|
||||
border: 1px solid transparent;
|
||||
padding: 2px 5px;
|
||||
height: 18px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li em.editable:hover {
|
||||
background: #fffbe5;
|
||||
border: 1px dotted #efe4a5;
|
||||
cursor: pointer;
|
||||
color: #17171D;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.item span.actions {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
@ -281,27 +304,6 @@ form.formtastic fieldset.editable-list ol li.added select {
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added em {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
color: #8b8d9a;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-left: 3px;
|
||||
border: 1px solid transparent;
|
||||
padding: 2px 5px;
|
||||
height: 18px;
|
||||
line-height: 16px;
|
||||
}
|
||||
form.formtastic fieldset.editable-list ol li.added em:hover {
|
||||
background: #fffbe5;
|
||||
border: 1px dotted #efe4a5;
|
||||
cursor: pointer;
|
||||
color: #17171D;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added select,
|
||||
form.formtastic fieldset.editable-list ol li.added em {
|
||||
width: 150px;
|
||||
@ -552,6 +554,42 @@ form.formtastic fieldset.email li.full input {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.memberships ol li .role {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 30px;
|
||||
width: 170px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.memberships ol li .role em {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
color: #757575;
|
||||
font-size: 0.8em;
|
||||
padding: 2px 5px 2px 17px;
|
||||
height: 18px;
|
||||
line-height: 16px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.memberships ol li .role em.locked {
|
||||
background: transparent url(/images/admin/icons/membership_lock.png) no-repeat 1px 3px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.memberships ol li .role em.editable {
|
||||
background: transparent url(/images/admin/icons/membership_edit.png) no-repeat left 3px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.memberships ol li .role em.editable { font-style: normal; font-size: 0.8em; }
|
||||
form.formtastic fieldset.memberships ol li .role em.editable:hover { color: #000; font-style: normal; background: #fffbe5; padding-left: 5px; }
|
||||
|
||||
form.formtastic fieldset.memberships ol li select {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* ___ assets ___ */
|
||||
|
||||
.selector {
|
||||
|
@ -154,17 +154,19 @@
|
||||
text-decoration: none; }
|
||||
#submenu .popup a:hover {
|
||||
text-decoration: underline; }
|
||||
#submenu .popup .header {
|
||||
border-bottom: 1px dotted #bbbbbd;
|
||||
padding-bottom: 6px;
|
||||
margin: 0px 16px; }
|
||||
#submenu .popup .inner {
|
||||
padding: 8px 16px; }
|
||||
#submenu .popup h2 {
|
||||
font-size: 0.7em;
|
||||
font-weight: bold;
|
||||
color: #1e1f26;
|
||||
border-top: 1px dotted #bbbbbd;
|
||||
padding-top: 6px;
|
||||
margin-bottom: 0px; }
|
||||
#submenu .popup p {
|
||||
margin: 0px 15px;
|
||||
margin: 0px;
|
||||
padding: 10px 0 0 0px; }
|
||||
#submenu .popup p a {
|
||||
font-size: 0.8em;
|
||||
|
@ -164,19 +164,23 @@
|
||||
&:hover { text-decoration: underline; }
|
||||
}
|
||||
|
||||
.header {
|
||||
border-bottom: 1px dotted #bbbbbd;
|
||||
padding-bottom: 6px;
|
||||
margin: 0px 16px;
|
||||
}
|
||||
|
||||
.inner { padding: 8px 16px; }
|
||||
|
||||
h2 {
|
||||
font-size: 0.7em;
|
||||
font-weight: bold;
|
||||
color: #1e1f26;
|
||||
border-top: 1px dotted #bbbbbd;
|
||||
padding-top: 6px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0px 15px;
|
||||
margin: 0px;
|
||||
padding: 10px 0 0 0px;
|
||||
|
||||
a {
|
||||
|
Loading…
Reference in New Issue
Block a user