alchemy_cms 2.0.rc6 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.travis.yml +2 -1
  2. data/LICENSE +24 -619
  3. data/README.md +38 -36
  4. data/alchemy_cms.gemspec +1 -1
  5. data/app/controllers/admin/elements_controller.rb +3 -2
  6. data/app/controllers/admin/pages_controller.rb +20 -14
  7. data/app/controllers/admin/trash_controller.rb +4 -0
  8. data/app/controllers/pages_controller.rb +27 -16
  9. data/app/helpers/admin/elements_helper.rb +55 -0
  10. data/app/helpers/alchemy_helper.rb +0 -45
  11. data/app/helpers/elements_helper.rb +8 -1
  12. data/app/helpers/pages_helper.rb +0 -10
  13. data/app/models/element.rb +39 -5
  14. data/app/models/page.rb +27 -7
  15. data/app/sweepers/pages_sweeper.rb +0 -9
  16. data/app/views/admin/pages/update.js.erb +15 -1
  17. data/app/views/admin/partials/_upload_form.html.erb +2 -2
  18. data/app/views/elements/_contactform_view.html.erb +10 -23
  19. data/app/views/elements/_searchresult_view.html.erb +47 -40
  20. data/app/views/essences/_essence_html_view.html.erb +1 -1
  21. data/app/views/pages/show.rss.builder +20 -25
  22. data/assets/javascripts/alchemy.js +2 -2
  23. data/assets/stylesheets/elements.css +1 -1
  24. data/assets/stylesheets/standard_set.css +84 -14
  25. data/config/alchemy/elements.yml +2 -0
  26. data/config/alchemy/page_layouts.yml +1 -0
  27. data/config/locales/de.yml +5 -5
  28. data/lib/alchemy/version.rb +1 -1
  29. data/lib/rails/generators/alchemy/plugin/templates/config.yml +3 -3
  30. data/spec/controllers/admin/trash_controller_spec.rb +22 -0
  31. data/spec/controllers/pages_controller_spec.rb +39 -0
  32. data/spec/dummy/db/schema.rb +10 -9
  33. data/spec/factories.rb +5 -0
  34. data/spec/helpers/elements_helper_spec.rb +7 -0
  35. data/spec/integration/pages_controller_spec.rb +1 -1
  36. data/spec/models/element_spec.rb +51 -5
  37. data/spec/models/page_spec.rb +70 -10
  38. metadata +10 -7
  39. data/TODO.txt +0 -1
