Compare commits

...

76 Commits

Author SHA1 Message Date
Didier Lafforgue fbcda9cdd6 simply re-organize little piece of code (cleaning stuff) 2012-06-10 01:31:10 +02:00
Didier Lafforgue 6350652e4b Merge pull request #437 from paulsponagl/devise_2_1
Upgrade to devise 2.1
2012-06-09 15:52:05 -07:00
Paul Sponagl a6ba124a9e need to require devise-encryptable 2012-06-09 09:54:58 +02:00
Paul Sponagl 702283322d Upgrade to devise 2.1
rspec and cucumber tests working
untest within a real app
2012-06-08 12:52:00 +02:00
Didier Lafforgue ef66f88f5a forgot to update Gemfile.lock 2012-06-08 11:06:17 +02:00
Didier Lafforgue bae2fe4432 releasing new version: 2.0.0.rc8 2012-06-08 11:04:23 +02:00
Didier Lafforgue 50204b6556 dependency conflict with carrierwave and custom_fields (fixed) 2012-06-08 10:57:09 +02:00
did 11c4f98efc asset picker was not accessible from the Aloha toolbar (fixed now) 2012-06-06 01:34:37 -07:00
did 55b7d4dbf2 status of a redirect page is now 301 (previously 302) 2012-06-05 09:32:10 -07:00
Didier Lafforgue 2bd9e254c8 rails 3.2.5 + remove warnings + fix broken features + enhance upgrade script 2012-06-04 23:23:03 +02:00
did 3f1380408e fix issue #427 + make the content types API features more predictable 2012-06-04 08:26:10 -07:00
Didier Lafforgue 5f9c46b42c Merge pull request #419 from colibri-software/api_auth
API auth
2012-06-02 10:02:23 -07:00
Didier Lafforgue b4dd590f9a Merge pull request #432 from colibri-software/fix_page_drop
Fixed issue with page drop slug on templatized pages
2012-06-02 09:47:08 -07:00
Didier Lafforgue 34ba62c15c Merge pull request #431 from klaffenboeck/master
updated german locale with proper encoding (UTF-8)
2012-06-02 09:36:35 -07:00
did acd46f1c10 fix issue #430 2012-06-02 06:54:16 -07:00
Manfred Klaffenboeck 30557ef8b0 updated german locale with propper encoding (UTF-8) 2012-06-01 19:36:33 +02:00
Alex Sanford 6f5eec18c6 Fixed issue with page drop slug on templatized pages 2012-06-01 14:28:10 -03:00
Didier Lafforgue bc63cd8acf Merge pull request #429 from klaffenboeck/master
Updated german locale
2012-06-01 01:12:23 -07:00
Manfred Klaffenboeck b993b4f0ca Updated german locale 2012-05-31 21:14:46 +02:00
Manfred Klaffenboeck 590c4bb048 added translation for de.locomotive.messages.sending_form 2012-05-31 20:42:42 +02:00
did 2bec96e9f1 add a default message for the locomotive.messages.sending_form translation key (fix issue #428) 2012-05-31 10:03:57 -07:00
did 2568510f01 fix issue #426 2012-05-31 09:41:37 -07:00
did f8725ede67 fix issue #414 2012-05-31 01:41:19 -07:00
did f4ee99b72b fix issue #425 2012-05-30 15:41:11 -07:00
did 763fe5215f fix issue #407 (ie: boolean fields take the false value by default) 2012-05-30 15:30:42 -07:00
did 261575eb94 a little bit of cleaning for the upgrade scripts 2012-05-29 16:24:28 -07:00
Didier Lafforgue b706d50bdf Merge pull request #424 from Sharagoz/master
Update Norwegian locale for v2.0
2012-05-29 16:21:54 -07:00
did 3a4654e8f7 the upgrade script v1.1 did not work correctly + fix issue #403 (more generally, enhance the way jQuery modals were used within the back-office) + select the first inverse_of value of a has_many field (UI) 2012-05-29 15:53:48 -07:00
Bjørn Trondsen a662366637 bug in prev commit 2012-05-29 22:34:38 +02:00
Bjørn Trondsen 6ac3d72aed rename norwegian locale when upgrading 2012-05-29 22:13:41 +02:00
Bjørn Trondsen 8b56ee5889 update norwegian locale 2012-05-29 21:17:17 +02:00
Didier Lafforgue b751e3d990 trying to fix the big problem with jQuery UI modals (if modals have the same z-index, it causes weird behaviours) (WIP) 2012-05-29 01:26:51 +02:00
Didier Lafforgue ae16661286 trying to fix cucumber tests 2012-05-26 15:21:47 +02:00
Didier Lafforgue da2d689bdc fix issue #412 2012-05-26 14:35:04 +02:00
Didier Lafforgue 2ac74c9a50 fix issue #411 2012-05-25 13:24:10 +02:00
Didier Lafforgue 24ae75c6f5 Gemfile.lock gets updated 2012-05-24 23:37:48 +02:00
Didier Lafforgue c58075b7c3 Merge branch 'master' of github.com:locomotivecms/engine 2012-05-24 19:19:57 +02:00
Didier Lafforgue 662e359acb fix issue #393 2012-05-24 19:19:46 +02:00
Mario Visic 8be23f1cc2 Updated the readme a tad with PhantomJS info. 2012-05-25 00:23:13 +08:00
Mario Visic fbef06f103 Merge branch 'poltergeist' 2012-05-25 00:16:33 +08:00
Mario Visic 38098e93da Updated the doco to instruct users to install PhantomJS for testing. 2012-05-25 00:16:25 +08:00
Mario Visic 3a27845fe5 Small fix for poltergeist. 2012-05-25 00:13:55 +08:00
Mario Visic 4c5d6c92d0 Added poltergeist and a small JS fix. 2012-05-25 00:06:07 +08:00
Mario Visic 817ee8b0e5 Merge branch 'master' of github.com:locomotivecms/engine 2012-05-24 23:13:56 +08:00
Mario Visic 13b4d8c606 Do not pass the host option to URL helpers for sitemaps. Fixes #415. 2012-05-24 23:13:25 +08:00
did 6342b94ede Merge branch 'master' of github.com:locomotivecms/engine 2012-05-24 06:47:15 -07:00
did d3e219018b fix issue #388 2012-05-24 06:46:48 -07:00
Mario Visic 415817e20b Merge pull request #409 from waynegerard/label_field_name
Added a setter method for label_field_id in content_types.rb model
2012-05-24 03:35:30 -07:00
did 04d9fe17f8 Merge branch 'master' of github.com:locomotivecms/engine 2012-05-23 15:03:37 -07:00
did d90123e321 public forms are now protected from csrf attacks (see issue #382 2012-05-23 15:03:21 -07:00
waynegerard 2fa81112fc Removing inline rescue in favor of where/try combination for label ID in setter method. Thanks @mariovisic\! 2012-05-22 18:27:38 -07:00
waynegerard be26ae3059 Added a setter method for label_field_id in content_types.rb model. Should resolve issue where label_field_name was not being updated properly through the UI. Fix for issue #398. 2012-05-19 13:51:41 -07:00
Mario Visic 7e0fcb62ee Merge pull request #395 from willcosgrove/patch-1
Fixed typo for bootstrap setting: `data-toogle` to `data-toggle`
2012-05-08 20:49:23 -07:00
Will Cosgrove c0627bf8a7 Fixed typo for bootstrap setting: `data-toogle` to `data-toggle` 2012-05-08 22:31:20 -05:00
Didier Lafforgue e225ad2278 fix issue #385 2012-05-07 00:31:59 +02:00
did a939629cbf new multi_json gem 2012-05-05 02:39:23 -07:00
Didier Lafforgue f42fb1afe1 finally the new version of the multi_json gem (issue #387) 2012-05-05 11:17:18 +02:00
did 03d13d92d0 fix issue #387 2012-05-04 03:35:07 -07:00
Alex Sanford 24e5eb855a Added auth feature for memberships 2012-04-30 16:29:32 -03:00
Alex Sanford 9c15f1cbab Added API sites_controller and auth feature 2012-04-27 14:52:09 -03:00
Alex Sanford 06e493477c Added auth feature for current site 2012-04-27 12:05:21 -03:00
Alex Sanford 96007174cb Added auth feature for content_assets 2012-04-27 08:27:05 -03:00
Alex Sanford a921c44ce9 Fixed access denied error checking step 2012-04-26 16:12:39 -03:00
Alex Sanford 37f87e694c Added auth feature for theme assets 2012-04-26 12:48:05 -03:00
Alex Sanford 4e6c106772 Added auth feature for snippets 2012-04-25 10:56:38 -03:00
Alex Sanford d2da4b659e Added auth feature for content entries 2012-04-25 09:51:11 -03:00
Alex Sanford 44aadb8926 Added auth feature for content_types 2012-04-24 15:48:23 -03:00
Alex Sanford 75e694a6f0 Cleaned up API features 2012-04-24 13:37:29 -03:00
Alex Sanford 7dcc1ba3f6 Merge branch 'master' into api_auth
Conflicts:
	features/step_definitions/api_steps.rb
2012-04-24 13:22:35 -03:00
Alex Sanford 1344463222 Cleaned up features 2012-04-24 12:47:32 -03:00
Alex Sanford bf65fa47f3 Added destroy API method for pages and fixed feature 2012-04-24 12:13:26 -03:00
Alex Sanford f53ab18f90 Added features for destroying pages 2012-04-24 12:04:24 -03:00
Alex Sanford 494e3c9b51 Fixed author page update scenario 2012-04-17 12:02:29 -03:00
Alex Sanford edd236b202 Got authorization working for pages 2012-04-17 10:12:42 -03:00
Alex Sanford 97edb0e4b7 Added show action to page API and fixed some features 2012-04-16 15:36:39 -03:00
Alex Sanford 4f12c2cd10 Created some initial features for API authentication 2012-04-16 15:17:11 -03:00
128 changed files with 2904 additions and 410 deletions

10
Gemfile
View File

@ -22,9 +22,8 @@ end
group :assets do
gem 'sass-rails', '~> 3.2.4'
gem 'coffee-rails', '~> 3.2.2'
gem 'uglifier', '~> 1.2.3'
gem 'compass', :git => 'git://github.com/chriseppstein/compass.git', :branch => 'no_rails_integration'
gem 'compass-rails', :git => 'git://github.com/Compass/compass-rails.git'
gem 'uglifier', '~> 1.2.4'
gem 'compass-rails'
end
group :test do
@ -36,6 +35,7 @@ group :test do
# gem 'growl-glue'
gem 'cucumber-rails', :require => false
gem 'poltergeist'
gem 'rspec-rails', '~> 2.8.0'
gem 'shoulda-matchers'
@ -50,4 +50,6 @@ group :test do
gem 'json_spec'
gem 'database_cleaner'
end
# gem 'debugger', :git => 'git://github.com/cldwalker/debugger.git'
end

View File

@ -1,42 +1,25 @@
GIT
remote: git://github.com/Compass/compass-rails.git
revision: 47d889ad8dabdab1e9f44da4447f94846c089c50
specs:
compass-rails (1.0.0.rc.3)
compass (~> 0.12.rc.0)
GIT
remote: git://github.com/chriseppstein/compass.git
revision: 0f96e881019c364aa6124845cbd83b1fd75c4ffe
branch: no_rails_integration
specs:
compass (0.12.rc.4.0f96e88)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
PATH
remote: .
specs:
locomotive_cms (2.0.0.rc7)
locomotive_cms (2.0.0.rc8)
RedCloth (~> 4.2.8)
actionmailer-with-request (~> 0.3.0)
bson_ext (~> 1.5.2)
cancan (~> 1.6.7)
carrierwave (~> 0.6.0)
carrierwave-mongoid (~> 0.1.3)
carrierwave-mongoid (~> 0.2.1)
cells (~> 3.8.0)
codemirror-rails (~> 2.21)
custom_fields (~> 2.0.0.rc10)
devise (~> 1.5.3)
custom_fields (~> 2.0.0.rc12)
devise (~> 2.1.0)
devise-encryptable (~> 0.1.1)
dragonfly (~> 0.9.8)
flash_cookie_session (~> 1.1.1)
fog (~> 1.3.1)
formtastic (~> 2.0.2)
haml (~> 3.1.4)
haml (~> 3.1.6)
highline (~> 1.6.2)
httparty (~> 0.8.1)
jquery-rails (~> 1.0.16)
jquery-rails (~> 1.0.19)
kaminari (~> 0.13.0)
locomotive-aloha-rails (~> 0.20.1.4)
locomotive-mongoid-tree (~> 0.6.2)
@ -45,9 +28,9 @@ PATH
mimetype-fu (~> 0.1.2)
mongo (~> 1.5.2)
mongoid (~> 2.4.9)
multi_json (= 1.2.0)
multi_json (~> 1.3.4)
rack-cache (~> 1.1)
rails (~> 3.2.3)
rails (~> 3.2.5)
rails-backbone (~> 0.6.1)
rake (~> 0.9.2)
responders (~> 0.6.4)
@ -59,36 +42,36 @@ GEM
remote: http://rubygems.org/
specs:
RedCloth (4.2.9)
actionmailer (3.2.3)
actionpack (= 3.2.3)
actionmailer (3.2.5)
actionpack (= 3.2.5)
mail (~> 2.4.4)
actionmailer-with-request (0.3.0)
rails (>= 3)
actionpack (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
actionpack (3.2.5)
activemodel (= 3.2.5)
activesupport (= 3.2.5)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.3)
activesupport (= 3.2.3)
sprockets (~> 2.1.3)
activemodel (3.2.5)
activesupport (= 3.2.5)
builder (~> 3.0.0)
activerecord (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
activerecord (3.2.5)
activemodel (= 3.2.5)
activesupport (= 3.2.5)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
activesupport (3.2.3)
activeresource (3.2.5)
activemodel (= 3.2.5)
activesupport (= 3.2.5)
activesupport (3.2.5)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.2.7)
addressable (2.2.8)
arel (3.0.2)
bcrypt-ruby (3.0.1)
bson (1.5.2)
@ -106,13 +89,13 @@ GEM
carrierwave (0.6.2)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
carrierwave-mongoid (0.1.3)
carrierwave (>= 0.5.6)
carrierwave-mongoid (0.2.1)
carrierwave (~> 0.6.1)
mongoid (~> 2.1)
cells (3.8.3)
cells (3.8.5)
actionpack (~> 3.0)
railties (~> 3.0)
childprocess (0.3.1)
childprocess (0.3.2)
ffi (~> 1.0.6)
chunky_png (1.2.5)
codemirror-rails (2.24)
@ -123,39 +106,50 @@ GEM
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.3.1)
cucumber (1.1.9)
coffee-script-source (1.3.3)
compass (0.12.1)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
compass-rails (1.0.2)
compass (>= 0.12.0, < 0.14)
cucumber (1.2.1)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.9.0)
diff-lcs (>= 1.1.3)
gherkin (~> 2.11.0)
json (>= 1.4.6)
term-ansicolor (>= 1.0.6)
cucumber-rails (1.3.0)
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
nokogiri (>= 1.5.0)
custom_fields (2.0.0.rc10)
custom_fields (2.0.0.rc12)
activesupport (~> 3.2.1)
carrierwave-mongoid (~> 0.1.3)
mongoid (~> 2.4.7)
database_cleaner (0.7.2)
devise (1.5.3)
carrierwave-mongoid (~> 0.2.1)
mongoid (~> 2.4.9)
database_cleaner (0.8.0)
devise (2.1.0)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.0.3)
warden (~> 1.1)
orm_adapter (~> 0.0.7)
railties (~> 3.1)
warden (~> 1.1.1)
devise-encryptable (0.1.1)
devise (>= 2.1.0.rc)
diff-lcs (1.1.3)
dragonfly (0.9.12)
rack
ejs (1.0.0)
erubis (2.7.0)
eventmachine (0.12.10)
excon (0.13.4)
execjs (1.3.0)
execjs (1.4.0)
multi_json (~> 1.0)
factory_girl (2.5.2)
activesupport (>= 2.3.9)
factory_girl_rails (1.6.0)
factory_girl (~> 2.5.0)
railties (>= 3.0.0)
faye-websocket (0.4.5)
eventmachine (>= 0.12.0)
ffi (1.0.11)
flash_cookie_session (1.1.3)
rails (~> 3.0)
@ -169,15 +163,16 @@ GEM
net-ssh (>= 2.1.3)
nokogiri (~> 1.5.0)
ruby-hmac
formatador (0.2.1)
formatador (0.2.3)
formtastic (2.0.2)
rails (~> 3.0)
fssm (0.2.9)
gherkin (2.9.3)
gherkin (2.11.0)
json (>= 1.4.6)
haml (3.1.4)
highline (1.6.11)
haml (3.1.6)
highline (1.6.12)
hike (1.2.1)
http_parser.rb (0.5.3)
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
@ -186,8 +181,8 @@ GEM
jquery-rails (1.0.19)
railties (~> 3.0)
thor (~> 0.14)
json (1.6.6)
json_spec (1.0.0)
json (1.7.3)
json_spec (1.0.3)
multi_json (~> 1.0)
rspec (~> 2.0)
kaminari (0.13.0)
@ -215,20 +210,26 @@ GEM
mocha (0.9.12)
mongo (1.5.2)
bson (= 1.5.2)
mongoid (2.4.9)
mongoid (2.4.11)
activemodel (~> 3.1)
mongo (~> 1.3)
mongo (<= 1.6.2)
tzinfo (~> 0.3.22)
multi_json (1.2.0)
multi_xml (0.4.4)
multi_json (1.3.6)
multi_xml (0.5.1)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.3.0)
nokogiri (1.5.2)
net-ssh (2.5.2)
nokogiri (1.5.3)
orm_adapter (0.0.7)
pickle (0.4.10)
cucumber (>= 0.8)
rake
poltergeist (0.6.0)
capybara (~> 1.0)
childprocess (~> 0.3)
faye-websocket (~> 0.4, >= 0.4.4)
http_parser.rb (~> 0.5.3)
multi_json (~> 1.0)
polyglot (0.3.3)
rack (1.4.1)
rack-cache (1.2)
@ -237,26 +238,26 @@ GEM
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.2.3)
actionmailer (= 3.2.3)
actionpack (= 3.2.3)
activerecord (= 3.2.3)
activeresource (= 3.2.3)
activesupport (= 3.2.3)
rails (3.2.5)
actionmailer (= 3.2.5)
actionpack (= 3.2.5)
activerecord (= 3.2.5)
activeresource (= 3.2.5)
activesupport (= 3.2.5)
bundler (~> 1.0)
railties (= 3.2.3)
railties (= 3.2.5)
rails-backbone (0.6.1)
coffee-script (~> 2.2.0)
ejs (~> 1.0.0)
railties (>= 3.1.0)
railties (3.2.3)
actionpack (= 3.2.3)
activesupport (= 3.2.3)
railties (3.2.5)
actionpack (= 3.2.5)
activesupport (= 3.2.5)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
raindrops (0.8.0)
thor (>= 0.14.6, < 2.0)
raindrops (0.9.0)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
@ -280,28 +281,27 @@ GEM
railties (>= 3.0)
rspec (~> 2.8.0)
ruby-hmac (0.4.0)
rubyzip (0.9.7)
rubyzip (0.9.8)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
sass (3.1.15)
sass (3.1.19)
sass-rails (3.2.5)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
selenium-webdriver (2.21.1)
selenium-webdriver (2.22.2)
childprocess (>= 0.2.5)
ffi (~> 1.0)
libwebsocket (~> 0.1.3)
multi_json (< 1.3)
multi_json (~> 1.0)
rubyzip
shoulda-matchers (1.1.0)
activesupport (>= 3.0.0)
sprockets (2.1.2)
sprockets (2.1.3)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
term-ansicolor (1.0.7)
thor (0.14.6)
thor (0.15.2)
tilt (1.3.3)
treetop (1.4.10)
polyglot
@ -310,7 +310,7 @@ GEM
uglifier (1.2.4)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
unicorn (4.2.1)
unicorn (4.3.1)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
@ -326,8 +326,7 @@ PLATFORMS
DEPENDENCIES
capybara
coffee-rails (~> 3.2.2)
compass!
compass-rails!
compass-rails
cucumber-rails
database_cleaner
factory_girl_rails (~> 1.6.0)
@ -336,10 +335,11 @@ DEPENDENCIES
locomotive_cms!
mocha (= 0.9.12)
pickle
poltergeist
rspec-cells
rspec-rails (~> 2.8.0)
sass-rails (~> 3.2.4)
shoulda-matchers
uglifier (~> 1.2.3)
uglifier (~> 1.2.4)
unicorn
xpath (~> 0.1.4)

View File

@ -24,8 +24,8 @@ h2. Gems
Here is a short list of main gems / technologies used in the application.
* Rails 3.2.3
* Mongoid 2.4.6 (with MongoDB 2.0)
* Rails 3.2.5
* Mongoid 2.4.9 (with MongoDB 2.0)
* Liquid
* Devise
* Carrierwave
@ -46,7 +46,7 @@ h2. Community
* Get help or discuss locomotive CMS at the "LocomotiveCMS Google group":https://groups.google.com/forum/?fromgroups#!forum/locomotivecms or the "LocomotiveCMS Discussion Forums":http://locomotive.vanillaforums.com/ (deprecated)
* Join us on IRC "#locomotivecms at irc.freenode.net!":http://webchat.freenode.net/
* "Follow us on twitter":http://twitter.com/locomotiveapp
* "Follow us on twitter":http://twitter.com/locomotivecms
h2. Contributing to Locomotive
@ -55,6 +55,7 @@ Locomotive CMS is an open source project, we encourage contributions. If you hav
* Install ruby and mongoDB
* Clone the project <code>git clone git@github.com:locomotivecms/engine.git</code>
* Setup a virtual host entry for <code>test.example.com</code> to point to localhost
* Install PhantomJS (Required for the cucumber suite See: https://github.com/jonleighton/poltergeist)
* Run the tests <code>rake</code>
* Write your failing tests
* Make the tests pass

View File

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 611 B

View File

@ -25,7 +25,7 @@ define(
picker.close();
}
picker.render()
picker.fetch_assets();
},
/**

View File

@ -69,5 +69,7 @@
doc.head.appendChild(stylesheet);
}
$.ui.dialog.prototype.overlayEl = function() { return this.overlay.$el; }
})();

View File

@ -83,3 +83,10 @@ class Locomotive.Views.ApplicationView extends Backbone.View
locale = $(@).attr('data-locale')
window.addParameterToURL 'content_locale', locale
unique_dialog_zindex: ->
# returns the number of jQuery UI modals created in order to set a valid zIndex for each of them.
# Each modal window should have a different zIndex, otherwise there will be conflicts between them.
window.Locomotive.jQueryModals ||= 0
998 + window.Locomotive.jQueryModals++

View File

@ -12,7 +12,10 @@ class Locomotive.Views.ContentAssets.PickerView extends Locomotive.Views.Shared.
ich.content_asset_picker
fetch_assets: ->
@collection.fetch()
@_reset()
@collection.fetch
success: () =>
@open()
build_uploader: (el, link) ->
link.bind 'click', (event) ->

View File

@ -50,11 +50,13 @@ class Locomotive.Views.ContentEntries.FormView extends Locomotive.Views.Shared.F
settings = _.extend {}, @tinyMCE_settings(),
oninit: ((editor) =>
$.cmd 'S', (() =>
$(textarea).val(editor.getBody().innerHTML).trigger('change')
editor.save()
$(textarea).trigger('changeSilently')
@$('form').trigger('submit')
), [], ignoreCase: true, document: editor.dom.doc),
onchange_callback: (editor) =>
$(textarea).val(editor.getBody().innerHTML).trigger('change')
editor.save()
$(textarea).trigger('changeSilently')
$(textarea).tinymce(settings)

View File

@ -26,11 +26,9 @@ class Locomotive.Views.ContentEntries.PopupFormView extends Locomotive.Views.Con
@dialog = $(@el).dialog
autoOpen: false
modal: true
zIndex: 998
zIndex: window.application_view.unique_dialog_zindex()
width: 770,
create: (event, ui) =>
$('.ui-widget-overlay').bind 'click', => @close()
$(@el).prev().find('.ui-dialog-title').html(@$('h2').html())
@$('h2').remove()
actions = @$('.dialog-actions').appendTo($(@el).parent()).addClass('ui-dialog-buttonpane ui-widget-content ui-helper-clearfix')
@ -39,6 +37,7 @@ class Locomotive.Views.ContentEntries.PopupFormView extends Locomotive.Views.Con
actions.find('input[type=submit]').click (event) => @save(event)
open: (event, ui, extra) =>
$(@el).dialog('overlayEl').bind 'click', => @close()
# nothing to do
open: ->
@ -57,6 +56,7 @@ class Locomotive.Views.ContentEntries.PopupFormView extends Locomotive.Views.Con
close: (event) ->
event.stopPropagation() & event.preventDefault() if event?
@clear_errors()
$(@el).dialog('overlayEl').unbind('click')
$(@el).dialog('close')
center: ->
@ -71,6 +71,9 @@ class Locomotive.Views.ContentEntries.PopupFormView extends Locomotive.Views.Con
else
@refresh()
slugify_label_field: ->
# disabled in a popup form
enable_has_many_fields: ->
# disabled in a popup form

View File

@ -12,8 +12,6 @@ class Locomotive.Views.ContentTypes.FormView extends Locomotive.Views.Shared.For
initialize: ->
@model = new Locomotive.Models.ContentType(@options.content_type)
window.foo = @model
Backbone.ModelBinding.bind @
render: ->

View File

@ -58,11 +58,18 @@ class Locomotive.Views.ContentTypes.CustomFieldEntryView extends Backbone.View
fetch_inverse_of_list: ->
@$('li.input.inverse-of select option').remove()
_.each @inverse_of_list[@model.get('type')], (data) =>
list = @inverse_of_list[@model.get('type')] || []
_.each list, (data) =>
if data.class_name == @model.get('class_name')
option = new Option(data.label, data.name, data.class_name == @model.get('inverse_of') || @inverse_of_list.length == 1)
option = new Option(data.label, data.name, data.name == @model.get('inverse_of') || list.length == 1)
@$('li.input.inverse-of select').append(option)
# by default, select the first option
if !@model.get('inverse_of')? && list.length > 0
@model.set
inverse_of: list[0].name
render_select_options_view: ->
@select_options_view = new Locomotive.Views.ContentTypes.SelectOptionsView
model: @model

View File

@ -15,8 +15,6 @@ class Locomotive.Views.CurrentSite.EditView extends Locomotive.Views.Shared.Form
Backbone.ModelBinding.bind @, checkbox: 'class'
window.foo = @model
render: ->
super()
@ -57,15 +55,16 @@ class Locomotive.Views.CurrentSite.EditView extends Locomotive.Views.Shared.Form
@$('#site_memberships_input').append(@memberships_view.render().el)
enable_liquid_editing: ->
input = @$('#site_robots_txt')
@editor = CodeMirror.fromTextArea input.get()[0],
mode: 'liquid'
autoMatchParens: false
lineNumbers: false
passDelay: 50
tabMode: 'shift'
theme: 'default'
onChange: (editor) => @model.set(robots_txt: editor.getValue())
if($('#site_robots_txt').length)
input = @$('#site_robots_txt')
@editor = CodeMirror.fromTextArea input.get()[0],
mode: 'liquid'
autoMatchParens: false
lineNumbers: false
passDelay: 50
tabMode: 'shift'
theme: 'default'
onChange: (editor) => @model.set(robots_txt: editor.getValue())
save: (event) ->
if @model.includes_domain(window.location.host)

View File

@ -24,6 +24,8 @@ class Locomotive.Views.InlineEditor.ApplicationView extends Backbone.View
@toolbar_view.render()
@content_assets_picker_view.render()
enable_iframe_autoheight: ->
iframe = @iframe
@ -89,6 +91,13 @@ class Locomotive.Views.InlineEditor.ApplicationView extends Backbone.View
toolbar_view.show_status 'loading'
window.history.pushState('Object', 'Title', link.attr('href').replace('_edit', '_admin'))
unique_dialog_zindex: ->
# returns the number of jQuery UI modals created in order to set a valid zIndex for each of them.
# Each modal window should have a different zIndex, otherwise there will be conflicts between them.
window.Locomotive.jQueryModals ||= 0
1050 + window.Locomotive.jQueryModals++
_$: (selector) ->
$(selector, @iframe[0].contentWindow.document)

View File

@ -16,14 +16,14 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
@model = new Locomotive.Models.Page(@options.page)
window.foo = @model
@touched_url = false
@image_picker_view = new Locomotive.Views.ThemeAssets.ImagePickerView
collection: new Locomotive.Models.ThemeAssetsCollection()
on_select: @insert_image
@image_picker_view.render()
Backbone.ModelBinding.bind @
@editable_elements_view = new Locomotive.Views.EditableElements.EditAllView(collection: @model.get('editable_elements'))
@ -58,7 +58,7 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
open_image_picker: (event) ->
event.stopPropagation() & event.preventDefault()
@image_picker_view.editor = @editor
@image_picker_view.render()
@image_picker_view.fetch_assets()
insert_image: (path) ->
text = "{{ '#{path}' | theme_image_url }}"

View File

@ -11,11 +11,9 @@ class Locomotive.Views.Shared.AssetPickerView extends Backbone.View
@collection.bind('remove', @remove_asset)
render: ->
@_reset()
$(@el).html(@template()())
@fetch_assets()
@create_dialog()
return @
@ -29,33 +27,32 @@ class Locomotive.Views.Shared.AssetPickerView extends Backbone.View
# please overide build_uploader
create_dialog: ->
@dialog = $(@el).dialog
@dialog ||= $(@el).dialog
autoOpen: false
modal: true
zIndex: 998
zIndex: window.application_view.unique_dialog_zindex()
width: 650,
create: (event, ui) =>
$('.ui-widget-overlay').bind 'click', => @close()
$(@el).prev().find('.ui-dialog-title').html(@$('h2').html())
@$('h2').remove()
actions = @$('.dialog-actions').appendTo($(@el).parent()).addClass('ui-dialog-buttonpane ui-widget-content ui-helper-clearfix')
actions.find('#close-link').click (event) => @close(event)
input = actions.find('input[type=file]')
link = actions.find('#upload-link')
@build_uploader(input, link)
open: (event, ui, extra) =>
actions = $(@el).parent().find('.ui-dialog-buttonpane')
el = actions.find('input[type=file]')
link = actions.find('#upload-link')
@build_uploader(el, link)
@open()
$(@el).dialog('overlayEl').bind 'click', => @close()
open: ->
$(@el).dialog('open')
close: (event) ->
event.stopPropagation() & event.preventDefault() if event?
$(@el).dialog('overlayEl').unbind('click')
$(@el).dialog('close')
shake: ->
@ -70,8 +67,6 @@ class Locomotive.Views.Shared.AssetPickerView extends Backbone.View
@_refresh()
setTimeout (=> @create_dialog()), 30 # disable flickering
add_asset: (asset, first) ->
# please overide add_asset (the 'first' param is to know if it comes from the first collection fetch)
@ -94,6 +89,4 @@ class Locomotive.Views.Shared.AssetPickerView extends Backbone.View
_on_refresh: ->
_reset: ->
$('.ui-widget-overlay').unbind 'click'
@$('.actions input[type=file]').remove()
@dialog.dialog('destroy') if @dialog?
# for nothing to do

View File

@ -36,7 +36,8 @@ class Locomotive.Views.Shared.FormView extends Backbone.View
previous_attributes = _.clone @model.attributes
@model.save {},
headers: options.headers
headers: options.headers
silent: true # since we pass an empty hash above, no need to trigger the callbacks
success: (model, response, xhr) =>
form.trigger('ajax:complete')

View File

@ -19,6 +19,8 @@ class Locomotive.Views.Snippets.FormView extends Locomotive.Views.Shared.FormVie
collection: new Locomotive.Models.ThemeAssetsCollection()
on_select: @insert_image
@image_picker_view.render()
Backbone.ModelBinding.bind @
render: ->
@ -38,7 +40,7 @@ class Locomotive.Views.Snippets.FormView extends Locomotive.Views.Shared.FormVie
open_image_picker: (event) ->
event.stopPropagation() & event.preventDefault()
@image_picker_view.editor = @editor
@image_picker_view.render()
@image_picker_view.fetch_assets()
insert_image: (path) ->
text = "{{ '#{path}' | theme_image_url }}"

View File

@ -15,10 +15,10 @@ class Locomotive.Views.ThemeAssets.FormView extends Locomotive.Views.Shared.Form
@model = new Locomotive.Models.ThemeAsset(@options.theme_asset)
window.foo = @model
@image_picker_view = new Locomotive.Views.ThemeAssets.ImagePickerView on_select: @insert_image
@image_picker_view.render()
Backbone.ModelBinding.bind @
render: ->
@ -72,10 +72,10 @@ class Locomotive.Views.ThemeAssets.FormView extends Locomotive.Views.Shared.Form
open_image_picker: (event) ->
event.stopPropagation() & event.preventDefault()
@image_picker_view.editor = @editor
@image_picker_view.render()
@image_picker_view.fetch_assets()
insert_image: (path) ->
text = "{{ '#{path}' | theme_image_url }}"
text = "'#{path}'"
@editor.replaceSelection(text)
@image_picker_view.close()

View File

@ -15,7 +15,12 @@ class Locomotive.Views.ThemeAssets.ImagePickerView extends Locomotive.Views.Shar
ich.theme_image_picker
fetch_assets: ->
@collection.fetch data: { content_type: 'image' }
@_reset()
@collection.fetch
data:
content_type: 'image'
success: () =>
@open()
build_uploader: (el, link) ->
link.bind 'click', (event) ->
@ -37,4 +42,7 @@ class Locomotive.Views.ThemeAssets.ImagePickerView extends Locomotive.Views.Shar
add_asset: (asset) ->
@$('ul.list').append(ich.theme_asset(asset.toJSON()))
@_refresh()
@_refresh()
_reset: ->
@$('ul.list').empty()

View File

@ -43,13 +43,16 @@
'collection': new Locomotive.Models.ContentAssetsCollection()
});
view.render();
// Register commands
ed.addCommand('locomotiveMedia', function() {
view.options.on_select = function(asset) {
insertImage(ed, asset);
view.close();
}
view.render();
view.fetch_assets();
});
// Register buttons

View File

@ -7,6 +7,7 @@
font-size: 13px;
font-weight: normal;
line-height: 15px;
@include background-image(linear-gradient(top, #f0f0f0, #f9f9f9 4px, #f9f9f9 4px, #ffffff 12px, #ffffff));

View File

@ -60,7 +60,7 @@
.ui-dialog-content {
position: relative;
z-index: 999;
z-index: 1200;
text-align: left;
@ -124,14 +124,14 @@
top: 8px;
right: 10px;
z-index: 1003;
z-index: 1203;
a, input[type=submit] {
@include light-button;
}
input[type=file] {
z-index: 1003;
z-index: 1203;
}
} // .button-wrapper

View File

@ -421,8 +421,10 @@ form.formtastic {
}
&.no-label {
padding-top: 12px;
> textarea, .CodeMirror, .CodeMirror-scroll {
margin-top: 12px;
margin-top: 0px;
width: 868px;
}
}

View File

@ -24,7 +24,7 @@ module Locomotive
add :switch, :url => '#', :id => 'sites-picker-link'
end
add :help, :url => '#', :class => 'tutorial', :id => 'help'
add :help, :url => 'http://doc.locomotivecms.com/templates/basics', :class => 'tutorial', :id => 'help'
add :logout, :url => destroy_locomotive_session_url, :confirm => t('locomotive.messages.confirm'), :method => :delete
end

View File

@ -7,15 +7,13 @@ module Locomotive
skip_before_filter :verify_authenticity_token
skip_load_and_authorize_resource
before_filter :require_account
before_filter :require_site
before_filter :set_locale
# before_filter :validate_site_membership
before_filter :set_current_thread_variables
self.responder = Locomotive::ActionController::Responder # custom responder
@ -23,6 +21,11 @@ module Locomotive
protected
def set_current_thread_variables
Thread.current[:account] = current_locomotive_account
Thread.current[:site] = current_site
end
def current_ability
@current_ability ||= Ability.new(current_locomotive_account, current_site)
end
@ -40,4 +43,4 @@ module Locomotive
end
end
end
end

View File

@ -2,11 +2,18 @@ module Locomotive
module Api
class ContentAssetsController < BaseController
load_and_authorize_resource :class => Locomotive::ContentAsset
def index
@content_assets = current_site.content_assets
respond_with(@content_assets)
end
def show
@content_asset = current_site.content_assets.find(params[:id])
respond_with(@content_asset)
end
def create
@content_asset = current_site.content_assets.create(params[:content_asset])
respond_with @content_asset, :location => main_app.locomotive_api_content_assets_url
@ -18,6 +25,12 @@ module Locomotive
respond_with @content_asset, :location => main_app.locomotive_api_content_assets_url
end
def destroy
@content_asset = current_site.content_assets.find(params[:id])
@content_asset.destroy
respond_with @content_asset
end
end
end
end

View File

@ -2,11 +2,18 @@ module Locomotive
module Api
class ContentTypesController < BaseController
load_and_authorize_resource :class => Locomotive::ContentType
def index
@content_types = current_site.content_types
@content_types = current_site.content_types.order_by([[:name, :asc]])
respond_with(@content_types)
end
def show
@content_type = current_site.content_types.find(params[:id])
respond_with @content_type
end
def create
@content_type = current_site.content_types.create(params[:content_type])
respond_with @content_type, :location => main_app.locomotive_api_content_types_url
@ -18,6 +25,12 @@ module Locomotive
respond_with @content_type, :location => main_app.locomotive_api_content_types_url
end
def destroy
@content_type = current_site.content_types.find(params[:id])
@content_type.destroy
respond_with @content_type
end
end
end
end

View File

@ -3,7 +3,9 @@ module Locomotive
class CurrentSiteController < BaseController
def show
respond_with(current_site)
@site = current_site
authorize! :show, @site
respond_with(@site)
end
end

View File

@ -0,0 +1,49 @@
module Locomotive
module Api
class MembershipsController < BaseController
# It's an embedded document, so we'll just load manually
before_filter :load_membership, :only => [ :show, :update, :destroy ]
before_filter :load_memberships, :only => [ :index ]
authorize_resource :class => Locomotive::Membership
def index
respond_with(@memberships)
end
def show
respond_with(@membership)
end
def create
build_params = params[:membership].merge({ :role => 'author' }) # force author by default
@membership = current_site.memberships.create(build_params)
respond_with(@membership)
end
def update
@membership.update_attributes(params[:membership])
respond_with(@membership)
end
def destroy
@membership.destroy
respond_with(@membership)
end
protected
def load_membership
@membership ||= load_memberships.find(params[:id])
end
def load_memberships
@memberships ||= current_site.memberships
end
end
end
end

View File

@ -2,11 +2,18 @@ module Locomotive
module Api
class PagesController < BaseController
load_and_authorize_resource :class => Locomotive::Page
def index
@pages = current_site.pages.all
@pages = current_site.pages.order_by([[:depth, :asc], [:position, :asc]])
respond_with(@pages)
end
def show
@page = current_site.pages.find(params[:id])
respond_with(@page)
end
def create
@page = current_site.pages.create(params[:page])
respond_with @page, :location => main_app.locomotive_api_pages_url
@ -18,6 +25,12 @@ module Locomotive
respond_with @page, :location => main_app.locomotive_api_pages_url
end
def destroy
@page = current_site.pages.find(params[:id])
@page.destroy
respond_with @page
end
end
end

View File

@ -0,0 +1,44 @@
module Locomotive
module Api
class SitesController < BaseController
load_and_authorize_resource :class => Locomotive::Site
# FIXME: the auto-loaded site won't pass authorization for show, update, or destroy
skip_load_and_authorize_resource :only => [ :show, :update, :destroy ]
def index
@sites = Locomotive::Site.all
respond_with(@sites)
end
def show
@site = Locomotive::Site.find(params[:id])
authorize! :show, @site
respond_with(@site)
end
def create
@site = Locomotive::Site.create(params[:site])
respond_with(@site)
end
def update
@site = Locomotive::Site.find(params[:id])
authorize! :update, @site
@site.update_attributes(params[:site])
respond_with @site
end
def destroy
@site = Locomotive::Site.find(params[:id])
authorize! :destroy, @site
@site.destroy
respond_with @site
end
end
end
end

View File

@ -2,11 +2,18 @@ module Locomotive
module Api
class SnippetsController < BaseController
load_and_authorize_resource :class => Locomotive::Snippet
def index
@snippets = current_site.snippets.all
@snippets = current_site.snippets.order_by([[:name, :asc]])
respond_with(@snippets)
end
def show
@snippet = current_site.snippets.find(params[:id])
respond_with @snippet
end
def create
@snippet = current_site.snippets.create(params[:snippet])
respond_with @snippet, :location => main_app.locomotive_api_snippets_url
@ -18,6 +25,12 @@ module Locomotive
respond_with @snippet, :location => main_app.locomotive_api_snippets_url
end
def destroy
@snippet = current_site.snippets.find(params[:id])
@snippet.destroy
respond_with @snippet
end
end
end
end

View File

@ -2,11 +2,18 @@ module Locomotive
module Api
class ThemeAssetsController < BaseController
load_and_authorize_resource :class => Locomotive::ThemeAsset
def index
@theme_assets = current_site.theme_assets.all
respond_with(@theme_assets)
end
def show
@theme_asset = current_site.theme_assets.find(params[:id])
respond_with @theme_asset
end
def create
@theme_asset = current_site.theme_assets.create(params[:theme_asset])
respond_with @theme_asset, :location => main_app.locomotive_api_theme_assets_url
@ -18,6 +25,12 @@ module Locomotive
respond_with @theme_asset, :location => main_app.locomotive_api_theme_assets_url
end
def destroy
@theme_asset = current_site.theme_assets.find(params[:id])
@theme_asset.destroy
respond_with @theme_asset
end
end
end
end

View File

@ -6,8 +6,6 @@ module Locomotive
before_filter :sanitize_entry_params, :only => :create
skip_before_filter :verify_authenticity_token
skip_load_and_authorize_resource
self.responder = Locomotive::ActionController::PublicResponder # custom responder
@ -17,7 +15,6 @@ module Locomotive
def create
@entry = @content_type.entries.create(params[:entry] || params[:content])
flash[@content_type.slug.singularize] = @entry.to_presenter(:include_errors => true).as_json
Rails.logger.debug @entry.to_presenter(:include_errors => true).as_json
respond_with @entry, :location => self.callback_url
end
@ -48,6 +45,13 @@ module Locomotive
end
end
def handle_unverified_request
if Locomotive.config.csrf_protection
reset_session
redirect_to '/', :status => 302
end
end
end
end
end

View File

@ -37,6 +37,8 @@ module Locomotive
can :touch, Site do |site|
site == @site
end
can :read, ContentType
end
def setup_designer_permissions!

View File

@ -5,6 +5,20 @@ module Locomotive
devise *Locomotive.config.devise_modules
## devise fields (need to be declared since 2.x) ##
field :remember_created_at, :type => Time
field :email, :type => String, :null => false
field :encrypted_password, :type => String, :null => false
field :authentication_token, :type => String
field :reset_password_token, :type => String
field :reset_password_sent_at, :type => Time
field :password_salt, :type => String
field :sign_in_count, :type => Integer
field :current_sign_in_at, :type => Time
field :last_sign_in_at, :type => Time
field :current_sign_in_ip, :type => String
field :last_sign_in_ip, :type => String
## attributes ##
field :name
field :locale, :default => Locomotive.config.default_locale.to_s or 'en'

View File

@ -45,9 +45,11 @@ module Locomotive
self.send((type || self.content_type).label_field_name.to_sym)
end
value.respond_to?(:to_label) ? value.to_label : value
value.respond_to?(:to_label) ? value.to_label : value.to_s
end
alias :to_label :_label
def translated?
if self.respond_to?(:"#{self._label_field_name}_translations")
self.send(:"#{self._label_field_name}_translations").key?(::Mongoid::Fields::I18n.locale.to_s) #rescue false

View File

@ -84,6 +84,13 @@ module Locomotive
self.class.class_name_to_content_type(class_name, self.site)
end
def label_field_id=(value)
# update the label_field_name if the label_field_id is changed
new_label_field_name = self.entries_custom_fields.where(:_id => value).first.try(:name)
self.label_field_name = new_label_field_name
super(value)
end
def label_field_name=(value)
# mandatory if we allow the API to set the label field name without an id of the field
@new_label_field_name = value unless value.blank?

View File

@ -27,7 +27,7 @@ module Locomotive
def set_label_field
if @new_label_field_name.present?
self.label_field_id = self.entries_custom_fields.detect { |f| f.name == @new_label_field_name.underscore }._id
self.label_field_id = self.entries_custom_fields.detect { |f| f.name == @new_label_field_name.underscore }.try(:_id)
end
# unknown label_field_name, get the first one instead

View File

@ -9,7 +9,7 @@ module Locomotive
## fields ##
field :redirect, :type => Boolean, :default => false
field :redirect_url, :type => String
field :redirect_url, :type => String, :localize => true
## validations ##
validates_presence_of :redirect_url, :if => :redirect

View File

@ -26,7 +26,7 @@ module Locomotive
module ClassMethods
# Returns the pages tree from the site with the most minimal amount of queries.
# Returns the tree of pages from the site with the most minimal amount of queries.
# This method should only be used for read-only purpose since
# the mongodb returns the minimal set of required attributes to build
# the tree.

View File

@ -145,7 +145,7 @@ module Locomotive
def escape_shortcut_urls(text)
return if text.blank?
text.gsub(/[("'](\/(stylesheets|javascripts|images|media)\/(([^;.]+)\/)*([a-z_\-0-9]+)\.[a-z]{2,3})[)"']/) do |path|
text.gsub(/[("'](\/(stylesheets|javascripts|images|media)\/(([^;.]+)\/)*([a-zA-Z_\-0-9]+)\.[a-z]{2,3})[)"']/) do |path|
sanitized_path = path.gsub(/[("')]/, '').gsub(/^\//, '')

View File

@ -11,9 +11,9 @@
= link_to t('locomotive.buttons.close'), '#', :id => 'close-link'
.button-wrapper.upload
= form_tag theme_assets_url(:json), :class => 'quick-upload' do
= file_field_tag 'theme_asset[source]', :multiple => 'multiple'
= link_to t('locomotive.theme_assets.image_picker.upload'), theme_assets_url(:json), :class => 'new', :id => 'upload-link'
= form_tag content_assets_url(:json), :class => 'quick-upload' do
= file_field_tag 'content_asset[source]', :multiple => 'multiple'
= link_to t('locomotive.content_assets.image_picker.upload'), content_assets_url(:json), :class => 'new', :id => 'upload-link'
%script{ :type => 'text/html', :id => 'content_asset' }

View File

@ -3,7 +3,7 @@
- content_for :backbone_view_data do
:plain
content_entry: #{@content_entry.to_json}
content_entry: #{j @content_entry.to_json.html_safe}
= f.inputs :name => :attributes do
- @content_type.ordered_entries_custom_fields.each_with_index do |field, index|

View File

@ -3,8 +3,8 @@
- content_for :backbone_view_data do
:plain
content_type: #{@content_type.persisted? ? @content_type.to_json : 'null'},
inverse_of_list: #{options_for_content_type_inverse_of.to_json}
content_type: #{j @content_type.persisted? ? @content_type.to_json.html_safe : 'null'},
inverse_of_list: #{j options_for_content_type_inverse_of.to_json.html_safe}
= f.inputs :name => :information do

View File

@ -4,7 +4,7 @@
- content_for :backbone_view_data do
:plain
site: #{@site.to_json(:current_account => current_locomotive_account, :current_site => current_site)},
site: #{j @site.to_json(:current_account => current_locomotive_account, :current_site => current_site).html_safe},
errors: #{@site.errors.to_json}
= f.inputs :name => :information do

View File

@ -38,4 +38,4 @@
- content_for :backbone_view_data do
:plain
, all_#{name}_entries: #{target_content_type.list_or_group_entries.to_json(:depth => 1)}
, all_#{name}_entries: #{j target_content_type.list_or_group_entries.to_json(:depth => 1).html_safe}

View File

@ -2,7 +2,7 @@
- content_for :backbone_view_data do
:plain
account: #{@account.to_json}
account: #{j @account.to_json.html_safe}
- content_for :submenu do
= render_cell 'locomotive/settings_menu', :show

View File

@ -5,7 +5,7 @@
- content_for :backbone_view_data do
:plain
page: #{@page.to_presenter.as_json_for_html_view.to_json}
page: #{j @page.to_presenter.as_json_for_html_view.to_json.html_safe}
- if can?(:manage, @page)

View File

@ -20,8 +20,8 @@
window.content_locale = '#{::Mongoid::Fields::I18n.locale}';
Locomotive.mounted_on = '#{Locomotive.mounted_on}';
Locomotive.current_site = new Locomotive.Models.Site(#{current_site.to_json});
Locomotive.current_account = new Locomotive.Models.Account(#{current_locomotive_account.to_json});
Locomotive.current_site = new Locomotive.Models.Site(#{j current_site.to_json.html_safe});
Locomotive.current_account = new Locomotive.Models.Account(#{j current_locomotive_account.to_json.html_safe});
$(document).ready(function() {

View File

@ -11,14 +11,14 @@ xml.urlset "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do
if page.templatized?
page.content_type.entries.visible.each do |c|
xml.url do
xml.loc page_url(page, { :content => c, :host => true })
xml.loc page_url(page, { :content => c })
xml.lastmod c.updated_at.to_date.to_s('%Y-%m-%d')
xml.priority 0.9
end
end
else
xml.url do
xml.loc page_url(page, { :host => true })
xml.loc page_url(page)
xml.lastmod page.updated_at.to_date.to_s('%Y-%m-%d')
xml.priority 0.9
end

View File

@ -9,6 +9,6 @@
.span-12.last
%p
= submit_tag button_label.is_a?(Symbol) ? t(".#{button_label}") : button_label, :disable_with => t('.disable_with'), :'data-sending-form-message' => t('locomotive.messages.sending_form')
= submit_tag button_label.is_a?(Symbol) ? t(".#{button_label}") : button_label, :'data-disable-with' => t('.disable_with'), :'data-sending-form-message' => t('locomotive.messages.sending_form')
.clear

View File

@ -19,8 +19,8 @@
window.locale = '#{I18n.locale}';
window.content_locale = '#{::Mongoid::Fields::I18n.locale}';
Locomotive.current_site = new Locomotive.Models.Site(#{current_site.to_presenter.as_json_for_html_view.to_json});
Locomotive.current_account = new Locomotive.Models.Account(#{current_locomotive_account.to_json});
Locomotive.current_site = new Locomotive.Models.Site(#{j current_site.to_presenter.as_json_for_html_view.to_json.html_safe});
Locomotive.current_account = new Locomotive.Models.Account(#{j current_locomotive_account.to_json.html_safe});
$(document).ready(function() {

View File

@ -3,7 +3,7 @@
- content_for :backbone_view_data do
:plain
snippet: #{@snippet.persisted? ? @snippet.to_presenter.as_json_for_html_view.to_json : 'null'}
snippet: #{j @snippet.persisted? ? @snippet.to_presenter.as_json_for_html_view.to_json.html_safe : 'null'}
= f.inputs :name => :information do
= f.input :name, :wrapper_html => { :class => 'highlighted' }

View File

@ -3,7 +3,7 @@
- content_for :backbone_view_data do
:plain
theme_asset: #{@theme_asset.persisted? ? @theme_asset.to_json : 'null'}
theme_asset: #{j @theme_asset.persisted? ? @theme_asset.to_json.html_safe : 'null'}
= f.hidden_field :performing_plain_text

View File

@ -16,11 +16,11 @@
- content_for :backbone_view_data do
:plain
snippets: #{can?(:manage, Locomotive::Snippet) ? @snippets.map { |snippet| snippet.to_presenter.as_json_for_html_view }.to_json : 'null'},
images: #{theme_assets_to_json(@assets[:images])},
media: #{theme_assets_to_json(@assets[:media])},
js_and_css_assets: #{can?(:manage, Locomotive::ThemeAsset) ? theme_assets_to_json(@js_and_css_assets) : 'null'},
fonts: #{can?(:manage, Locomotive::ThemeAsset) ? theme_assets_to_json(@assets[:fonts]) : 'null'}
snippets: #{j can?(:manage, Locomotive::Snippet) ? @snippets.map { |snippet| snippet.to_presenter.as_json_for_html_view }.to_json.html_safe : 'null'},
images: #{j theme_assets_to_json(@assets[:images]).html_safe},
media: #{j theme_assets_to_json(@assets[:media]).html_safe},
js_and_css_assets: #{j can?(:manage, Locomotive::ThemeAsset) ? theme_assets_to_json(@js_and_css_assets).html_safe : 'null'},
fonts: #{j can?(:manage, Locomotive::ThemeAsset) ? theme_assets_to_json(@assets[:fonts]).html_safe : 'null'}
- content_for :submenu do
= render_cell 'locomotive/settings_menu', :show

View File

@ -1,5 +1,26 @@
de:
locomotive:
errors:
"500":
title: Anwendungs-Fehler
notice: "Entschuldigung, irgendetwas ist hier schief gelaufen."
link: "&rarr; Zurück zur Anwendung"
"404":
title: Seite nicht gefunden
notice: "Die angefragte Seite existiert nicht."
link: "&rarr; Zurück zur Anwendung"
locales:
en: Englisch
de: Deutsch
fr: Französisch
pt-BR: "Bras. Portugiesisch"
it: Italienisch
nl: Niederländisch
nb: Norwegisch
es: Spanisch
ru: Russisch
buttons:
login: Einloggen
send_password: Senden
@ -9,19 +30,9 @@ de:
delete: "Löschen"
close: "Schließen"
locales:
en: Englisch
de: Deutsch
fr: Französisch
pt-BR: "Bras. Portugiesisch"
it: Italienisch
nl: Niederländisch
"no": Norwegisch
es: Spanisch
ru: Russisch
messages:
confirm: Sind Sie sicher ?
sending_form: "Formular wird gesendet"
shared:
header:
@ -39,8 +50,10 @@ de:
account: Mein Zugang
site: Webseite
theme_assets: Dateien
list:
untranslated: "Nicht übersetzt"
form:
change_file: Ändern
change_file: ändern
delete_file: Löschen
cancel: Abbrechen
form_actions:
@ -48,24 +61,14 @@ de:
create: Neu
update: Speichern
send: Senden
disable_with: "locomotive.disable_with.form_actions"
disable_with: "Transfer..."
footer:
who_is_behind: "Dienst entwickelt von %{development} und entworfen von <a href=\"http://www.sachagreif.com\">Sacha Greif</a> &mdash; <small>version</small> %{version}"
errors:
"500":
title: Anwendungs-Fehler
notice: "Entschuldigung, irgendetwas ist hier schief gelaufen."
link: "&rarr; Zurück zur Anwendung"
"404":
title: Seite nicht gefunden
notice: "Die angefragte Seite existiert nicht."
link: "&rarr; Zurück zur Anwendung"
who_is_behind: "LocomitveCMS - entwickelt von %{development} und entworfen von <a href=\"http://www.sachagreif.com\">Sacha Greif</a> &mdash; <small>version</small> %{version}"
notifications:
new_content_entry:
subject: "[%{type}] neu"
title: "Hi %{name}, nur damit Sie Bescheid wissen! Am %{date} wurde folgende neue Instanz erstellt."
title: "Hi %{name}, nur damit Sie Bescheid wissen, am %{date} wurde eine neue Instanz erstellt."
type: "Model: %{type}"
sites_picker:
@ -157,16 +160,16 @@ de:
sites:
new:
title: Neuer Webseite
help: "Fülle das folgende Formular aus, um einen neuen Webseiteen zu erstellen."
title: Neue Webseite
help: "Füllen Sie das folgende Formular aus, um eine neue Webseite zu erstellen."
domains:
empty: "Bisher sind keine Domains/Hostnamen an diese Seite gebunden. Wenn Sie unten Domains/Hostnamen angeben, achten Sie bitte darauf <b>Ihren DNS Server zu prüfen/aktualisieren.</b>"
current_site:
edit:
new_membership: Account hinzufügen
help: "Der Name des Webseiteen kann durch einen Klick auf den Namen bearbeitet werden."
ask_for_name: "Bitte geben Sie den neuen Namen der Webseite an"
help: "Der Name der Webseiten kann durch einen Klick auf den Namen bearbeitet werden."
ask_for_name: "Bitte geben Sie den neuen Namen der Webseite ein"
memberships:
roles:
@ -195,10 +198,11 @@ de:
new: neue Datei
snippets: Snippets
css_and_js: Style und Javascript
fonts: Fonts
images: Bilder
media: Medien
fonts: Fonts
no_items: "Momentan gibt es keine Dateien. Klicken Sie einfach <a href='%{url}'>hier</a>, um die erste Datei zu erstellen."
quick_upload: Schneller Upload
asset:
updated_at: Aktualisiert am
new:
@ -291,13 +295,15 @@ de:
explanations: "Es ist fast geschafft. Bitte geben Sie Ihrer ersten Webseite einen Namen und wählen Sie eine Sprache aus."
default_site_locale: Sprachauswahl
default_site_locales_hints: Sie können später weitere Sprachen im Einstellungsdialog vornehmen.
next: Webseite erstellen
next: Webseite erstellen
back_to_default_template: "Klicken Sie <a href='#'>hier</a> um die Standardeinstellungen wieder herzustellen"
public:
pages:
show_toolbar:
statuses:
loading: "Lade...."
loading: "Laden..."
disabled: "Inline Editor deaktiviert"
labels:
save_changes: "Änderungen speichern: "

View File

@ -16,7 +16,7 @@ en:
pt-BR: "Brazilian Portuguese"
it: Italian
nl: Dutch
"no": Norwegian
nb: Norwegian
es: Spanish
ru: Russian

View File

@ -20,6 +20,7 @@ es:
messages:
confirm: Por favor confirme
sending_form: "locomotive.messages.sending_form"
shared:
header:
@ -165,7 +166,7 @@ es:
pt-BR: "Portugués (Brasil)"
it: Italiano
nl: holandés
"no": Noruego
nb: Noruego
es: Español
ru: Ruso
ask_for_name: "Por favor escriba su nuevo nombre"

View File

@ -17,7 +17,7 @@ fr:
pt-BR: "Portugais"
it: "Italien"
nl: "Hollandais"
"no": "Norvégien"
nb: "Norvégien"
es: Espagnol
ru: Russe

View File

@ -10,6 +10,7 @@ it:
messages:
confirm: Sicuro?
sending_form: "locomotive.messages.sending_form"
shared:
header:
@ -165,7 +166,7 @@ it:
pt-BR: "Portoghese Brasiliano"
it: Italiano
nl: Olandese
"no": Norvegese
nb: Norvegese
es: Spagnolo
ru: Russo
ask_for_name: "Prego, digita il tuo nome"

View File

@ -1,4 +1,4 @@
"no":
nb:
locomotive:
buttons:
login: Logg inn
@ -6,10 +6,23 @@
change_password: Endre
new_item: "+ ny"
switch_to_site: Vis
delete: "Slett"
delete: Slett
close: Lukk
locales:
en: Engelsk
de: Tysk
fr: Fransk
pt-BR: "Brazilian Portuguese"
it: Italiensk
nl: Nederlandsk
nb: Norsk
es: Spansk
ru: Russisk
messages:
confirm: Er du sikker?
sending_form: Skjemaet blir sendt
shared:
header:
@ -27,14 +40,20 @@
account: Min konto
site: Siden
theme_assets: Temafiler
footer:
who_is_behind: "Tjenesten utviklet av %{development} og designet av <a href=\"http://www.sachagreif.com\">Sacha Greif</a>"
form:
change_file: Endre
delete_file: Slett
cancel: Avbryt
form_actions:
back: Tilbake uten å lagre
create: Opprett
update: Lagre
send: Send
disable_with: "locomotive.disable_with.form_actions"
disable_with: "Vent litt.."
list:
untranslated: ikke oversatt
footer:
who_is_behind: "LocomotiveCMS er utviklet av %{development} og designet av <a href=\"http://www.sachagreif.com\">Sacha Greif</a> &mdash; <small>versjon</small> %{version}"
errors:
"500":
@ -66,9 +85,16 @@
delete_file: Slett fil
has_many:
empty: Tom
index:
is_required: er nødvendig
new_entry: + Legg til ny
many_to_many:
empty: Listen er tom. Legg til ny fra listen nedenfor.
form:
required: Nødvendig
optional: Valgfri
default_label: Feltnavn
select_options:
ask_name: "Skriv inn etiketten for dette valget"
sessions:
new:
@ -105,7 +131,9 @@
help: "Tittelen til siden kan oppdateres ved å klikke på den. For å lagre endringene, klikk på \"Lagre\" -knappen."
ask_for_title: "Endre tittelen"
form:
delete_file: Slett fil
change_file: endre
delete_file: slett fil
cancel: avbryt
default_block: Standard
cache_strategy:
none: Ingen
@ -134,14 +162,14 @@
new:
title: Ny nettside
help: "Fyll ut skjemaet nedenfor for å opprette en ny nettside."
domains:
empty: "Det er ingen domener tilknyttet denne siden ennå. Legg til domenene dine nedenfor. <b>Ikke glem å oppdatere DNS-pekerene.</b>"
current_site:
edit:
export: eksporter
import: importer
new_membership: nytt medlemskap
new_membership: legg til konto
help: "Navnet på siden kan oppdateres ved å trykke på det. For å lagre endringen, trykk på \"Lagre\" -knappen."
ask_for_name: "Rediger navnet"
ask_for_name: "Skriv inn det nye navnet"
memberships:
roles:
@ -161,21 +189,13 @@
edit:
help: "Navnet ditt kan endres ved å trykke på det. For å lagre endringen, trykk på \"Lagre\" -knappen."
new_site: ny nettside
en: Engelsk
de: Tysk
fr: Fransk
pt-BR: "Brasiliansk Portugisisk"
it: Italiensk
nl: Nederlandsk
"no": Norsk
es: Spansk
ru: Russisk
ask_for_name: "Rediger navnet ditt"
theme_assets:
index:
title: Temafiler
help: "I denne seksjonen kan du håndtere alle layout-relaterte filer. Hvis du trenger et bildegalleri så opprett en ny innholdstype i stedet.<br/><b>Advarsel:</b> hvis det er enkelte filer du ikke ser så kan dette skyldes manglende rettigheter."
quick_upload: Hurtigopplasting
new: ny fil
snippets: HTML-snutter
css_and_js: Stilark og javascript
@ -198,29 +218,29 @@
picker_link: Sett inn en fil i koden
choose_file: Velg fil
choose_plain_text: Velg klartekst
images:
title: Bilder
no_items: "Det er ingen filer her ennå."
image_picker:
title: "Sett inn bilde"
no_items: "Det finnes ingen bilder foreløpig."
upload: "Last opp bilder"
assets:
new:
title: New asset
help: "Fill in the form below to create your asset."
edit:
title: Edit asset
help: "Fill in the form below to update your asset."
content_assets:
picker:
title: "Sett inn media"
no_items: "Det finnes ingen media foreløpig."
upload: "Last opp media"
content_types:
index:
new: Ny modell
edit: Rediger modell
new:
title: Ny modell
help: "Lag din egen datamodell (Prosjekter, Ansatte, ...etc). Modellen må ha minst ett felt. Det første feltet vil være obligatorisk for elementer som opprettes av denne innholdstypen."
edit:
title: Rediger modellen
title: Redigerer modell
help: "Modellen må ha minst ett felt. Det første feltet vil være obligatorisk for elementer som opprettes av denne innholdstypen."
show_items: vis elementer
new_item: nytt element
show_entries: vis elementer
new_entry: nytt element
form:
order_by:
created_at: 'Etter dato opprettet'
@ -241,7 +261,7 @@
latest_entries: "Siste elementer"
updated_at: "Sist oppdatert"
list:
no_entries: "Det har ikke blitt opprettet noen elementer her ennå. Klikk <a href=\"%{url}\">her</a> for å opprette det første."
no_entries: "Det finnes ingen elementer her ennå. Klikk <a href=\"%{url}\">her</a> for å opprette det første."
new:
title: '%{type} &mdash; nytt element'
edit:
@ -258,25 +278,6 @@
title: Cross-domain authentication
notice: Du blir sendt videre til nettsiden i løpet av noen få sekunder.
import:
new:
title: Importer en mal for siden
help: "Be careful when you upload a new template for your existing website, your current data could be modified or even removed."
help: "Vær forsiktig hvis du laster opp en ny mal til en ekisterende side. Dataene dine kan bli slettet eller endret."
show:
title: Import pågår
help: "Your site is being updated from the theme zip file you have just uploaded. It lasts a couple of seconds."
help: "Siden blir oppdatert fra temafilen som ble lastet opp. Dette vil ta noen sekunder."
steps:
site: Sideinformasjon
content_types: Tilpassede innholdstyper
assets: Temafiler
snippets: HTML-snutter
pages: Sider
messages:
success: "Importen var velykket og siden har blitt oppdatert."
failure: "Importen kunne ikke gjennomføres."
installation:
common:
title: Locomotive førstegangsoppsett
@ -292,5 +293,21 @@
step_2:
title: "Steg 2/2 &mdash; Lag din første nettside"
explanations: "Hvis du allerede har lastet opp standardmalen (se instruksjoner), så kan du bruke denne med en gang. Du kan også laste opp en sidemal som en zip-fil (gratis maler finnes <a href=\"http://www.locomotivecms.com/support/themes\">her</a>)."
back_to_default_template: "Trykk <a href='#'>her</a> for å velge standardmalen i stedet"
default_site_locale: Språk
default_site_locales_hints: Du kan legge til flere språk under Innstilinger senere
next: Opprett side
public:
pages:
show_toolbar:
statuses:
loading: "Vent litt...."
disabled: "Inline Editor disabled"
labels:
save_changes: "Lagre endringer: "
editing_mode: "Redigeringsmodus: "
lang: "Språk: "
buttons:
back: Tilbake til admin-siden
confirm: Bekreft
cancel: Avbryt

View File

@ -9,6 +9,7 @@ nl:
messages:
confirm: Weet u het zeker ?
sending_form: "locomotive.messages.sending_form"
shared:
header:
@ -153,7 +154,7 @@ nl:
pt-BR: "Braziliaans Portugees"
it: Italiaans
nl: Nederlands
"no": Noors
nb: Noors
ru: Russisch
ask_for_name: "Voer uw nieuwe naam in"

View File

@ -9,6 +9,7 @@ pt-BR:
messages:
confirm: Você tem certeza ?
sending_form: "locomotive.messages.sending_form"
shared:
header:
@ -151,7 +152,7 @@ pt-BR:
pt-BR: "Português do Brasil"
it: Italiano
nl: Holandês
"no": Norueguês
nb: Norueguês
es: Espanhol
ru: Russo
ask_for_name: "Por favor preencha o novo nome"

View File

@ -16,7 +16,7 @@ ru:
pt-BR: "Браз. - Португальский"
it: Итальянский
nl: Голландский
"no": Норвежский
nb: Норвежский
es: Испанский
ru: Русский

View File

@ -1,4 +1,4 @@
"no":
nb:
carrierwave:
errors:
integrity: 'er ikke en tillatt filtype.'

View File

@ -34,10 +34,13 @@ de:
array_too_short: "ist zu kurz (minimale Element-Zahl ist %{count})"
liquid_syntax: "Liquid Syntax-Fehler, bitte überprüfe die Syntax ('%{error}')"
invalid_theme_file: "darf nicht leer sein oder ist keine zip-Datei"
security: "stellt ein Sicherheitproblem dar"
site:
default_locale_removed: Die vorige Standard-Spracheinstellung kann noch nicht gelöscht werden
page:
liquid_syntax: "Liquid Syntax-Fehler, bitte überprüfe die Syntax ('%{error}'/'%{fullpath}')"
liquid_extend: "Die Seite '%{fullpath}' verwendet eine Vorlage, die gar nicht existiert"
liquid_translation: "Die Seite '%{fullpath}' erweitert eine Vorlage die noch nicht übersetzt wurde"
attributes:
defaults:
@ -52,17 +55,24 @@ de:
body: "{% extends 'parent' %}"
mongoid:
errors:
messages:
blank_on_locale: "darf nicht leer sein"
attributes:
page:
title: Titel
parent: Parent
parent: Elternprozess
pared_id: Elternprozess
slug: Slug
listed: Im Menü
templatized: Templatized
published: Veröffentlicht
listed: Im Menü
redirect: Umleitung
redirect_url: Umleitungs-URL
cache_strategy: Cache
response_type: Antworttyp
seo_title: SEO-Titel
content_type:
name: Name
description: Beschreibung

View File

@ -3,6 +3,11 @@ en:
formats:
default: "%m/%d/%Y"
mongoid:
errors:
messages:
blank_on_locale: "can't be blank"
errors:
messages:
domain_taken: "%{value} is already taken"

View File

@ -53,6 +53,10 @@ fr:
body: "{% extends 'parent' %}"
mongoid:
errors:
messages:
blank_on_locale: "doit être rempli(e)"
attributes:
locomotive/page:
title: Titre

View File

@ -1,4 +1,46 @@
"no":
nb:
date:
formats:
default: "%d.%m.%Y"
mongoid:
errors:
messages:
blank_on_locale: "må fylles ut"
errors:
messages:
domain_taken: "%{value} er alerede tatt"
invalid_domain: "%{value} er ugyldig"
needs_admin_account: "Det må være minst en admin-konto"
protected_page: "Du kan ikke fjerne index eller 404 sidene"
extname_changed: "Den nye filen har ikke det opprinnelige filetternavnet"
array_too_short: "er for liten (minimum elementnummer er %{count})"
invalid_theme_file: "må være en zip-fil"
site:
default_locale_removed: Forrige standardspråk kan ikke fjernes før nytt er valgt.
page:
liquid_syntax: "Liquid Syntax error ('%{error}' on '%{fullpath}')"
liquid_extend: "The page '%{fullpath}' extends a template which does not exist"
liquid_translation: "The page '%{fullpath}' extends a template which is not translated"
too_few_custom_fields: "At least, one custom field is required"
security: "presents a security problem"
attributes:
defaults:
pages:
index:
title: "Startside"
body: "Startsidens innholds"
"404":
title: "Siden finnes ikke"
body: "Sidens innhold"
other:
body: "{% extends 'parent' %}"
pagination:
previous: "&laquo; Previous"
next: "Next &raquo;"
support:
array:
words_connector: ", "

View File

@ -1,4 +1,4 @@
"no":
nb:
errors:
messages:
not_found: "finnes ikke"
@ -7,7 +7,7 @@
devise:
failure:
locomotive:
locomotive_account:
unauthenticated: 'Du må logge inn eller registrere deg før du kan fortsette.'
unconfirmed: 'Kontoen må aktiveres før du kan fortsette.'
locked: 'Brukerkontoen er sperret.'
@ -17,34 +17,34 @@
timeout: 'Innloggingen har utløpt. Logg inn på nytt for å fortsette.'
inactive: 'Kontoen din er ikke aktivert ennå.'
sessions:
locomotive:
locomotive_account:
signed_in: 'Du er nå logget inn.'
signed_out: 'Du har blitt logget ut.'
passwords:
locomotive:
locomotive_account:
send_instructions: 'Du vil motta en epost med instruksjoner for hvordan du kan tilbakestille passordet i løpet av kort tid.'
updated: 'Passordendringen var vellykket. Du er nå logget inn.'
confirmations:
locomotive:
locomotive_account:
send_instructions: 'Du vil motta en epost med instrusksjoner for hvordan brukerkontoen aktiveres i løpet av kort tid.'
confirmed: 'Aktiveringen av brukerkontoen var vellykket. Du er nå logget inn.'
registrations:
locomotive:
locomotive_account:
signed_up: 'Registreringen var vellykket.'
updated: 'Oppdateringen av kontoen var vellykket'
destroyed: 'Kontoen din har blitt kansellert. Vi håper å se deg igjen en annen gang.'
unlocks:
locomotive:
locomotive_account:
send_instructions: 'Du vil motta en epost med instruksjoner for hvordan du kan åpne kontoen i løpet av kort tid.'
unlocked: 'Kontoen har blitt åpnet og du er nå logget inn.'
mailer:
locomotive:
locomotive_account:
confirmation_instructions: 'Bekreftelsesinstrukser'
reset_password_instructions: 'Instrukser for tilbakestilling av passord'
unlock_instructions: 'Instrukser for åpning'
locomotive:
locomotive_account:
mailer:
common:
hello: Hallo

View File

@ -1,4 +1,4 @@
"no":
nb:
flash:
locomotive:
pages:

View File

@ -1,4 +1,4 @@
"no":
nb:
formtastic:
titles:
information: Generell informasjon
@ -33,37 +33,40 @@
custom_fields:
field:
name: Alias
import:
new:
source: Fil
samples: Kopier testdata
reset: Reset side
default_site_template: "Bruk standardmalen. Klikk <a href='#'>her</a> for å laste opp en mal fra en zip-fil i stedet."
content_type:
item_template: Elementmal
api_accounts: Varslede kontoer
raw_item_template: Elementmal
public_submission_enabled: Kan opprettes utenfra
public_submission_accounts: Kontoer som varsles
"custom_fields/field":
select_options: "Valg"
content_entry:
_slug: Permalink
account:
edit:
locale: Språk
password: Nytt passord
password_confirmation: Bekreft nytt passord
page:
seo_title: Tittel
target_klass_name: Modell
site:
locales: Språk
hints:
page:
handle: "En unik ID for å hente siden fra en ekstern controller"
published: "Kun autoriserte kontoer kan se ikke-publiserte sider"
cache_strategy: "Buffre siden for å bedre ytelsen. \"Enkel\" er et bra kompromiss."
templatized: "Bruk denne siden som mal for en modell."
listed: "Styr om siden skal vises i de genererte menyene."
content_type_id: "Innholdstypen denne siden skal være mal for."
target_klass_name: "Innholdstypen denne siden skal være mal for."
seo_title: "Definer en egen tittel for siden. Denne blir benyttet av nettleseren. Hvis denne står tom blir standardverdien fra sideinnstillingene benyttet."
meta_keywords: "Overstyr søkemotor-metadata for denne siden. Separer med komma."
meta_description: "Overstyr søkemotorbeskrivelsen for denne siden."
snippet:
slug: "Denne brukes for å inkludere HTML-snutten i en side."
site:
locales: "Dra et flagg til første posisjon for å sette som standard."
seo_title: "Definer en global verdi her som brukes som verdi for titteltaggen i sidens head-seksjon."
meta_keywords: "Meta-nøkkelord blir brukt i head-seksjonen og leses av søkemotorer. Separeres med komma."
meta_description: "Meta-beskrivelse brukes i head-seksjonen og leses av søkemotorer."
@ -79,10 +82,6 @@
source: "Filen er tilgjengelig her: %{url}"
update:
source: "Filen er tilgjengelig her: %{url}"
custom_fields:
field:
name: "Verdi tilgjengelig i liquid-maler"
hint: "Tekst som vises i skjemaet rett under feltet"
content_entry:
_slug: "Verdien brukes til å generere en url for en side som fungerer som en mal for denne innholdstypen (f.eks: \"template_page/{{ your_object._permalink }})\"."
seo_title: "Verdien benyttes til å erstatte sidetittelen for malen knyttet til modellen. Leses av søkemotorer."
@ -93,7 +92,12 @@
samples: "Vil gjøre at importen kopierer innhold og assets"
reset: "Vil gjøre at alle data slettes for den nye siden importeres."
content_type:
item_template: "Du kan justere teksten som vises for hvert element i listen. Bruk liquid, f.eks: {{ entry.name }}"
api_enabled: "Brukes for å la sidebrukere opprette ny elementer (f.eks: meldinger i et kontaktskjema)"
api_accounts: "En varslingsepost vil bli sendt til alle kontoene ovenfor når et nytt element blir opprettet."
name: "Vi foreslår at du bruker flertals form (E.G: Personer)"
slug: "It will be used as the name of the collection in the liquid templates. Ex: <span class='code'>{{ contents.my_projects }}</span>"
raw_item_template: "You can customize the text displayed for each item in the list. Simply use Liquid. Ex: <span class='code'>{{ entry.name }})</span>"
public_submission_enabled: "It is used to let people from outside to create new entries (example: messages in a contact form)"
public_submission_accounts: "If the public submission option is enabled and for each entry created, sends a notification email to the accounts listed above."
"custom_fields/field":
name: "Name of the property for liquid templates. Ex: <span class='code'>&#123;&#123; your_object.&lt;name_of_your_field&gt; &#125;&#125;</span>"
hint: "Text displayed in the model form just below the field"

View File

@ -6,7 +6,9 @@ Locomotive::Engine.routes.draw do
:path => '',
:path_prefix => nil,
:failure_app => 'Locomotive::Devise::FailureApp',
:controllers => { :sessions => 'locomotive/sessions', :passwords => 'locomotive/passwords' } do
:controllers => { :sessions => 'locomotive/sessions', :passwords => 'locomotive/passwords' }
devise_scope :locomotive_account do
match '/' => 'sessions#new'
delete 'signout' => 'sessions#destroy', :as => :destroy_locomotive_session
end
@ -68,6 +70,10 @@ Rails.application.routes.draw do
resources :content_entries, :path => 'content_types/:slug/entries'
resources :sites
resources :memberships
resource :current_site, :controller => 'current_site'
end

View File

@ -0,0 +1,147 @@
Feature: Content Assets
In order to ensure content assets are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have the following content assets:
| id | file |
| 4f832c2cb0d86d3f42fffffe | 5k.png |
| 4f832c2cb0d86d3f42ffffff | 5k_2.png |
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to content_assets.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing content assets
Scenario: Accessing content assets as an Admin
Given I have an "admin" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing content assets as a Designer
Given I have a "designer" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing content assets as an Author
Given I have an "author" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
# showing content asset
Scenario: Accessing content asset as an Admin
Given I have an "admin" API token
When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "filename" should be "5k.png"
Scenario: Accessing content asset as a Designer
Given I have a "designer" API token
When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "filename" should be "5k.png"
Scenario: Accessing content asset as an Author
Given I have an "author" API token
When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "filename" should be "5k.png"
# create content asset
Scenario: Creating new content asset as an Admin
Given I have an "admin" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do a multipart API POST to content_assets.json with base key "content_asset" and:
| source | assets/application.js |
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON at "2/filename" should be "application.js"
Scenario: Creating new content asset as a Designer
Given I have a "designer" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do a multipart API POST to content_assets.json with base key "content_asset" and:
| source | assets/application.js |
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON at "2/filename" should be "application.js"
Scenario: Creating new content asset as an Author
Given I have an "author" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do a multipart API POST to content_assets.json with base key "content_asset" and:
| source | assets/application.js |
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON at "2/filename" should be "application.js"
# update content asset
Scenario: Updating content asset as an Admin
Given I have an "admin" API token
When I do a multipart API PUT to content_assets/4f832c2cb0d86d3f42fffffe.json with base key "content_asset" and:
| source | assets/main.css |
When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "filename" should be "main.css"
Scenario: Updating content asset as a Designer
Given I have a "designer" API token
When I do a multipart API PUT to content_assets/4f832c2cb0d86d3f42fffffe.json with base key "content_asset" and:
| source | assets/main.css |
When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "filename" should be "main.css"
Scenario: Updating content asset as an Author
Given I have a "author" API token
When I do a multipart API PUT to content_assets/4f832c2cb0d86d3f42fffffe.json with base key "content_asset" and:
| source | assets/main.css |
When I do an API GET request to content_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "filename" should be "main.css"
# destroy content asset
Scenario: Destroying content asset as an Admin
Given I have an "admin" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to content_assets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Destroying content asset as a Designer
Given I have a "designer" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to content_assets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Deleting content asset as an Author
Given I have a "author" API token
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to content_assets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_assets.json
Then the JSON response should be an array
And the JSON response should have 1 entry

View File

@ -0,0 +1,202 @@
Feature: Content Entries
In order to ensure content entries are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a custom model named "Projects" with
| label | type | required |
| Name | string | true |
| Description | text | false |
And I have entries for "Projects" with
| id | name | description |
| 4f832c2cb0d86d3f42fffffe | Project 1 | The first project |
| 4f832c2cb0d86d3f42ffffff | Project 2 | The second project |
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to content_types/projects/entries.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing content entries
Scenario: Accessing content entries as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing content entries as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing content entries as an Author
Given I have an "author" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
# showing content entry
Scenario: Accessing content entry as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Project 1"
Scenario: Accessing content entry as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Project 1"
Scenario: Accessing content entry as an Author
Given I have an "author" API token
When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Project 1"
# create content entry
Scenario: Creating new content entry as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API POST to content_types/projects/entries.json with:
"""
{
"content_entry": {
"name": "Project 3",
"description": "The third..."
}
}
"""
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON should have the following:
| 2/name | "Project 3" |
| 2/description | "The third..." |
Scenario: Creating new content entry as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API POST to content_types/projects/entries.json with:
"""
{
"content_entry": {
"name": "Project 3",
"description": "The third..."
}
}
"""
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON should have the following:
| 2/name | "Project 3" |
| 2/description | "The third..." |
Scenario: Creating new content entry as an Author
Given I have an "author" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API POST to content_types/projects/entries.json with:
"""
{
"content_entry": {
"name": "Project 3",
"description": "The third..."
}
}
"""
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON should have the following:
| 2/name | "Project 3" |
| 2/description | "The third..." |
# update content entry
Scenario: Updating content entry as an Admin
Given I have an "admin" API token
When I do an API PUT to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"content_entry": {
"description": "The awesomest project ever!"
}
}
"""
When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Project 1"
And the JSON response at "description" should be "The awesomest project ever!"
Scenario: Updating content entry as a Designer
Given I have a "designer" API token
When I do an API PUT to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"content_entry": {
"description": "The awesomest project ever!"
}
}
"""
When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Project 1"
And the JSON response at "description" should be "The awesomest project ever!"
Scenario: Updating content entry as an Author
Given I have a "author" API token
When I do an API PUT to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"content_entry": {
"description": "The awesomest project ever!"
}
}
"""
When I do an API GET request to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Project 1"
And the JSON response at "description" should be "The awesomest project ever!"
# destroy content entry
Scenario: Destroying content entry as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Destroying content entry as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Deleting content entry as an Author
Given I have a "author" API token
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to content_types/projects/entries/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_types/projects/entries.json
Then the JSON response should be an array
And the JSON response should have 1 entry

View File

@ -0,0 +1,237 @@
Feature: Content Types
In order to ensure content types are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a custom model named "Projects" with id "4f832c2cb0d86d3f42fffffe" and
| label | type | required |
| Name | string | true |
| Description | text | false |
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to content_types.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing content types
Scenario: Accessing content types as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Accessing content types as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Accessing content types as an Author
Given I have an "author" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
# showing content type
Scenario: Accessing content type as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Projects"
Scenario: Accessing content type as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Projects"
Scenario: Accessing content type as an Author
Given I have an "author" API token
When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Projects"
# create content type
Scenario: Creating new content type as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API POST to content_types.json with:
"""
{
"content_type": {
"name": "Employees",
"slug": "employees",
"entries_custom_fields": [
{
"label": "Name",
"name": "name",
"type": "string"
},
{
"label": "Position",
"name": "position",
"type": "string"
}
]
}
}
"""
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 2 entries
And the JSON should have the following:
| 0/name | "Employees" |
| 0/slug | "employees" |
| 0/entries_custom_fields/0/label | "Name" |
| 0/entries_custom_fields/0/name | "name" |
| 0/entries_custom_fields/0/type | "string" |
| 0/entries_custom_fields/1/label | "Position" |
| 0/entries_custom_fields/1/name | "position" |
| 0/entries_custom_fields/1/type | "string" |
Scenario: Creating new content type as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API POST to content_types.json with:
"""
{
"content_type": {
"name": "Employees",
"slug": "employees",
"entries_custom_fields": [
{
"label": "Name",
"name": "name",
"type": "string"
},
{
"label": "Position",
"name": "position",
"type": "string"
}
]
}
}
"""
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 2 entries
And the JSON should have the following:
| 0/name | "Employees" |
| 0/slug | "employees" |
| 0/entries_custom_fields/0/label | "Name" |
| 0/entries_custom_fields/0/name | "name" |
| 0/entries_custom_fields/0/type | "string" |
| 0/entries_custom_fields/1/label | "Position" |
| 0/entries_custom_fields/1/name | "position" |
| 0/entries_custom_fields/1/type | "string" |
Scenario: Creating new content type as an Author
Given I have an "author" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API POST to content_types.json with:
"""
{
"content_type": {
"name": "Employees",
"slug": "employees",
"entries_custom_fields": [
{
"label": "Name",
"name": "name",
"type": "string"
},
{
"label": "Position",
"name": "position",
"type": "string"
}
]
}
}
"""
Then an access denied error should occur
# update content type
Scenario: Updating content type as an Admin
Given I have an "admin" API token
When I do an API PUT to content_types/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"content_type": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Brand new updated name"
Scenario: Updating content type as a Designer
Given I have a "designer" API token
When I do an API PUT to content_types/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"content_type": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to content_types/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Brand new updated name"
Scenario: Updating content type as an Author
Given I have a "author" API token
When I do an API PUT to content_types/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"content_type": {
"name": "Brand new updated name"
}
}
"""
Then an access denied error should occur
# destroy content type
Scenario: Destroying content type as an Admin
Given I have an "admin" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API DELETE to content_types/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 0 entries
Scenario: Destroying content type as a Designer
Given I have a "designer" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API DELETE to content_types/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 0 entries
Scenario: Deleting content type as an Author
Given I have a "author" API token
When I do an API GET request to content_types.json
Then the JSON response should be an array
And the JSON response should have 1 entries
When I do an API DELETE to content_types/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur

View File

@ -0,0 +1,30 @@
Feature: Current Site
In order to ensure the current site can be viewed by all authenticated users
As an admin, designer or author
I should be able to show the current site
Background:
Given I have the site: "test site" set up
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to current_site.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# showing current site
Scenario: Accessing current site as an Admin
Given I have an "admin" API token
When I do an API GET to current_site.json
Then the JSON response at "name" should be "Locomotive test website"
Scenario: Accessing current site as a Designer
Given I have a "designer" API token
When I do an API GET to current_site.json
Then the JSON response at "name" should be "Locomotive test website"
Scenario: Accessing current site as an Author
Given I have an "author" API token
When I do an API GET to current_site.json
Then the JSON response at "name" should be "Locomotive test website"

View File

@ -0,0 +1,225 @@
Feature: Memberships
In order to ensure memberships are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up with id: "4f832c2cb0d86d3f42fffffb"
And I have accounts:
| email | id |
| new-user@a.com | 4f832c2cb0d86d3f42fffffc |
And I have memberships:
| email | role | id |
| admin@a.com | admin | 4f832c2cb0d86d3f42fffffd |
| designer@a.com | designer | 4f832c2cb0d86d3f42fffffe |
| author@a.com | author | 4f832c2cb0d86d3f42ffffff |
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to memberships.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing memberships
Scenario: Accessing memberships as an Admin
Given I have an "admin" API token
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 4 entries
Scenario: Accessing memberships as a Designer
Given I have a "designer" API token
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 4 entries
Scenario: Accessing memberships as an Author
Given I have an "author" API token
When I do an API GET request to memberships.json
Then an access denied error should occur
# showing membership
Scenario: Accessing membership as an Admin
Given I have an "admin" API token
When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffd.json
Then the JSON response at "email" should be "admin@a.com"
When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "email" should be "designer@a.com"
When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json
Then the JSON response at "email" should be "author@a.com"
Scenario: Accessing membership as a Designer
Given I have a "designer" API token
When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffd.json
Then the JSON response at "email" should be "admin@a.com"
When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "email" should be "designer@a.com"
When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json
Then the JSON response at "email" should be "author@a.com"
Scenario: Accessing membership as an Author
Given I have an "author" API token
When I do an API GET request to memberships/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur
# create membership
Scenario: Creating new membership as an Admin
Given I have an "admin" API token
When I do an API POST to memberships.json with:
"""
{
"membership": {
"site_id": "4f832c2cb0d86d3f42fffffb",
"account_id": "4f832c2cb0d86d3f42fffffc"
}
}
"""
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 5 entries
Scenario: Creating new membership as a Designer
Given I have a "designer" API token
When I do an API POST to memberships.json with:
"""
{
"membership": {
"site_id": "4f832c2cb0d86d3f42fffffb",
"account_id": "4f832c2cb0d86d3f42fffffc"
}
}
"""
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 5 entries
Scenario: Creating new membership as an Author
Given I have an "author" API token
When I do an API POST to memberships.json with:
"""
{
"membership": {
"site_id": "4f832c2cb0d86d3f42fffffb",
"account_id": "4f832c2cb0d86d3f42fffffc"
}
}
"""
Then an access denied error should occur
Scenario: Created membership should always be Author
Given I have an "admin" API token
When I do an API POST to memberships.json with:
"""
{
"membership": {
"site_id": "4f832c2cb0d86d3f42fffffb",
"account_id": "4f832c2cb0d86d3f42fffffc",
"role": "admin"
}
}
"""
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 5 entries
And the JSON at "4/role" should be "author"
# update membership
Scenario: Updating membership as an Admin
Given I have an "admin" API token
When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"membership": {
"role": "admin"
}
}
"""
When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json
Then the JSON response at "role" should be "admin"
Scenario: Updating membership as a Designer
Given I have a "designer" API token
When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"membership": {
"role": "admin"
}
}
"""
When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json
Then the JSON response at "role" should be "author"
When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"membership": {
"role": "designer"
}
}
"""
When I do an API GET request to memberships/4f832c2cb0d86d3f42ffffff.json
Then the JSON response at "role" should be "designer"
Scenario: Updating membership as an Author
Given I have a "author" API token
When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"membership": {
"role": "admin"
}
}
"""
Then an access denied error should occur
When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"membership": {
"role": "designer"
}
}
"""
Then an access denied error should occur
When I do an API PUT to memberships/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"membership": {
"role": "author"
}
}
"""
Then an access denied error should occur
# destroy membership
Scenario: Destroying membership as an Admin
Given I have an "admin" API token
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API DELETE to memberships/4f832c2cb0d86d3f42ffffff.json
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 3 entries
Scenario: Destroying membership as a Designer
Given I have a "designer" API token
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API DELETE to memberships/4f832c2cb0d86d3f42ffffff.json
When I do an API GET request to memberships.json
Then the JSON response should be an array
And the JSON response should have 3 entries
When I do an API DELETE to memberships/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur
When I do an API DELETE to memberships/4f832c2cb0d86d3f42fffffd.json
Then an access denied error should occur
Scenario: Deleting membership as an Author
Given I have a "author" API token
When I do an API DELETE to memberships/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur

View File

@ -0,0 +1,187 @@
Feature: Pages
In order to ensure pages are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And I have a custom model named "Projects" with
| label | type | required |
| Name | string | true |
| Description | text | false |
And I have a designer and an author
And a page named "hello-world" with id "4f832c2cb0d86d3f42fffffe"
And a page named "goodbye-world" with id "4f832c2cb0d86d3f42ffffff"
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to pages.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing pages
Scenario: Accessing pages as an Admin
Given I have an "admin" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
Scenario: Accessing pages as a Designer
Given I have a "designer" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
Scenario: Accessing pages as an Author
Given I have an "author" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
# showing page
Scenario: Accessing page as an Admin
Given I have an "admin" API token
When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "slug" should be "hello-world"
Scenario: Accessing page as a Designer
Given I have a "designer" API token
When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "slug" should be "hello-world"
Scenario: Accessing page as an Author
Given I have an "author" API token
When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "slug" should be "hello-world"
# create page
Scenario: Creating new page as an Admin
Given I have an "admin" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API POST to pages.json with:
"""
{
"page": {
"title": "New Page",
"slug": "new-page",
"parent_id": "4f832c2cb0d86d3f42fffffe"
}
}
"""
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 5 entries
Scenario: Creating new page as a Designer
Given I have a "designer" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API POST to pages.json with:
"""
{
"page": {
"title": "New Page",
"slug": "new-page",
"parent_id": "4f832c2cb0d86d3f42fffffe"
}
}
"""
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 5 entries
Scenario: Creating new page as an Author
Given I have an "author" API token
When I do an API POST to pages.json with:
"""
{
"page": {
"title": "New Page",
"slug": "new-page",
"parent_id": "4f832c2cb0d86d3f42fffffe"
}
}
"""
Then an access denied error should occur
# update page
Scenario: Updating page as an Admin
Given I have an "admin" API token
When I do an API PUT to pages/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"page": {
"title": "Brand new updated title"
}
}
"""
When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "title" should be "Brand new updated title"
Scenario: Updating page as a Designer
Given I have a "designer" API token
When I do an API PUT to pages/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"page": {
"title": "Brand new updated title"
}
}
"""
When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "title" should be "Brand new updated title"
Scenario: Updating page as an Author
Given I have a "author" API token
When I do an API PUT to pages/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"page": {
"title": "Brand new updated title"
}
}
"""
When I do an API GET request to pages/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "title" should be "Brand new updated title"
# destroy page
Scenario: Destroying page as an Admin
Given I have an "admin" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API DELETE to pages/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 3 entries
Scenario: Destroying page as a Designer
Given I have a "designer" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API DELETE to pages/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 3 entries
Scenario: Deleting page as an Author
Given I have a "author" API token
When I do an API GET request to pages.json
Then the JSON response should be an array
And the JSON response should have 4 entries
When I do an API DELETE to pages/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur

View File

@ -0,0 +1,206 @@
Feature: Sites
In order to ensure sites are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up with id: "4f832c2cb0d86d3f42fffffe"
And I have the site: "another site" set up with id: "4f832c2cb0d86d3f42ffffff"
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to sites.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing sites
Scenario: Accessing sites as an Admin
Given I have an "admin" API token
When I do an API GET request to sites.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing sites as a Designer
Given I have a "designer" API token
When I do an API GET request to sites.json
Then an access denied error should occur
Scenario: Accessing sites as an Author
Given I have an "author" API token
When I do an API GET request to sites.json
Then an access denied error should occur
# showing site
Scenario: Accessing site as an Admin
Given I have an "admin" API token
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Locomotive test website"
Scenario: Accessing my site as a Designer
Given I have a "designer" API token
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Locomotive test website"
Scenario: Accessing other site as a Designer
Given I have a "designer" API token
When I do an API GET request to sites/4f832c2cb0d86d3f42ffffff.json
Then an access denied error should occur
Scenario: Accessing my site as an Author
Given I have an "author" API token
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Locomotive test website"
Scenario: Accessing other site as an Author
Given I have an "author" API token
When I do an API GET request to sites/4f832c2cb0d86d3f42ffffff.json
Then an access denied error should occur
# create site
Scenario: Creating new site as an Admin
Given I have an "admin" API token
When I do an API GET request to sites.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API POST to sites.json with:
"""
{
"site": {
"name": "New site",
"subdomain": "new-site"
}
}
"""
When I do an API GET request to sites.json
Then the JSON response should be an array
And the JSON response should have 3 entries
Scenario: Creating new site as a Designer
Given I have a "designer" API token
When I do an API POST to sites.json with:
"""
{
"site": {
"name": "New site",
"subdomain": "new-site"
}
}
"""
Then an access denied error should occur
Scenario: Creating new site as an Author
Given I have an "author" API token
When I do an API POST to sites.json with:
"""
{
"site": {
"name": "New site",
"subdomain": "new-site"
}
}
"""
Then an access denied error should occur
# update site
Scenario: Updating site as an Admin
Given I have an "admin" API token
When I do an API PUT to sites/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"site": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Brand new updated name"
Scenario: Updating my site as a Designer
Given I have a "designer" API token
When I do an API PUT to sites/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"site": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Brand new updated name"
Scenario: Updating other site as a Designer
Given I have a "designer" API token
When I do an API PUT to sites/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"site": {
"name": "Brand new updated name"
}
}
"""
Then an access denied error should occur
Scenario: Updating my site as an Author
Given I have a "author" API token
When I do an API PUT to sites/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"site": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "Brand new updated name"
Scenario: Updating other site as an Author
Given I have a "author" API token
When I do an API PUT to sites/4f832c2cb0d86d3f42ffffff.json with:
"""
{
"site": {
"name": "Brand new updated name"
}
}
"""
Then an access denied error should occur
# destroy site
Scenario: Destroying site as an Admin
Given I have an "admin" API token
When I do an API GET request to sites.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to sites/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to sites.json
Then the JSON response should be an array
And the JSON response should have 1 entries
Scenario: Destroying my site as a Designer
Given I have a "designer" API token
When I do an API DELETE to sites/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to sites/4f832c2cb0d86d3f42fffffe.json
Then it should not exist
Scenario: Deleting other site as a Designer
Given I have a "designer" API token
When I do an API DELETE to sites/4f832c2cb0d86d3f42ffffff.json
Then an access denied error should occur
Scenario: Deleting my site as an Author
Given I have a "author" API token
When I do an API DELETE to sites/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur
Scenario: Deleting other site as an Author
Given I have a "author" API token
When I do an API DELETE to sites/4f832c2cb0d86d3f42ffffff.json
Then an access denied error should occur

View File

@ -0,0 +1,179 @@
Feature: Snippets
In order to ensure snippets are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And a snippet named "My Snippet" with id "4f832c2cb0d86d3f42fffffe" and template:
"""
My Snippet
"""
And I have a designer and an author
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to snippets.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing content types
Scenario: Accessing snippets as an Admin
Given I have an "admin" API token
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Accessing snippets as a Designer
Given I have a "designer" API token
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
Scenario: Accessing snippets as an Author
Given I have an "author" API token
When I do an API GET request to snippets.json
Then an access denied error should occur
# showing snippet
Scenario: Accessing snippet as an Admin
Given I have an "admin" API token
When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "My Snippet"
Scenario: Accessing snippet as a Designer
Given I have a "designer" API token
When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "id" should be "4f832c2cb0d86d3f42fffffe"
And the JSON response at "name" should be "My Snippet"
Scenario: Accessing snippet as an Author
Given I have an "author" API token
When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur
# create snippet
Scenario: Creating new snippet as an Admin
Given I have an "admin" API token
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API POST to snippets.json with:
"""
{
"snippet": {
"name": "Another snippet",
"template": "<h1>Another Snippet!</h1>"
}
}
"""
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
And the JSON should have the following:
| 0/name | "Another Snippet" |
| 0/template | "<h1>Another Snippet!</h1>" |
Scenario: Creating new snippet as a Designer
Given I have a "designer" API token
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API POST to snippets.json with:
"""
{
"snippet": {
"name": "Another snippet",
"template": "<h1>Another Snippet!</h1>"
}
}
"""
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
And the JSON should have the following:
| 0/name | "Another Snippet" |
| 0/template | "<h1>Another Snippet!</h1>" |
Scenario: Creating new snippet as an Author
Given I have an "author" API token
When I do an API POST to snippets.json with:
"""
{
"snippet": {
"name": "Another snippet",
"template": "<h1>Another Snippet!</h1>"
}
}
"""
Then an access denied error should occur
# update snippet
Scenario: Updating snippet as an Admin
Given I have an "admin" API token
When I do an API PUT to snippets/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"snippet": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Brand new updated name"
Scenario: Updating snippet as a Designer
Given I have a "designer" API token
When I do an API PUT to snippets/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"snippet": {
"name": "Brand new updated name"
}
}
"""
When I do an API GET request to snippets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "name" should be "Brand new updated name"
Scenario: Updating snippet as an Author
Given I have a "author" API token
When I do an API PUT to snippets/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"snippet": {
"name": "Brand new updated name"
}
}
"""
Then an access denied error should occur
# destroy snippet
Scenario: Destroying snippet as an Admin
Given I have an "admin" API token
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API DELETE to snippets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 0 entries
Scenario: Destroying snippet as a Designer
Given I have a "designer" API token
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 1 entry
When I do an API DELETE to snippets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to snippets.json
Then the JSON response should be an array
And the JSON response should have 0 entries
Scenario: Deleting snippet as an Author
Given I have a "author" API token
When I do an API DELETE to snippets/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur

View File

@ -0,0 +1,185 @@
Feature: Theme Assets
In order to ensure theme assets are not tampered with
As an admin, designer or author
I will be restricted based on my role
Background:
Given I have the site: "test site" set up
And a javascript asset named "my_javascript.js" with id "4f832c2cb0d86d3f42fffffe"
And a stylesheet asset named "my_stylesheet.css" with id "4f832c2cb0d86d3f42ffffff"
Scenario: As an unauthenticated user
Given I am not authenticated
When I do an API GET to theme_assets.json
Then the JSON response at "error" should be "You need to sign in or sign up before continuing."
# listing theme assets
Scenario: Accessing theme assets as an Admin
Given I have an "admin" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing theme assets as a Designer
Given I have a "designer" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
Scenario: Accessing theme assets as an Author
Given I have an "author" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
# showing theme asset
Scenario: Accessing theme asset as an Admin
Given I have an "admin" API token
When I do an API GET request to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "local_path" should be "my_javascript.js"
Scenario: Accessing theme asset as a Designer
Given I have a "designer" API token
When I do an API GET request to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "local_path" should be "my_javascript.js"
Scenario: Accessing theme asset as an Author
Given I have an "author" API token
When I do an API GET request to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response at "local_path" should be "my_javascript.js"
# create theme asset
Scenario: Creating new theme asset as an Admin
Given I have an "admin" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API POST to theme_assets.json with:
"""
{
"theme_asset": {
"plain_text_name": "new-javascript.js",
"plain_text": "function doNothing() {}",
"plain_text_type": "javascript",
"performing_plain_text": "true"
}
}
"""
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON should have the following:
| 2/local_path | "new-javascript.js" |
| 2/content_type | "javascript" |
Scenario: Creating new theme asset as a Designer
Given I have a "designer" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API POST to theme_assets.json with:
"""
{
"theme_asset": {
"plain_text_name": "new-javascript.js",
"plain_text": "function doNothing() {}",
"plain_text_type": "javascript",
"performing_plain_text": "true"
}
}
"""
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 3 entries
And the JSON should have the following:
| 2/local_path | "new-javascript.js" |
| 2/content_type | "javascript" |
Scenario: Creating new theme asset as an Author
Given I have an "author" API token
When I do an API POST to theme_assets.json with:
"""
{
"theme_asset": {
"plain_text_name": "new-javascript.js",
"plain_text": "function doNothing() {}",
"plain_text_type": "javascript",
"performing_plain_text": "true"
}
}
"""
Then an access denied error should occur
# update theme asset
Scenario: Updating theme asset as an Admin
Given I have an "admin" API token
When I do an API PUT to theme_assets/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"theme_asset": {
"plain_text_name": "newer-javascript.js"
}
}
"""
When I do an API GET request to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response should have the following:
| local_path | "newer-javascript.js" |
Scenario: Updating theme asset as a Designer
Given I have a "designer" API token
When I do an API PUT to theme_assets/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"theme_asset": {
"plain_text_name": "newer-javascript.js"
}
}
"""
When I do an API GET request to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response should have the following:
| local_path | "newer-javascript.js" |
Scenario: Updating theme asset as an Author
Given I have a "author" API token
When I do an API PUT to theme_assets/4f832c2cb0d86d3f42fffffe.json with:
"""
{
"theme_asset": {
"plain_text_name": "newer-javascript.js"
}
}
"""
When I do an API GET request to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then the JSON response should have the following:
| local_path | "newer-javascript.js" |
# destroy theme asset
Scenario: Destroying theme asset as an Admin
Given I have an "admin" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to theme_assets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 1 entries
Scenario: Destroying theme asset as a Designer
Given I have a "designer" API token
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 2 entries
When I do an API DELETE to theme_assets/4f832c2cb0d86d3f42fffffe.json
When I do an API GET request to theme_assets.json
Then the JSON response should be an array
And the JSON response should have 1 entries
Scenario: Deleting theme asset as an Author
Given I have a "author" API token
When I do an API DELETE to theme_assets/4f832c2cb0d86d3f42fffffe.json
Then an access denied error should occur

View File

@ -4,6 +4,7 @@ Feature: Contact form
I want to be able to send them a message
Background:
Given I enable the CSRF protection for public submission requests
Given I have the site: "test site" set up
And I have a custom model named "Messages" with
| label | type | required |
@ -16,6 +17,7 @@ Feature: Contact form
<head></head>
<body>
<form action="{{ contents.messages.public_submission_url }}" method="post">
{% csrf_param %}
<input type="hidden" value="/success" name="success_callback" />
<input type="hidden" value="/contact" name="error_callback" />
<label for="email">E-Mail Address</label>
@ -55,6 +57,20 @@ Feature: Contact form
And I press "Submit"
Then I should see "Thanks did@locomotivecms.com"
Scenario: Can not send a message if the csrf tag is missing
Given I delete the following code "{% csrf_param %}" from the "contact" page
When I view the rendered page at "/contact"
And I press "Submit"
Then I should see "Content of the home page"
Scenario: Can send a message if the csrf protection is disabled
Given I disable the CSRF protection for public submission requests
And I view the rendered page at "/contact"
And I fill in "E-Mail Address" with "did@locomotivecms.com"
And I fill in "Message" with "LocomotiveCMS rocks"
And I press "Submit"
Then I should see "Thanks did@locomotivecms.com"
Scenario: Display errors
When I view the rendered page at "/contact"
And I fill in "Message" with "LocomotiveCMS rocks"

View File

@ -1,3 +1,26 @@
def api_base_url
"http://#{Locomotive::Site.first.domains.first}/locomotive/api/"
end
def do_api_request(type, url, param_string_or_hash = nil)
begin
if param_string_or_hash
if param_string_or_hash.is_a? Hash
params = param_string_or_hash
else
params = JSON.parse(param_string_or_hash)
end
else
params = {}
end
@json_response = do_request(type, api_base_url, url,
params.merge({ 'CONTENT_TYPE' => 'application/json' }))
rescue CanCan::AccessDenied, Mongoid::Errors::DocumentNotFound
@error = $!
end
end
def last_json
@json_response.try(:body) || page.source
end
@ -11,13 +34,16 @@ Given /^I have an? "([^"]*)" API token$/ do |role|
'password' => 'easyone'
}
response = post("http://#{@site.domains.first}/locomotive/api/tokens.json", login_params.to_json, { 'CONTENT_TYPE' => 'application/json' })
response = do_request('POST', api_base_url, 'tokens.json',
login_params.merge({ 'CONTENT_TYPE' => 'application/json' }))
if response.status == 200
@auth_token = JSON.parse(response.body)['token']
else
raise JSON.parse(response.body)['message']
end
add_default_params(:auth_token => @auth_token)
end
Given /^I do not have an API token$/ do
@ -35,4 +61,36 @@ end
When /^I post to "([^"]*)" with:$/ do |path, json_string|
@json_response = post("http://#{@site.domains.first}#{path}", json_string, { 'CONTENT_TYPE' => 'application/json' })
end
end
When /^I do an API (\w+) (?:request )?to ([\w.\/]+)$/ do |request_type, url|
do_api_request(request_type, url)
end
When /^I do an API (\w+) (?:request )?to ([\w.\/]+) with:$/ do |request_type, url, param_string|
do_api_request(request_type, url, param_string)
end
Then /^an access denied error should occur$/ do
@error.should_not be_nil
@error.is_a?(CanCan::AccessDenied).should be_true
end
Then /^it should not exist$/ do
@error.should_not be_nil
@error.is_a?(Mongoid::Errors::DocumentNotFound).should be_true
end
When /^I do a multipart API (\w+) (?:request )?to ([\w.\/]+) with base key "([^"]*)" and:$/ \
do |request_type, url, base_key, table|
params = {}
params = table.rows_hash
params.each do |key, filename|
params[key] = Rack::Test::UploadedFile.new(Rails.root.join('..', 'fixtures', filename))
end
do_api_request(request_type, url, { base_key => params })
end
Then /^I print the json response$/ do
puts %{JSON: "#{last_json}"}
end

View File

@ -0,0 +1,12 @@
Given /^I have the following content assets:$/ do |table|
site = Locomotive::Site.first
table.hashes.each do |asset_hash|
asset_hash['site'] = site
asset_hash['source'] = FixturedAsset.open(asset_hash['file'])
asset_hash.delete('file')
asset = FactoryGirl.build(:asset, asset_hash)
asset.save.should be_true
end
end

View File

@ -1,6 +1,9 @@
Given %r{^I have a custom model named "([^"]*)" with$} do |name, fields|
def build_content_type(name)
site = Locomotive::Site.first
content_type = FactoryGirl.build(:content_type, :site => site, :name => name, :order_by => '_position')
FactoryGirl.build(:content_type, :site => site, :name => name, :order_by => '_position')
end
def set_custom_fields_from_table(content_type, fields)
fields.hashes.each do |field|
# found a belongs_to association
if field['type'] == 'belongs_to'
@ -12,6 +15,19 @@ Given %r{^I have a custom model named "([^"]*)" with$} do |name, fields|
content_type.entries_custom_fields.build field
end
end
Given %r{^I have a custom model named "([^"]*)" with id "([^"]*)" and$} do |name, id, fields|
content_type = build_content_type(name)
content_type.id = BSON::ObjectId(id)
set_custom_fields_from_table(content_type, fields)
content_type.valid?
content_type.save.should be_true
end
Given %r{^I have a custom model named "([^"]*)" with$} do |name, fields|
content_type = build_content_type(name)
set_custom_fields_from_table(content_type, fields)
content_type.valid?
content_type.save.should be_true
end

View File

@ -0,0 +1,19 @@
Given /^I have accounts:$/ do |accounts_table|
accounts_table.hashes.each do |account_hash|
FactoryGirl.create(:account, account_hash)
end
end
Given /^I have memberships:$/ do |members_table|
members_table.hashes.each do |member_hash|
email = member_hash[:email]
account = Locomotive::Account.where(:email => email).first \
|| FactoryGirl.create(:account, :email => email)
member_hash.delete(:email)
member_hash.merge!({ :account => account, :site => @site })
FactoryGirl.create(:membership, member_hash)
end
end

View File

@ -38,4 +38,19 @@ end
When /^I reload the page$/ do
visit current_path
end
end
Given /^I enable the CSRF protection for public submission requests$/ do
Locomotive.config.csrf_protection = true
Locomotive::Public::ContentEntriesController.any_instance.stubs(:protect_against_forgery?).returns(true)
end
Given /^I disable the CSRF protection for public submission requests$/ do
Locomotive.config.csrf_protection = false
# pending # express the regexp above with the code you wish you had
end
Then /^it returns a (\d+) error page$/ do |code|
puts page.status_code
page.status_code.should == code.to_i
end

View File

@ -2,9 +2,16 @@
# helps create a simple content page (parent: "index") with a slug, contents, and template
def create_content_page(page_slug, page_contents, template = nil)
@home = @site.pages.where(:slug => "index").first || FactoryGirl.create(:page)
page = @site.pages.create(:slug => page_slug, :body => page_contents, :parent => @home, :title => "some title", :published => true, :raw_template => template)
page = new_content_page(page_slug, page_contents, template)
page.should be_valid
page.save!
page
end
# build page without saving
def new_content_page(page_slug, page_contents, template = nil)
@home = @site.pages.where(:slug => "index").first || FactoryGirl.create(:page)
page = @site.pages.new(:slug => page_slug, :body => page_contents, :parent => @home, :title => "some title", :published => true, :raw_template => template)
page
end
@ -17,6 +24,12 @@ Given /^a page named "([^"]*)" with the template:$/ do |page_slug, template|
@page = create_content_page(page_slug, '', template)
end
Given /^a page named "([^"]*)" with id "([^"]*)"$/ do |page_slug, id|
@page = new_content_page(page_slug, '')
@page.id = BSON::ObjectId(id)
@page.save!
end
# change the title
When /^I change the page title to "([^"]*)"$/ do |page_title|
page.evaluate_script "window.prompt = function() { return '#{page_title}'; }"
@ -35,10 +48,16 @@ When /^I update the "([^"]*)" page with the template:$/ do |page_slug, template|
page.save!
end
Given /^I delete the following code "([^"]*)" from the "([^"]*)" page$/ do |code, page_slug|
page = @site.pages.where(:slug => page_slug).first
page.raw_template = page.raw_template.gsub(code, '')
page.save!
end
# try to render a page by slug
When /^I view the rendered page at "([^"]*)"$/ do |path|
# If we're running selenium then we need to use a differnt port
if Capybara.current_driver == :selenium
# If we're running poltergeist then we need to use a different port
if Capybara.current_driver == :poltergeist
visit "http://#{@site.domains.first}:#{Capybara.server_port}#{path}"
else
visit "http://#{@site.domains.first}#{path}"

View File

@ -1,9 +1,13 @@
### Snippets
# helps create a simple snippet with a slug and template
def new_snippet(name, template = nil)
@site.snippets.new(:name => name, :template => template)
end
def create_snippet(name, template = nil)
snippet = @site.snippets.create(:name => name, :template => template)
snippet.should be_valid
snippet = new_snippet(name, template)
snippet.save!
snippet
end
@ -13,6 +17,12 @@ Given /^a snippet named "([^"]*)" with the template:$/ do |name, template|
@snippet = create_snippet(name, template)
end
Given /^a snippet named "([^"]*)" with id "([^"]*)" and template:$/ do |name, id, template|
@snippet = new_snippet(name, template)
@snippet.id = BSON::ObjectId(id)
@snippet.save!
end
When /^I change the snippet template to "([^"]*)"$/ do |code|
page.evaluate_script "window.application_view.view.editor.setValue('#{code}')"
end

View File

@ -1,15 +1,18 @@
### Theme assets
# helps create a theme asset
def create_plain_text_asset(name, type)
asset = FactoryGirl.build(:theme_asset, {
def new_plain_text_asset(name, type)
FactoryGirl.build(:theme_asset, {
:site => @site,
:plain_text_name => name,
:plain_text => 'Lorem ipsum',
:plain_text_type => type,
:performing_plain_text => true
})
end
def create_plain_text_asset(name, type)
asset = new_plain_text_asset(name, type)
asset.save!
end
@ -19,10 +22,22 @@ Given /^a javascript asset named "([^"]*)"$/ do |name|
@asset = create_plain_text_asset(name, 'javascript')
end
Given /^a javascript asset named "([^"]*)" with id "([^"]*)"$/ do |name, id|
@asset = new_plain_text_asset(name, 'javascript')
@asset.id = BSON::ObjectId(id)
@asset.save!
end
Given /^a stylesheet asset named "([^"]*)"$/ do |name|
@asset = create_plain_text_asset(name, 'stylesheet')
end
Given /^a stylesheet asset named "([^"]*)" with id "([^"]*)"$/ do |name, id|
@asset = new_plain_text_asset(name, 'stylesheet')
@asset.id = BSON::ObjectId(id)
@asset.save!
end
Given /^I have an image theme asset named "([^"]*)"$/ do |name|
@asset = FactoryGirl.create(:theme_asset, :site => @site, :source => File.open(Rails.root.join('..', 'fixtures', 'assets', '5k.png')))
@asset.source_filename = name

View File

@ -18,6 +18,7 @@ require 'capybara'
require 'capybara/rails'
require 'capybara/cucumber'
require 'capybara/session'
require 'capybara/poltergeist'
require 'json_spec/cucumber'
@ -44,7 +45,7 @@ end
Capybara.default_wait_time = 5
# Capybara.javascript_driver = :rack_test
Capybara.javascript_driver = :poltergeist
# Stop endless errors like
# ~/.rvm/gems/ruby-1.9.2-p0@global/gems/rack-1.2.1/lib/rack/utils.rb:16:

22
features/support/http.rb Normal file
View File

@ -0,0 +1,22 @@
module HTTPHelpers
attr_accessor :default_params
def add_default_params(params)
default_params.merge!(params)
end
def do_request(type, base_url, url, params)
request_method = type.downcase.to_sym
send(request_method, "#{base_url}/#{url}", default_params.merge(params))
end
protected
def default_params
@default_params ||= {}
end
end
World(HTTPHelpers)

View File

@ -22,11 +22,11 @@ Locomotive.configure do |config|
# :max_content_types => 4
# }
# default locale (for now, only en, de, fr, pt-BR and it are supported)
# default locale (for now, only en, de, fr, pt-BR, it and nb are supported)
config.default_locale = :en
# available locales suggested to "localize" a site. You will have to pick up at least one among that list.
# config.site_locales = %w{en de fr pt-BR it nl no es ru}
# config.site_locales = %w{en de fr pt-BR it nl nb es ru}
# tell if logs are enabled. Useful for debug purpose.
config.enable_logs = true
@ -48,6 +48,13 @@ Locomotive.configure do |config|
# add extra classes other than the defined content types among a site which will potentially used by the templatized pages.
# config.models_for_templatization = %w(Product)
# "Public" forms can be protected from Cross-Site Request Forgery (CSRF) attacks.
# By default, that protection is disabled (false) in order to keep backwards compatibility with the existing public forms.
#
# Note: we strongly recommend to enable it. See the documentation about the "csrf_param" liquid tag.
#
# config.csrf_protection = true
# Rack-cache settings, mainly used for the inline resizing image module. Default options:
# config.rack_cache = {
# :verbose => true,

View File

@ -1,6 +1,7 @@
require 'locomotive/version'
require 'locomotive/core_ext'
require 'locomotive/configuration'
require 'locomotive/devise'
require 'locomotive/logger'
require 'locomotive/haml'
require 'locomotive/formtastic'
@ -12,6 +13,7 @@ require 'locomotive/carrierwave'
require 'locomotive/custom_fields'
require 'locomotive/httparty'
require 'locomotive/action_controller'
require 'locomotive/rails'
require 'locomotive/routing'
require 'locomotive/regexps'
require 'locomotive/render'

View File

@ -7,8 +7,8 @@ module Locomotive
:reserved_subdomains => %w{www admin email blog webmail mail support help site sites},
# :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api},
:reserved_slugs => %w{stylesheets javascripts assets admin locomotive images api pages edit},
:locales => %w{en de fr pt-BR it nl no es ru},
:site_locales => %w{en de fr pt-BR it nl no es ru},
:locales => %w{en de fr pt-BR it nl nb es ru},
:site_locales => %w{en de fr pt-BR it nl nb es ru},
:cookie_key => '_locomotive_session',
:enable_logs => false,
:delayed_job => false,
@ -27,7 +27,8 @@ module Locomotive
},
:devise_modules => [:rememberable, :database_authenticatable, :token_authenticatable, :recoverable, :trackable, :validatable, :encryptable, { :encryptor => :sha1 }],
:context_assign_extensions => { },
:models_for_templatization => []
:models_for_templatization => [],
:csrf_protection => false
}
cattr_accessor :settings

View File

@ -22,6 +22,7 @@ module CustomFields
module Types
module File
class FileUploader < ::CarrierWave::Uploader::Base
# Set correct paths
@ -34,6 +35,7 @@ module CustomFields
end
end
end
end

View File

@ -3,6 +3,7 @@ require 'mongoid/railtie'
require 'mongoid/tree'
require 'devise'
require 'devise/orm/mongoid'
require 'devise-encryptable'
require 'kaminari'
require 'haml'
require 'liquid'

Some files were not shown because too many files have changed in this diff Show More