From e4b34eac3f908338b1858eecc0023e1cc62997c2 Mon Sep 17 00:00:00 2001 From: did Date: Tue, 8 Mar 2011 16:05:07 +0100 Subject: [PATCH] patch mongoid to enable the limit option for embedded collections + ui widget to allow more than 5 custom models in the content section --- Gemfile | 5 +- Gemfile.lock | 16 ++-- app/helpers/admin/content_types_helper.rb | 62 +++++++++++++ app/models/content_type.rb | 3 +- .../admin/shared/menu/_contents.html.haml | 30 ++++--- lib/locomotive/engine.rb | 2 +- .../liquid/tags/editable/content.rb | 16 ++-- lib/locomotive/mongoid/patches.rb | 90 ++++++++++++++++++- locomotive_cms.gemspec | 2 +- public/stylesheets/admin/menu.css | 33 +++---- public/stylesheets/sass/admin/menu.scss | 25 +++--- 11 files changed, 211 insertions(+), 73 deletions(-) create mode 100644 app/helpers/admin/content_types_helper.rb diff --git a/Gemfile b/Gemfile index 63b0a639..3897723a 100644 --- a/Gemfile +++ b/Gemfile @@ -21,14 +21,13 @@ gem 'rmagick', '2.12.2' gem 'locomotive_carrierwave', '0.5.0.1.beta3', :require => 'carrierwave' gem 'custom_fields', '1.0.0.beta.5' -# gem 'custom_fields', :path => '../gems/custom_fields' gem 'fog', '0.3.7' gem 'mimetype-fu' gem 'actionmailer-with-request' -gem 'heroku' +gem 'heroku', '1.18.2' gem 'httparty', '>= 0.6.1' gem 'RedCloth', '4.2.7' -gem 'delayed_job', '2.1.2' +gem 'delayed_job', '2.1.4' gem 'delayed_job_mongoid', '1.0.2' gem 'rubyzip' gem 'locomotive_jammit-s3', :require => 'jammit-s3' diff --git a/Gemfile.lock b/Gemfile.lock index 305eebd4..0a409d5b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,7 +83,7 @@ GEM mongoid (~> 2.0.0.rc.7) daemons (1.1.0) database_cleaner (0.6.4) - delayed_job (2.1.2) + delayed_job (2.1.4) activesupport (~> 3.0) daemons delayed_job_mongoid (1.0.2) @@ -121,10 +121,10 @@ GEM growl-glue (1.0.7) haml (3.0.25) has_scope (0.5.0) - heroku (1.6.3) - json (>= 1.1.0) - launchy (>= 0.3.2) - rest-client (>= 1.2.0, < 2.0.0) + heroku (1.18.2) + json (~> 1.5.1) + launchy (~> 0.3.2) + rest-client (>= 1.4.0, < 1.7.0) httparty (0.7.4) crack (= 0.1.8) i18n (0.5.0) @@ -137,7 +137,7 @@ GEM json (1.5.1) json_pure (1.4.6) kgio (2.3.2) - launchy (0.4.0) + launchy (0.3.7) configuration (>= 0.0.5) rake (>= 0.8.1) linecache (0.43) @@ -266,7 +266,7 @@ DEPENDENCIES cucumber-rails custom_fields (= 1.0.0.beta.5) database_cleaner - delayed_job (= 2.1.2) + delayed_job (= 2.1.4) delayed_job_mongoid (= 1.0.2) devise (= 1.1.3) factory_girl_rails @@ -274,7 +274,7 @@ DEPENDENCIES formtastic (~> 1.2.3) growl-glue haml (= 3.0.25) - heroku + heroku (= 1.18.2) httparty (>= 0.6.1) inherited_resources (~> 1.1.2) launchy diff --git a/app/helpers/admin/content_types_helper.rb b/app/helpers/admin/content_types_helper.rb new file mode 100644 index 00000000..f3f35681 --- /dev/null +++ b/app/helpers/admin/content_types_helper.rb @@ -0,0 +1,62 @@ +module Admin::ContentTypesHelper + + MAX_DISPLAYED_CONTENTS = 4 + + def fetch_content_types + return @content_types if @content_types + + @content_types = current_site.content_types.ordered. + limit(:contents => Locomotive.config.lastest_items_nb). + only(:name, :slug, :highlighted_field_name, :updated_at).to_a + + if @content_type && @content_type.persisted? && @content_types.index(@content_type) >= MAX_DISPLAYED_CONTENTS + @content_types.delete(@content_type) + @content_types.insert(0, @content_type) + end + + @content_types + end + + def each_content_type_menu_item(which = :first, &block) + types = fetch_content_types + sliced = [] + + if which == :first + sliced = types[0..MAX_DISPLAYED_CONTENTS - 1] + elsif types.size > MAX_DISPLAYED_CONTENTS + sliced = types[MAX_DISPLAYED_CONTENTS, types.size - MAX_DISPLAYED_CONTENTS] + end + + return [] if sliced.empty? + + sliced.each do |content_type| + next if content_type.new_record? + item_on = (content_type.slug == @content_type.slug) rescue nil + + label = truncate(content_type.name, :length => 15) + url = admin_contents_url(content_type.slug) + css = @content_type && content_type.slug == @content_type.slug ? 'on' : '' + + html = admin_submenu_item(label, url, :i18n => false, :css => css) do + yield(content_type) + end + + haml_concat(html) + end + end + + def other_content_types(&block) + types = fetch_content_types + + if types.size > MAX_DISPLAYED_CONTENTS + sliced = types[MAX_DISPLAYED_CONTENTS, types.size - MAX_DISPLAYED_CONTENTS] + + html = admin_submenu_item('...', '#', :i18n => false) do + yield(sliced) + end + + haml_concat(html) + end + end + +end \ No newline at end of file diff --git a/app/models/content_type.rb b/app/models/content_type.rb index fcc41511..7b9ff7c4 100644 --- a/app/models/content_type.rb +++ b/app/models/content_type.rb @@ -21,7 +21,8 @@ class ContentType end ## named scopes ## - scope :first_by_slug, lambda { |slug| where(:slug => slug) } + scope :ordered, :order_by => :updated_at.desc + ## indexes ## index [[:site_id, Mongo::ASCENDING], [:slug, Mongo::ASCENDING]] diff --git a/app/views/admin/shared/menu/_contents.html.haml b/app/views/admin/shared/menu/_contents.html.haml index c9fd39e0..106a4080 100644 --- a/app/views/admin/shared/menu/_contents.html.haml +++ b/app/views/admin/shared/menu/_contents.html.haml @@ -10,19 +10,23 @@ = link_to truncate(page.title, :length => 25), edit_admin_page_url(page) %span= time_ago_in_words(page.updated_at) - - current_site.content_types.to_a.each do |content_type| - - next if content_type.new_record? - - item_on = (content_type.slug == @content_type.slug) rescue nil - = admin_submenu_item content_type.name, admin_contents_url(content_type.slug), :i18n => false, :css => (item_on ? 'on' : '') do - .header - %p= link_to t('admin.contents.index.new'), new_admin_content_url(content_type.slug) - .inner - %h2!= t('admin.contents.index.lastest_items') - %ul - - content_type.contents.latest_updated.each do |content| - %li - = link_to truncate(content.send(content_type.highlighted_field_name), :length => 30), edit_admin_content_path(content_type.slug, content) - %span= time_ago_in_words(content.updated_at) + - each_content_type_menu_item do |content_type| + .header + %p= link_to t('admin.contents.index.new'), new_admin_content_url(content_type.slug) + .inner + %h2!= t('admin.contents.index.lastest_items') + %ul + - content_type.contents.latest_updated.each do |content| + %li + = link_to truncate(content.send(content_type.highlighted_field_name), :length => 20), edit_admin_content_path(content_type.slug, content) + %span= time_ago_in_words(content.updated_at) + + - other_content_types do |content_types| + .inner + %ul.big-links + - content_types.each do |content_type| + %li + = link_to truncate(content_type.name, :length => 20), admin_contents_url(content_type.slug) .action = 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/lib/locomotive/engine.rb b/lib/locomotive/engine.rb index 8f50f106..f3254a3f 100644 --- a/lib/locomotive/engine.rb +++ b/lib/locomotive/engine.rb @@ -16,7 +16,7 @@ require 'actionmailer_with_request' require 'heroku' require 'httparty' require 'redcloth' -#require 'delayed_job_mongoid' +require 'delayed_job_mongoid' require 'zip/zipfilesystem' require 'jammit-s3' diff --git a/lib/locomotive/liquid/tags/editable/content.rb b/lib/locomotive/liquid/tags/editable/content.rb index 1964f92a..280d6a9a 100644 --- a/lib/locomotive/liquid/tags/editable/content.rb +++ b/lib/locomotive/liquid/tags/editable/content.rb @@ -3,9 +3,9 @@ module Locomotive module Tags module Editable class Content < ::Liquid::Tag - + Syntax = /(#{::Liquid::Expression}+)?/ - + def initialize(tag_name, markup, tokens, context) if markup =~ Syntax @slug = $1 @@ -17,31 +17,31 @@ module Locomotive super end - + def render(context) page = context.registers[:page] element = find_element(page) - + if element.nil? && @options[:inherit] != false while page.parent.present? && element.nil? page = page.parent element = find_element(page) end end - + if element.present? return element.content else raise ::Liquid::SyntaxError.new("Error in 'content' - Can't find editable element called `#{@slug}`") end end - + def find_element(page) page.editable_elements.where(:slug => @slug).first end - + end - + ::Liquid::Template.register_tag('content', Content) end end diff --git a/lib/locomotive/mongoid/patches.rb b/lib/locomotive/mongoid/patches.rb index 23b88516..d5ec0440 100644 --- a/lib/locomotive/mongoid/patches.rb +++ b/lib/locomotive/mongoid/patches.rb @@ -1,3 +1,91 @@ +# encoding: utf-8 + require 'mongoid' -## For now, no mongoid monkey patch is required, that is a good thing after all :-) \ No newline at end of file +# Limit feature for embedded documents + +module Mongoid #:nodoc: + module Criterion #:nodoc: + module Exclusion + + def only(*args) + clone.tap do |crit| + crit.options[:fields] ||= {} + crit.options[:fields][:only] = args.flatten if args.any? + end + end + + end + + module Optional + + # Adds a criterion to the +Criteria+ that specifies the maximum number of + # results to return. This is mostly used in conjunction with skip() + # to handle paginated results. + # + # Options: + # + # value: An +Integer+ specifying the max number of results. Defaults to 20. + # hash: A +Hash+ specifying the max number of results for the embedded collections. + # + # Example: + # + # criteria.limit(100) + # criteria.limit(100, { :contents => 5 }) + # criteria.limit(:contents => 5) + # + # Returns: self + def limit(*args) + clone.tap do |crit| + arguments = args.first || 20 + fields = nil # hash of embedded collections + + case arguments + when Integer + crit.options[:limit] = arguments + fields = args[1] if args.size > 1 + when Hash + fields = arguments + end + + if fields + crit.options[:fields] ||= {} + crit.options[:fields][:limit] = fields + end + end + end + + end + end + + module Contexts #:nodoc: + class Mongo + + # Filters the field list. If no fields have been supplied, then it will be + # empty. If fields have been defined then _type will be included as well. + def process_options + fields = options[:fields] + only = fields.delete(:only) if fields + limits = fields.delete(:limit) if fields + + # only ? + if only && only.size > 0 + only << :_type if !only.include?(:_type) + only.each do |field| + options[:fields].merge!(field => 1) + end + end + + # limit for embedded collections ? + if limits && limits.size > 0 + limits.each do |field, limit| + options[:fields][field] = { '$slice' => limit } + end + end + + options.dup + end + + end + end +end \ No newline at end of file diff --git a/locomotive_cms.gemspec b/locomotive_cms.gemspec index 5db84620..fc92b0ad 100644 --- a/locomotive_cms.gemspec +++ b/locomotive_cms.gemspec @@ -40,7 +40,7 @@ Gem::Specification.new do |s| s.add_dependency "heroku" s.add_dependency "httparty", ">= 0.6.1" s.add_dependency "RedCloth", "4.2.7" - s.add_dependency "delayed_job", "2.1.2" + s.add_dependency "delayed_job", "2.1.4" s.add_dependency "delayed_job_mongoid", "1.0.2" s.add_dependency "rubyzip" s.add_dependency "locomotive_jammit-s3" diff --git a/public/stylesheets/admin/menu.css b/public/stylesheets/admin/menu.css index fba1dfe3..c67876c0 100644 --- a/public/stylesheets/admin/menu.css +++ b/public/stylesheets/admin/menu.css @@ -8,8 +8,7 @@ position: relative; top: -1px; z-index: 998; - min-height: 60px; - width: 950px; + height: 60px; margin: 0px; padding: 0 8px; background: transparent url(/images/admin/menu/shadow.png) repeat-y 0 0; @@ -25,24 +24,12 @@ border-top-right-radius: 3px ; -moz-border-radius-top-right: 3px ; -webkit-border-top-right-radius: 3px ; - width: 826px; - padding-right: 124px; - padding-top: 8px; - padding-bottom: 8px; - min-height: 44px; } - #submenu > ul:after { - content: "."; - display: block; - height: 0; - clear: both; - visibility: hidden; } + height: 60px; } #submenu > ul a { text-decoration: none; } #submenu > ul > li { - margin: 7px 7px 7px 8px; - height: 30px; - float: left; - position: relative; } + margin: 15px 7px 0 8px; + float: left; } #submenu > ul > li.hoverable > a span em { display: inline-block; background: transparent url(/images/admin/menu/icons.png) no-repeat 0 -16px; @@ -114,9 +101,10 @@ position: absolute; top: 0px; right: 22px; - height: 100%; + height: 60px; padding-left: 20px; - z-index: 1; } + z-index: 1; + background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; } #submenu > .action a { margin-top: 18px; display: inline-block; @@ -148,7 +136,7 @@ border-color: black; } #submenu .popup { position: absolute; - top: 26px; + top: 42px; min-width: 250px; background: #fff; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); @@ -197,6 +185,7 @@ color: #8b8d9a; font-size: 0.7em; } #submenu .popup ul.big-links { + list-style: none; margin: 0px; } #submenu .popup ul.big-links li { margin: 10px 0; } @@ -207,7 +196,7 @@ padding: 3px 10px; background: #ebedf4; font-weight: bold; - font-size: 14px; } + font-size: 0.8em; } #submenu .popup .footer { background: #ebedf4; padding: 8px 16px; @@ -323,7 +312,7 @@ body.contents #submenu > ul { background: -moz-linear-gradient(0% 100% 90deg, #212229, #1e1e24); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#1e1e24), to(#212229)); } body.contents #submenu > .action { - background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; } + background-image: url(/images/admin/menu/submenu/black-action-border.png) !important; } #menu li.assets a em { position: relative; diff --git a/public/stylesheets/sass/admin/menu.scss b/public/stylesheets/sass/admin/menu.scss index 230bec14..aedbb53e 100644 --- a/public/stylesheets/sass/admin/menu.scss +++ b/public/stylesheets/sass/admin/menu.scss @@ -9,8 +9,7 @@ position: relative; top: -1px; z-index: 998; - min-height: 60px; - width: 950px; + height: 60px; margin: 0px; padding: 0 8px; background: transparent url(/images/admin/menu/shadow.png) repeat-y 0 0; @@ -18,22 +17,16 @@ /* ___ submenu items ___ */ & > ul { - @include clearfix; @include reset; border-top: 1px solid rgba(255, 255, 255, 0.4); background: transparent url(/images/admin/menu/submenu/shadow.png) repeat-x 0 0; @include rounded(top-right, 3px); - width: 826px; - padding-right: 124px; - padding-top: 8px; - padding-bottom: 8px; - min-height: 44px; + + height: 60px; & > li { - margin: 7px 7px 7px 8px; - height: 30px; + margin: 15px 7px 0 8px; float: left; - position: relative; &.hoverable > a span { em { @@ -112,9 +105,10 @@ & > .action { @include absolute-position(top, 0px, right, 22px); - height: 100%; + height: 60px; padding-left: 20px; z-index: 1; + background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; a { margin-top: 18px; @@ -156,7 +150,7 @@ .popup { position: absolute; - top: 26px; + top: 42px; min-width: 250px; background: #fff; @include box-shadow(0px, 0px, 10px, rgba(0, 0, 0, 0.5)); @@ -209,6 +203,7 @@ } &.big-links { + list-style: none; margin: 0px; li { @@ -219,7 +214,7 @@ padding: 3px 10px; background: #ebedf4; font-weight: bold; - font-size: 14px; + font-size: 0.8em; } } } @@ -259,7 +254,7 @@ } & > .action { - background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; + background-image: url(/images/admin/menu/submenu/black-action-border.png) !important; } }