locomotive_cms 0.0.4.beta10 → 0.0.4.beta11

Sign up to get free protection for your applications and to get access to all the features.
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: