locomotive_cms 0.0.4.beta10 → 0.0.4.beta11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile CHANGED
@@ -30,7 +30,7 @@ gem 'RedCloth'
30
30
  gem 'delayed_job', '2.1.2'
31
31
  gem 'delayed_job_mongoid', '1.0.1'
32
32
  gem 'rubyzip'
33
- gem 'jammit-s3'
33
+ gem 'locomotive_jammit-s3', :require => 'jammit-s3'
34
34
 
35
35
  # The rest of the dependencies are for use when in the locomotive dev environment
36
36
 
@@ -31,7 +31,9 @@ module Admin
31
31
  flash[:notice] = t("flash.admin.imports.create.#{Locomotive.config.delayed_job ? 'notice' : 'done'}")
32
32
 
33
33
  redirect_to Locomotive.config.delayed_job ? admin_import_url : new_admin_import_url
34
- rescue
34
+ rescue Exception => e
35
+ logger.error "[Locomotive import] #{e.message}"
36
+
35
37
  @error = t('errors.messages.invalid_theme_file')
36
38
  flash[:alert] = t('flash.admin.imports.create.alert')
37
39
 
@@ -6,7 +6,7 @@ module Admin
6
6
  respond_to :json, :only => [:update, :sort, :get_path]
7
7
 
8
8
  def index
9
- @pages = current_site.pages.roots
9
+ @pages = current_site.all_pages_in_once
10
10
  end
11
11
 
12
12
  def new
@@ -27,8 +27,62 @@ module Models
27
27
  alias :descendants :hacked_descendants
28
28
  end
29
29
 
30
+ module ClassMethods
31
+
32
+ # Warning: used only in read-only
33
+ def quick_tree(site)
34
+ pages = site.pages.minimal_attributes.order_by([[:depth, :asc], [:position, :asc]]).to_a
35
+
36
+ tmp = []
37
+
38
+ while !pages.empty?
39
+ tmp << _quick_tree(pages.delete_at(0), pages)
40
+ end
41
+
42
+ tmp
43
+ end
44
+
45
+ def _quick_tree(current_page, pages)
46
+ i, children = 0, []
47
+
48
+ while !pages.empty?
49
+ page = pages[i]
50
+
51
+ break if page.nil?
52
+
53
+ if page.parent_id == current_page.id
54
+ page = pages.delete_at(i)
55
+
56
+ children << _quick_tree(page, pages)
57
+ else
58
+ i += 1
59
+ end
60
+ end
61
+
62
+ current_page.instance_eval do
63
+ def children=(list); @children = list; end
64
+ def children; @children || []; end
65
+ end
66
+
67
+ current_page.children = children
68
+
69
+ current_page
70
+ end
71
+
72
+ end
73
+
30
74
  module InstanceMethods
31
75
 
76
+ def children?
77
+ self.class.where(self.parent_id_field => self.id).count
78
+ end
79
+
80
+ def children_with_minimal_attributes
81
+ self.class.where(self.parent_id_field => self.id).
82
+ order_by(self.tree_order).
83
+ minimal_attributes
84
+ end
85
+
32
86
  def sort_children!(ids)
33
87
  ids.each_with_index do |id, position|
34
88
  child = self.children.detect { |p| p._id == BSON::ObjectId(id) }
@@ -53,8 +107,12 @@ module Models
53
107
  def change_parent
54
108
  if self.parent_id_changed?
55
109
  self.fix_position(false)
56
- self.position = nil # make it move to bottom
57
- self.add_to_list_bottom
110
+
111
+ unless self.parent_id_was.nil?
112
+ self.position = nil # make it move to bottom
113
+ self.add_to_list_bottom
114
+ end
115
+
58
116
  self.instance_variable_set :@_will_move, true
59
117
  end
60
118
  end
@@ -40,6 +40,8 @@ class Page
40
40
  scope :index, :where => { :slug => 'index', :depth => 0 }