@@ -428,10 +428,10 @@ if (typeof(Alchemy) === 'undefined') {
428
428
 
429
429
  openLicencseWindow : function() {
430
430
  var height = $(window).height() - 150;
431
- var $iframe = $('<iframe src="http://www.gnu.org/licenses/gpl-3.0.txt"></iframe>');
431
+ var $iframe = $('<iframe src="https://raw.github.com/magiclabs/alchemy_cms/master/LICENSE"></iframe>');
432
432
  $iframe.dialog({
433
433
  bgiframe: true,
434
- title: 'GNU GPL License',
434
+ title: 'Alchemy CMS License',
435
435
  width: 650,
436
436
  height: height,
437
437
  autoResize: true,
@@ -469,7 +469,7 @@ div.essence_picture_css_class {
469
469
  position: absolute;
470
470
  z-index: 1;
471
471
  bottom: 24px;
472
- width: 102px;
472
+ width: 99px;
473
473
  background-color: white;
474
474
  background-color: rgba(255,254,255,0.7);
475
475
  padding: 4px 8px;
@@ -1,3 +1,5 @@
1
+ /* @override http://localhost:3000/stylesheets/alchemy/standard_set.css */
2
+
1
3
  body {
2
4
  margin: 0;
3
5
  padding: 0;
@@ -128,11 +130,10 @@ div#header_image {
128
130
  #navigation {
129
131
  font-family: "Lucida Grande", Lucida, Verdana, sans-serif;
130
132
  color: #fdfdfd;
131
- position: relative;
132
- top: 40%;
133
133
  text-transform: uppercase;
134
- float: left;
135
- clear: both;
134
+ position: absolute;
135
+ top: 215px;
136
+ z-index: 100;
136
137
  }
137
138
 
138
139
  #navigation ul {
@@ -333,36 +334,62 @@ input.field {
333
334
  margin: 0;
334
335
  font: 12px "Lucida Grande", Lucida, Verdana, sans-serif;
335
336
  border: 1px solid silver;
336
- width: 290px;
337
+ width: 310px;
338
+ }
339
+
340
+ td.field {
341
+ white-space: nowrap;
337
342
  }
338
343
 
339
344
  input.field.short {
340
- width: 100px;
345
+ width: 105px;
341
346
  }
342
347
 
343
348
  input.field.medium {
344
- width: 186px;
349
+ width: 200px;
345
350
  }
346
351
 
347
352
  td.label {
348
- width: 100px;
353
+ width: 130px;
349
354
  }
350
355
 
351
356
  textarea {
352
- width: 290px;
357
+ width: 310px;
353
358
  padding: 2px;
354
359
  font: 12px "Lucida Grande", Lucida, Verdana, sans-serif;
355
360
  border: 1px solid silver;
356
- margin-left: 107px;
361
+ margin-left: 137px;
357
362
  height: 10em;
358
363
  margin-top: 2em;
359
364
  margin-bottom: 1em;
360
365
  }
361
366
 
367
+ div.field_with_errors {
368
+ color: #fe1b15;
369
+ }
370
+
371
+ div.field_with_errors input.field {
372
+ border-color: #fe1b15;
373
+ }
374
+
375
+ p.foot_notice {
376
+ font-size: 0.8em;
377
+ }
378
+
362
379
  form#contact {
363
- padding: 10px;
364
- width: 400px;
365
- margin-left: 200px;
380
+ padding: 0 0 0 12px;
381
+ width: 450px;
382
+ }
383
+
384
+ #errorExplanation {
385
+ color: #fe1d16;
386
+ margin-bottom: 20px;
387
+ margin-top: 20px;
388
+ }
389
+
390
+ #errorExplanation ul {
391
+ list-style-type: disc;
392
+ padding: 0 0 0 1.5em;
366
393
  }
367
394
 
368
395
  label {
@@ -371,4 +398,47 @@ label {
371
398
 
372
399
  p.right {
373
400
  text-align: right;
374
- }
401
+ }
402
+
403
+ .searchresult {
404
+ margin-top: 1em;
405
+ margin-bottom: 1em;
406
+ }
407
+
408
+ .searchresult form {
409
+ padding-left: 12px;
410
+ }
411
+
412
+ .searchresult h2 {
413
+ padding-left: 12px;
414
+ margin-bottom: 2em;
415
+ margin-top: 1em;
416
+ }
417
+
418
+ .search_results ul {
419
+ list-style-type: none;
420
+ padding: 0 0 0 12px;
421
+ }
422
+
423
+ .search_results ul li {
424
+ margin-bottom: 2em;
425
+ }
426
+
427
+ .search_results ul li p {
428
+ margin-top: 0.5em;
429
+ margin-bottom: 0.5em;
430
+ }
431
+
432
+ .search_results ul li h3 {
433
+ margin-bottom: 0.5em;
434
+ margin-top: 0.5em;
435
+ }
436
+
437
+ .search_results ul li h4 {
438
+ margin-top: 0.5em;
439
+ margin-bottom: 0.5em;
440
+ }
441
+
442
+ .search_results em {
443
+ font-weight: bold;
444
+ }
@@ -196,8 +196,10 @@
196
196
  type: EssenceDate
197
197
  - name: news_headline
198
198
  type: EssenceText
199
+ rss_title: true
199
200
  - name: body
200
201
  type: EssenceRichtext
202
+ rss_description: true
201
203
 
202
204
  - name: searchresult
203
205
  unique: true
@@ -47,6 +47,7 @@
47
47
  unique: true
48
48
  elements: [headline, news]
49
49
  autogenerate: [news]
50
+ feed_elements: [news]
50
51
 
51
52
  - name: search
52
53
  unique: true
@@ -85,7 +85,7 @@ de:
85
85
  # == Translations for the build in full text search.
86
86
  search:
87
87
  no_results: 'Ihre Suche ergab kein Ergebnis.'
88
- result_heading: 'Ihre Suche nach %{query} ergab %{count} Ergebnisse'
88
+ result_heading: "Ihre Suche nach '%{query}' hat %{count} Ergebnisse"
89
89
 
90
90
  # == Contactform translations
91
91
  contactform:
@@ -102,7 +102,7 @@ de:
102
102
  email: 'Email'
103
103
  message: 'Nachricht'
104
104
  send: 'Nachricht absenden!'
105
- mandatory_fields: 'Pflichtfelder. Vielen Dank!'
105
+ mandatory_fields: 'Pflichtfelder. Bitte füllen Sie diese Felder aus. Vielen Dank!'
106
106
  success_page: 'Folgeseite'
107
107
  # Translations for the contactform validations.
108
108
  # Validations are set in the config/alchemy/config.yml mailer section.
@@ -323,9 +323,9 @@ de:
323
323
  errors:
324
324
  template:
325
325
  header:
326
- one: "Konnte dieses Formular nicht speichern: 1 Fehler."
327
- other: "Konnte dieses Formular nicht speichern: %{count} Fehler."
328
- body: "Bitte überprüfen Sie:"
326
+ one: "Konnte nicht fortfahren, da 1 Fehler auftrat."
327
+ other: "Konnte nicht fortfahren, da %{count} Fehler auftraten."
328
+ body: "Fehler:"
329
329
  messages:
330
330
  inclusion: "%{attribute} ist kein gültiger Wert"
331
331
  exclusion: "%{attribute} ist nicht verfügbar"
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
2
 
3
- VERSION = "2.0.rc6"
3
+ VERSION = "2.0"
4
4
 
5
5
  end
@@ -1,4 +1,4 @@
1
- # These is your plugin configuration file.
1
+ # This is your plugin configuration file.
2
2
  # ========================================
3
3
  #
4
4
  # Options:
@@ -9,9 +9,9 @@
9
9
  # settings: # [Optional] Put your own settings in YAML format into here. You can access them with plugin_conf('<%= @plugin_name.underscore %>')['settings'] [YAML]
10
10
  # navigation:
11
11
  # name: # The name is used in the Alchemy mainnavi. [String]
12
- # controller: # The name of the controller to be called when clicking the mainnavi button [String]
12
+ # controller: # The name of the controller to be called when clicking the mainnavi button. Example usage: 'admin/controllername' [String]
13
13
  # action: # The name of the action from the controller to be called when clicking the mainnavi button [String]
14
- # image: # Path to an image inside your public folder used for the icon in the alchemy mainnavi. [String]
14
+ # icon: # CSS classname for the link in the alchemy mainnavi. [String]
15
15
  # sub_navigation: # Your plugins sub navigation. Put as much as sub navi entries as you want into here
16
16
  # - name: # [String]
17
17
  # controller: # Typically your controller and action you set for mainnavi entry.
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Admin::TrashController do
4
+
5
+ before(:each) do
6
+ activate_authlogic
7
+ UserSession.create Factory(:admin_user)
8
+ end
9
+
10
+ it "should hold trashed elements" do
11
+ pending "The controller behaves correct, the test not."
12
+ @page = Factory(:page, :parent_id => Page.rootpage.id)
13
+ @element = Factory(:element, :page => @page)
14
+ # Rails, RSpec and co. are sucking
15
+ @element.reload
16
+ @element.trash
17
+ @element.reload
18
+ get :index, :page_id => @page.id
19
+ response.body.should have_selector('#trash_items #element_4.element_editor')
20
+ end
21
+
22
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe PagesController do
4
+
5
+ before(:each) do
6
+ @default_language = Language.get_default
7
+ @default_language_root = Factory(:language_root_page, :language => @default_language, :name => 'Home', :public => true)
8
+ end
9
+
10
+ context "requested for a page containing a feed" do
11
+
12
+ before(:each) do
13
+ @page = Factory(:public_page, :parent_id => @default_language_root.id, :page_layout => 'news', :name => 'News', :language => @default_language)
14
+ end
15
+
16
+ it "should render a rss feed" do
17
+ get :show, :urlname => 'news', :format => :rss
18
+ response.content_type.should == 'application/rss+xml'
19
+ end
20
+
21
+ it "should include content" do
22
+ pending "I didn't figured out how to test XML response bodies"
23
+ @page.elements.first.content_by_name('news_headline').essence.update_attributes({:body => 'Peters Petshop'})
24
+ get :show, :urlname => 'news', :format => :rss
25
+ response.body.should match /Peters Petshop/
26
+ end
27
+
28
+ end
29
+
30
+ context "requested for a page that does not contain a feed" do
31
+
32
+ it "should render xml 404 error" do
33
+ get :show, :urlname => 'home', :format => :rss
34
+ response.status.should == 404
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  # This file is auto-generated from the current state of the database. Instead
2
3
  # of editing this file, please use the migrations feature of Active Record to
3
4
  # incrementally modify your database, and then regenerate this schema definition.
@@ -197,7 +198,7 @@ ActiveRecord::Schema.define(:version => 20110919110451) do
197
198
  t.string "urlname"
198
199
  t.string "title"
199
200
  t.string "language_code"
200
- t.boolean "language_root"
201
+ t.boolean "language_root", :limit => 255
201
202
  t.string "page_layout"
202
203
  t.text "meta_keywords"
203
204
  t.text "meta_description"
@@ -205,15 +206,15 @@ ActiveRecord::Schema.define(:version => 20110919110451) do
205
206
  t.integer "rgt"
206
207
  t.integer "parent_id"
207
208
  t.integer "depth"
208
- t.boolean "visible", :default => false
209
- t.boolean "public", :default => false
210
- t.boolean "locked", :default => false
209
+ t.boolean "visible", :default => false
210
+ t.boolean "public", :default => false
211
+ t.boolean "locked", :default => false
211
212
  t.integer "locked_by"
212
- t.boolean "restricted", :default => false
213
- t.boolean "robot_index", :default => true
214
- t.boolean "robot_follow", :default => true
215
- t.boolean "sitemap", :default => true
216
- t.boolean "layoutpage", :default => false
213
+ t.boolean "restricted", :default => false
214
+ t.boolean "robot_index", :default => true
215
+ t.boolean "robot_follow", :default => true
216
+ t.boolean "sitemap", :default => true
217
+ t.boolean "layoutpage", :default => false
217
218
  t.datetime "created_at"
218
219
  t.datetime "updated_at"
219
220
  t.integer "creator_id"
@@ -54,6 +54,11 @@ FactoryGirl.define do
54
54
 
55
55
  end
56
56
 
57
+ factory :cell do
58
+ page { Page.find_by_language_root(true) || Factory(:language_root_page) }
59
+ name "A Cell"
60
+ end
61
+
57
62
  factory :element do
58
63
  name 'article'
59
64
  end
@@ -23,6 +23,13 @@ describe ElementsHelper do
23
23
  helper.element_dom_id(@element).should == "#{@element.name}_#{@element.id}"
24
24
  end
25
25
 
26
+ it "should render elements for a cell", :focus => true do
27
+ cell = Factory(:cell)
28
+ Factory(:element, :cell_id => cell.id)
29
+ helper.stub(:configuration).and_return(true)
30
+ helper.render_cell_elements(cell).should match(/id="article_7"/)
31
+ end
32
+
26
33
  context "in preview mode" do
27
34
 
28
35
  it "should return the data-alchemy-element HTML attribute for element" do
@@ -27,7 +27,7 @@ describe PagesController do
27
27
  visit '/'
28
28
  within('div#navigation ul') { page.should have_selector('li a[href="/page-1"], li a[href="/page-2"]') }
29
29
  end
30
-
30
+
31
31
  end
32
32
 
33
33
  context "performing a fulltext search" do
@@ -33,10 +33,10 @@ describe Element do
33
33
  definitions.first.fetch("name").should == 'article'
34
34
  end
35
35
 
36
- it "should always return an array calling all_definitions_for()" do
36
+ it "should always return an array calling all_definitions_for()" do
37
37
  definitions = Element.all_definitions_for(nil)
38
38
  definitions.should == []
39
- end
39
+ end
40
40
 
41
41
  it "should raise an error if no descriptions are found" do
42
42
  FileUtils.mv(File.join(File.dirname(__FILE__), '..', '..', 'config', 'alchemy', 'elements.yml'), File.join(File.dirname(__FILE__), '..', '..', 'config', 'alchemy', 'elements.yml.bak'))
@@ -44,9 +44,55 @@ describe Element do
44
44
  FileUtils.mv(File.join(File.dirname(__FILE__), '..', '..', 'config', 'alchemy', 'elements.yml.bak'), File.join(File.dirname(__FILE__), '..', '..', 'config', 'alchemy', 'elements.yml'))
45
45
  end
46
46
 
47
- it "should return an ingredient by name" do
48
- element = Factory(:element)
49
- element.ingredient('intro').should == EssenceText.first.ingredient
47
+ context "retrieving contents, essences and ingredients" do
48
+
49
+ before(:each) do
50
+ @element = Factory(:element, :name => 'news')
51
+ end
52
+
53
+ it "should return an ingredient by name" do
54
+ @element.ingredient('news_headline').should == EssenceText.first.ingredient
55
+ end
56
+
57
+ it "should return the content for rss title" do
58
+ @element.content_for_rss_title.should == @element.contents.find_by_name('news_headline')
59
+ end
60
+
61
+ it "should return the content for rss description" do
62
+ @element.content_for_rss_description.should == @element.contents.find_by_name('body')
63
+ end
64
+
65
+ end
66
+
67
+ it "should return a collection of trashed elements" do
68
+ @element = Factory(:element)
69
+ @element.trash
70
+ Element.trashed.should include(@element)
71
+ end
72
+
73
+ context "trashed" do
74
+
75
+ before(:each) do
76
+ @element = Factory(:element)
77
+ @element.trash
78
+ end
79
+
80
+ it "should be not public" do
81
+ @element.public.should be_false
82
+ end
83
+
84
+ it "should have no page" do
85
+ @element.page.should == nil
86
+ end
87
+
88
+ it "should be folded" do
89
+ @element.folded.should == true
90
+ end
91
+
92
+ end
93
+
94
+ it "should raise error if all_for_page method has no page" do
95
+ expect { Element.all_for_page(nil) }.should raise_error(TypeError)
50
96
  end
51
97
 
52
98
  end