From 09171555a76f6abca3e599b0b0cb6818b5a2323a Mon Sep 17 00:00:00 2001 From: did Date: Sat, 25 Jun 2011 09:25:31 -0700 Subject: [PATCH] fix CSRF issues with tinymce and some ajax actions + begin to work on the roles feature powered by cancan (in progress, not stable) --- Gemfile | 1 + Gemfile.lock | 2 + app/controllers/admin/assets_controller.rb | 2 +- app/controllers/admin/base_controller.rb | 22 ++++- app/controllers/admin/contents_controller.rb | 3 + app/models/ability.rb | 56 ++++++++++++ app/models/asset.rb | 2 + .../extensions/site/first_installation.rb | 2 +- app/models/membership.rb | 22 ++++- app/models/site.rb | 1 + app/views/admin/current_sites/_form.html.haml | 33 ++++++-- .../admin/custom_fields/_index.html.haml | 2 +- app/views/admin/pages/_form.html.haml | 57 +++++++------ app/views/admin/pages/_page.html.haml | 10 ++- app/views/admin/pages/index.html.haml | 7 +- .../admin/shared/actions/_contents.html.haml | 3 +- .../admin/shared/menu/_contents.html.haml | 10 ++- config/locales/admin_ui.en.yml | 8 +- config/locales/admin_ui.fr.yml | 4 +- doc/TODO | 17 +++- lib/tasks/locomotive.rake | 16 ++++ public/images/admin/icons/membership_edit.png | Bin 0 -> 302 bytes public/images/admin/icons/membership_lock.png | Bin 0 -> 219 bytes public/javascripts/admin/pages.js | 1 + .../tiny_mce/plugins/locomedia/dialog.htm | 2 +- .../plugins/locomedia/editor_plugin.js | 2 +- .../tiny_mce/plugins/locomedia/js/dialog.js | 14 +-- public/javascripts/admin/rails.js | 10 +++ public/javascripts/admin/site.js | 25 ++++++ .../stylesheets/admin/formtastic_changes.css | 80 +++++++++++++----- public/stylesheets/admin/menu.css | 8 +- public/stylesheets/sass/admin/menu.scss | 10 ++- 32 files changed, 340 insertions(+), 92 deletions(-) create mode 100644 app/models/ability.rb create mode 100644 public/images/admin/icons/membership_edit.png create mode 100644 public/images/admin/icons/membership_lock.png diff --git a/Gemfile b/Gemfile index fd65e34c..4ac6f6d6 100644 --- a/Gemfile +++ b/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' diff --git a/Gemfile.lock b/Gemfile.lock index 43f83ea4..db0a66c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/app/controllers/admin/assets_controller.rb b/app/controllers/admin/assets_controller.rb index 6da6f544..4daf9015 100644 --- a/app/controllers/admin/assets_controller.rb +++ b/app/controllers/admin/assets_controller.rb @@ -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 diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 8277040e..4e12e6a5 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -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 diff --git a/app/controllers/admin/contents_controller.rb b/app/controllers/admin/contents_controller.rb index 8fe2813c..acf18055 100644 --- a/app/controllers/admin/contents_controller.rb +++ b/app/controllers/admin/contents_controller.rb @@ -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 diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 00000000..497c0415 --- /dev/null +++ b/app/models/ability.rb @@ -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 diff --git a/app/models/asset.rb b/app/models/asset.rb index a0e323f5..9305f5cf 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -25,6 +25,8 @@ class Asset ## methods ## + alias :name :source_filename + def extname return nil unless self.source? File.extname(self.source_filename).gsub(/^\./, '') diff --git a/app/models/extensions/site/first_installation.rb b/app/models/extensions/site/first_installation.rb index 4e9473c3..3679f663 100644 --- a/app/models/extensions/site/first_installation.rb +++ b/app/models/extensions/site/first_installation.rb @@ -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 diff --git a/app/models/membership.rb b/app/models/membership.rb index a3f3a6b7..f9d9d8a6 100644 --- a/app/models/membership.rb +++ b/app/models/membership.rb @@ -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 diff --git a/app/models/site.rb b/app/models/site.rb index 19c9dde8..3d18dfcc 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -28,6 +28,7 @@ class Site ## behaviours ## enable_subdomain_n_domains_if_multi_sites + accepts_nested_attributes_for :memberships ## methods ## diff --git a/app/views/admin/current_sites/_form.html.haml b/app/views/admin/current_sites/_form.html.haml index 79f633de..5e387acd 100644 --- a/app/views/admin/current_sites/_form.html.haml +++ b/app/views/admin/current_sites/_form.html.haml @@ -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}" } - %strong= account.name - %em= account.email - - if account != current_admin - %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 \ No newline at end of file +- 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.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 + + - else + .role + %em.locked= t("admin.memberships.roles.#{membership.role}") diff --git a/app/views/admin/custom_fields/_index.html.haml b/app/views/admin/custom_fields/_index.html.haml index c7c60283..4a5f1d29 100644 --- a/app/views/admin/custom_fields/_index.html.haml +++ b/app/views/admin/custom_fields/_index.html.haml @@ -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' diff --git a/app/views/admin/pages/_form.html.haml b/app/views/admin/pages/_form.html.haml index bfcc7e7d..a26dd326 100644 --- a/app/views/admin/pages/_form.html.haml +++ b/app/views/admin/pages/_form.html.haml @@ -2,14 +2,18 @@ = include_javascripts :image_picker, :edit_page = include_stylesheets :editable_elements, :fancybox -= f.foldable_inputs :name => :information do +- if can?(:manage, @page) - = f.input :title + = f.foldable_inputs :name => :information do - - if not @page.index? and not @page.not_found? - = f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false + = f.input :title - = 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" } + - if not @page.index? and not @page.not_found? + = f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false + + = 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 @@ -17,33 +21,34 @@ = f.input :meta_keywords = f.input :meta_description -= f.foldable_inputs :name => :advanced_options do +- if can?(:manage, @page) - = 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" } + = f.foldable_inputs :name => :advanced_options do - = f.custom_input :templatized, :css => 'toggle', :style => "#{'display: none' if @page.redirect?}" do - = f.check_box :templatized + = 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" } - = f.custom_input :published, :css => 'toggle' do - = f.check_box :published + = f.custom_input :templatized, :css => 'toggle', :style => "#{'display: none' if @page.redirect?}" do + = f.check_box :templatized - = f.custom_input :listed, :css => 'toggle' do - = f.check_box :listed + = f.custom_input :published, :css => 'toggle' do + = f.check_box :published - = f.custom_input :redirect, :css => 'toggle', :style => "#{'display: none' if @page.templatized?}" do - = f.check_box :redirect + = f.custom_input :listed, :css => 'toggle' do + = f.check_box :listed - = f.input :cache_strategy, :as => :select, :collection => options_for_page_cache_strategy, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @page.redirect?}" } + = f.custom_input :redirect, :css => 'toggle', :style => "#{'display: none' if @page.templatized?}" do + = f.check_box :redirect - = f.input :redirect_url, :required => true, :wrapper_html => { :style => "#{'display: none' unless @page.redirect?}" } + = f.input :cache_strategy, :as => :select, :collection => options_for_page_cache_strategy, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @page.redirect?}" } -= render 'editable_elements', :page => @page + = f.input :redirect_url, :required => true, :wrapper_html => { :style => "#{'display: none' unless @page.redirect?}" } -= 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' } - = f.text_area :raw_template - = f.errors_on :template - .more - = link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link' \ No newline at end of file + + = 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' } + = f.text_area :raw_template + = f.errors_on :template + .more + = link_to t('admin.image_picker.link'), admin_theme_assets_path, :id => 'image-picker-link' \ No newline at end of file diff --git a/app/views/admin/pages/_page.html.haml b/app/views/admin/pages/_page.html.haml index 7540a46a..2b612881 100644 --- a/app/views/admin/pages/_page.html.haml +++ b/app/views/admin/pages/_page.html.haml @@ -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 \ No newline at end of file + + = render children \ No newline at end of file diff --git a/app/views/admin/pages/index.html.haml b/app/views/admin/pages/index.html.haml index 0e4957d8..9ab7f8b2 100644 --- a/app/views/admin/pages/index.html.haml +++ b/app/views/admin/pages/index.html.haml @@ -5,12 +5,13 @@ - content_for :submenu do = render 'admin/shared/menu/contents' - + - content_for :actions do = render 'admin/shared/actions/contents' -- content_for :buttons do - = admin_button_tag :new, new_admin_page_url, :class => 'new' +- if can? :create, Page + - content_for :buttons do + = admin_button_tag :new, new_admin_page_url, :class => 'new' %p!= t('.help') diff --git a/app/views/admin/shared/actions/_contents.html.haml b/app/views/admin/shared/actions/_contents.html.haml index d5b72e43..86d1b3bc 100644 --- a/app/views/admin/shared/actions/_contents.html.haml +++ b/app/views/admin/shared/actions/_contents.html.haml @@ -1 +1,2 @@ -= link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url \ No newline at end of file +- if can? :manage, ContentType + = link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url \ No newline at end of file diff --git a/app/views/admin/shared/menu/_contents.html.haml b/app/views/admin/shared/menu/_contents.html.haml index 3ed7b6c7..490e0c9c 100644 --- a/app/views/admin/shared/menu/_contents.html.haml +++ b/app/views/admin/shared/menu/_contents.html.haml @@ -1,6 +1,7 @@ = admin_submenu_item 'pages', admin_pages_url do - .header - %p= link_to t('admin.pages.index.new'), new_admin_page_url + - if can? :manage, @page + .header + %p= link_to t('admin.pages.index.new'), new_admin_page_url .inner %h2!= t('admin.pages.index.lastest_items') %ul @@ -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) - %p.edit= link_to t('admin.contents.index.edit'), edit_admin_content_type_url(content_type) + + - 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 diff --git a/config/locales/admin_ui.en.yml b/config/locales/admin_ui.en.yml index f6f7fa35..354e44ae 100644 --- a/config/locales/admin_ui.en.yml +++ b/config/locales/admin_ui.en.yml @@ -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 diff --git a/config/locales/admin_ui.fr.yml b/config/locales/admin_ui.fr.yml index c37fb767..484c4ca7 100644 --- a/config/locales/admin_ui.fr.yml +++ b/config/locales/admin_ui.fr.yml @@ -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 diff --git a/doc/TODO b/doc/TODO index 91d406d3..0c03ed81 100644 --- a/doc/TODO +++ b/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: diff --git a/lib/tasks/locomotive.rake b/lib/tasks/locomotive.rake index d17be54f..4e89182a 100644 --- a/lib/tasks/locomotive.rake +++ b/lib/tasks/locomotive.rake @@ -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)..." diff --git a/public/images/admin/icons/membership_edit.png b/public/images/admin/icons/membership_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..59cb94b7c833e9197509b92829496734fe4163b1 GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^d_c^@!3HFQ8hUO4DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MHf6>978H@CF$turYbOcFnqLD_;=K$NzG5ej_1hg#u>e= zS|^x~ys|La!}e&u!yDcscO7gbJ9-_ZZZ`Z-5y(!wSR&t{+#vBp-c=!v`N%4VKO9d& zL=~nnC+R7qxg6f+5W`~XBHppS(WKErq4!L{@*4NezEU043qREb%r-x$68WL!9*c(b zvP|1eOfd_sggbZ_%v$8w*tm{iwp*P-8EcZ_ie=&sGTuFIfeL)ANro93NvsO?buDgr wc2Nwf%B|1DbEM=QmCeNbS82M5GjJF%%xX|%JQ>%$5a>4sPgg&ebxsLQ0AHnLfdBvi literal 0 HcmV?d00001 diff --git a/public/images/admin/icons/membership_lock.png b/public/images/admin/icons/membership_lock.png new file mode 100644 index 0000000000000000000000000000000000000000..706156289432aad730fa185b3cf9a2f3f84d59ef GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRO!3HEZ#7tidq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c71esWIEGZ*O48BQO?6;ubjV>lQr=LZDp1eJxk*7Yf<2Ul z`Km(>>k<7K3zz}}ST8q5$aM%eM(}mqcARA3IxS$AM29@%MAp{uCjhMbn+WJcjLDM<=UJZ`QGjBE_Prh?B?CUvO* P9m3%0>gTe~DWM4fcBV#L literal 0 HcmV?d00001 diff --git a/public/javascripts/admin/pages.js b/public/javascripts/admin/pages.js index 07e29133..c1c11bb5 100644 --- a/public/javascripts/admin/pages.js +++ b/public/javascripts/admin/pages.js @@ -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'; diff --git a/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/dialog.htm b/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/dialog.htm index 1261f296..8f42a34d 100644 --- a/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/dialog.htm +++ b/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/dialog.htm @@ -43,7 +43,7 @@ diff --git a/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/editor_plugin.js b/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/editor_plugin.js index f35dff80..3e0c0bb7 100644 --- a/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/editor_plugin.js +++ b/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/editor_plugin.js @@ -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)})(); \ No newline at end of file +(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)})(); \ No newline at end of file diff --git a/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/js/dialog.js b/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/js/dialog.js index 6b03acf6..0e7dd9c7 100644 --- a/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/js/dialog.js +++ b/public/javascripts/admin/plugins/tiny_mce/plugins/locomedia/js/dialog.js @@ -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); }); diff --git a/public/javascripts/admin/rails.js b/public/javascripts/admin/rails.js index 103032a5..c13675b0 100644 --- a/public/javascripts/admin/rails.js +++ b/public/javascripts/admin/rails.js @@ -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, diff --git a/public/javascripts/admin/site.js b/public/javascripts/admin/site.js index 16f0e388..bca2dd00 100644 --- a/public/javascripts/admin/site.js +++ b/public/javascripts/admin/site.js @@ -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(); + + }); diff --git a/public/stylesheets/admin/formtastic_changes.css b/public/stylesheets/admin/formtastic_changes.css index 6c6b636c..863be370 100644 --- a/public/stylesheets/admin/formtastic_changes.css +++ b/public/stylesheets/admin/formtastic_changes.css @@ -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 { diff --git a/public/stylesheets/admin/menu.css b/public/stylesheets/admin/menu.css index 94ffe8c5..21665c72 100644 --- a/public/stylesheets/admin/menu.css +++ b/public/stylesheets/admin/menu.css @@ -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; diff --git a/public/stylesheets/sass/admin/menu.scss b/public/stylesheets/sass/admin/menu.scss index 9861e94f..12d90d9a 100644 --- a/public/stylesheets/sass/admin/menu.scss +++ b/public/stylesheets/sass/admin/menu.scss @@ -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 {