41
41
  scope :not_found, :where => { :slug => '404', :depth => 0 }
42
42
  scope :published, :where => { :published => true }
43
+ scope :fullpath, lambda { |fullpath| { :where => { :fullpath => fullpath } } }
44
+ scope :minimal_attributes, :only => %w(title slug fullpath position depth published templatized parent_id created_at updated_at)
43
45
 
44
46
  ## methods ##
45
47
 
@@ -40,6 +40,10 @@ class Site
40
40
 
41
41
  ## methods ##
42
42
 
43
+ def all_pages_in_once
44
+ Page.quick_tree(self)
45
+ end
46
+
43
47
  def domains=(array)
44
48
  array = [] if array.blank?; super(array)
45
49
  end
@@ -60,7 +60,7 @@ class AssetUploader < CarrierWave::Uploader::Base
60
60
 
61
61
  def self.content_types
62
62
  {
63
- :image => ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg'],
63
+ :image => ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg', 'image/x-icon'],
64
64
  :video => [/^video/, 'application/x-shockwave-flash', 'application/x-swf'],
65
65
  :audio => [/^audio/, 'application/ogg', 'application/x-mp3'],
66
66
  :pdf => ['application/pdf', 'application/x-pdf'],
@@ -15,7 +15,18 @@ class ThemeAssetUploader < AssetUploader
15
15
  end
16
16
 
17
17
  def extension_white_list
18
- %w(jpg jpeg gif png css js swf flv eot svg ttf woff otf)
18
+ %w(jpg jpeg gif png css js swf flv eot svg ttf woff otf ico)
19
+ end
20
+
21
+ def self.url_for(site, path)
22
+ build(site, path).url
23
+ end
24
+
25
+ def self.build(site, path)
26
+ asset = ThemeAsset.new(:site => site, :folder => File.dirname(path))
27
+ uploader = ThemeAssetUploader.new(asset)
28
+ uploader.retrieve_from_store!(File.basename(path))
29
+ uploader
19
30
  end
20
31
 
21
32
  end
@@ -1,5 +1,7 @@
1
1
  %li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? } #{'templatized' if page.templatized?}"}
2
- - if not page.index? and not page.children.empty?
2
+ - with_children = !page.children.empty?
3
+
4
+ - if not page.index? and with_children
3
5
  = image_tag 'admin/list/icons/node_closed.png', :class => 'toggler'
4
6
  %em
5
7
  %strong= link_to truncate(page.title, :length => 80), edit_admin_page_url(page)
@@ -11,6 +13,6 @@
11
13
  - if not page.index? and not page.not_found?
12
14
  = link_to image_tag('admin/list/icons/trash.png'), admin_page_url(page), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
13
15
 
14
- - if not page.children.empty?
16
+ - if with_children
15
17
  %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'}" }
16
18
  = render page.children
@@ -5,7 +5,7 @@
5
5
  .inner
6
6
  %h2!= t('admin.pages.index.lastest_items')
7
7
  %ul
8
- - current_site.pages.latest_updated.each do |page|
8
+ - current_site.pages.latest_updated.minimal_attributes.each do |page|
9
9
  %li
10
10
  = link_to truncate(page.title, :length => 25), edit_admin_page_url(page)
11
11
  %span= time_ago_in_words(page.updated_at)
@@ -8,6 +8,8 @@ Locomotive.configure do |config|
8
8
  # If you use locomotive for a single site in Heroku, use "heroku.com" as default domain name.
9
9
  # Your heroku app name (<app_name>.heroku.name) will be used as the sub domain name in Locomotive
10
10
  # during the installation wizzard.
11
+ # Ex:
12
+ # config.default_domain = Rails.env.production? ? 'heroku.com' : 'example.com'
11
13
  config.default_domain = 'example.com'
12
14
 
13
15
  # configure how many items we display in sub menu in the "Contents" section.
@@ -290,7 +290,7 @@ en:
290
290
  next: Create account
291
291
  step_3:
