namespace controllers, models, ...etc (wip)
This commit is contained in:
parent
9f8d2e4146
commit
99e442673c
2
.gitignore
vendored
2
.gitignore
vendored
@ -34,5 +34,5 @@ sites/
|
||||
permanent
|
||||
doc/bushido
|
||||
*.swp
|
||||
|
||||
.sass-cache/
|
||||
|
||||
|
4
Gemfile
4
Gemfile
@ -11,7 +11,7 @@ gem 'cancan', '~> 1.6.7'
|
||||
|
||||
gem 'mongoid', '~> 2.3.2'
|
||||
gem 'bson_ext', '~> 1.3.1'
|
||||
gem 'locomotive_mongoid_acts_as_tree', '0.1.5.7', :require => 'mongoid_acts_as_tree'
|
||||
gem 'locomotive_mongoid_acts_as_tree', '0.1.5.7', :require => 'mongoid_acts_as_tree', :path => '../gems/acts_as_tree' # TODO: REPLACE IT
|
||||
gem 'custom_fields', '~> 1.1.0.rc1'
|
||||
gem 'will_paginate', '~> 3.0.2'
|
||||
|
||||
@ -42,7 +42,7 @@ gem 'rubyzip'
|
||||
|
||||
gem 'actionmailer-with-request', '~> 0.3.0', :require => 'actionmailer_with_request'
|
||||
gem 'httparty', '~> 0.8.1'
|
||||
gem 'delayed_job', '~> 3.0.0.pre2'
|
||||
gem 'delayed_job', '~> 2.1.1'
|
||||
gem 'delayed_job_mongoid', '~> 1.0.4'
|
||||
gem 'SystemTimer', :platforms => :ruby_18
|
||||
|
||||
|
348
Gemfile.lock
348
Gemfile.lock
@ -1,58 +1,75 @@
|
||||
GIT
|
||||
remote: git://github.com/chriseppstein/compass.git
|
||||
revision: 22e2458b77519e8eb8463170c1a1fe4bab105f3e
|
||||
branch: rails31
|
||||
specs:
|
||||
compass (0.12.0.alpha.0.22e2458)
|
||||
chunky_png (~> 1.2)
|
||||
fssm (>= 0.2.7)
|
||||
sass (~> 3.1)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/yabawock/handlebars-rails.git
|
||||
revision: a09077aa91f10e08403af84586b2f2f0b38d9e2f
|
||||
specs:
|
||||
handlebars-rails (0.9.1)
|
||||
|
||||
PATH
|
||||
remote: ../gems/acts_as_tree
|
||||
specs:
|
||||
locomotive_mongoid_acts_as_tree (0.1.5.7)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
POpen4 (0.1.4)
|
||||
Platform (>= 0.4.0)
|
||||
open4
|
||||
Platform (0.4.0)
|
||||
RedCloth (4.2.8)
|
||||
SystemTimer (1.2.3)
|
||||
ZenTest (4.6.2)
|
||||
abstract (1.0.0)
|
||||
actionmailer (3.0.10)
|
||||
actionpack (= 3.0.10)
|
||||
mail (~> 2.2.19)
|
||||
actionmailer (3.1.1)
|
||||
actionpack (= 3.1.1)
|
||||
mail (~> 2.3.0)
|
||||
actionmailer-with-request (0.3.0)
|
||||
rails (>= 3)
|
||||
actionpack (3.0.10)
|
||||
activemodel (= 3.0.10)
|
||||
activesupport (= 3.0.10)
|
||||
builder (~> 2.1.2)
|
||||
erubis (~> 2.6.6)
|
||||
i18n (~> 0.5.0)
|
||||
rack (~> 1.2.1)
|
||||
rack-mount (~> 0.6.14)
|
||||
rack-test (~> 0.5.7)
|
||||
tzinfo (~> 0.3.23)
|
||||
activemodel (3.0.10)
|
||||
activesupport (= 3.0.10)
|
||||
builder (~> 2.1.2)
|
||||
i18n (~> 0.5.0)
|
||||
activerecord (3.0.10)
|
||||
activemodel (= 3.0.10)
|
||||
activesupport (= 3.0.10)
|
||||
arel (~> 2.0.10)
|
||||
tzinfo (~> 0.3.23)
|
||||
activeresource (3.0.10)
|
||||
activemodel (= 3.0.10)
|
||||
activesupport (= 3.0.10)
|
||||
activesupport (3.0.10)
|
||||
actionpack (3.1.1)
|
||||
activemodel (= 3.1.1)
|
||||
activesupport (= 3.1.1)
|
||||
builder (~> 3.0.0)
|
||||
erubis (~> 2.7.0)
|
||||
i18n (~> 0.6)
|
||||
rack (~> 1.3.2)
|
||||
rack-cache (~> 1.1)
|
||||
rack-mount (~> 0.8.2)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.0.2)
|
||||
activemodel (3.1.1)
|
||||
activesupport (= 3.1.1)
|
||||
builder (~> 3.0.0)
|
||||
i18n (~> 0.6)
|
||||
activerecord (3.1.1)
|
||||
activemodel (= 3.1.1)
|
||||
activesupport (= 3.1.1)
|
||||
arel (~> 2.2.1)
|
||||
tzinfo (~> 0.3.29)
|
||||
activeresource (3.1.1)
|
||||
activemodel (= 3.1.1)
|
||||
activesupport (= 3.1.1)
|
||||
activesupport (3.1.1)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.2.6)
|
||||
archive-tar-minitar (0.5.2)
|
||||
arel (2.0.10)
|
||||
arel (2.2.1)
|
||||
autotest (4.4.6)
|
||||
ZenTest (>= 4.4.1)
|
||||
bcrypt-ruby (2.1.4)
|
||||
bson (1.4.0)
|
||||
bson_ext (1.4.0)
|
||||
builder (2.1.2)
|
||||
bcrypt-ruby (3.0.1)
|
||||
bson (1.4.1)
|
||||
bson_ext (1.3.1)
|
||||
builder (3.0.0)
|
||||
bushido (0.0.35)
|
||||
highline (>= 1.6.1)
|
||||
json (>= 1.4.6)
|
||||
orm_adapter (~> 0.0.3)
|
||||
rest-client (>= 1.6.1)
|
||||
bushido_stub (0.0.3)
|
||||
activesupport (>= 3.0.7)
|
||||
cancan (1.6.5)
|
||||
cancan (1.6.7)
|
||||
capybara (1.1.1)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
@ -60,16 +77,22 @@ GEM
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (~> 2.0)
|
||||
xpath (~> 0.1.4)
|
||||
carrierwave (0.5.6)
|
||||
carrierwave (0.5.7)
|
||||
activesupport (~> 3.0)
|
||||
cells (3.6.6)
|
||||
carrierwave-mongoid (0.1.3)
|
||||
carrierwave (>= 0.5.6)
|
||||
mongoid (~> 2.1)
|
||||
cells (3.7.0)
|
||||
actionpack (~> 3.0)
|
||||
railties (~> 3.0)
|
||||
childprocess (0.2.2)
|
||||
ffi (~> 1.0.6)
|
||||
chunky_png (1.2.5)
|
||||
coffee-script (2.2.0)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.1.2)
|
||||
columnize (0.3.4)
|
||||
configuration (1.3.1)
|
||||
crack (0.1.8)
|
||||
cucumber (1.1.0)
|
||||
builder (>= 2.1.2)
|
||||
diff-lcs (>= 1.1.2)
|
||||
@ -80,133 +103,134 @@ GEM
|
||||
capybara (>= 1.1.1)
|
||||
cucumber (>= 1.1.0)
|
||||
nokogiri (>= 1.5.0)
|
||||
custom_fields (1.0.0.beta.25)
|
||||
activesupport (~> 3.0.9)
|
||||
mongoid (= 2.0.2)
|
||||
custom_fields (1.1.0.rc1)
|
||||
activesupport (~> 3.1.1)
|
||||
carrierwave-mongoid (~> 0.1.3)
|
||||
mongoid (= 2.3.2)
|
||||
daemons (1.1.4)
|
||||
database_cleaner (0.6.7)
|
||||
delayed_job (2.1.4)
|
||||
activesupport (~> 3.0)
|
||||
daemons
|
||||
delayed_job_mongoid (1.0.2)
|
||||
delayed_job_mongoid (1.0.4)
|
||||
delayed_job (~> 2.1.1)
|
||||
mongoid (~> 2.0.0.rc)
|
||||
devise (1.3.4)
|
||||
bcrypt-ruby (~> 2.1.2)
|
||||
mongoid (>= 2.0)
|
||||
devise (1.4.9)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
orm_adapter (~> 0.0.3)
|
||||
warden (~> 1.0.3)
|
||||
devise_bushido_authenticatable (1.0.0.alpha10)
|
||||
devise
|
||||
devise (>= 1.0.6)
|
||||
rubycas-client (>= 2.2.1)
|
||||
diff-lcs (1.1.3)
|
||||
dragonfly (0.9.8)
|
||||
rack
|
||||
erubis (2.6.6)
|
||||
abstract (>= 1.0.0)
|
||||
excon (0.6.6)
|
||||
factory_girl (2.1.2)
|
||||
ejs (1.0.0)
|
||||
erubis (2.7.0)
|
||||
excon (0.7.6)
|
||||
execjs (1.2.9)
|
||||
multi_json (~> 1.0)
|
||||
factory_girl (2.2.0)
|
||||
activesupport
|
||||
factory_girl_rails (1.2.0)
|
||||
factory_girl (~> 2.1.0)
|
||||
factory_girl_rails (1.3.0)
|
||||
factory_girl (~> 2.2.0)
|
||||
railties (>= 3.0.0)
|
||||
ffi (1.0.9)
|
||||
fog (0.8.2)
|
||||
fog (1.0.0)
|
||||
builder
|
||||
excon (~> 0.6.1)
|
||||
formatador (>= 0.1.3)
|
||||
json
|
||||
excon (~> 0.7.3)
|
||||
formatador (~> 0.2.0)
|
||||
mime-types
|
||||
net-ssh (>= 2.1.3)
|
||||
nokogiri (>= 1.4.4)
|
||||
multi_json (~> 1.0.3)
|
||||
net-scp (~> 1.0.4)
|
||||
net-ssh (~> 2.1.4)
|
||||
nokogiri (~> 1.5.0)
|
||||
ruby-hmac
|
||||
formatador (0.2.1)
|
||||
formtastic (1.2.4)
|
||||
actionpack (>= 2.3.7)
|
||||
activesupport (>= 2.3.7)
|
||||
i18n (~> 0.4)
|
||||
gherkin (2.5.2)
|
||||
fssm (0.2.7)
|
||||
gherkin (2.5.4)
|
||||
json (>= 1.4.6)
|
||||
growl-glue (1.0.7)
|
||||
haml (3.1.2)
|
||||
haml (3.1.3)
|
||||
has_scope (0.5.1)
|
||||
heroku (1.19.1)
|
||||
activesupport (>= 2.1.0)
|
||||
launchy (~> 0.3.2)
|
||||
rest-client (>= 1.4.0, < 1.7.0)
|
||||
highline (1.6.2)
|
||||
httparty (0.7.8)
|
||||
crack (= 0.1.8)
|
||||
i18n (0.5.0)
|
||||
inherited_resources (1.1.2)
|
||||
hike (1.2.1)
|
||||
httparty (0.8.1)
|
||||
multi_json
|
||||
multi_xml
|
||||
i18n (0.6.0)
|
||||
inherited_resources (1.3.0)
|
||||
has_scope (~> 0.5.0)
|
||||
responders (~> 0.6.0)
|
||||
jammit (0.6.3)
|
||||
yui-compressor (>= 0.9.3)
|
||||
jquery-rails (1.0.16)
|
||||
railties (~> 3.0)
|
||||
thor (~> 0.14)
|
||||
json (1.6.1)
|
||||
json_pure (1.6.1)
|
||||
kgio (2.6.0)
|
||||
launchy (0.3.7)
|
||||
configuration (>= 0.0.5)
|
||||
rake (>= 0.8.1)
|
||||
launchy (2.0.5)
|
||||
addressable (~> 2.2.6)
|
||||
linecache (0.43)
|
||||
linecache19 (0.5.12)
|
||||
ruby_core_source (>= 0.1.4)
|
||||
locomotive_jammit-s3 (0.5.4.4)
|
||||
jammit (>= 0.5.4)
|
||||
mimemagic (>= 0.1.7)
|
||||
s3 (>= 0.3.7)
|
||||
locomotive_liquid (2.2.2)
|
||||
locomotive_mongoid_acts_as_tree (0.1.5.7)
|
||||
mongoid (= 2.0.2)
|
||||
mail (2.2.19)
|
||||
activesupport (>= 2.3.6)
|
||||
mail (2.3.0)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
mime-types (1.16)
|
||||
mimemagic (0.1.8)
|
||||
mime-types (1.17.2)
|
||||
mimetype-fu (0.1.2)
|
||||
mocha (0.9.12)
|
||||
mongo (1.4.0)
|
||||
bson (= 1.4.0)
|
||||
mongoid (2.0.2)
|
||||
activemodel (~> 3.0)
|
||||
mongo (~> 1.3)
|
||||
mongo (1.4.1)
|
||||
bson (= 1.4.1)
|
||||
mongoid (2.3.2)
|
||||
activemodel (~> 3.1)
|
||||
mongo (~> 1.4)
|
||||
tzinfo (~> 0.3.22)
|
||||
net-ssh (2.2.1)
|
||||
multi_json (1.0.3)
|
||||
multi_xml (0.4.1)
|
||||
net-scp (1.0.4)
|
||||
net-ssh (>= 1.99.1)
|
||||
net-ssh (2.1.4)
|
||||
nokogiri (1.5.0)
|
||||
open4 (1.1.0)
|
||||
orm_adapter (0.0.5)
|
||||
pickle (0.4.10)
|
||||
cucumber (>= 0.8)
|
||||
rake
|
||||
polyglot (0.3.2)
|
||||
proxies (0.2.1)
|
||||
rack (1.2.4)
|
||||
rack (1.3.5)
|
||||
rack-cache (1.1)
|
||||
rack (>= 0.4)
|
||||
rack-mount (0.6.14)
|
||||
rack-mount (0.8.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (0.5.7)
|
||||
rack-ssl (1.3.2)
|
||||
rack
|
||||
rack-test (0.6.1)
|
||||
rack (>= 1.0)
|
||||
rails (3.0.10)
|
||||
actionmailer (= 3.0.10)
|
||||
actionpack (= 3.0.10)
|
||||
activerecord (= 3.0.10)
|
||||
activeresource (= 3.0.10)
|
||||
activesupport (= 3.0.10)
|
||||
rails (3.1.1)
|
||||
actionmailer (= 3.1.1)
|
||||
actionpack (= 3.1.1)
|
||||
activerecord (= 3.1.1)
|
||||
activeresource (= 3.1.1)
|
||||
activesupport (= 3.1.1)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.0.10)
|
||||
railties (3.0.10)
|
||||
actionpack (= 3.0.10)
|
||||
activesupport (= 3.0.10)
|
||||
railties (= 3.1.1)
|
||||
rails-backbone (0.5.4)
|
||||
coffee-script (~> 2.2.0)
|
||||
ejs (~> 1.0.0)
|
||||
rails (~> 3.1.0)
|
||||
railties (3.1.1)
|
||||
actionpack (= 3.1.1)
|
||||
activesupport (= 3.1.1)
|
||||
rack-ssl (~> 1.3.2)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (~> 0.14.4)
|
||||
raindrops (0.7.0)
|
||||
thor (~> 0.14.6)
|
||||
raindrops (0.8.0)
|
||||
rake (0.9.2)
|
||||
rdoc (3.9.4)
|
||||
rdoc (3.11)
|
||||
json (~> 1.4)
|
||||
responders (0.6.4)
|
||||
rest-client (1.6.7)
|
||||
mime-types (>= 1.16)
|
||||
@ -215,8 +239,7 @@ GEM
|
||||
rspec-core (~> 2.6.0)
|
||||
rspec-expectations (~> 2.6.0)
|
||||
rspec-mocks (~> 2.6.0)
|
||||
rspec-cells (0.0.5)
|
||||
cells (~> 3.4)
|
||||
rspec-cells (0.1.0)
|
||||
rails (~> 3.0)
|
||||
rspec-rails (~> 2.2)
|
||||
rspec-core (2.6.4)
|
||||
@ -244,82 +267,91 @@ GEM
|
||||
ruby-hmac (0.4.0)
|
||||
ruby_core_source (0.1.5)
|
||||
archive-tar-minitar (>= 0.5.2)
|
||||
rubycas-client (2.2.1)
|
||||
activesupport
|
||||
rubyzip (0.9.4)
|
||||
s3 (0.3.8)
|
||||
proxies (~> 0.2.0)
|
||||
sanitize (2.0.3)
|
||||
nokogiri (>= 1.4.4, < 1.6)
|
||||
sass (3.1.2)
|
||||
selenium-webdriver (2.8.0)
|
||||
nokogiri (< 1.6, >= 1.4.4)
|
||||
sass (3.1.10)
|
||||
sass-rails (3.1.4)
|
||||
actionpack (~> 3.1.0)
|
||||
railties (~> 3.1.0)
|
||||
sass (>= 3.1.4)
|
||||
sprockets (~> 2.0.0)
|
||||
tilt (~> 1.3.2)
|
||||
selenium-webdriver (2.10.0)
|
||||
childprocess (>= 0.2.1)
|
||||
ffi (>= 1.0.7)
|
||||
ffi (= 1.0.9)
|
||||
json_pure
|
||||
rubyzip
|
||||
spork (0.9.0.rc9)
|
||||
sprockets (2.0.3)
|
||||
hike (~> 1.2)
|
||||
rack (~> 1.0)
|
||||
tilt (!= 1.3.0, ~> 1.1)
|
||||
term-ansicolor (1.0.7)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.3)
|
||||
treetop (1.4.10)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.29)
|
||||
tzinfo (0.3.30)
|
||||
uglifier (1.0.4)
|
||||
execjs (>= 0.3.0)
|
||||
multi_json (>= 1.0.2)
|
||||
unicorn (4.1.1)
|
||||
kgio (~> 2.4)
|
||||
rack
|
||||
raindrops (~> 0.6)
|
||||
warden (1.0.5)
|
||||
warden (1.0.6)
|
||||
rack (>= 1.0)
|
||||
will_paginate (3.0.2)
|
||||
xpath (0.1.4)
|
||||
nokogiri (~> 1.3)
|
||||
yui-compressor (0.9.6)
|
||||
POpen4 (>= 0.1.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
RedCloth (= 4.2.8)
|
||||
RedCloth (~> 4.2.8)
|
||||
SystemTimer
|
||||
ZenTest
|
||||
actionmailer-with-request
|
||||
actionmailer-with-request (~> 0.3.0)
|
||||
autotest
|
||||
bson_ext (~> 1.4.0)
|
||||
bson_ext (~> 1.3.1)
|
||||
bushido (= 0.0.35)
|
||||
bushido_stub (= 0.0.3)
|
||||
cancan
|
||||
cancan (~> 1.6.7)
|
||||
capybara
|
||||
carrierwave (= 0.5.6)
|
||||
cells
|
||||
carrierwave-mongoid (~> 0.1.3)
|
||||
cells (~> 3.7.0)
|
||||
coffee-script (~> 2.2.0)
|
||||
compass!
|
||||
cucumber-rails
|
||||
custom_fields (= 1.0.0.beta.25)
|
||||
custom_fields (~> 1.1.0.rc1)
|
||||
database_cleaner
|
||||
delayed_job (= 2.1.4)
|
||||
delayed_job_mongoid (= 1.0.2)
|
||||
devise (= 1.3.4)
|
||||
devise_bushido_authenticatable (= 1.0.0.alpha10)
|
||||
dragonfly (~> 0.9.1)
|
||||
delayed_job (~> 2.1.1)
|
||||
delayed_job_mongoid (~> 1.0.4)
|
||||
devise (~> 1.4.9)
|
||||
dragonfly (~> 0.9.8)
|
||||
factory_girl_rails (~> 1.1)
|
||||
fog (= 0.8.2)
|
||||
fog (~> 1.0.0)
|
||||
formtastic (~> 1.2.3)
|
||||
growl-glue
|
||||
haml (= 3.1.2)
|
||||
heroku (= 1.19.1)
|
||||
highline
|
||||
httparty (= 0.7.8)
|
||||
inherited_resources (~> 1.1.2)
|
||||
haml (~> 3.1.3)
|
||||
handlebars-rails!
|
||||
highline (~> 1.6.2)
|
||||
httparty (~> 0.8.1)
|
||||
inherited_resources (~> 1.3.0)
|
||||
jquery-rails (~> 1.0.16)
|
||||
launchy
|
||||
linecache (= 0.43)
|
||||
locomotive_jammit-s3
|
||||
locomotive_liquid (= 2.2.2)
|
||||
locomotive_mongoid_acts_as_tree (= 0.1.5.7)
|
||||
mimetype-fu
|
||||
locomotive_mongoid_acts_as_tree (= 0.1.5.7)!
|
||||
mimetype-fu (~> 0.1.2)
|
||||
mocha (= 0.9.12)
|
||||
mongoid (~> 2.0.2)
|
||||
mongoid (~> 2.3.2)
|
||||
pickle
|
||||
rack-cache
|
||||
rails (= 3.0.10)
|
||||
rack-cache (~> 1.1)
|
||||
rails (~> 3.1.1)
|
||||
rails-backbone (= 0.5.4)
|
||||
rake (= 0.9.2)
|
||||
rmagick (= 2.12.2)
|
||||
rspec-cells
|
||||
@ -327,10 +359,10 @@ DEPENDENCIES
|
||||
ruby-debug
|
||||
ruby-debug19
|
||||
rubyzip
|
||||
sanitize
|
||||
sass (= 3.1.2)
|
||||
sanitize (~> 2.0.3)
|
||||
sass-rails (~> 3.1.4)
|
||||
spork (~> 0.9.0.rc)
|
||||
uglifier (~> 1.0.4)
|
||||
unicorn
|
||||
warden
|
||||
will_paginate (~> 3.0.0)
|
||||
will_paginate (~> 3.0.2)
|
||||
xpath (~> 0.1.4)
|
||||
|
@ -1,34 +0,0 @@
|
||||
class Admin::GlobalActionsCell < ::Admin::MenuCell
|
||||
|
||||
attr_reader :current_admin, :current_site_url
|
||||
|
||||
def show(args)
|
||||
@current_admin = args[:current_admin]
|
||||
@current_site_url = args[:current_site_url]
|
||||
super
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def build_list
|
||||
add :welcome, :url => edit_admin_my_account_url, :i18n_options => {
|
||||
:key => 'admin.shared.header.welcome',
|
||||
:arg => :name,
|
||||
:value => @current_admin.name
|
||||
}
|
||||
|
||||
add :see, :url => current_site_url, :id => 'viewsite', :target => '_blank'
|
||||
|
||||
if Locomotive.config.multi_sites? && current_admin.sites.size > 1
|
||||
add :switch, :url => '#', :id => 'sites-picker-link'
|
||||
end
|
||||
|
||||
add :help, :url => '#', :class => 'tutorial', :id => 'help'
|
||||
add :logout, :url => destroy_admin_session_url, :confirm => t('admin.messages.confirm')
|
||||
end
|
||||
|
||||
def localize_label(label, options = {})
|
||||
I18n.t("admin.shared.header.#{label}", options)
|
||||
end
|
||||
|
||||
end
|
@ -1,10 +0,0 @@
|
||||
class Admin::MainMenuCell < ::Admin::MenuCell
|
||||
|
||||
protected
|
||||
|
||||
def build_list
|
||||
add :contents, :url => admin_pages_url
|
||||
add :settings, :url => edit_admin_current_site_url
|
||||
end
|
||||
|
||||
end
|
@ -1,17 +0,0 @@
|
||||
class Admin::SettingsMenuCell < ::Admin::SubMenuCell #::Admin::MenuCell
|
||||
|
||||
protected
|
||||
|
||||
def build_list
|
||||
add :site, :url => edit_admin_current_site_url
|
||||
add :theme_assets, :url => admin_theme_assets_url
|
||||
add :account, :url => edit_admin_my_account_url
|
||||
end
|
||||
|
||||
# def build_item(name, attributes)
|
||||
# item = super
|
||||
# enhanced_class = "#{'on' if name.to_s == sections(:sub)} #{item[:class]}"
|
||||
# item.merge(:class => enhanced_class)
|
||||
# end
|
||||
|
||||
end
|
34
app/cells/locomotive/global_actions_cell.rb
Normal file
34
app/cells/locomotive/global_actions_cell.rb
Normal file
@ -0,0 +1,34 @@
|
||||
class Locomotive::GlobalActionsCell < ::Locomotive::MenuCell
|
||||
|
||||
attr_reader :current_account, :current_site_url
|
||||
|
||||
def show(args)
|
||||
@current_account = args[:current_account]
|
||||
@current_site_url = args[:current_site_url]
|
||||
super
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def build_list
|
||||
add :welcome, :url => edit_locomotive_my_account_url, :i18n_options => {
|
||||
:key => 'locomotive.shared.header.welcome',
|
||||
:arg => :name,
|
||||
:value => @current_account.name
|
||||
}
|
||||
|
||||
add :see, :url => current_site_url, :id => 'viewsite', :target => '_blank'
|
||||
|
||||
if Locomotive.config.multi_sites? && current_account.sites.size > 1
|
||||
add :switch, :url => '#', :id => 'sites-picker-link'
|
||||
end
|
||||
|
||||
add :help, :url => '#', :class => 'tutorial', :id => 'help'
|
||||
add :logout, :url => destroy_locomotive_session_url, :confirm => t('locomotive.messages.confirm')
|
||||
end
|
||||
|
||||
def localize_label(label, options = {})
|
||||
I18n.t("locomotive.shared.header.#{label}", options)
|
||||
end
|
||||
|
||||
end
|
10
app/cells/locomotive/main_menu_cell.rb
Normal file
10
app/cells/locomotive/main_menu_cell.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class Locomotive::MainMenuCell < ::Locomotive::MenuCell
|
||||
|
||||
protected
|
||||
|
||||
def build_list
|
||||
add :contents, :url => locomotive_pages_url
|
||||
add :settings, :url => edit_locomotive_current_site_url
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class Admin::MenuCell < Cell::Base
|
||||
class Locomotive::MenuCell < Cell::Base
|
||||
|
||||
include ::Rails.application.routes.url_helpers
|
||||
|
||||
@ -94,7 +94,7 @@ class Admin::MenuCell < Cell::Base
|
||||
end
|
||||
|
||||
def localize_label(label)
|
||||
I18n.t("admin.shared.menu.#{label}")
|
||||
I18n.t("locomotive.shared.menu.#{label}")
|
||||
end
|
||||
|
||||
end
|
11
app/cells/locomotive/settings_menu_cell.rb
Normal file
11
app/cells/locomotive/settings_menu_cell.rb
Normal file
@ -0,0 +1,11 @@
|
||||
class Locomotive::SettingsMenuCell < ::Locomotive::SubMenuCell
|
||||
|
||||
protected
|
||||
|
||||
def build_list
|
||||
add :site, :url => edit_locomotive_current_site_url
|
||||
add :theme_assets, :url => locomotive_theme_assets_url
|
||||
add :account, :url => edit_locomotive_my_account_url
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class Admin::SubMenuCell < ::Admin::MenuCell
|
||||
class Locomotive::SubMenuCell < ::Locomotive::MenuCell
|
||||
|
||||
protected
|
||||
|
@ -1,11 +0,0 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
protect_from_forgery
|
||||
|
||||
protected
|
||||
|
||||
# rescue_from Exception, :with => :render_error
|
||||
#
|
||||
# def render_error
|
||||
# render :template => "/admin/errors/500", :layout => '/admin/layouts/box', :status => 500
|
||||
# end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class AccountsController < BaseController
|
||||
|
||||
sections 'settings'
|
||||
@ -11,7 +11,7 @@ module Admin
|
||||
@account = Account.create(params[:account])
|
||||
current_site.memberships.create(:account => @account) if @account.errors.empty?
|
||||
|
||||
respond_with @account, :location => edit_admin_current_site_url
|
||||
respond_with @account, :location => edit_locomotive_current_site_url
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class ApiContentsController < ActionController::Base
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class AssetsController < BaseController
|
||||
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
@ -48,7 +48,7 @@ module Admin
|
||||
:content_type => asset.content_type,
|
||||
:url => asset.source.url,
|
||||
:vignette_url => asset.vignette_url,
|
||||
:destroy_url => admin_asset_url(asset, :json)
|
||||
:destroy_url => locomotive_asset_url(asset, :json)
|
||||
}
|
||||
end
|
||||
|
@ -1,11 +1,11 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class BaseController < InheritedResources::Base
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
||||
|
||||
layout '/admin/layouts/application'
|
||||
layout '/locomotive/layouts/application'
|
||||
|
||||
before_filter :require_admin
|
||||
before_filter :require_account
|
||||
|
||||
before_filter :require_site
|
||||
|
||||
@ -21,12 +21,12 @@ module Admin
|
||||
|
||||
# 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|
|
||||
helper "admin/#{File.basename(file, '.rb').gsub(/_helper$/, '')}"
|
||||
helper "locomotive/#{File.basename(file, '.rb').gsub(/_helper$/, '')}"
|
||||
end
|
||||
|
||||
self.responder = Locomotive::AdminResponder # custom responder
|
||||
|
||||
defaults :route_prefix => 'admin'
|
||||
defaults :route_prefix => 'locomotive'
|
||||
|
||||
respond_to :html
|
||||
|
||||
@ -38,23 +38,23 @@ module Admin
|
||||
else
|
||||
flash[:alert] = exception.message
|
||||
|
||||
redirect_to admin_pages_url
|
||||
redirect_to locomotive_pages_url
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_current_thread_variables
|
||||
Thread.current[:admin] = current_admin
|
||||
Thread.current[:account] = current_account
|
||||
Thread.current[:site] = current_site
|
||||
end
|
||||
|
||||
def current_ability
|
||||
@current_ability ||= Ability.new(current_admin, current_site)
|
||||
@current_ability ||= Ability.new(current_account, current_site)
|
||||
end
|
||||
|
||||
def require_admin
|
||||
authenticate_admin!
|
||||
def require_account
|
||||
authenticate_account!
|
||||
end
|
||||
|
||||
def begin_of_association_chain
|
||||
@ -78,7 +78,7 @@ module Admin
|
||||
end
|
||||
|
||||
def set_locale
|
||||
I18n.locale = current_admin.locale rescue Locomotive.config.default_locale
|
||||
I18n.locale = current_account.locale rescue Locomotive.config.default_locale
|
||||
end
|
||||
|
||||
# ___ site/page urls builder ___
|
@ -1,10 +1,10 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class ContentTypesController < BaseController
|
||||
|
||||
sections 'contents'
|
||||
|
||||
def destroy
|
||||
destroy! { admin_pages_url }
|
||||
destroy! { locomotive_pages_url }
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class ContentsController < BaseController
|
||||
|
||||
sections 'contents'
|
||||
@ -36,11 +36,11 @@ module Admin
|
||||
def sort
|
||||
@content_type.sort_contents!(params[:children])
|
||||
|
||||
respond_with(@content_type, :location => admin_contents_url(@content_type.slug))
|
||||
respond_with(@content_type, :location => locomotive_contents_url(@content_type.slug))
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy! { admin_contents_url(@content_type.slug) }
|
||||
destroy! { locomotive_contents_url(@content_type.slug) }
|
||||
end
|
||||
|
||||
protected
|
||||
@ -55,7 +55,7 @@ module Admin
|
||||
|
||||
def after_create_or_update_url
|
||||
if params[:breadcrumb_alias].blank?
|
||||
edit_admin_content_url(@content_type.slug, @content.id)
|
||||
edit_locomotive_content_url(@content_type.slug, @content.id)
|
||||
else
|
||||
self.breadcrumb_url
|
||||
end
|
||||
@ -72,11 +72,11 @@ module Admin
|
||||
end
|
||||
|
||||
def breadcrumb_url
|
||||
edit_admin_content_url(self.breadcrumb_root._parent.slug, self.breadcrumb_root)
|
||||
edit_locomotive_content_url(self.breadcrumb_root._parent.slug, self.breadcrumb_root)
|
||||
end
|
||||
|
||||
def back_url
|
||||
self.breadcrumb_root ? self.breadcrumb_url : admin_contents_url(@content_type.slug)
|
||||
self.breadcrumb_root ? self.breadcrumb_url : locomotive_contents_url(@content_type.slug)
|
||||
end
|
||||
|
||||
end
|
@ -1,25 +1,25 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class CrossDomainSessionsController < BaseController
|
||||
|
||||
layout '/admin/layouts/box'
|
||||
layout '/locomotive/layouts/box'
|
||||
|
||||
skip_before_filter :verify_authenticity_token
|
||||
|
||||
skip_before_filter :validate_site_membership
|
||||
|
||||
before_filter :require_admin, :only => :new
|
||||
before_filter :require_account, :only => :new
|
||||
|
||||
skip_load_and_authorize_resource
|
||||
|
||||
def new
|
||||
if site = current_admin.sites.detect { |s| s._id.to_s == params[:target_id] }
|
||||
if site = current_account.sites.detect { |s| s._id.to_s == params[:target_id] }
|
||||
if Rails.env == 'development'
|
||||
@target = site.full_subdomain
|
||||
else
|
||||
@target = site.domains_without_subdomain.first || site.full_subdomain
|
||||
end
|
||||
|
||||
current_admin.reset_switch_site_token!
|
||||
current_account.reset_switch_site_token!
|
||||
else
|
||||
redirect_to admin_pages_path
|
||||
end
|
||||
@ -31,7 +31,7 @@ module Admin
|
||||
sign_in(account)
|
||||
redirect_to admin_pages_path
|
||||
else
|
||||
redirect_to new_admin_session_path, :alert => t('flash.admin.cross_domain_sessions.create.alert')
|
||||
redirect_to new_admin_session_path, :alert => t('fash.locomotive.cross_domain_sessions.create.alert')
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class CurrentSiteController < BaseController
|
||||
|
||||
defaults :instance_name => 'site'
|
||||
@ -15,7 +15,7 @@ module Admin
|
||||
|
||||
def update
|
||||
update! do |success, failure|
|
||||
success.html { redirect_to edit_admin_current_site_url(new_host_if_subdomain_changed) }
|
||||
success.html { redirect_to edit_locomotive_current_site_url(new_host_if_subdomain_changed) }
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class CustomFieldsController < BaseController
|
||||
|
||||
layout false
|
||||
@ -18,7 +18,7 @@ module Admin
|
||||
if @field.update_attributes(params[:custom_field])
|
||||
render :json => @field.to_json
|
||||
else
|
||||
render :json => { :error => t('flash.admin.custom_fields.update.alert') }
|
||||
render :json => { :error => t('flash.locomotive.custom_fields.update.alert') }
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class ExportController < BaseController
|
||||
|
||||
skip_load_and_authorize_resource
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class ImportController < BaseController
|
||||
|
||||
sections 'settings', 'site'
|
||||
@ -14,7 +14,7 @@ module Admin
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to new_admin_import_url if @job.nil?
|
||||
redirect_to new_locomotive_import_url if @job.nil?
|
||||
end
|
||||
format.json { render :json => {
|
||||
:step => @job.nil? ? 'done' : @job.step,
|
||||
@ -32,14 +32,14 @@ module Admin
|
||||
:reset => Boolean.set(params[:reset])
|
||||
})
|
||||
|
||||
flash[:notice] = t("flash.admin.import.create.#{Locomotive.config.delayed_job ? 'notice' : 'done'}")
|
||||
flash[:notice] = t("fash.locomotive.import.create.#{Locomotive.config.delayed_job ? 'notice' : 'done'}")
|
||||
|
||||
redirect_to Locomotive.config.delayed_job ? admin_import_url : new_admin_import_url
|
||||
redirect_to Locomotive.config.delayed_job ? locomotive_import_url : new_locomotive_import_url
|
||||
rescue Exception => e
|
||||
logger.error "[Locomotive import] #{e.message} / #{e.backtrace}"
|
||||
|
||||
@error = e.message
|
||||
flash[:alert] = t('flash.admin.import.create.alert')
|
||||
flash[:alert] = t('fash.locomotive.import.create.alert')
|
||||
|
||||
render 'new'
|
||||
end
|
@ -1,11 +1,11 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class InstallationController < BaseController
|
||||
|
||||
layout '/admin/layouts/box'
|
||||
layout '/locomotive/layouts/box'
|
||||
|
||||
skip_before_filter :require_site
|
||||
|
||||
skip_before_filter :require_admin
|
||||
skip_before_filter :require_account
|
||||
|
||||
skip_before_filter :verify_authenticity_token
|
||||
|
||||
@ -36,7 +36,7 @@ module Admin
|
||||
when 1 # create account
|
||||
@account = Account.create(params[:account])
|
||||
if @account.valid?
|
||||
redirect_to admin_installation_step_url(2)
|
||||
redirect_to locomotive_installation_step_url(2)
|
||||
else
|
||||
render 'step_1'
|
||||
end
|
||||
@ -58,7 +58,7 @@ module Admin
|
||||
case params[:step].to_i
|
||||
when 1 # already an account in db
|
||||
if account = Account.first
|
||||
@step_done = I18n.t('admin.installation.step_1.done', :name => account.name, :email => account.email)
|
||||
@step_done = I18n.t('locomotive.installation.step_1.done', :name => account.name, :email => account.email)
|
||||
render 'step_1' and return false
|
||||
end
|
||||
else
|
||||
@ -67,14 +67,14 @@ module Admin
|
||||
end
|
||||
|
||||
def allow_installation?
|
||||
redirect_to admin_pages_url if Site.count > 0 && Account.count > 0
|
||||
redirect_to locomotive_pages_url if Site.count > 0 && Account.count > 0
|
||||
end
|
||||
|
||||
def last_url
|
||||
if Locomotive.config.manage_domains?
|
||||
admin_session_url(:host => Site.first.domains.first, :port => request.port)
|
||||
locomotive_session_url(:host => Site.first.domains.first, :port => request.port)
|
||||
else
|
||||
admin_session_url
|
||||
locomotive_session_url
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class MembershipsController < BaseController
|
||||
|
||||
sections 'settings'
|
||||
@ -8,18 +8,18 @@ module Admin
|
||||
|
||||
case resource.process!
|
||||
when :create_account
|
||||
redirect_to new_admin_account_url(:email => resource.email)
|
||||
redirect_to new_locomotive_account_url(:email => resource.email)
|
||||
when :save_it
|
||||
respond_with resource, :location => edit_admin_current_site_url
|
||||
respond_with resource, :location => edit_locomotive_current_site_url
|
||||
when :error
|
||||
respond_with resource, :flash => true
|
||||
when :already_created
|
||||
respond_with resource, :alert => t('flash.admin.memberships.create.already_created'), :location => edit_admin_current_site_url
|
||||
respond_with resource, :alert => t('flash.locomotive.memberships.create.already_created'), :location => edit_locomotive_current_site_url
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy! { edit_admin_current_site_url }
|
||||
destroy! { edit_locomotive_current_site_url }
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class MyAccountController < BaseController
|
||||
|
||||
sections 'settings', 'account'
|
||||
@ -10,13 +10,13 @@ module Admin
|
||||
skip_load_and_authorize_resource
|
||||
|
||||
def update
|
||||
update! { edit_admin_my_account_url }
|
||||
update! { edit_locomotive_my_account_url }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def resource
|
||||
@account = current_admin
|
||||
@account = current_account
|
||||
end
|
||||
|
||||
def begin_of_association_chain; nil; end # not related directly to current_site
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class PagesController < BaseController
|
||||
|
||||
sections 'contents'
|
||||
@ -17,9 +17,9 @@ module Admin
|
||||
update! do |success, failure|
|
||||
success.json do
|
||||
render :json => {
|
||||
:notice => t('flash.admin.pages.update.notice'),
|
||||
:notice => t('flash.locomotive.pages.update.notice'),
|
||||
:editable_elements => @page.template_changed ?
|
||||
render_to_string(:partial => 'admin/pages/editable_elements.html.haml') : ''
|
||||
render_to_string(:partial => 'locomotive/pages/editable_elements.html.haml') : ''
|
||||
}
|
||||
end
|
||||
end
|
@ -1,13 +1,13 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class PasswordsController < Devise::PasswordsController
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
||||
|
||||
layout '/admin/layouts/box'
|
||||
layout '/locomotive/layouts/box'
|
||||
|
||||
before_filter :require_site
|
||||
|
||||
helper 'admin/base', 'admin/box'
|
||||
helper 'locomotive/base', 'locomotive/box'
|
||||
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class RenderingController < ActionController::Base
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class RobotsController < ActionController::Base
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
@ -1,18 +1,18 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class SessionsController < Devise::SessionsController
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
||||
|
||||
layout '/admin/layouts/box'
|
||||
layout '/locomotive/layouts/box'
|
||||
|
||||
before_filter :require_site
|
||||
|
||||
helper 'admin/base', 'admin/box'
|
||||
helper 'locomotive/base', 'locomotive/box'
|
||||
|
||||
protected
|
||||
|
||||
def after_sign_in_path_for(resource)
|
||||
admin_pages_url
|
||||
locomotive_pages_url
|
||||
end
|
||||
|
||||
def after_sign_out_path_for(resource)
|
@ -1,7 +1,7 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class SitemapsController < BaseController
|
||||
|
||||
skip_before_filter :require_admin, :validate_site_membership, :set_locale
|
||||
skip_before_filter :require_account, :validate_site_membership, :set_locale
|
||||
|
||||
before_filter :require_site
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class SitesController < BaseController
|
||||
|
||||
defaults :instance_name => 'site'
|
||||
@ -7,13 +7,13 @@ module Admin
|
||||
|
||||
def create
|
||||
@site = Site.new(params[:site])
|
||||
@site.memberships.build :account => @current_admin, :role => 'admin'
|
||||
@site.memberships.build :account => @current_account, :role => 'admin'
|
||||
|
||||
create! { edit_admin_my_account_url }
|
||||
create! { edit_locomotive_my_account_url }
|
||||
end
|
||||
|
||||
def destroy
|
||||
@site = current_admin.sites.find(params[:id])
|
||||
@site = current_account.sites.find(params[:id])
|
||||
|
||||
if @site != current_site
|
||||
@site.destroy
|
||||
@ -21,7 +21,7 @@ module Admin
|
||||
@site.errors.add(:base, 'Can not destroy the site you are logging in now')
|
||||
end
|
||||
|
||||
respond_with @site, :location => edit_admin_my_account_url
|
||||
respond_with @site, :location => edit_locomotive_my_account_url
|
||||
end
|
||||
|
||||
protected
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class SnippetsController < BaseController
|
||||
|
||||
sections 'settings', 'theme_assets'
|
||||
@ -7,7 +7,7 @@ module Admin
|
||||
|
||||
def destroy
|
||||
destroy! do |format|
|
||||
format.html { redirect_to admin_theme_assets_url }
|
||||
format.html { redirect_to locomotive_theme_assets_url }
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class ThemeAssetsController < BaseController
|
||||
|
||||
include ActionView::Helpers::SanitizeHelper
|
@ -1,7 +1,7 @@
|
||||
module Admin::AccountsHelper
|
||||
module Locomotive::AccountsHelper
|
||||
|
||||
def admin_on?(site = current_site)
|
||||
site.memberships.detect { |m| m.admin? && m.account == current_admin }
|
||||
site.memberships.detect { |m| m.admin? && m.account == current_account }
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin::AssetsHelper
|
||||
module Locomotive::AssetsHelper
|
||||
|
||||
def vignette_tag(asset)
|
||||
if asset.image?
|
@ -1,4 +1,4 @@
|
||||
module Admin::BaseHelper
|
||||
module Locomotive::BaseHelper
|
||||
|
||||
def title(title = nil)
|
||||
if title.nil?
|
||||
@ -15,7 +15,7 @@ module Admin::BaseHelper
|
||||
|
||||
css = "#{'on' if name == sections(:sub)} #{options[:css]}"
|
||||
|
||||
label_link = default_options[:i18n] ? t("admin.shared.menu.#{name}") : name
|
||||
label_link = default_options[:i18n] ? t("locomotive.shared.menu.#{name}") : name
|
||||
if block_given?
|
||||
popup = content_tag(:div, capture(&block), :class => 'popup', :style => 'display: none')
|
||||
link = link_to(content_tag(:span, preserve(label_link) + content_tag(:em)) + content_tag(:em), url, :class => css)
|
@ -1,4 +1,4 @@
|
||||
module Admin::BoxHelper
|
||||
module Locomotive::BoxHelper
|
||||
|
||||
def box_flash_message
|
||||
if not flash.empty?
|
||||
@ -15,7 +15,7 @@ module Admin::BoxHelper
|
||||
end
|
||||
|
||||
def next_installation_step_link(step = 1, label = nil)
|
||||
link_to(content_tag(:span, label || t('admin.installation.common.next')), admin_installation_step_url(step), :class => 'button')
|
||||
link_to(content_tag(:span, label || t('locomotive.installation.common.next')), locomotive_installation_step_url(step), :class => 'button')
|
||||
end
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
module Admin::ContentTypesHelper
|
||||
module Locomotive::ContentTypesHelper
|
||||
|
||||
MAX_DISPLAYED_CONTENTS = 4
|
||||
|
||||
@ -42,7 +42,7 @@ module Admin::ContentTypesHelper
|
||||
item_on = (content_type.slug == @content_type.slug) rescue nil
|
||||
|
||||
label = truncate(content_type.name, :length => 15)
|
||||
url = admin_contents_url(content_type.slug)
|
||||
url = locomotive_contents_url(content_type.slug)
|
||||
css = @content_type && content_type.slug == @content_type.slug ? 'on' : ''
|
||||
|
||||
html = admin_content_menu_item(label, url, :i18n => false, :css => css) do
|
||||
@ -79,7 +79,7 @@ module Admin::ContentTypesHelper
|
||||
registers = {
|
||||
:controller => self,
|
||||
:site => current_site,
|
||||
:current_admin => current_admin
|
||||
:current_account => current_account
|
||||
}
|
||||
|
||||
preserve(content._parent.item_template.render(::Liquid::Context.new({}, assigns, registers)))
|
@ -1,4 +1,4 @@
|
||||
module Admin::CustomFieldsHelper
|
||||
module Locomotive::CustomFieldsHelper
|
||||
|
||||
def options_for_field_kind
|
||||
%w(string text category boolean date file has_one has_many).map do |kind|
|
||||
@ -8,14 +8,14 @@ module Admin::CustomFieldsHelper
|
||||
|
||||
def options_for_order_by(content_type, collection_name)
|
||||
options = %w{created_at updated_at _position_in_list}.map do |type|
|
||||
[t("admin.content_types.form.order_by.#{type.gsub(/^_/, '')}"), type]
|
||||
[t("locomotive.content_types.form.order_by.#{type.gsub(/^_/, '')}"), type]
|
||||
end
|
||||
options + options_for_highlighted_field(content_type, collection_name)
|
||||
end
|
||||
|
||||
def options_for_order_direction
|
||||
%w(asc desc).map do |direction|
|
||||
[t("admin.content_types.form.order_direction.#{direction}"), direction]
|
||||
[t("locomotive.content_types.form.order_direction.#{direction}"), direction]
|
||||
end
|
||||
end
|
||||
|
||||
@ -35,7 +35,7 @@ module Admin::CustomFieldsHelper
|
||||
|
||||
def options_for_text_formatting
|
||||
options = %w(none html).map do |option|
|
||||
[t("admin.custom_fields.text_formatting.#{option}"), option]
|
||||
[t("locomotive.custom_fields.text_formatting.#{option}"), option]
|
||||
end
|
||||
end
|
||||
|
||||
@ -128,10 +128,10 @@ module Admin::CustomFieldsHelper
|
||||
|
||||
options.merge!(
|
||||
:new_item => {
|
||||
:label => t('admin.contents.form.has_many.new_item'),
|
||||
:url => new_admin_content_url(field.target_klass._parent.slug, url_options)
|
||||
:label => t('locomotive.contents.form.has_many.new_item'),
|
||||
:url => new_locomotive_content_url(field.target_klass._parent.slug, url_options)
|
||||
},
|
||||
:edit_item_url => edit_admin_content_url(field.target_klass._parent.slug, 42, url_options)
|
||||
:edit_item_url => edit_locomotive_content_url(field.target_klass._parent.slug, 42, url_options)
|
||||
)
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Admin::PagesHelper
|
||||
module Locomotive::PagesHelper
|
||||
|
||||
def parent_pages_options
|
||||
roots = current_site.pages.roots.where(:slug.ne => '404').and(:_id.ne => @page.id)
|
@ -1,4 +1,4 @@
|
||||
module Admin::SitesHelper
|
||||
module Locomotive::SitesHelper
|
||||
|
||||
def application_domain
|
||||
domain = Locomotive.config.domain
|
@ -1,4 +1,4 @@
|
||||
module Admin
|
||||
module Locomotive
|
||||
class Notifications < ActionMailer::Base
|
||||
|
||||
default :from => Locomotive.config.mailer_sender
|
||||
@ -6,7 +6,7 @@ module Admin
|
||||
def new_content_instance(account, content)
|
||||
@account, @content = account, content
|
||||
|
||||
subject = t('admin.notifications.new_content_instance.subject', :type => content.content_type.name, :locale => account.locale)
|
||||
subject = t('locomotive.notifications.new_content_instance.subject', :type => content.content_type.name, :locale => account.locale)
|
||||
|
||||
mail :subject => subject, :to => account.email
|
||||
end
|
@ -1,83 +0,0 @@
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
ROLES = %w(admin designer author)
|
||||
|
||||
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]
|
||||
|
||||
can :touch, Site do |site|
|
||||
site == @site
|
||||
end
|
||||
end
|
||||
|
||||
def setup_designer_permissions!
|
||||
can :manage, Page
|
||||
|
||||
can :manage, ContentInstance
|
||||
|
||||
can :manage, ContentType
|
||||
|
||||
can :manage, Snippet
|
||||
|
||||
can :manage, ThemeAsset
|
||||
|
||||
can :manage, Asset
|
||||
|
||||
can :manage, Site do |site|
|
||||
site == @site
|
||||
end
|
||||
|
||||
can :import, Site
|
||||
|
||||
can :export, Site
|
||||
|
||||
can :point, Site
|
||||
|
||||
cannot :create, Site
|
||||
|
||||
can :manage, Membership
|
||||
|
||||
cannot :grant_admin, Membership
|
||||
|
||||
cannot [:update, :destroy], Membership do |membership|
|
||||
@membership.account_id == membership.account_id || # can not edit myself
|
||||
membership.admin? # can not modify an administrator
|
||||
end
|
||||
end
|
||||
|
||||
def setup_admin_permissions!
|
||||
can :manage, :all
|
||||
|
||||
cannot [:update, :destroy], Membership do |membership|
|
||||
@membership.account_id == membership.account_id # can not edit myself
|
||||
end
|
||||
end
|
||||
end
|
@ -1,60 +0,0 @@
|
||||
require 'digest'
|
||||
|
||||
class Account
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
devise *Locomotive.config.devise_modules
|
||||
|
||||
## attributes ##
|
||||
field :name
|
||||
field :locale, :default => Locomotive.config.default_locale.to_s or 'en'
|
||||
field :switch_site_token
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name
|
||||
|
||||
## associations ##
|
||||
|
||||
## callbacks ##
|
||||
before_destroy :remove_memberships!
|
||||
|
||||
## methods ##
|
||||
|
||||
def sites
|
||||
@sites ||= Site.where({ 'memberships.account_id' => self._id })
|
||||
end
|
||||
|
||||
def reset_switch_site_token!
|
||||
self.switch_site_token = ActiveSupport::SecureRandom.base64(8).gsub("/", "_").gsub(/=+$/, "")
|
||||
self.save
|
||||
end
|
||||
|
||||
def self.find_using_switch_site_token(token, age = 1.minute)
|
||||
return if token.blank?
|
||||
self.where(:switch_site_token => token, :updated_at.gt => age.ago.utc).first
|
||||
end
|
||||
|
||||
def self.find_using_switch_site_token!(token, age = 1.minute)
|
||||
self.find_using_switch_site_token(token, age) || raise(Mongoid::Errors::DocumentNotFound.new(self, token))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def password_required?
|
||||
!persisted? || !password.blank? || !password_confirmation.blank?
|
||||
end
|
||||
|
||||
def remove_memberships!
|
||||
self.sites.each do |site|
|
||||
membership = site.memberships.where(:account_id => self._id).first
|
||||
|
||||
if site.admin_memberships.size == 1 && membership.admin?
|
||||
raise I18n.t('errors.messages.needs_admin_account')
|
||||
else
|
||||
membership.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,39 +0,0 @@
|
||||
class Asset
|
||||
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## extensions ##
|
||||
include Extensions::Asset::Types
|
||||
include Extensions::Asset::Vignette
|
||||
|
||||
## fields ##
|
||||
field :content_type, :type => String
|
||||
field :width, :type => Integer
|
||||
field :height, :type => Integer
|
||||
field :size, :type => Integer
|
||||
field :position, :type => Integer, :default => 0
|
||||
mount_uploader :source, AssetUploader
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :source
|
||||
|
||||
## behaviours ##
|
||||
|
||||
## methods ##
|
||||
|
||||
alias :name :source_filename
|
||||
|
||||
def extname
|
||||
return nil unless self.source?
|
||||
File.extname(self.source_filename).gsub(/^\./, '')
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes).stringify_keys
|
||||
end
|
||||
|
||||
end
|
@ -1,125 +0,0 @@
|
||||
class ContentInstance
|
||||
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## extensions ##
|
||||
include CustomFields::ProxyClassEnabler
|
||||
include Extensions::Shared::Seo
|
||||
|
||||
## fields (dynamic fields) ##
|
||||
field :_slug
|
||||
field :_position_in_list, :type => Integer, :default => 0
|
||||
field :_visible, :type => Boolean, :default => true
|
||||
|
||||
## validations ##
|
||||
validate :require_highlighted_field
|
||||
validate :validate_uniqueness_of_slug
|
||||
validates_presence_of :_slug
|
||||
|
||||
## associations ##
|
||||
embedded_in :content_type, :inverse_of => :contents
|
||||
|
||||
## callbacks ##
|
||||
before_validation :set_slug
|
||||
before_save :set_visibility
|
||||
before_create :add_to_list_bottom
|
||||
after_create :send_notifications
|
||||
|
||||
## named scopes ##
|
||||
scope :latest_updated, :order_by => :updated_at.desc, :limit => Locomotive.config.lastest_items_nb
|
||||
|
||||
## methods ##
|
||||
|
||||
delegate :site, :to => :content_type
|
||||
|
||||
alias :visible? :_visible?
|
||||
alias :_permalink :_slug
|
||||
alias :_permalink= :_slug=
|
||||
|
||||
def site_id # needed by the uploader of custom fields
|
||||
self.content_type.site_id
|
||||
end
|
||||
|
||||
def highlighted_field_value
|
||||
self.send(self.content_type.highlighted_field_name)
|
||||
end
|
||||
|
||||
alias :_label :highlighted_field_value
|
||||
|
||||
def visible?
|
||||
self._visible || self._visible.nil?
|
||||
end
|
||||
|
||||
def next
|
||||
content_type.contents.where(:_position_in_list => _position_in_list + 1).first()
|
||||
end
|
||||
|
||||
def previous
|
||||
content_type.contents.where(:_position_in_list => _position_in_list - 1).first()
|
||||
end
|
||||
|
||||
def errors_to_hash
|
||||
Hash.new.replace(self.errors)
|
||||
end
|
||||
|
||||
def reload_parent!
|
||||
self.class.reload_parent!
|
||||
end
|
||||
|
||||
def self.reload_parent!
|
||||
self._parent = self._parent.reload
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Content.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_slug
|
||||
self._slug = self.highlighted_field_value.dup if self._slug.blank? && self.highlighted_field_value.present?
|
||||
self._slug.permalink! if self._slug.present?
|
||||
end
|
||||
|
||||
def set_visibility
|
||||
field = self.content_type.content_custom_fields.detect { |f| %w{visible active}.include?(f._alias) }
|
||||
self._visible = self.send(field._name) rescue true
|
||||
end
|
||||
|
||||
def add_to_list_bottom
|
||||
self._position_in_list = self.content_type.contents.size
|
||||
end
|
||||
|
||||
def require_highlighted_field
|
||||
_alias = self.highlighted_field_alias
|
||||
if self.send(_alias).blank?
|
||||
self.errors.add(_alias, :blank)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_uniqueness_of_slug
|
||||
if self._parent.contents.any? { |c| c._id != self._id && c._slug == self._slug }
|
||||
self.errors.add(:_slug, :taken)
|
||||
end
|
||||
end
|
||||
|
||||
def highlighted_field_alias
|
||||
self.content_type.highlighted_field._alias.to_sym
|
||||
end
|
||||
|
||||
def send_notifications
|
||||
return unless self.content_type.api_enabled? && !self.content_type.api_accounts.blank?
|
||||
|
||||
accounts = self.content_type.site.accounts.to_a
|
||||
|
||||
self.content_type.api_accounts.each do |account_id|
|
||||
next if account_id.blank?
|
||||
|
||||
account = accounts.detect { |a| a.id.to_s == account_id.to_s }
|
||||
|
||||
Admin::Notifications.new_content_instance(account, self).deliver
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,145 +0,0 @@
|
||||
class ContentType
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## extensions ##
|
||||
include Extensions::ContentType::ItemTemplate
|
||||
|
||||
## fields ##
|
||||
field :name
|
||||
field :description
|
||||
field :slug
|
||||
field :order_by
|
||||
field :order_direction, :default => 'asc'
|
||||
field :highlighted_field_name
|
||||
field :group_by_field_name
|
||||
field :api_enabled, :type => Boolean, :default => false
|
||||
field :api_accounts, :type => Array
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
embeds_many :contents, :class_name => 'ContentInstance', :validate => false do
|
||||
def visible
|
||||
@target.find_all { |c| c.visible? }
|
||||
end
|
||||
end
|
||||
|
||||
## named scopes ##
|
||||
scope :ordered, :order_by => :updated_at.desc
|
||||
|
||||
## indexes ##
|
||||
index [[:site_id, Mongo::ASCENDING], [:slug, Mongo::ASCENDING]]
|
||||
|
||||
## callbacks ##
|
||||
before_validation :normalize_slug
|
||||
before_save :set_default_values
|
||||
after_destroy :remove_uploaded_files
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :name, :slug
|
||||
validates_uniqueness_of :slug, :scope => :site_id
|
||||
validates_size_of :content_custom_fields, :minimum => 1, :message => :array_too_short
|
||||
|
||||
## behaviours ##
|
||||
custom_fields_for :contents
|
||||
|
||||
## methods ##
|
||||
|
||||
def groupable?
|
||||
self.group_by_field && group_by_field.category?
|
||||
end
|
||||
|
||||
def order_manually?
|
||||
self.order_by == '_position_in_list'
|
||||
end
|
||||
|
||||
def asc_order?
|
||||
self.order_direction.blank? || self.order_direction == 'asc'
|
||||
end
|
||||
|
||||
def list_or_group_contents
|
||||
if self.groupable?
|
||||
groups = self.contents.klass.send(:"group_by_#{self.group_by_field._alias}", :ordered_contents)
|
||||
|
||||
# look for items with no category or unknown ones
|
||||
items_without_category = self.contents.find_all { |c| !self.group_by_field.category_ids.include?(c.send(self.group_by_field_name)) }
|
||||
if not items_without_category.empty?
|
||||
groups << { :name => nil, :items => items_without_category }
|
||||
else
|
||||
groups
|
||||
end
|
||||
else
|
||||
self.ordered_contents
|
||||
end
|
||||
end
|
||||
|
||||
def latest_updated_contents
|
||||
self.contents.latest_updated.reject { |c| !c.persisted? }
|
||||
end
|
||||
|
||||
def ordered_contents(conditions = {})
|
||||
column = self.order_by.to_sym
|
||||
|
||||
list = (if conditions.nil? || conditions.empty?
|
||||
self.contents
|
||||
else
|
||||
conditions_with_names = {}
|
||||
|
||||
conditions.each do |key, value|
|
||||
# convert alias (key) to name
|
||||
field = self.content_custom_fields.detect { |f| f._alias == key }
|
||||
|
||||
case field.kind.to_sym
|
||||
when :category
|
||||
if (category_item = field.category_items.where(:name => value).first).present?
|
||||
conditions_with_names[field._name.to_sym] = category_item._id
|
||||
end
|
||||
else
|
||||
conditions_with_names[field._name.to_sym] = value
|
||||
end
|
||||
end
|
||||
|
||||
self.contents.where(conditions_with_names)
|
||||
end).sort { |a, b| (a.send(column) || 0) <=> (b.send(column) || 0) }
|
||||
|
||||
return list if self.order_manually?
|
||||
|
||||
self.asc_order? ? list : list.reverse
|
||||
end
|
||||
|
||||
def sort_contents!(ids)
|
||||
ids.each_with_index do |id, position|
|
||||
self.contents.find(BSON::ObjectId(id))._position_in_list = position
|
||||
end
|
||||
self.save
|
||||
end
|
||||
|
||||
def highlighted_field
|
||||
self.content_custom_fields.detect { |f| f._name == self.highlighted_field_name }
|
||||
end
|
||||
|
||||
def group_by_field
|
||||
@group_by_field ||= self.content_custom_fields.detect { |f| f._name == self.group_by_field_name }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_default_values
|
||||
self.order_by ||= 'created_at'
|
||||
self.highlighted_field_name ||= self.content_custom_fields.first._name
|
||||
end
|
||||
|
||||
def normalize_slug
|
||||
self.slug = self.name.clone if self.slug.blank? && self.name.present?
|
||||
self.slug.permalink! if self.slug.present?
|
||||
end
|
||||
|
||||
def remove_uploaded_files # callbacks are not called on each content so we do it manually
|
||||
self.contents.each do |content|
|
||||
self.content_custom_fields.each do |field|
|
||||
content.send(:"remove_#{field._name}!") if field.kind == 'file'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,27 +0,0 @@
|
||||
class EditableElement
|
||||
|
||||
include Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :slug
|
||||
field :block
|
||||
field :default_content
|
||||
field :default_attribute
|
||||
field :hint
|
||||
field :priority, :type => Integer, :default => 0
|
||||
field :disabled, :type => Boolean, :default => false
|
||||
field :assignable, :type => Boolean, :default => true
|
||||
field :from_parent, :type => Boolean, :default => false
|
||||
|
||||
## associations ##
|
||||
embedded_in :page, :inverse_of => :editable_elements
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :slug
|
||||
|
||||
## scopes ##
|
||||
scope :by_priority, :order_by => [[:priority, :desc]]
|
||||
|
||||
## methods ##
|
||||
|
||||
end
|
@ -1,9 +0,0 @@
|
||||
class EditableFile < EditableElement
|
||||
|
||||
mount_uploader :source, EditableFileUploader
|
||||
|
||||
def content
|
||||
self.source? ? self.source.url : self.default_content
|
||||
end
|
||||
|
||||
end
|
@ -1,3 +0,0 @@
|
||||
class EditableLongText < EditableShortText
|
||||
|
||||
end
|
@ -1,12 +0,0 @@
|
||||
class EditableShortText < EditableElement
|
||||
|
||||
## fields ##
|
||||
field :content
|
||||
|
||||
## methods ##
|
||||
|
||||
def content
|
||||
self.read_attribute(:content).blank? ? self.default_content : self.read_attribute(:content)
|
||||
end
|
||||
|
||||
end
|
@ -1,19 +0,0 @@
|
||||
module Extensions
|
||||
module Asset
|
||||
module Types
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
%w{media image stylesheet javascript font pdf}.each do |type|
|
||||
scope :"only_#{type}", where(:content_type => type)
|
||||
|
||||
define_method("#{type}?") do
|
||||
self.content_type.to_s == type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,17 +0,0 @@
|
||||
module Extensions
|
||||
module Asset
|
||||
module Vignette
|
||||
|
||||
def vignette_url
|
||||
if self.image?
|
||||
if self.width < 80 && self.height < 80
|
||||
self.source.url
|
||||
else
|
||||
Locomotive::Dragonfly.resize_url(self.source, '80x80#')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,49 +0,0 @@
|
||||
module Extensions
|
||||
module ContentType
|
||||
module ItemTemplate
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :raw_item_template
|
||||
field :serialized_item_template, :type => Binary
|
||||
|
||||
before_validation :serialize_item_template
|
||||
|
||||
validate :item_template_must_be_valid
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def item_template
|
||||
@item_template ||= Marshal.load(read_attribute(:serialized_item_template).to_s) rescue nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def serialize_item_template
|
||||
if self.new_record? || self.raw_item_template_changed?
|
||||
@item_parsing_errors = []
|
||||
|
||||
begin
|
||||
self._parse_and_serialize_item_template
|
||||
rescue ::Liquid::SyntaxError => error
|
||||
@item_parsing_errors << I18n.t(:liquid_syntax, :error => error.to_s, :scope => [:errors, :messages])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _parse_and_serialize_item_template
|
||||
item_template = ::Liquid::Template.parse(self.raw_item_template, {})
|
||||
self.serialized_item_template = BSON::Binary.new(Marshal.dump(item_template))
|
||||
end
|
||||
|
||||
def item_template_must_be_valid
|
||||
@item_parsing_errors.try(:each) { |msg| self.errors.add :item_template, msg }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,118 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module EditableElements
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
embeds_many :editable_elements
|
||||
|
||||
after_save :remove_disabled_editable_elements
|
||||
|
||||
# editable file callbacks
|
||||
after_save :store_file_sources!
|
||||
before_save :write_file_source_identifiers
|
||||
after_destroy :remove_file_sources!
|
||||
|
||||
accepts_nested_attributes_for :editable_elements
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def disable_parent_editable_elements(block)
|
||||
self.editable_elements.each { |el| el.disabled = true if el.from_parent? && el.block == block }
|
||||
end
|
||||
|
||||
def disable_all_editable_elements
|
||||
self.editable_elements.each { |el| el.disabled = true }
|
||||
end
|
||||
|
||||
def editable_element_blocks
|
||||
self.editable_elements.collect(&:block)
|
||||
end
|
||||
|
||||
def editable_elements_grouped_by_blocks
|
||||
all_enabled = self.editable_elements.by_priority.reject { |el| el.disabled? }
|
||||
groups = all_enabled.group_by(&:block)
|
||||
groups.delete_if { |block, elements| elements.empty? }
|
||||
end
|
||||
|
||||
def find_editable_element(block, slug)
|
||||
self.editable_elements.detect { |el| el.block == block && el.slug == slug }
|
||||
end
|
||||
|
||||
def find_editable_files
|
||||
self.editable_elements.find_all { |el| el.respond_to?(:source) }
|
||||
end
|
||||
|
||||
def add_or_update_editable_element(attributes, type)
|
||||
element = self.find_editable_element(attributes[:block], attributes[:slug])
|
||||
|
||||
if element
|
||||
element.attributes = attributes
|
||||
else
|
||||
self.editable_elements.build(attributes, type)
|
||||
end
|
||||
end
|
||||
|
||||
def enable_editable_elements(block)
|
||||
self.editable_elements.each { |el| el.disabled = false if el.block == block }
|
||||
end
|
||||
|
||||
def merge_editable_elements_from_page(source)
|
||||
source.editable_elements.each do |el|
|
||||
next if el.disabled? or !el.assignable?
|
||||
|
||||
existing_el = self.find_editable_element(el.block, el.slug)
|
||||
|
||||
if existing_el.nil? # new one from parents
|
||||
new_attributes = el.attributes.merge(:from_parent => true)
|
||||
|
||||
if new_attributes['default_attribute'].present?
|
||||
new_attributes['default_content'] = self.send(new_attributes['default_attribute']) || el.content
|
||||
else
|
||||
if el.respond_to?(:content) # only for text
|
||||
new_attributes['default_content'] = el.content
|
||||
end
|
||||
end
|
||||
|
||||
self.editable_elements.build(new_attributes, el.class)
|
||||
elsif existing_el.default_attribute.nil?
|
||||
existing_el.attributes = { :disabled => false, :default_content => el.content }
|
||||
else
|
||||
existing_el.attributes = { :disabled => false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_disabled_editable_elements
|
||||
return unless self.editable_elements.any? { |el| el.disabled? }
|
||||
|
||||
# super fast way to remove useless elements all in once (TODO callbacks)
|
||||
self.collection.update(self._selector, '$pull' => { 'editable_elements' => { 'disabled' => true } })
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
## callbacks for editable files
|
||||
|
||||
# equivalent to "after_save :store_source!" in EditableFile
|
||||
def store_file_sources!
|
||||
self.find_editable_files.collect(&:store_source!)
|
||||
end
|
||||
|
||||
# equivalent to "before_save :write_source_identifier" in EditableFile
|
||||
def write_file_source_identifiers
|
||||
self.find_editable_files.collect(&:write_source_identifier)
|
||||
end
|
||||
|
||||
# equivalent to "after_destroy :remove_source!" in EditableFile
|
||||
def remove_file_sources!
|
||||
self.find_editable_files.collect(&:remove_source!)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,15 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module Listed
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
field :listed, :type => Boolean, :default => true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,111 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module Parse
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :serialized_template, :type => Binary
|
||||
field :template_dependencies, :type => Array, :default => []
|
||||
field :snippet_dependencies, :type => Array, :default => []
|
||||
|
||||
attr_reader :template_changed
|
||||
|
||||
before_validation :serialize_template
|
||||
after_save :update_template_descendants
|
||||
|
||||
validate :template_must_be_valid
|
||||
|
||||
scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } }
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def template
|
||||
@template ||= Marshal.load(read_attribute(:serialized_template).to_s) rescue nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def serialize_template
|
||||
if self.new_record? || self.raw_template_changed?
|
||||
@template_changed = true
|
||||
|
||||
@parsing_errors = []
|
||||
|
||||
begin
|
||||
self._parse_and_serialize_template
|
||||
rescue ::Liquid::SyntaxError => error
|
||||
@parsing_errors << I18n.t(:liquid_syntax, :fullpath => self.fullpath, :error => error.to_s, :scope => [:errors, :messages, :page])
|
||||
rescue ::Locomotive::Liquid::PageNotFound => error
|
||||
@parsing_errors << I18n.t(:liquid_extend, :fullpath => self.fullpath, :scope => [:errors, :messages, :page])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _parse_and_serialize_template(context = {})
|
||||
self.parse(context)
|
||||
self._serialize_template
|
||||
end
|
||||
|
||||
def _serialize_template
|
||||
self.serialized_template = BSON::Binary.new(Marshal.dump(@template))
|
||||
end
|
||||
|
||||
def parse(context = {})
|
||||
self.disable_all_editable_elements
|
||||
|
||||
default_context = { :site => self.site, :page => self, :templates => [], :snippets => [] }
|
||||
|
||||
context = default_context.merge(context)
|
||||
|
||||
@template = ::Liquid::Template.parse(self.raw_template, context)
|
||||
|
||||
self.template_dependencies = context[:templates]
|
||||
self.snippet_dependencies = context[:snippets]
|
||||
|
||||
@template.root.context.clear
|
||||
end
|
||||
|
||||
def template_must_be_valid
|
||||
@parsing_errors.try(:each) { |msg| self.errors.add :template, msg }
|
||||
end
|
||||
|
||||
def update_template_descendants
|
||||
return unless @template_changed == true
|
||||
|
||||
# we admit at this point that the current template is up-to-date
|
||||
template_descendants = self.site.pages.any_in(:template_dependencies => [self.id]).to_a
|
||||
|
||||
# group them by fullpath for better performance
|
||||
cached = template_descendants.inject({}) { |memo, page| memo[page.fullpath] = page; memo }
|
||||
|
||||
self._update_direct_template_descendants(template_descendants.clone, cached)
|
||||
|
||||
# finally save them all
|
||||
::Page.without_callback(:save, :after, :update_template_descendants) do
|
||||
template_descendants.each do |page|
|
||||
page.save(:validate => false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _update_direct_template_descendants(template_descendants, cached)
|
||||
direct_descendants = template_descendants.select do |page|
|
||||
((self.template_dependencies || []) + [self._id]) == (page.template_dependencies || [])
|
||||
end
|
||||
|
||||
direct_descendants.each do |page|
|
||||
page.send(:_parse_and_serialize_template, { :cached_parent => self, :cached_pages => cached })
|
||||
|
||||
template_descendants.delete(page) # no need to loop over it next time
|
||||
|
||||
page.send(:_update_direct_template_descendants, template_descendants, cached) # move down
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,21 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module Redirect
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
field :redirect, :type => Boolean, :default => false
|
||||
|
||||
field :redirect_url, :type => String
|
||||
|
||||
validates_presence_of :redirect_url, :if => :redirect
|
||||
|
||||
validates_format_of :redirect_url, :with => Locomotive::Regexps::URL, :allow_blank => true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,11 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module Render
|
||||
|
||||
def render(context)
|
||||
self.template.render(context)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,28 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module Templatized
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
referenced_in :content_type
|
||||
|
||||
field :templatized, :type => Boolean, :default => false
|
||||
|
||||
field :content_type_visible_column
|
||||
|
||||
before_validation :set_slug_if_templatized
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def set_slug_if_templatized
|
||||
self.slug = 'content_type_template' if self.templatized?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,158 +0,0 @@
|
||||
module Extensions
|
||||
module Page
|
||||
module Tree
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include Mongoid::Acts::Tree
|
||||
|
||||
## fields ##
|
||||
field :position, :type => Integer
|
||||
|
||||
## indexes ##
|
||||
index :position
|
||||
index [[:depth, :asc], [:position, :asc]]
|
||||
|
||||
## behaviours ##
|
||||
acts_as_tree :order => ['position', 'asc']
|
||||
|
||||
## callbacks ##
|
||||
before_validation :reset_parent
|
||||
before_save { |p| p.send(:write_attribute, :parent_id, nil) if p.parent_id.blank? }
|
||||
before_save :change_parent
|
||||
before_create { |p| p.send(:fix_position, false) }
|
||||
before_create :add_to_list_bottom
|
||||
before_destroy :remove_from_list
|
||||
|
||||
# Fixme (Didier L.): Instances methods are defined before the include itself
|
||||
alias :fix_position :hacked_fix_position
|
||||
alias :descendants :hacked_descendants
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Warning: should be used only in read-only
|
||||
def quick_tree(site, minimal_attributes = true)
|
||||
pages = (minimal_attributes ? site.pages.minimal_attributes : site.pages).order_by([[:depth, :asc], [:position, :asc]]).to_a
|
||||
|
||||
tmp = []
|
||||
|
||||
while !pages.empty?
|
||||
tmp << _quick_tree(pages.delete_at(0), pages)
|
||||
end
|
||||
|
||||
tmp
|
||||
end
|
||||
|
||||
def _quick_tree(current_page, pages)
|
||||
i, children = 0, []
|
||||
|
||||
while !pages.empty?
|
||||
page = pages[i]
|
||||
|
||||
break if page.nil?
|
||||
|
||||
if page.parent_id == current_page.id
|
||||
page = pages.delete_at(i)
|
||||
|
||||
children << _quick_tree(page, pages)
|
||||
else
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
current_page.instance_eval do
|
||||
def children=(list); @children = list; end
|
||||
def children; @children || []; end
|
||||
end
|
||||
|
||||
current_page.children = children
|
||||
|
||||
current_page
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def children?
|
||||
self.class.where(self.parent_id_field => self.id).count
|
||||
end
|
||||
|
||||
def children_with_minimal_attributes
|
||||
self.class.where(self.parent_id_field => self.id).
|
||||
order_by(self.tree_order).
|
||||
minimal_attributes
|
||||
end
|
||||
|
||||
def sort_children!(ids)
|
||||
ids.each_with_index do |id, position|
|
||||
child = self.children.detect { |p| p._id == BSON::ObjectId(id) }
|
||||
child.position = position
|
||||
child.save
|
||||
end
|
||||
end
|
||||
|
||||
def parent=(owner) # missing in acts_as_tree
|
||||
@_parent = owner
|
||||
self.fix_position(false)
|
||||
self.instance_variable_set :@_will_move, true
|
||||
end
|
||||
|
||||
def hacked_descendants
|
||||
return [] if new_record?
|
||||
self.class.all_in(path_field => [self._id]).order_by tree_order
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def change_parent
|
||||
if self.parent_id_changed?
|
||||
self.fix_position(false)
|
||||
|
||||
unless self.parent_id_was.nil?
|
||||
self.position = nil # make it move to bottom
|
||||
self.add_to_list_bottom
|
||||
end
|
||||
|
||||
self.instance_variable_set :@_will_move, true
|
||||
end
|
||||
end
|
||||
|
||||
def hacked_fix_position(perform_save = true)
|
||||
if parent.nil?
|
||||
self.write_attribute parent_id_field, nil
|
||||
self[path_field] = []
|
||||
self[depth_field] = 0
|
||||
else
|
||||
self.write_attribute parent_id_field, parent._id
|
||||
self[path_field] = parent[path_field] + [parent._id]
|
||||
self[depth_field] = parent[depth_field] + 1
|
||||
self.save if perform_save
|
||||
end
|
||||
end
|
||||
|
||||
def reset_parent
|
||||
if self.parent_id_changed?
|
||||
@_parent = nil
|
||||
end
|
||||
end
|
||||
|
||||
def add_to_list_bottom
|
||||
self.position ||= (::Page.where(:_id.ne => self._id).and(:parent_id => self.parent_id).max(:position) || 0) + 1
|
||||
end
|
||||
|
||||
def remove_from_list
|
||||
return if (self.site rescue nil).nil?
|
||||
|
||||
::Page.where(:parent_id => self.parent_id).and(:position.gt => self.position).each do |p|
|
||||
p.position -= 1
|
||||
p.save
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,14 +0,0 @@
|
||||
module Extensions
|
||||
module Shared
|
||||
module Seo
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :seo_title, :type => String
|
||||
field :meta_keywords, :type => String
|
||||
field :meta_description, :type => String
|
||||
end
|
||||
|
||||
end # Seo
|
||||
end # Shared
|
||||
end # Extensions
|
@ -1,36 +0,0 @@
|
||||
module Extensions
|
||||
module Site
|
||||
module FirstInstallation
|
||||
|
||||
# only called during the installation workflow, just after the admin account has been created
|
||||
def create_first_one(attributes)
|
||||
site = self.new(attributes)
|
||||
|
||||
site.memberships.build :account => Account.first, :role => 'admin'
|
||||
|
||||
site.save
|
||||
|
||||
site
|
||||
end
|
||||
|
||||
def install_template(site, options = {})
|
||||
default_template = Boolean.set(options.delete(:default_site_template)) || false
|
||||
|
||||
zipfile = options.delete(:zipfile)
|
||||
|
||||
# do not try to process anything if said so
|
||||
return unless default_template || zipfile.present?
|
||||
|
||||
# default template options has a higher priority than the zipfile
|
||||
source = default_template ? Locomotive.default_site_template_path : zipfile
|
||||
|
||||
begin
|
||||
Locomotive::Import::Job.run!(source, site, { :samples => true })
|
||||
rescue Exception => e
|
||||
Rails.logger.error "The import of the site template failed because of #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,84 +0,0 @@
|
||||
module Extensions
|
||||
module Site
|
||||
module SubdomainDomains
|
||||
|
||||
def enable_subdomain_n_domains_if_multi_sites
|
||||
# puts "multi_sites? #{Locomotive.config.multi_sites?} / manage_domains? #{Locomotive.config.manage_domains?} / heroku? #{Locomotive.heroku?} / bushido? #{Locomotive.bushido?}"
|
||||
|
||||
if Locomotive.config.multi_sites? || Locomotive.config.manage_domains?
|
||||
|
||||
## fields ##
|
||||
field :subdomain
|
||||
field :domains, :type => Array, :default => []
|
||||
|
||||
## indexes
|
||||
index :domains
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :subdomain
|
||||
validates_uniqueness_of :subdomain
|
||||
validates_exclusion_of :subdomain, :in => Locomotive.config.reserved_subdomains
|
||||
validates_format_of :subdomain, :with => Locomotive::Regexps::SUBDOMAIN, :allow_blank => true
|
||||
validate :domains_must_be_valid_and_unique
|
||||
|
||||
## callbacks ##
|
||||
before_save :add_subdomain_to_domains
|
||||
|
||||
## named scopes ##
|
||||
scope :match_domain, lambda { |domain| { :any_in => { :domains => [*domain] } } }
|
||||
scope :match_domain_with_exclusion_of, lambda { |domain, site|
|
||||
{ :any_in => { :domains => [*domain] }, :where => { :_id.ne => site.id } }
|
||||
}
|
||||
|
||||
send :include, InstanceMethods
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def domains=(array)
|
||||
array = [] if array.blank?; super(array)
|
||||
end
|
||||
|
||||
def add_subdomain_to_domains
|
||||
self.domains ||= []
|
||||
(self.domains << self.full_subdomain).uniq!
|
||||
end
|
||||
|
||||
def domains_without_subdomain
|
||||
(self.domains || []) - [self.full_subdomain_was] - [self.full_subdomain]
|
||||
end
|
||||
|
||||
def domains_with_subdomain
|
||||
((self.domains || []) + [self.full_subdomain]).uniq
|
||||
end
|
||||
|
||||
def full_subdomain
|
||||
"#{self.subdomain}.#{Locomotive.config.domain}"
|
||||
end
|
||||
|
||||
def full_subdomain_was
|
||||
"#{self.subdomain_was}.#{Locomotive.config.domain}"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def domains_must_be_valid_and_unique
|
||||
return if self.domains.empty?
|
||||
|
||||
self.domains_without_subdomain.each do |domain|
|
||||
if self.class.match_domain_with_exclusion_of(domain, self).any?
|
||||
self.errors.add(:domains, :domain_taken, :value => domain)
|
||||
end
|
||||
|
||||
if not domain =~ Locomotive::Regexps::DOMAIN
|
||||
self.errors.add(:domains, :invalid_domain, :value => domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
85
app/models/locomotive/ability.rb
Normal file
85
app/models/locomotive/ability.rb
Normal file
@ -0,0 +1,85 @@
|
||||
Locomotive
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
ROLES = %w(admin designer author)
|
||||
|
||||
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]
|
||||
|
||||
can :touch, Site do |site|
|
||||
site == @site
|
||||
end
|
||||
end
|
||||
|
||||
def setup_designer_permissions!
|
||||
can :manage, Page
|
||||
|
||||
can :manage, ContentInstance
|
||||
|
||||
can :manage, ContentType
|
||||
|
||||
can :manage, Snippet
|
||||
|
||||
can :manage, ThemeAsset
|
||||
|
||||
can :manage, Asset
|
||||
|
||||
can :manage, Site do |site|
|
||||
site == @site
|
||||
end
|
||||
|
||||
can :import, Site
|
||||
|
||||
can :export, Site
|
||||
|
||||
can :point, Site
|
||||
|
||||
cannot :create, Site
|
||||
|
||||
can :manage, Membership
|
||||
|
||||
cannot :grant_admin, Membership
|
||||
|
||||
cannot [:update, :destroy], Membership do |membership|
|
||||
@membership.account_id == membership.account_id || # can not edit myself
|
||||
membership.admin? # can not modify an administrator
|
||||
end
|
||||
end
|
||||
|
||||
def setup_admin_permissions!
|
||||
can :manage, :all
|
||||
|
||||
cannot [:update, :destroy], Membership do |membership|
|
||||
@membership.account_id == membership.account_id # can not edit myself
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
62
app/models/locomotive/account.rb
Normal file
62
app/models/locomotive/account.rb
Normal file
@ -0,0 +1,62 @@
|
||||
require 'digest'
|
||||
|
||||
module Locomotive
|
||||
class Account
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
devise *Locomotive.config.devise_modules
|
||||
|
||||
## attributes ##
|
||||
field :name
|
||||
field :locale, :default => Locomotive.config.default_locale.to_s or 'en'
|
||||
field :switch_site_token
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name
|
||||
|
||||
## associations ##
|
||||
|
||||
## callbacks ##
|
||||
before_destroy :remove_memberships!
|
||||
|
||||
## methods ##
|
||||
|
||||
def sites
|
||||
@sites ||= Site.where({ 'memberships.account_id' => self._id })
|
||||
end
|
||||
|
||||
def reset_switch_site_token!
|
||||
self.switch_site_token = ActiveSupport::SecureRandom.base64(8).gsub("/", "_").gsub(/=+$/, "")
|
||||
self.save
|
||||
end
|
||||
|
||||
def self.find_using_switch_site_token(token, age = 1.minute)
|
||||
return if token.blank?
|
||||
self.where(:switch_site_token => token, :updated_at.gt => age.ago.utc).first
|
||||
end
|
||||
|
||||
def self.find_using_switch_site_token!(token, age = 1.minute)
|
||||
self.find_using_switch_site_token(token, age) || raise(Mongoid::Errors::DocumentNotFound.new(self, token))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def password_required?
|
||||
!persisted? || !password.blank? || !password_confirmation.blank?
|
||||
end
|
||||
|
||||
def remove_memberships!
|
||||
self.sites.each do |site|
|
||||
membership = site.memberships.where(:account_id => self._id).first
|
||||
|
||||
if site.admin_memberships.size == 1 && membership.admin?
|
||||
raise I18n.t('errors.messages.needs_admin_account')
|
||||
else
|
||||
membership.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
41
app/models/locomotive/asset.rb
Normal file
41
app/models/locomotive/asset.rb
Normal file
@ -0,0 +1,41 @@
|
||||
module Locomotive
|
||||
class Asset
|
||||
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## extensions ##
|
||||
include Extensions::Asset::Types
|
||||
include Extensions::Asset::Vignette
|
||||
|
||||
## fields ##
|
||||
field :content_type, :type => String
|
||||
field :width, :type => Integer
|
||||
field :height, :type => Integer
|
||||
field :size, :type => Integer
|
||||
field :position, :type => Integer, :default => 0
|
||||
mount_uploader :source, AssetUploader
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :source
|
||||
|
||||
## behaviours ##
|
||||
|
||||
## methods ##
|
||||
|
||||
alias :name :source_filename
|
||||
|
||||
def extname
|
||||
return nil unless self.source?
|
||||
File.extname(self.source_filename).gsub(/^\./, '')
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes).stringify_keys
|
||||
end
|
||||
|
||||
end
|
||||
end
|
127
app/models/locomotive/content_instance.rb
Normal file
127
app/models/locomotive/content_instance.rb
Normal file
@ -0,0 +1,127 @@
|
||||
module Locomotive
|
||||
class ContentInstance
|
||||
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## extensions ##
|
||||
include CustomFields::ProxyClassEnabler
|
||||
include Extensions::Shared::Seo
|
||||
|
||||
## fields (dynamic fields) ##
|
||||
field :_slug
|
||||
field :_position_in_list, :type => Integer, :default => 0
|
||||
field :_visible, :type => Boolean, :default => true
|
||||
|
||||
## validations ##
|
||||
validate :require_highlighted_field
|
||||
validate :validate_uniqueness_of_slug
|
||||
validates_presence_of :_slug
|
||||
|
||||
## associations ##
|
||||
embedded_in :content_type, :inverse_of => :contents
|
||||
|
||||
## callbacks ##
|
||||
before_validation :set_slug
|
||||
before_save :set_visibility
|
||||
before_create :add_to_list_bottom
|
||||
after_create :send_notifications
|
||||
|
||||
## named scopes ##
|
||||
scope :latest_updated, :order_by => :updated_at.desc, :limit => Locomotive.config.lastest_items_nb
|
||||
|
||||
## methods ##
|
||||
|
||||
delegate :site, :to => :content_type
|
||||
|
||||
alias :visible? :_visible?
|
||||
alias :_permalink :_slug
|
||||
alias :_permalink= :_slug=
|
||||
|
||||
def site_id # needed by the uploader of custom fields
|
||||
self.content_type.site_id
|
||||
end
|
||||
|
||||
def highlighted_field_value
|
||||
self.send(self.content_type.highlighted_field_name)
|
||||
end
|
||||
|
||||
alias :_label :highlighted_field_value
|
||||
|
||||
def visible?
|
||||
self._visible || self._visible.nil?
|
||||
end
|
||||
|
||||
def next
|
||||
content_type.contents.where(:_position_in_list => _position_in_list + 1).first()
|
||||
end
|
||||
|
||||
def previous
|
||||
content_type.contents.where(:_position_in_list => _position_in_list - 1).first()
|
||||
end
|
||||
|
||||
def errors_to_hash
|
||||
Hash.new.replace(self.errors)
|
||||
end
|
||||
|
||||
def reload_parent!
|
||||
self.class.reload_parent!
|
||||
end
|
||||
|
||||
def self.reload_parent!
|
||||
self._parent = self._parent.reload
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Content.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_slug
|
||||
self._slug = self.highlighted_field_value.dup if self._slug.blank? && self.highlighted_field_value.present?
|
||||
self._slug.permalink! if self._slug.present?
|
||||
end
|
||||
|
||||
def set_visibility
|
||||
field = self.content_type.content_custom_fields.detect { |f| %w{visible active}.include?(f._alias) }
|
||||
self._visible = self.send(field._name) rescue true
|
||||
end
|
||||
|
||||
def add_to_list_bottom
|
||||
self._position_in_list = self.content_type.contents.size
|
||||
end
|
||||
|
||||
def require_highlighted_field
|
||||
_alias = self.highlighted_field_alias
|
||||
if self.send(_alias).blank?
|
||||
self.errors.add(_alias, :blank)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_uniqueness_of_slug
|
||||
if self._parent.contents.any? { |c| c._id != self._id && c._slug == self._slug }
|
||||
self.errors.add(:_slug, :taken)
|
||||
end
|
||||
end
|
||||
|
||||
def highlighted_field_alias
|
||||
self.content_type.highlighted_field._alias.to_sym
|
||||
end
|
||||
|
||||
def send_notifications
|
||||
return unless self.content_type.api_enabled? && !self.content_type.api_accounts.blank?
|
||||
|
||||
accounts = self.content_type.site.accounts.to_a
|
||||
|
||||
self.content_type.api_accounts.each do |account_id|
|
||||
next if account_id.blank?
|
||||
|
||||
account = accounts.detect { |a| a.id.to_s == account_id.to_s }
|
||||
|
||||
Locomotive::Notifications.new_content_instance(account, self).deliver
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
147
app/models/locomotive/content_type.rb
Normal file
147
app/models/locomotive/content_type.rb
Normal file
@ -0,0 +1,147 @@
|
||||
module Locomotive
|
||||
class ContentType
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## extensions ##
|
||||
include Extensions::ContentType::ItemTemplate
|
||||
|
||||
## fields ##
|
||||
field :name
|
||||
field :description
|
||||
field :slug
|
||||
field :order_by
|
||||
field :order_direction, :default => 'asc'
|
||||
field :highlighted_field_name
|
||||
field :group_by_field_name
|
||||
field :api_enabled, :type => Boolean, :default => false
|
||||
field :api_accounts, :type => Array
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
embeds_many :contents, :class_name => 'Locomotive::ContentInstance', :validate => false do
|
||||
def visible
|
||||
@target.find_all { |c| c.visible? }
|
||||
end
|
||||
end
|
||||
|
||||
## named scopes ##
|
||||
scope :ordered, :order_by => :updated_at.desc
|
||||
|
||||
## indexes ##
|
||||
index [[:site_id, Mongo::ASCENDING], [:slug, Mongo::ASCENDING]]
|
||||
|
||||
## callbacks ##
|
||||
before_validation :normalize_slug
|
||||
before_save :set_default_values
|
||||
after_destroy :remove_uploaded_files
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :name, :slug
|
||||
validates_uniqueness_of :slug, :scope => :site_id
|
||||
validates_size_of :content_custom_fields, :minimum => 1, :message => :array_too_short
|
||||
|
||||
## behaviours ##
|
||||
custom_fields_for :contents
|
||||
|
||||
## methods ##
|
||||
|
||||
def groupable?
|
||||
self.group_by_field && group_by_field.category?
|
||||
end
|
||||
|
||||
def order_manually?
|
||||
self.order_by == '_position_in_list'
|
||||
end
|
||||
|
||||
def asc_order?
|
||||
self.order_direction.blank? || self.order_direction == 'asc'
|
||||
end
|
||||
|
||||
def list_or_group_contents
|
||||
if self.groupable?
|
||||
groups = self.contents.klass.send(:"group_by_#{self.group_by_field._alias}", :ordered_contents)
|
||||
|
||||
# look for items with no category or unknown ones
|
||||
items_without_category = self.contents.find_all { |c| !self.group_by_field.category_ids.include?(c.send(self.group_by_field_name)) }
|
||||
if not items_without_category.empty?
|
||||
groups << { :name => nil, :items => items_without_category }
|
||||
else
|
||||
groups
|
||||
end
|
||||
else
|
||||
self.ordered_contents
|
||||
end
|
||||
end
|
||||
|
||||
def latest_updated_contents
|
||||
self.contents.latest_updated.reject { |c| !c.persisted? }
|
||||
end
|
||||
|
||||
def ordered_contents(conditions = {})
|
||||
column = self.order_by.to_sym
|
||||
|
||||
list = (if conditions.nil? || conditions.empty?
|
||||
self.contents
|
||||
else
|
||||
conditions_with_names = {}
|
||||
|
||||
conditions.each do |key, value|
|
||||
# convert alias (key) to name
|
||||
field = self.content_custom_fields.detect { |f| f._alias == key }
|
||||
|
||||
case field.kind.to_sym
|
||||
when :category
|
||||
if (category_item = field.category_items.where(:name => value).first).present?
|
||||
conditions_with_names[field._name.to_sym] = category_item._id
|
||||
end
|
||||
else
|
||||
conditions_with_names[field._name.to_sym] = value
|
||||
end
|
||||
end
|
||||
|
||||
self.contents.where(conditions_with_names)
|
||||
end).sort { |a, b| (a.send(column) || 0) <=> (b.send(column) || 0) }
|
||||
|
||||
return list if self.order_manually?
|
||||
|
||||
self.asc_order? ? list : list.reverse
|
||||
end
|
||||
|
||||
def sort_contents!(ids)
|
||||
ids.each_with_index do |id, position|
|
||||
self.contents.find(BSON::ObjectId(id))._position_in_list = position
|
||||
end
|
||||
self.save
|
||||
end
|
||||
|
||||
def highlighted_field
|
||||
self.content_custom_fields.detect { |f| f._name == self.highlighted_field_name }
|
||||
end
|
||||
|
||||
def group_by_field
|
||||
@group_by_field ||= self.content_custom_fields.detect { |f| f._name == self.group_by_field_name }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_default_values
|
||||
self.order_by ||= 'created_at'
|
||||
self.highlighted_field_name ||= self.content_custom_fields.first._name
|
||||
end
|
||||
|
||||
def normalize_slug
|
||||
self.slug = self.name.clone if self.slug.blank? && self.name.present?
|
||||
self.slug.permalink! if self.slug.present?
|
||||
end
|
||||
|
||||
def remove_uploaded_files # callbacks are not called on each content so we do it manually
|
||||
self.contents.each do |content|
|
||||
self.content_custom_fields.each do |field|
|
||||
content.send(:"remove_#{field._name}!") if field.kind == 'file'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
29
app/models/locomotive/editable_element.rb
Normal file
29
app/models/locomotive/editable_element.rb
Normal file
@ -0,0 +1,29 @@
|
||||
module Locomotive
|
||||
class EditableElement
|
||||
|
||||
include Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :slug
|
||||
field :block
|
||||
field :default_content
|
||||
field :default_attribute
|
||||
field :hint
|
||||
field :priority, :type => Integer, :default => 0
|
||||
field :disabled, :type => Boolean, :default => false
|
||||
field :assignable, :type => Boolean, :default => true
|
||||
field :from_parent, :type => Boolean, :default => false
|
||||
|
||||
## associations ##
|
||||
embedded_in :page, :inverse_of => :editable_elements
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :slug
|
||||
|
||||
## scopes ##
|
||||
scope :by_priority, :order_by => [[:priority, :desc]]
|
||||
|
||||
## methods ##
|
||||
|
||||
end
|
||||
end
|
11
app/models/locomotive/editable_file.rb
Normal file
11
app/models/locomotive/editable_file.rb
Normal file
@ -0,0 +1,11 @@
|
||||
module Locomotive
|
||||
class EditableFile < EditableElement
|
||||
|
||||
mount_uploader :source, EditableFileUploader
|
||||
|
||||
def content
|
||||
self.source? ? self.source.url : self.default_content
|
||||
end
|
||||
|
||||
end
|
||||
end
|
5
app/models/locomotive/editable_long_text.rb
Normal file
5
app/models/locomotive/editable_long_text.rb
Normal file
@ -0,0 +1,5 @@
|
||||
module Locomotive
|
||||
class EditableLongText < EditableShortText
|
||||
|
||||
end
|
||||
end
|
14
app/models/locomotive/editable_short_text.rb
Normal file
14
app/models/locomotive/editable_short_text.rb
Normal file
@ -0,0 +1,14 @@
|
||||
module Locomotive
|
||||
class EditableShortText < EditableElement
|
||||
|
||||
## fields ##
|
||||
field :content
|
||||
|
||||
## methods ##
|
||||
|
||||
def content
|
||||
self.read_attribute(:content).blank? ? self.default_content : self.read_attribute(:content)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
21
app/models/locomotive/extensions/asset/types.rb
Normal file
21
app/models/locomotive/extensions/asset/types.rb
Normal file
@ -0,0 +1,21 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Asset
|
||||
module Types
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
%w{media image stylesheet javascript font pdf}.each do |type|
|
||||
scope :"only_#{type}", where(:content_type => type)
|
||||
|
||||
define_method("#{type}?") do
|
||||
self.content_type.to_s == type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
19
app/models/locomotive/extensions/asset/vignette.rb
Normal file
19
app/models/locomotive/extensions/asset/vignette.rb
Normal file
@ -0,0 +1,19 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Asset
|
||||
module Vignette
|
||||
|
||||
def vignette_url
|
||||
if self.image?
|
||||
if self.width < 80 && self.height < 80
|
||||
self.source.url
|
||||
else
|
||||
Locomotive::Dragonfly.resize_url(self.source, '80x80#')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,51 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module ContentType
|
||||
module ItemTemplate
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :raw_item_template
|
||||
field :serialized_item_template, :type => Binary
|
||||
|
||||
before_validation :serialize_item_template
|
||||
|
||||
validate :item_template_must_be_valid
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def item_template
|
||||
@item_template ||= Marshal.load(read_attribute(:serialized_item_template).to_s) rescue nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def serialize_item_template
|
||||
if self.new_record? || self.raw_item_template_changed?
|
||||
@item_parsing_errors = []
|
||||
|
||||
begin
|
||||
self._parse_and_serialize_item_template
|
||||
rescue ::Liquid::SyntaxError => error
|
||||
@item_parsing_errors << I18n.t(:liquid_syntax, :error => error.to_s, :scope => [:errors, :messages])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _parse_and_serialize_item_template
|
||||
item_template = ::Liquid::Template.parse(self.raw_item_template, {})
|
||||
self.serialized_item_template = BSON::Binary.new(Marshal.dump(item_template))
|
||||
end
|
||||
|
||||
def item_template_must_be_valid
|
||||
@item_parsing_errors.try(:each) { |msg| self.errors.add :item_template, msg }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
120
app/models/locomotive/extensions/page/editable_elements.rb
Normal file
120
app/models/locomotive/extensions/page/editable_elements.rb
Normal file
@ -0,0 +1,120 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module EditableElements
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
embeds_many :editable_elements
|
||||
|
||||
after_save :remove_disabled_editable_elements
|
||||
|
||||
# editable file callbacks
|
||||
after_save :store_file_sources!
|
||||
before_save :write_file_source_identifiers
|
||||
after_destroy :remove_file_sources!
|
||||
|
||||
accepts_nested_attributes_for :editable_elements
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def disable_parent_editable_elements(block)
|
||||
self.editable_elements.each { |el| el.disabled = true if el.from_parent? && el.block == block }
|
||||
end
|
||||
|
||||
def disable_all_editable_elements
|
||||
self.editable_elements.each { |el| el.disabled = true }
|
||||
end
|
||||
|
||||
def editable_element_blocks
|
||||
self.editable_elements.collect(&:block)
|
||||
end
|
||||
|
||||
def editable_elements_grouped_by_blocks
|
||||
all_enabled = self.editable_elements.by_priority.reject { |el| el.disabled? }
|
||||
groups = all_enabled.group_by(&:block)
|
||||
groups.delete_if { |block, elements| elements.empty? }
|
||||
end
|
||||
|
||||
def find_editable_element(block, slug)
|
||||
self.editable_elements.detect { |el| el.block == block && el.slug == slug }
|
||||
end
|
||||
|
||||
def find_editable_files
|
||||
self.editable_elements.find_all { |el| el.respond_to?(:source) }
|
||||
end
|
||||
|
||||
def add_or_update_editable_element(attributes, type)
|
||||
element = self.find_editable_element(attributes[:block], attributes[:slug])
|
||||
|
||||
if element
|
||||
element.attributes = attributes
|
||||
else
|
||||
self.editable_elements.build(attributes, type)
|
||||
end
|
||||
end
|
||||
|
||||
def enable_editable_elements(block)
|
||||
self.editable_elements.each { |el| el.disabled = false if el.block == block }
|
||||
end
|
||||
|
||||
def merge_editable_elements_from_page(source)
|
||||
source.editable_elements.each do |el|
|
||||
next if el.disabled? or !el.assignable?
|
||||
|
||||
existing_el = self.find_editable_element(el.block, el.slug)
|
||||
|
||||
if existing_el.nil? # new one from parents
|
||||
new_attributes = el.attributes.merge(:from_parent => true)
|
||||
|
||||
if new_attributes['default_attribute'].present?
|
||||
new_attributes['default_content'] = self.send(new_attributes['default_attribute']) || el.content
|
||||
else
|
||||
if el.respond_to?(:content) # only for text
|
||||
new_attributes['default_content'] = el.content
|
||||
end
|
||||
end
|
||||
|
||||
self.editable_elements.build(new_attributes, el.class)
|
||||
elsif existing_el.default_attribute.nil?
|
||||
existing_el.attributes = { :disabled => false, :default_content => el.content }
|
||||
else
|
||||
existing_el.attributes = { :disabled => false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_disabled_editable_elements
|
||||
return unless self.editable_elements.any? { |el| el.disabled? }
|
||||
|
||||
# super fast way to remove useless elements all in once (TODO callbacks)
|
||||
self.collection.update(self._selector, '$pull' => { 'editable_elements' => { 'disabled' => true } })
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
## callbacks for editable files
|
||||
|
||||
# equivalent to "after_save :store_source!" in EditableFile
|
||||
def store_file_sources!
|
||||
self.find_editable_files.collect(&:store_source!)
|
||||
end
|
||||
|
||||
# equivalent to "before_save :write_source_identifier" in EditableFile
|
||||
def write_file_source_identifiers
|
||||
self.find_editable_files.collect(&:write_source_identifier)
|
||||
end
|
||||
|
||||
# equivalent to "after_destroy :remove_source!" in EditableFile
|
||||
def remove_file_sources!
|
||||
self.find_editable_files.collect(&:remove_source!)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
app/models/locomotive/extensions/page/listed.rb
Normal file
17
app/models/locomotive/extensions/page/listed.rb
Normal file
@ -0,0 +1,17 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module Listed
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
field :listed, :type => Boolean, :default => true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
113
app/models/locomotive/extensions/page/parse.rb
Normal file
113
app/models/locomotive/extensions/page/parse.rb
Normal file
@ -0,0 +1,113 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module Parse
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :serialized_template, :type => Binary
|
||||
field :template_dependencies, :type => Array, :default => []
|
||||
field :snippet_dependencies, :type => Array, :default => []
|
||||
|
||||
attr_reader :template_changed
|
||||
|
||||
before_validation :serialize_template
|
||||
after_save :update_template_descendants
|
||||
|
||||
validate :template_must_be_valid
|
||||
|
||||
scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } }
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def template
|
||||
@template ||= Marshal.load(read_attribute(:serialized_template).to_s) rescue nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def serialize_template
|
||||
if self.new_record? || self.raw_template_changed?
|
||||
@template_changed = true
|
||||
|
||||
@parsing_errors = []
|
||||
|
||||
begin
|
||||
self._parse_and_serialize_template
|
||||
rescue ::Liquid::SyntaxError => error
|
||||
@parsing_errors << I18n.t(:liquid_syntax, :fullpath => self.fullpath, :error => error.to_s, :scope => [:errors, :messages, :page])
|
||||
rescue ::Locomotive::Liquid::PageNotFound => error
|
||||
@parsing_errors << I18n.t(:liquid_extend, :fullpath => self.fullpath, :scope => [:errors, :messages, :page])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _parse_and_serialize_template(context = {})
|
||||
self.parse(context)
|
||||
self._serialize_template
|
||||
end
|
||||
|
||||
def _serialize_template
|
||||
self.serialized_template = BSON::Binary.new(Marshal.dump(@template))
|
||||
end
|
||||
|
||||
def parse(context = {})
|
||||
self.disable_all_editable_elements
|
||||
|
||||
default_context = { :site => self.site, :page => self, :templates => [], :snippets => [] }
|
||||
|
||||
context = default_context.merge(context)
|
||||
|
||||
@template = ::Liquid::Template.parse(self.raw_template, context)
|
||||
|
||||
self.template_dependencies = context[:templates]
|
||||
self.snippet_dependencies = context[:snippets]
|
||||
|
||||
@template.root.context.clear
|
||||
end
|
||||
|
||||
def template_must_be_valid
|
||||
@parsing_errors.try(:each) { |msg| self.errors.add :template, msg }
|
||||
end
|
||||
|
||||
def update_template_descendants
|
||||
return unless @template_changed == true
|
||||
|
||||
# we admit at this point that the current template is up-to-date
|
||||
template_descendants = self.site.pages.any_in(:template_dependencies => [self.id]).to_a
|
||||
|
||||
# group them by fullpath for better performance
|
||||
cached = template_descendants.inject({}) { |memo, page| memo[page.fullpath] = page; memo }
|
||||
|
||||
self._update_direct_template_descendants(template_descendants.clone, cached)
|
||||
|
||||
# finally save them all
|
||||
::Page.without_callback(:save, :after, :update_template_descendants) do
|
||||
template_descendants.each do |page|
|
||||
page.save(:validate => false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _update_direct_template_descendants(template_descendants, cached)
|
||||
direct_descendants = template_descendants.select do |page|
|
||||
((self.template_dependencies || []) + [self._id]) == (page.template_dependencies || [])
|
||||
end
|
||||
|
||||
direct_descendants.each do |page|
|
||||
page.send(:_parse_and_serialize_template, { :cached_parent => self, :cached_pages => cached })
|
||||
|
||||
template_descendants.delete(page) # no need to loop over it next time
|
||||
|
||||
page.send(:_update_direct_template_descendants, template_descendants, cached) # move down
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
23
app/models/locomotive/extensions/page/redirect.rb
Normal file
23
app/models/locomotive/extensions/page/redirect.rb
Normal file
@ -0,0 +1,23 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module Redirect
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
field :redirect, :type => Boolean, :default => false
|
||||
|
||||
field :redirect_url, :type => String
|
||||
|
||||
validates_presence_of :redirect_url, :if => :redirect
|
||||
|
||||
validates_format_of :redirect_url, :with => Locomotive::Regexps::URL, :allow_blank => true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
13
app/models/locomotive/extensions/page/render.rb
Normal file
13
app/models/locomotive/extensions/page/render.rb
Normal file
@ -0,0 +1,13 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module Render
|
||||
|
||||
def render(context)
|
||||
self.template.render(context)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
app/models/locomotive/extensions/page/templatized.rb
Normal file
30
app/models/locomotive/extensions/page/templatized.rb
Normal file
@ -0,0 +1,30 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module Templatized
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
referenced_in :content_type
|
||||
|
||||
field :templatized, :type => Boolean, :default => false
|
||||
|
||||
field :content_type_visible_column
|
||||
|
||||
before_validation :set_slug_if_templatized
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def set_slug_if_templatized
|
||||
self.slug = 'content_type_template' if self.templatized?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
160
app/models/locomotive/extensions/page/tree.rb
Normal file
160
app/models/locomotive/extensions/page/tree.rb
Normal file
@ -0,0 +1,160 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Page
|
||||
module Tree
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include ::Mongoid::Acts::Tree
|
||||
|
||||
## fields ##
|
||||
field :position, :type => Integer
|
||||
|
||||
## indexes ##
|
||||
index :position
|
||||
index [[:depth, :asc], [:position, :asc]]
|
||||
|
||||
## behaviours ##
|
||||
acts_as_tree :order => ['position', 'asc']
|
||||
|
||||
## callbacks ##
|
||||
before_validation :reset_parent
|
||||
before_save { |p| p.send(:write_attribute, :parent_id, nil) if p.parent_id.blank? }
|
||||
before_save :change_parent
|
||||
before_create { |p| p.send(:fix_position, false) }
|
||||
before_create :add_to_list_bottom
|
||||
before_destroy :remove_from_list
|
||||
|
||||
# Fixme (Didier L.): Instances methods are defined before the include itself
|
||||
alias :fix_position :hacked_fix_position
|
||||
alias :descendants :hacked_descendants
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Warning: should be used only in read-only
|
||||
def quick_tree(site, minimal_attributes = true)
|
||||
pages = (minimal_attributes ? site.pages.minimal_attributes : site.pages).order_by([[:depth, :asc], [:position, :asc]]).to_a
|
||||
|
||||
tmp = []
|
||||
|
||||
while !pages.empty?
|
||||
tmp << _quick_tree(pages.delete_at(0), pages)
|
||||
end
|
||||
|
||||
tmp
|
||||
end
|
||||
|
||||
def _quick_tree(current_page, pages)
|
||||
i, children = 0, []
|
||||
|
||||
while !pages.empty?
|
||||
page = pages[i]
|
||||
|
||||
break if page.nil?
|
||||
|
||||
if page.parent_id == current_page.id
|
||||
page = pages.delete_at(i)
|
||||
|
||||
children << _quick_tree(page, pages)
|
||||
else
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
current_page.instance_eval do
|
||||
def children=(list); @children = list; end
|
||||
def children; @children || []; end
|
||||
end
|
||||
|
||||
current_page.children = children
|
||||
|
||||
current_page
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def children?
|
||||
self.class.where(self.parent_id_field => self.id).count
|
||||
end
|
||||
|
||||
def children_with_minimal_attributes
|
||||
self.class.where(self.parent_id_field => self.id).
|
||||
order_by(self.tree_order).
|
||||
minimal_attributes
|
||||
end
|
||||
|
||||
def sort_children!(ids)
|
||||
ids.each_with_index do |id, position|
|
||||
child = self.children.detect { |p| p._id == BSON::ObjectId(id) }
|
||||
child.position = position
|
||||
child.save
|
||||
end
|
||||
end
|
||||
|
||||
def parent=(owner) # missing in acts_as_tree
|
||||
@_parent = owner
|
||||
self.fix_position(false)
|
||||
self.instance_variable_set :@_will_move, true
|
||||
end
|
||||
|
||||
def hacked_descendants
|
||||
return [] if new_record?
|
||||
self.class.all_in(path_field => [self._id]).order_by tree_order
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def change_parent
|
||||
if self.parent_id_changed?
|
||||
self.fix_position(false)
|
||||
|
||||
unless self.parent_id_was.nil?
|
||||
self.position = nil # make it move to bottom
|
||||
self.add_to_list_bottom
|
||||
end
|
||||
|
||||
self.instance_variable_set :@_will_move, true
|
||||
end
|
||||
end
|
||||
|
||||
def hacked_fix_position(perform_save = true)
|
||||
if parent.nil?
|
||||
self.write_attribute parent_id_field, nil
|
||||
self[path_field] = []
|
||||
self[depth_field] = 0
|
||||
else
|
||||
self.write_attribute parent_id_field, parent._id
|
||||
self[path_field] = parent[path_field] + [parent._id]
|
||||
self[depth_field] = parent[depth_field] + 1
|
||||
self.save if perform_save
|
||||
end
|
||||
end
|
||||
|
||||
def reset_parent
|
||||
if self.parent_id_changed?
|
||||
@_parent = nil
|
||||
end
|
||||
end
|
||||
|
||||
def add_to_list_bottom
|
||||
self.position ||= (::Page.where(:_id.ne => self._id).and(:parent_id => self.parent_id).max(:position) || 0) + 1
|
||||
end
|
||||
|
||||
def remove_from_list
|
||||
return if (self.site rescue nil).nil?
|
||||
|
||||
::Page.where(:parent_id => self.parent_id).and(:position.gt => self.position).each do |p|
|
||||
p.position -= 1
|
||||
p.save
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
16
app/models/locomotive/extensions/shared/seo.rb
Normal file
16
app/models/locomotive/extensions/shared/seo.rb
Normal file
@ -0,0 +1,16 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Shared
|
||||
module Seo
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :seo_title, :type => String
|
||||
field :meta_keywords, :type => String
|
||||
field :meta_description, :type => String
|
||||
end
|
||||
|
||||
end # Seo
|
||||
end # Shared
|
||||
end # Extensions
|
||||
end # Locomotive
|
38
app/models/locomotive/extensions/site/first_installation.rb
Normal file
38
app/models/locomotive/extensions/site/first_installation.rb
Normal file
@ -0,0 +1,38 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Site
|
||||
module FirstInstallation
|
||||
|
||||
# only called during the installation workflow, just after the admin account has been created
|
||||
def create_first_one(attributes)
|
||||
site = self.new(attributes)
|
||||
|
||||
site.memberships.build :account => Account.first, :role => 'admin'
|
||||
|
||||
site.save
|
||||
|
||||
site
|
||||
end
|
||||
|
||||
def install_template(site, options = {})
|
||||
default_template = Boolean.set(options.delete(:default_site_template)) || false
|
||||
|
||||
zipfile = options.delete(:zipfile)
|
||||
|
||||
# do not try to process anything if said so
|
||||
return unless default_template || zipfile.present?
|
||||
|
||||
# default template options has a higher priority than the zipfile
|
||||
source = default_template ? Locomotive.default_site_template_path : zipfile
|
||||
|
||||
begin
|
||||
Locomotive::Import::Job.run!(source, site, { :samples => true })
|
||||
rescue Exception => e
|
||||
Rails.logger.error "The import of the site template failed because of #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
86
app/models/locomotive/extensions/site/subdomain_domains.rb
Normal file
86
app/models/locomotive/extensions/site/subdomain_domains.rb
Normal file
@ -0,0 +1,86 @@
|
||||
module Locomotive
|
||||
module Extensions
|
||||
module Site
|
||||
module SubdomainDomains
|
||||
|
||||
def enable_subdomain_n_domains_if_multi_sites
|
||||
# puts "multi_sites? #{Locomotive.config.multi_sites?} / manage_domains? #{Locomotive.config.manage_domains?} / heroku? #{Locomotive.heroku?} / bushido? #{Locomotive.bushido?}"
|
||||
|
||||
if Locomotive.config.multi_sites? || Locomotive.config.manage_domains?
|
||||
|
||||
## fields ##
|
||||
field :subdomain
|
||||
field :domains, :type => Array, :default => []
|
||||
|
||||
## indexes
|
||||
index :domains
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :subdomain
|
||||
validates_uniqueness_of :subdomain
|
||||
validates_exclusion_of :subdomain, :in => Locomotive.config.reserved_subdomains
|
||||
validates_format_of :subdomain, :with => Locomotive::Regexps::SUBDOMAIN, :allow_blank => true
|
||||
validate :domains_must_be_valid_and_unique
|
||||
|
||||
## callbacks ##
|
||||
before_save :add_subdomain_to_domains
|
||||
|
||||
## named scopes ##
|
||||
scope :match_domain, lambda { |domain| { :any_in => { :domains => [*domain] } } }
|
||||
scope :match_domain_with_exclusion_of, lambda { |domain, site|
|
||||
{ :any_in => { :domains => [*domain] }, :where => { :_id.ne => site.id } }
|
||||
}
|
||||
|
||||
send :include, InstanceMethods
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def domains=(array)
|
||||
array = [] if array.blank?; super(array)
|
||||
end
|
||||
|
||||
def add_subdomain_to_domains
|
||||
self.domains ||= []
|
||||
(self.domains << self.full_subdomain).uniq!
|
||||
end
|
||||
|
||||
def domains_without_subdomain
|
||||
(self.domains || []) - [self.full_subdomain_was] - [self.full_subdomain]
|
||||
end
|
||||
|
||||
def domains_with_subdomain
|
||||
((self.domains || []) + [self.full_subdomain]).uniq
|
||||
end
|
||||
|
||||
def full_subdomain
|
||||
"#{self.subdomain}.#{Locomotive.config.domain}"
|
||||
end
|
||||
|
||||
def full_subdomain_was
|
||||
"#{self.subdomain_was}.#{Locomotive.config.domain}"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def domains_must_be_valid_and_unique
|
||||
return if self.domains.empty?
|
||||
|
||||
self.domains_without_subdomain.each do |domain|
|
||||
if self.class.match_domain_with_exclusion_of(domain, self).any?
|
||||
self.errors.add(:domains, :domain_taken, :value => domain)
|
||||
end
|
||||
|
||||
if not domain =~ Locomotive::Regexps::DOMAIN
|
||||
self.errors.add(:domains, :invalid_domain, :value => domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
75
app/models/locomotive/membership.rb
Normal file
75
app/models/locomotive/membership.rb
Normal file
@ -0,0 +1,75 @@
|
||||
module Locomotive
|
||||
class Membership
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :role, :default => 'author'
|
||||
|
||||
## associations ##
|
||||
referenced_in :account, :validate => false
|
||||
embedded_in :site, :inverse_of => :memberships
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :account
|
||||
validate :can_change_role, :if => :role_changed?
|
||||
|
||||
## 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)
|
||||
@email = email
|
||||
self.account = Account.where(:email => email).first
|
||||
end
|
||||
|
||||
def process!
|
||||
if @email.blank?
|
||||
self.errors.add_on_blank(:email)
|
||||
:error
|
||||
elsif self.account.blank?
|
||||
:create_account
|
||||
elsif self.site.memberships.any? { |m| m.account_id == self.account_id && m._id != self._id }
|
||||
self.errors.add(:base, 'Already created')
|
||||
:already_created
|
||||
else
|
||||
self.save
|
||||
:save_it
|
||||
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
|
||||
|
||||
# Users should not be able to set the role of another user to be higher than
|
||||
# their own. A designer for example should not be able to set another user to
|
||||
# be an administrator
|
||||
def can_change_role
|
||||
current_site = Thread.current[:site]
|
||||
current_membership = current_site.memberships.where(:account_id => Thread.current[:account].id).first if current_site.present?
|
||||
|
||||
if current_membership.present?
|
||||
# The role cannot be set higher than the current one (we use the index in
|
||||
# the roles array to check role presidence)
|
||||
errors.add(:role, :invalid) if Ability::ROLES.index(role) < Ability::ROLES.index(current_membership.role)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
109
app/models/locomotive/page.rb
Normal file
109
app/models/locomotive/page.rb
Normal file
@ -0,0 +1,109 @@
|
||||
module Locomotive
|
||||
class Page
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## Extensions ##
|
||||
include Extensions::Page::Tree
|
||||
include Extensions::Page::EditableElements
|
||||
include Extensions::Page::Parse
|
||||
include Extensions::Page::Render
|
||||
include Extensions::Page::Templatized
|
||||
include Extensions::Page::Redirect
|
||||
include Extensions::Page::Listed
|
||||
include Extensions::Shared::Seo
|
||||
|
||||
## fields ##
|
||||
field :title
|
||||
field :slug
|
||||
field :fullpath
|
||||
field :raw_template
|
||||
field :published, :type => Boolean, :default => false
|
||||
field :cache_strategy, :default => 'none'
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
|
||||
## indexes ##
|
||||
index :site_id
|
||||
index :parent_id
|
||||
index [[:fullpath, Mongo::ASCENDING], [:site_id, Mongo::ASCENDING]]
|
||||
|
||||
## callbacks ##
|
||||
after_initialize :set_default_raw_template
|
||||
before_validation :normalize_slug
|
||||
before_save { |p| p.fullpath = p.fullpath(true) }
|
||||
before_destroy :do_not_remove_index_and_404_pages
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :title, :slug
|
||||
validates_uniqueness_of :slug, :scope => [:site_id, :parent_id]
|
||||
validates_exclusion_of :slug, :in => Locomotive.config.reserved_slugs, :if => Proc.new { |p| p.depth == 0 }
|
||||
|
||||
## named scopes ##
|
||||
scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
|
||||
scope :root, :where => { :slug => 'index', :depth => 0 }
|
||||
scope :not_found, :where => { :slug => '404', :depth => 0 }
|
||||
scope :published, :where => { :published => true }
|
||||
scope :fullpath, lambda { |fullpath| { :where => { :fullpath => fullpath } } }
|
||||
scope :minimal_attributes, :only => %w(title slug fullpath position depth published templatized redirect listed parent_id created_at updated_at)
|
||||
|
||||
## methods ##
|
||||
|
||||
def index?
|
||||
self.slug == 'index' && self.depth.to_i == 0
|
||||
end
|
||||
|
||||
def not_found?
|
||||
self.slug == '404' && self.depth.to_i == 0
|
||||
end
|
||||
|
||||
def index_or_not_found?
|
||||
self.index? || self.not_found?
|
||||
end
|
||||
|
||||
def fullpath(force = false)
|
||||
if read_attribute(:fullpath).present? && !force
|
||||
return read_attribute(:fullpath)
|
||||
end
|
||||
|
||||
if self.index? || self.not_found?
|
||||
self.slug
|
||||
else
|
||||
slugs = self.self_and_ancestors.sort_by(&:depth).map(&:slug)
|
||||
slugs.shift unless slugs.size == 1
|
||||
File.join slugs
|
||||
end
|
||||
end
|
||||
|
||||
def with_cache?
|
||||
self.cache_strategy != 'none'
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Page.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def do_not_remove_index_and_404_pages
|
||||
return if self.site.nil? || self.site.destroyed?
|
||||
|
||||
if self.index? || self.not_found?
|
||||
self.errors[:base] << I18n.t('errors.messages.protected_page')
|
||||
end
|
||||
|
||||
self.errors.empty?
|
||||
end
|
||||
|
||||
def normalize_slug
|
||||
self.slug = self.title.clone if self.slug.blank? && self.title.present?
|
||||
self.slug.permalink! if self.slug.present?
|
||||
end
|
||||
|
||||
def set_default_raw_template
|
||||
self.raw_template ||= I18n.t('attributes.defaults.pages.other.body')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
71
app/models/locomotive/site.rb
Normal file
71
app/models/locomotive/site.rb
Normal file
@ -0,0 +1,71 @@
|
||||
module Locomotive
|
||||
class Site
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## Extensions ##
|
||||
extend Extensions::Site::SubdomainDomains
|
||||
extend Extensions::Site::FirstInstallation
|
||||
include Extensions::Shared::Seo
|
||||
|
||||
## fields ##
|
||||
field :name
|
||||
field :robots_txt
|
||||
|
||||
## associations ##
|
||||
references_many :pages, :validate => false
|
||||
references_many :snippets, :dependent => :destroy, :validate => false
|
||||
references_many :theme_assets, :dependent => :destroy, :validate => false
|
||||
references_many :assets, :dependent => :destroy, :validate => false
|
||||
references_many :content_types, :dependent => :destroy, :validate => false
|
||||
embeds_many :memberships
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name
|
||||
|
||||
## callbacks ##
|
||||
after_create :create_default_pages!
|
||||
after_destroy :destroy_pages
|
||||
|
||||
## behaviours ##
|
||||
enable_subdomain_n_domains_if_multi_sites
|
||||
accepts_nested_attributes_for :memberships
|
||||
|
||||
## methods ##
|
||||
|
||||
def all_pages_in_once
|
||||
Page.quick_tree(self)
|
||||
end
|
||||
|
||||
def accounts
|
||||
Account.criteria.in(:_id => self.memberships.collect(&:account_id))
|
||||
end
|
||||
|
||||
def admin_memberships
|
||||
self.memberships.find_all { |m| m.admin? }
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Site.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def create_default_pages!
|
||||
%w{index 404}.each do |slug|
|
||||
self.pages.create({
|
||||
:slug => slug,
|
||||
:title => I18n.t("attributes.defaults.pages.#{slug}.title"),
|
||||
:raw_template => I18n.t("attributes.defaults.pages.#{slug}.body"),
|
||||
:published => true
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_pages
|
||||
# pages is a tree so we just need to delete the root (as well as the page not found page)
|
||||
self.pages.root.first.try(:destroy) && self.pages.not_found.first.try(:destroy)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
65
app/models/locomotive/snippet.rb
Normal file
65
app/models/locomotive/snippet.rb
Normal file
@ -0,0 +1,65 @@
|
||||
module
|
||||
class Snippet
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :name
|
||||
field :slug
|
||||
field :template
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
|
||||
## callbacks ##
|
||||
before_validation :normalize_slug
|
||||
after_save :update_templates
|
||||
after_destroy :update_templates
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :name, :slug, :template
|
||||
validates_uniqueness_of :slug, :scope => :site_id
|
||||
|
||||
## methods ##
|
||||
|
||||
protected
|
||||
|
||||
def normalize_slug
|
||||
# TODO: refactor it
|
||||
self.slug = self.name.clone if self.slug.blank? && self.name.present?
|
||||
self.slug.permalink! if self.slug.present?
|
||||
end
|
||||
|
||||
def update_templates
|
||||
return unless (self.site rescue false) # not run if the site is being destroyed
|
||||
|
||||
pages = self.site.pages.any_in(:snippet_dependencies => [self.slug]).to_a
|
||||
|
||||
pages.each do |page|
|
||||
self._change_snippet_inside_template(page.template.root)
|
||||
|
||||
page.send(:_serialize_template)
|
||||
|
||||
Page.without_callback(:save, :after, :update_template_descendants) do
|
||||
page.save(:validate => false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _change_snippet_inside_template(node)
|
||||
case node
|
||||
when Locomotive::Liquid::Tags::Snippet
|
||||
node.refresh(self) if node.slug == self.slug
|
||||
when Locomotive::Liquid::Tags::InheritedBlock
|
||||
_change_snippet_inside_template(node.parent) if node.parent
|
||||
end
|
||||
# Walk the children of this entry if they're available.
|
||||
if node.respond_to?(:nodelist)
|
||||
(node.nodelist || []).each do |child|
|
||||
self._change_snippet_inside_template(child)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
154
app/models/locomotive/theme_asset.rb
Normal file
154
app/models/locomotive/theme_asset.rb
Normal file
@ -0,0 +1,154 @@
|
||||
module Locomotive
|
||||
class ThemeAsset
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## extensions ##
|
||||
include Extensions::Asset::Types
|
||||
|
||||
## fields ##
|
||||
field :local_path
|
||||
field :content_type
|
||||
field :width, :type => Integer
|
||||
field :height, :type => Integer
|
||||
field :size, :type => Integer
|
||||
field :folder, :default => nil
|
||||
mount_uploader :source, ThemeAssetUploader
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
|
||||
## indexes ##
|
||||
index :site_id
|
||||
index [[:site_id, Mongo::ASCENDING], [:local_path, Mongo::ASCENDING]]
|
||||
|
||||
## callbacks ##
|
||||
before_validation :store_plain_text
|
||||
before_validation :sanitize_folder
|
||||
before_validation :build_local_path
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :source
|
||||
validates_presence_of :plain_text_name, :if => Proc.new { |a| a.performing_plain_text? }
|
||||
validates_uniqueness_of :local_path, :scope => :site_id
|
||||
validates_integrity_of :source
|
||||
validate :content_type_can_not_changed
|
||||
|
||||
## named scopes ##
|
||||
|
||||
## accessors ##
|
||||
attr_accessor :plain_text_name, :plain_text, :plain_text_type, :performing_plain_text
|
||||
|
||||
## methods ##
|
||||
|
||||
def stylesheet_or_javascript?
|
||||
self.stylesheet? || self.javascript?
|
||||
end
|
||||
|
||||
def local_path(short = false)
|
||||
if short
|
||||
self.read_attribute(:local_path).gsub(/^#{self.content_type.to_s.pluralize}\//, '')
|
||||
else
|
||||
self.read_attribute(:local_path)
|
||||
end
|
||||
end
|
||||
|
||||
def plain_text_name
|
||||
if not @plain_text_name_changed
|
||||
@plain_text_name ||= self.safe_source_filename
|
||||
end
|
||||
@plain_text_name.gsub(/(\.[a-z0-9A-Z]+)$/, '') rescue nil
|
||||
end
|
||||
|
||||
def plain_text_name=(name)
|
||||
@plain_text_name_changed = true
|
||||
@plain_text_name = name
|
||||
end
|
||||
|
||||
def plain_text
|
||||
if RUBY_VERSION =~ /1\.9/
|
||||
@plain_text ||= (self.source.read.force_encoding('UTF-8') rescue nil)
|
||||
else
|
||||
@plain_text ||= self.source.read
|
||||
end
|
||||
end
|
||||
|
||||
def plain_text_type
|
||||
@plain_text_type || (stylesheet_or_javascript? ? self.content_type : nil)
|
||||
end
|
||||
|
||||
def performing_plain_text?
|
||||
Boolean.set(self.performing_plain_text) || false
|
||||
end
|
||||
|
||||
def store_plain_text
|
||||
self.content_type ||= @plain_text_type if self.performing_plain_text?
|
||||
|
||||
data = self.performing_plain_text? ? self.plain_text : self.source.read
|
||||
|
||||
return if !self.stylesheet_or_javascript? || self.plain_text_name.blank? || data.blank?
|
||||
|
||||
sanitized_source = self.escape_shortcut_urls(data)
|
||||
|
||||
self.source = CarrierWave::SanitizedFile.new({
|
||||
:tempfile => StringIO.new(sanitized_source),
|
||||
:filename => "#{self.plain_text_name}.#{self.stylesheet? ? 'css' : 'js'}"
|
||||
})
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes).stringify_keys
|
||||
end
|
||||
|
||||
def self.all_grouped_by_folder(site)
|
||||
assets = site.theme_assets.order_by([[:slug, :asc]])
|
||||
assets.group_by { |a| a.folder.split('/').first.to_sym }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def safe_source_filename
|
||||
self.source_filename || self.source.send(:original_filename) rescue nil
|
||||
end
|
||||
|
||||
def sanitize_folder
|
||||
self.folder = self.content_type.to_s.pluralize if self.folder.blank?
|
||||
|
||||
# no accents, no spaces, no leading and ending trails
|
||||
self.folder = ActiveSupport::Inflector.transliterate(self.folder).gsub(/(\s)+/, '_').gsub(/^\//, '').gsub(/\/$/, '')
|
||||
|
||||
# folder should begin by a root folder
|
||||
if (self.folder =~ /^(stylesheets|javascripts|images|medias|fonts)/).nil?
|
||||
self.folder = File.join(self.content_type.to_s.pluralize, self.folder)
|
||||
end
|
||||
end
|
||||
|
||||
def build_local_path
|
||||
if filename = self.safe_source_filename
|
||||
self.local_path = File.join(self.folder, filename)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def escape_shortcut_urls(text)
|
||||
return if text.blank?
|
||||
|
||||
text.gsub(/[("'](\/(stylesheets|javascripts|images|medias)\/(([^;.]+)\/)*([a-z_\-0-9]+)\.[a-z]{2,3})[)"']/) do |path|
|
||||
|
||||
sanitized_path = path.gsub(/[("')]/, '').gsub(/^\//, '')
|
||||
|
||||
if asset = self.site.theme_assets.where(:local_path => sanitized_path).first
|
||||
"#{path.first}#{asset.source.url}#{path.last}"
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def content_type_can_not_changed
|
||||
self.errors.add(:source, :extname_changed) if !self.new_record? && self.content_type_changed?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,73 +0,0 @@
|
||||
class Membership
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :role, :default => 'author'
|
||||
|
||||
## associations ##
|
||||
referenced_in :account, :validate => false
|
||||
embedded_in :site, :inverse_of => :memberships
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :account
|
||||
validate :can_change_role, :if => :role_changed?
|
||||
|
||||
## 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)
|
||||
@email = email
|
||||
self.account = Account.where(:email => email).first
|
||||
end
|
||||
|
||||
def process!
|
||||
if @email.blank?
|
||||
self.errors.add_on_blank(:email)
|
||||
:error
|
||||
elsif self.account.blank?
|
||||
:create_account
|
||||
elsif self.site.memberships.any? { |m| m.account_id == self.account_id && m._id != self._id }
|
||||
self.errors.add(:base, 'Already created')
|
||||
:already_created
|
||||
else
|
||||
self.save
|
||||
:save_it
|
||||
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
|
||||
|
||||
# Users should not be able to set the role of another user to be higher than
|
||||
# their own. A designer for example should not be able to set another user to
|
||||
# be an administrator
|
||||
def can_change_role
|
||||
current_site = Thread.current[:site]
|
||||
current_membership = current_site.memberships.where(:account_id => Thread.current[:admin].id).first if current_site.present?
|
||||
|
||||
if current_membership.present?
|
||||
# The role cannot be set higher than the current one (we use the index in
|
||||
# the roles array to check role presidence)
|
||||
errors.add(:role, :invalid) if Ability::ROLES.index(role) < Ability::ROLES.index(current_membership.role)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,107 +0,0 @@
|
||||
class Page
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## Extensions ##
|
||||
include Extensions::Page::Tree
|
||||
include Extensions::Page::EditableElements
|
||||
include Extensions::Page::Parse
|
||||
include Extensions::Page::Render
|
||||
include Extensions::Page::Templatized
|
||||
include Extensions::Page::Redirect
|
||||
include Extensions::Page::Listed
|
||||
include Extensions::Shared::Seo
|
||||
|
||||
## fields ##
|
||||
field :title
|
||||
field :slug
|
||||
field :fullpath
|
||||
field :raw_template
|
||||
field :published, :type => Boolean, :default => false
|
||||
field :cache_strategy, :default => 'none'
|
||||
|
||||
## associations ##
|
||||
referenced_in :site
|
||||
|
||||
## indexes ##
|
||||
index :site_id
|
||||
index :parent_id
|
||||
index [[:fullpath, Mongo::ASCENDING], [:site_id, Mongo::ASCENDING]]
|
||||
|
||||
## callbacks ##
|
||||
after_initialize :set_default_raw_template
|
||||
before_validation :normalize_slug
|
||||
before_save { |p| p.fullpath = p.fullpath(true) }
|
||||
before_destroy :do_not_remove_index_and_404_pages
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :title, :slug
|
||||
validates_uniqueness_of :slug, :scope => [:site_id, :parent_id]
|
||||
validates_exclusion_of :slug, :in => Locomotive.config.reserved_slugs, :if => Proc.new { |p| p.depth == 0 }
|
||||
|
||||
## named scopes ##
|
||||
scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
|
||||
scope :root, :where => { :slug => 'index', :depth => 0 }
|
||||
scope :not_found, :where => { :slug => '404', :depth => 0 }
|
||||
scope :published, :where => { :published => true }
|
||||
scope :fullpath, lambda { |fullpath| { :where => { :fullpath => fullpath } } }
|
||||
scope :minimal_attributes, :only => %w(title slug fullpath position depth published templatized redirect listed parent_id created_at updated_at)
|
||||
|
||||
## methods ##
|
||||
|
||||
def index?
|
||||
self.slug == 'index' && self.depth.to_i == 0
|
||||
end
|
||||
|
||||
def not_found?
|
||||
self.slug == '404' && self.depth.to_i == 0
|
||||
end
|
||||
|
||||
def index_or_not_found?
|
||||
self.index? || self.not_found?
|
||||
end
|
||||
|
||||
def fullpath(force = false)
|
||||
if read_attribute(:fullpath).present? && !force
|
||||
return read_attribute(:fullpath)
|
||||
end
|
||||
|
||||
if self.index? || self.not_found?
|
||||
self.slug
|
||||
else
|
||||
slugs = self.self_and_ancestors.sort_by(&:depth).map(&:slug)
|
||||
slugs.shift unless slugs.size == 1
|
||||
File.join slugs
|
||||
end
|
||||
end
|
||||
|
||||
def with_cache?
|
||||
self.cache_strategy != 'none'
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Page.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def do_not_remove_index_and_404_pages
|
||||
return if self.site.nil? || self.site.destroyed?
|
||||
|
||||
if self.index? || self.not_found?
|
||||
self.errors[:base] << I18n.t('errors.messages.protected_page')
|
||||
end
|
||||
|
||||
self.errors.empty?
|
||||
end
|
||||
|
||||
def normalize_slug
|
||||
self.slug = self.title.clone if self.slug.blank? && self.title.present?
|
||||
self.slug.permalink! if self.slug.present?
|
||||
end
|
||||
|
||||
def set_default_raw_template
|
||||
self.raw_template ||= I18n.t('attributes.defaults.pages.other.body')
|
||||
end
|
||||
|
||||
end
|
@ -1,69 +0,0 @@
|
||||
class Site
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## Extensions ##
|
||||
extend Extensions::Site::SubdomainDomains
|
||||
extend Extensions::Site::FirstInstallation
|
||||
include Extensions::Shared::Seo
|
||||
|
||||
## fields ##
|
||||
field :name
|
||||
field :robots_txt
|
||||
|
||||
## associations ##
|
||||
references_many :pages, :validate => false
|
||||
references_many :snippets, :dependent => :destroy, :validate => false
|
||||
references_many :theme_assets, :dependent => :destroy, :validate => false
|
||||
references_many :assets, :dependent => :destroy, :validate => false
|
||||
references_many :content_types, :dependent => :destroy, :validate => false
|
||||
embeds_many :memberships
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name
|
||||
|
||||
## callbacks ##
|
||||
after_create :create_default_pages!
|
||||
after_destroy :destroy_pages
|
||||
|
||||
## behaviours ##
|
||||
enable_subdomain_n_domains_if_multi_sites
|
||||
accepts_nested_attributes_for :memberships
|
||||
|
||||
## methods ##
|
||||
|
||||
def all_pages_in_once
|
||||
Page.quick_tree(self)
|
||||
end
|
||||
|
||||
def accounts
|
||||
Account.criteria.in(:_id => self.memberships.collect(&:account_id))
|
||||
end
|
||||
|
||||
def admin_memberships
|
||||
self.memberships.find_all { |m| m.admin? }
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
Locomotive::Liquid::Drops::Site.new(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def create_default_pages!
|
||||
%w{index 404}.each do |slug|
|
||||
self.pages.create({
|
||||
:slug => slug,
|
||||
:title => I18n.t("attributes.defaults.pages.#{slug}.title"),
|
||||
:raw_template => I18n.t("attributes.defaults.pages.#{slug}.body"),
|
||||
:published => true
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_pages
|
||||
# pages is a tree so we just need to delete the root (as well as the page not found page)
|
||||
self.pages.root.first.try(:destroy) && self.pages.not_found.first.try(:destroy)
|
||||
end
|
||||
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user