292
292
  title: "Step 3/3 &mdash; Create first site"
293
- explanations: "This is the last step of the installation. You can upload a theme as a zip file. We have free available themes <a href=\"http://www.locomotivecms.com/docs/themes\">here</a>."
293
+ explanations: "This is the last step of the installation. You can upload a theme as a zip file. We have free available themes <a href=\"http://www.locomotivecms.com/support/themes\">here</a>."
294
294
  next: Create site
295
295
 
296
296
 
@@ -291,7 +291,7 @@ fr:
291
291
  next: Créer compte
292
292
  step_3:
293
293
  title: "Étape 3/3 &mdash; Créer premier site"
294
- explanations: "C'est la dernière étape de l'installation. Vous pouvez uploader un theme sous forme d'un fichier zip. Nous avons quelques themes disponibles <a href=\"http://www.locomotivecms.com/docs/themes\">ici</a>."
294
+ explanations: "C'est la dernière étape de l'installation. Vous pouvez uploader un theme sous forme d'un fichier zip. Nous avons quelques themes disponibles <a href=\"http://www.locomotivecms.com/support/themes\">ici</a>."
295
295
  next: Créer site
296
296
 
297
297
  formtastic:
@@ -18,8 +18,15 @@ test:
18
18
  production:
19
19
  <<: *defaults
20
20
  database: locomotive_prod
21
- host: <%= ENV['MONGOID_HOST'] %>
22
- port: <%= ENV['MONGOID_PORT'] %>
23
- username: <%= ENV['MONGOID_USERNAME'] %>
24
- password: <%= ENV['MONGOID_PASSWORD'] %>
25
- database: <%= ENV['MONGOID_DATABASE'] %>
21
+
22
+ # other settings for production
23
+
24
+ # host: <%= ENV['MONGOID_HOST'] %>
25
+ # port: <%= ENV['MONGOID_PORT'] %>
26
+ # username: <%= ENV['MONGOID_USERNAME'] %>
27
+ # password: <%= ENV['MONGOID_PASSWORD'] %>
28
+ # database: <%= ENV['MONGOID_DATABASE'] %>
29
+
30
+ # heroku
31
+ # uri: <%= ENV['MONGOHQ_URL'] %>
32
+
@@ -6,7 +6,9 @@ Locomotive.configure do |config|
6
6
  # If you use locomotive for a single site in Heroku, use "heroku.com" as default domain name.
7
7
  # Your heroku app name (<app_name>.heroku.name) will be used as the sub domain name in Locomotive
8
8
  # during the installation wizzard.
9
- config.default_domain = 'mydomain.com'
9
+ # Ex:
10
+ # config.default_domain = Rails.env.production? ? 'heroku.com' : 'example.com'
11
+ config.default_domain = 'example.com'
10
12
 
11
13
  # configure how many items we display in sub menu in the "Contents" section.
12
14
  config.lastest_items_nb = 5
@@ -35,4 +37,4 @@ Locomotive.configure do |config|
35
37
 
36
38
  # default locale (for now, only en and fr are supported)
37
39
  config.default_locale = :en
38
- end
40
+ end
@@ -18,7 +18,6 @@ require 'httparty'
18
18
  require 'redcloth'
19
19
  require 'delayed_job_mongoid'
20
20
  require 'zip/zipfilesystem'
21
- # require 'jammit'
22
21
  require 'jammit-s3'
23
22
 
24
23
  $:.unshift File.dirname(__FILE__)
@@ -30,5 +29,9 @@ module Locomotive
30
29
  load "railties/tasks.rake"
31
30
  end
32
31
 
32
+ initializer "serving fonts" do |app|
33
+ app.middleware.insert_after Rack::Lock, '::Locomotive::Middlewares::Fonts', :path => %r{^/fonts}
34
+ end
35
+
33
36
  end
34
37
  end
@@ -17,7 +17,18 @@ module Locomotive
17
17
 
18
18
  # puts "[WebService] consuming #{path}, #{options.inspect}"
19
19
 
20
- self.get(path, options).try(:underscore_keys)
20
+ response = self.get(path, options)
21
+
22
+ if response.code == 200
23
+ if response.respond_to?(:each)
24
+ response.collect(&:underscore_keys)
25
+ else
26
+ response.try(:underscore_keys)
27
+ end
28
+ else
29
+ nil
30
+ end
31
+
21
32
  end
22
33
 
23
34
  end
@@ -36,7 +36,8 @@ module Locomotive
36
36
  :title => fullpath.split('/').last.humanize,
37
37
  :slug => fullpath.split('/').last,
38
38
  :parent => parent,
39
- :raw_template => template
39
+ :raw_template => template,
40
+ :published => true
40
41
  }.merge(self.pages[fullpath] || {}).symbolize_keys
41
42
 
42
43
  # templatized ?
@@ -54,7 +55,7 @@ module Locomotive
54
55
 
55
56
  page.save!
56
57
 
57
- self.log "adding #{page.fullpath}"
58
+ self.log "adding #{page.fullpath} / #{page.position}"
58
59
 
59
60
  site.reload
60
61
 
@@ -137,10 +138,23 @@ module Locomotive
137
138
  pages = context[:database]['site']['pages']
138
139
 
139
140
  if pages.is_a?(Array) # ordered list of pages
140
- tmp = {}
141
- pages.each_with_index do |data, position|
141
+ tmp, positions = {}, Hash.new(0)
142
+ pages.each do |data|
143
+ position = nil
144
+ fullpath = data.keys.first.to_s
145
+
146
+ unless %w(index 404).include?(fullpath)
147
+ (segments = fullpath.split('/')).pop
148
+ position_key = segments.empty? ? 'index' : segments.join('/')
149
+
150
+ position = positions[position_key]
151
+
152
+ positions[position_key] += 1
153
+ end
154
+
142
155
  attributes = (data.values.first || {}).merge(:position => position)
143
- tmp[data.keys.first.to_s] = attributes
156
+
157
+ tmp[fullpath] = attributes
144
158
  end
145
159
  pages = tmp
146
160
  end
@@ -19,6 +19,10 @@ module Locomotive
19
19
  @fullpath ||= @source.fullpath
20
20
  end
21
21
 
22
+ def depth
23
+ @source.depth
24
+ end
25
+
22
26
  end
23
27
  end
24
28
  end
@@ -9,11 +9,7 @@ module Locomotive
9
9
  return '' if input.nil?
10
10
 
11
11
  unless input =~ /^(\/|http:)/
12
- segments = "stylesheets/#{input}".split('/')
13
-
14
- filename, folder = segments.pop, segments.join('/')
15
-
16
- input = asset_url(folder, filename)
12
+ input = asset_url("stylesheets/#{input}")
17
13
  end
18
14
 
19
15
  input = "#{input}.css" unless input.ends_with?('.css')
@@ -27,14 +23,7 @@ module Locomotive
27
23
  return '' if input.nil?
28
24
 
29
25
  unless input =~ /^(\/|http:)/
30
- segments = "javascripts/#{input}".split('/')
31
-
32
- filename, folder = segments.pop, segments.join('/')
33
-
34
- input = asset_url(folder, filename)
35
- # javascript = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
36
- #
37
- # input = '/' + ThemeAssetUploader.new(javascript).store_path(filename)
26
+ input = asset_url("javascripts/#{input}")
38
27
  end
39
28
 
40
29
  input = "#{input}.js" unless input.ends_with?('.js')
@@ -47,11 +36,7 @@ module Locomotive
47
36
 
48
37
  input = "images/#{input}" unless input.starts_with?('/')
49
38
 
50
- segments = input.split('/')
51
-
52
- filename, folder = segments.pop, segments.join('/')
53
-
54
- asset_url(folder, filename)
39
+ asset_url(input)
55
40
  end
56
41
 
57
42
  # Write an image tag
@@ -143,12 +128,10 @@ module Locomotive
143
128
  input.respond_to?(:url) ? input.url : input
144
129
  end
145
130
 
146
- def asset_url(folder, filename)
147
- asset = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
148
- uploader = ThemeAssetUploader.new(asset)
149
- uploader.retrieve_from_store!(filename)
150
- uploader.url
131
+ def asset_url(path)
132
+ ThemeAssetUploader.url_for(@context.registers[:site], path)
151
133
  end
134
+
152
135
  end
153
136
 
154
137
  ::Liquid::Template.register_filter(Html)
@@ -1,24 +1,26 @@
1
1
  module Locomotive
2
2
  module Liquid
3
3
  module Tags
4
- # Display the children pages of the site or the current page. If not precised, nav is applied on the current page.
4
+ # Display the children pages of the site, current page or the parent page. If not precised, nav is applied on the current page.
5
5
  # The html output is based on the ul/li tags.
6
6
  #
7
7
  # Usage:
8
8
  #
9
9
  # {% nav site %} => <ul class="nav"><li class="on"><a href="/features">Features</a></li></ul>
10
10
  #
11
+ # {% nav site, no_wrapper: true, exclude: 'contact|about', id: 'main-nav' }
12
+ #
11
13
  class Nav < ::Liquid::Tag
12
14
 
13
15
  Syntax = /(#{::Liquid::Expression}+)?/
14
16
 
15
17
  def initialize(tag_name, markup, tokens, context)
16
18
  if markup =~ Syntax
17
- @site_or_page = $1 || 'page'
18
- @options = {}
19
- markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value }
19
+ @source = ($1 || 'page').gsub(/"|'/, '')
20
+ @options = { :id => 'nav' }
21
+ markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
20
22
 
21
- @options[:exclude] = Regexp.new(@options[:exclude].gsub(/"|'/, '')) if @options[:exclude]
23
+ @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude]
22
24
  else
23
25
  raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <page|site> <options>")
24
26
  end
@@ -27,29 +29,45 @@ module Locomotive
27
29
  end
28
30
 
29
31
  def render(context)
30
- @current_page = context.registers[:page]
32
+ children_output = []
31
33
 
32
- source = context.registers[@site_or_page.to_sym]
34
+ entries = fetch_entries(context)
33
35
 
34
- if source.respond_to?(:name) # site ?
35
- source = source.pages.index.first # start from home page
36
- else
37
- source = source.parent || source
36
+ entries.each_with_index do |p, index|
37
+ css = []
38
+ css << 'first' if index == 0
39
+ css << 'last' if index == entries.size - 1
40
+
41
+ children_output << render_entry_link(p, css.join(' '))
38
42
  end
39
43
 
40
- output = source.children.map { |p| include_page?(p) ? render_child_link(p) : '' }.join("\n")
44
+ output = children_output.join("\n")
41
45
 
42
- if @options[:no_wrapper] != 'true'
43
- output = %{<ul id="nav">\n#{output}</ul>}
44
- end
46
+ if @options[:no_wrapper] != 'true'
47
+ output = %{<ul id="#{@options[:id]}">\n#{output}</ul>}
48
+ end
45
49
 
46
50
  output
47
51
  end
48
52
 
49
53
  private
50
54
 
55
+ def fetch_entries(context)
56
+ @current_page = context.registers[:page]
57
+
58
+ children = (case @source
59
+ when 'site' then context.registers[:site].pages.index.minimal_attributes.first # start from home page
60
+ when 'parent' then @current_page.parent || @current_page
61
+ when 'page' then @current_page
62
+ else
63
+ context.registers[:site].pages.fullpath(@source).minimal_attributes.first
64
+ end).children_with_minimal_attributes.to_a
65
+
66
+ children.delete_if { |p| !include_page?(p) }
67
+ end
68
+
51
69
  def include_page?(page)
52
- if page.templatized?
70
+ if page.templatized? || !page.published?
53
71
  false
54
72
  elsif @options[:exclude]
55
73
  (page.fullpath =~ @options[:exclude]).nil?
@@ -58,14 +76,14 @@ module Locomotive
58
76
  end
59
77
  end
60
78
 
61
- def render_child_link(page)
62
- selected = @current_page._id == page._id ? ' on' : ''
79
+ def render_entry_link(page, css)
80
+ selected = @current_page.fullpath =~ /^#{page.fullpath}/ ? ' on' : ''
63
81
 
64
82
  icon = @options[:icon] ? '<span></span>' : ''
65
83
  label = %{#{icon if @options[:icon] != 'after' }#{page.title}#{icon if @options[:icon] == 'after' }}
66
84
 
67
85
  %{
68
- <li id="#{page.slug.dasherize}" class="link#{selected}">
86
+ <li id="#{page.slug.dasherize}" class="link#{selected} #{css}">
69
87
  <a href="/#{page.fullpath}">#{label}</a>
70
88
  </li>
71
89
  }.strip
@@ -1,15 +1,11 @@
1
- require 'rack/utils'
2
-
3
1
  module Locomotive
4
2
  module Middlewares
5
3
  class Fonts
6
- include Rack::Utils
7
4
 
8
5
  def initialize(app, opts = {})
9
6
  @app = app
10
7
  @path_regexp = opts[:path] || %r{^/fonts/}
11
- @file_server = ::Rack::File.new(opts[:root] || "#{Rails.root}/public")
12
- @expires_in = opts[:expires_in] || 24.hour
8
+ @expires_in = opts[:expires_in] || 24.hour # varnish
13
9
  end
14
10
 
15
11
  def call(env)
@@ -19,13 +15,9 @@ module Locomotive
19
15
  if site.nil?
20
16
  @app.call(env)
21
17
  else
22
- env["PATH_INFO"] = ::File.join('/', 'sites', site.id.to_s, 'theme', env["PATH_INFO"])
23
-
24
- response = @file_server.call(env)
25
-
26
- response[1]['Cache-Control'] = "public; max-age=#{@expires_in}" # varnish
18
+ body = ThemeAssetUploader.build(site, env["PATH_INFO"]).read.to_s
27
19
 
28
- response
20
+ [200, { 'Cache-Control' => "public; max-age=#{@expires_in}" }, [body]]
29
21
  end
30
22
  else
31
23
  @app.call(env)
@@ -26,9 +26,11 @@ module Locomotive
26
26
  end
27
27
 
28
28
  def require_site
29
+ return true if current_site
30
+
29
31
  redirect_to admin_installation_url and return false if Account.count == 0 || Site.count == 0
30
32
 
31
- render_no_site_error and return false if current_site.nil?
33
+ render_no_site_error and return false
32
34
  end
33
35
 
34
36
  def render_no_site_error
@@ -1,3 +1,3 @@
1
1
  module Locomotive #:nodoc
2
- VERSION = "0.0.4.beta10"
2
+ VERSION = "0.0.4.beta11"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: locomotive_cms
3
3
  version: !ruby/object:Gem::Version
4
- hash: -1967355474
4
+ hash: -1967355473
5
5
  prerelease: true
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
9
  - 4
10
- - beta10
11
- version: 0.0.4.beta10
10
+ - beta11
11
+ version: 0.0.4.beta11
12
12
  platform: ruby
13
13
  authors:
14
14
  - Didier Lafforgue
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-12-30 00:00:00 +01:00
19
+ date: 2011-01-03 00:00:00 +01:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -380,7 +380,7 @@ dependencies:
380
380
  - !ruby/object:Gem::Dependency
381
381
  type: :runtime
382
382
  prerelease: false
383
- name: jammit-s3
383
+ name: locomotive_jammit-s3
384
384
  version_requirements: &id024 !ruby/object:Gem::Requirement
385
385
  none: false
386
386
  requirements: