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
data/README.md CHANGED
@@ -6,32 +6,20 @@ Alchemy CMS
6
6
  About
7
7
  -----
8
8
 
9
- Alchemy is a fully featured Content Management System (CMS) with an gorgeous Userinterface.
10
-
11
- Nearly every content management system stores the content of a page in a body column in the pages table. This is easy to develop and the user manages the content inside one of the fancy new Javascript based wysiwyg processors. Formatting, image placement, styling and positioning of the content is in the hand of the end-user.
12
-
13
- __We think this is completly wrong!__
14
-
15
- The content manager mustn‘t be able to change anything but the content and some basic text formatting. The content manager shouldn‘t care about headline formatting, image positioning or resizing. The developer should take care of this!
16
-
17
- __Alchemy is different!__
18
-
19
- We split the page into logical parts like headlines, paragraphs, images, etc. The only thing we store in the database is text: ids of images and richtext content. Nothing else. No markup (besides basic text formatting inside the richtext elements), no styling, no layout. Pure content!
20
-
21
- This gives the webdeveloper the power and flexibility to implement any kind of layout with the insurance that the content manager is not able to break up the layout.
9
+ Alchemy is a powerfull Content Management System (CMS) with an extremly flexible content storing architecture.
22
10
 
23
11
  Features
24
12
  --------
25
13
 
26
14
  - Highly flexible Templating:
27
- - Content is stored in small parts not as a complete, monolithic page.
15
+ - Content is stored in small parts not as a complete, monolithic page
28
16
  - The designer chooses the template structure, not the CMS!
29
- - Every Design is possible, no templating, or theming restrictions.
17
+ - Every Design is possible, no templating, or theming restrictions
30
18
  - Even Flash® Content Management is possible
31
19
  - Gorgious End-User centric interface:
32
- - No geeky markup editors and other meta programming crap.
20
+ - No markup editors
33
21
  - Multilingual:
34
- - Create as many (complete independent) language trees as you want.
22
+ - Create as many (complete independent) language trees as you want
35
23
  - URL based language switching
36
24
  - SEO
37
25
  - Every Part of SEO is manageable by the user
@@ -41,6 +29,7 @@ Features
41
29
  - Rolebased Authentification (RBAS)
42
30
  - Protect pages for restricted access
43
31
  - Fulltext Search
32
+ - RSS Feeds
44
33
  - Contactforms
45
34
  - Attachments and downloads
46
35
  - Powerfull image rendering
@@ -54,74 +43,87 @@ Features
54
43
  - Integrates in exsiting Rails Apps
55
44
  - Caching
56
45
  - Completely free:
57
- - GPLv3 License
46
+ - BSD License
58
47
  - No Enterprise Licences, or Community Editions
59
48
  - Hostable on any Server that supports RubyOnRails and ImageMagick ([Software Requirements](https://github.com/magiclabs/alchemy/wiki/Software-Requirements))
60
49
 
61
50
  Rails Version
62
51
  -------------
63
52
 
64
- This branch of Alchemy runs with Rails 3.0.10, Ruby 1.8.7 and Ruby 1.9.2.
53
+ This version of Alchemy runs with Rails 3.0.10.
54
+
55
+ If you are looking for a Rails 2 compatible version check the rails-2 branch.
56
+
57
+ A Rails 3.1 compatible beta version can be found in the next_stable branch.
58
+
59
+ Ruby Version
60
+ ------------
61
+
62
+ Alchemy runs with REE, Ruby 1.8.7, Ruby 1.9.2 and Ruby 1.9.3.
65
63
 
66
64
  Installation
67
65
  ------------
68
66
 
69
67
  Use the installer (recommended):
70
68
 
71
- gem install alchemy_cms --pre
69
+ gem install alchemy_cms
72
70
  alchemy new my_magicpage
73
71
 
74
72
  Start the local server:
75
73
 
76
74
  rails server
77
75
 
78
- Then just switch to your browser and open `http://localhost:3000`.
76
+ Then just switch to your browser and open `http://localhost:3000`
77
+
78
+ Add to existing Rails project
79
+ -----------------------------
79
80
 
80
- Add to existing Rails project:
81
+ In your Gemfile:
81
82
 
82
- add gem 'alchemy_cms' to Gemfile
83
+ gem 'alchemy_cms'
84
+
85
+ Run in terminal:
83
86
 
84
87
  bundle install
85
88
  rake alchemy:prepare
86
- rake alchemy:standard_set:install (optional)
87
89
  rake db:migrate
88
90
  rake db:seed
89
91
 
90
-
91
92
  Tipps
92
93
  -----
93
94
 
94
- 1. This task creates all necessary folders and files needed for creating your own pagelayouts and elements for your website
95
+ 1. This generator creates all necessary folders and files needed for creating your own page layouts and elements for your website:
95
96
 
96
- rake generate alchemy:scaffold:all
97
+ rails generate alchemy:scaffold
97
98
 
98
99
  2. If you use the ferret full text search (enabled by default), then please add a job to your crontab that reindexes the ferret index.
99
100
 
100
101
  cd /path/to/your/alchemy && RAILS_ENV=production rake ferret:rebuild_index > /dev/null
101
102
 
102
- 3. You can easily create your element-files (for view and editor) depending on the `elements.yml` with this generator
103
+ 3. You can easily create your element files (for view and editor) depending on the `elements.yml` with this generator:
103
104
 
104
105
  rails generate elements
105
106
 
106
107
  Resources
107
108
  ---------
108
109
 
109
- * Homepage: <http://alchemy-app.com>
110
- * Live-Demo: <http://demo.alchemy-app.com>
111
- * Wiki: <http://wiki.alchemy-app.com>
112
- * API Documentation: <http://api.alchemy-app.com>
113
- * Issue-Tracker: <http://issues.alchemy-app.com>
114
- * Sourcecode: <http://source.alchemy-app.com>
110
+ * Homepage: <http://alchemy-cms.com>
111
+ * Live-Demo: <http://demo.alchemy-cms.com>
112
+ * Wiki: <http://wiki.alchemy-cms.com>
113
+ * API Documentation: <http://api.alchemy-cms.com>
114
+ * Issue-Tracker: <http://issues.alchemy-cms.com>
115
+ * Sourcecode: <http://source.alchemy-cms.com>
115
116
  * User Group: <http://groups.google.com/group/alchemy-cms>
116
117
 
117
118
  Authors
118
119
  ---------
119
120
 
120
- * Carsten Fregin: <https://github.com/cfregin>
121
121
  * Thomas von Deyen: <https://github.com/tvdeyen>
122
122
  * Robin Böning: <https://github.com/robinboening>
123
+ * Marc Schettke: <https://github.com/masche842>
124
+ * Carsten Fregin: <https://github.com/cfregin>
123
125
 
124
126
  License
125
127
  -------
126
128
 
127
- * GPLv3: <http://www.gnu.org/licenses/gpl.html/>
129
+ * BSD: <https://raw.github.com/magiclabs/alchemy_cms/master/LICENSE>
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{An extremly flexbile CMS for Rails 3.}
13
13
  s.description = %q{Alchemy is an awesome Rails CMS with an extremely flexible content storing architecture.}
14
14
  s.requirements << 'ImageMagick (libmagick), v6.6 or greater.'
15
- s.license = 'GPL-3'
15
+ s.license = 'BSD New'
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -42,8 +42,10 @@ class Admin::ElementsController < AlchemyController
42
42
  @page = Page.find(params[:element][:page_id])
43
43
  if params[:paste_from_clipboard].blank?
44
44
  @element = Element.new_from_scratch(params[:element])
45
+ cell_definition = Cell.definition_for(params[:element][:name].split('#').last)
45
46
  else
46
- source_element = Element.find(params[:paste_from_clipboard])
47
+ source_element = Element.find(params[:paste_from_clipboard].to_i)
48
+ cell_definition = Cell.definition_for(params[:paste_from_clipboard].split('#').last)
47
49
  if source_element.page_id == blank? # aka. move
48
50
  @element = source_element
49
51
  else
@@ -52,7 +54,6 @@ class Admin::ElementsController < AlchemyController
52
54
  end
53
55
  # if page has cells, put element in cell
54
56
  if @page.has_cells?
55
- cell_definition = Cell.definition_for(params[:element][:name].split('#').last)
56
57
  if cell_definition
57
58
  @cell = @page.cells.find_or_create_by_name(cell_definition['name'])
58
59
  end
@@ -73,14 +73,14 @@ class Admin::PagesController < AlchemyController
73
73
  end
74
74
  end
75
75
 
76
- def update
77
- # fetching page via before filter
78
- if @page.update_attributes(params[:page])
79
- @notice = _("Page %{name} saved") % {:name => @page.name}
80
- else
81
- render_remote_errors(@page, "form#edit_page_#{@page.id} button.button")
82
- end
83
- end
76
+ def update
77
+ # fetching page via before filter
78
+ if @page.update_attributes(params[:page])
79
+ @notice = _("Page %{name} saved") % {:name => @page.name}
80
+ else
81
+ render_remote_errors(@page, "form#edit_page_#{@page.id} button.button")
82
+ end
83
+ end
84
84
 
85
85
  def destroy
86
86
  # fetching page via before filter
@@ -218,12 +218,8 @@ class Admin::PagesController < AlchemyController
218
218
  end
219
219
 
220
220
  def flush
221
- Page.flushables(session[:language_id]).each do |page|
222
- if multi_language?
223
- expire_action("#{page.language_code}/#{page.urlname}")
224
- else
225
- expire_action("#{page.urlname}")
226
- end
221
+ Page.with_language(session[:language_id]).flushables.each do |page|
222
+ expire_page(page)
227
223
  end
228
224
  respond_to do |format|
229
225
  format.js
@@ -240,4 +236,14 @@ private
240
236
  request.raw_post.split('&').map { |i| i = {i.split('=')[0].gsub(/[^0-9]/, '') => i.split('=')[1]} }
241
237
  end
242
238
 
239
+ def expire_page(page)
240
+ return if page.do_not_sweep
241
+ expire_action(
242
+ :controller => '/pages',
243
+ :action => :show,
244
+ :urlname => page.urlname_was,
245
+ :lang => multi_language? ? page.language_code : nil
246
+ )
247
+ end
248
+
243
249
  end
@@ -4,11 +4,15 @@ class Admin::TrashController < AlchemyController
4
4
 
5
5
  before_filter :set_translation
6
6
 
7
+ helper Admin::ElementsHelper
8
+
7
9
  def index
8
10
  @elements = Element.trashed
9
11
  @page = Page.find_by_id(params[:page_id])
10
12
  @allowed_elements = Element.all_for_page(@page)
11
13
  render :layout => false
14
+ rescue Exception => e
15
+ exception_handler(e)
12
16
  end
13
17
 
14
18
  def clear
@@ -7,12 +7,11 @@ class PagesController < AlchemyController
7
7
 
8
8
  caches_action(
9
9
  :show,
10
- :layout => false,
11
- :cache_path => Proc.new { |c| c.multi_language? ? "#{session[:language_code]}/#{c.params[:urlname]}" : "#{c.params[:urlname]}" },
12
- :if => Proc.new { |c|
10
+ :cache_path => proc { url_for(:action => :show, :urlname => params[:urlname], :lang => multi_language? ? params[:lang] : nil) },
11
+ :if => proc do
13
12
  if Alchemy::Config.get(:cache_pages)
14
13
  page = Page.find_by_urlname_and_language_id_and_public(
15
- c.params[:urlname],
14
+ params[:urlname],
16
15
  session[:language_id],
17
16
  true,
18
17
  :select => 'page_layout, language_id, urlname'
@@ -24,20 +23,31 @@ class PagesController < AlchemyController
24
23
  else
25
24
  false
26
25
  end
27
- }
26
+ end
28
27
  )
29
28
 
30
- # Showing page from params[:urlname]
31
- # @page is fetched via before filter
32
- # @root_page is fetched via before filter
33
- # @language fetched via before_filter in alchemy_controller
34
- # rendering page and querying for search results if any query is present
35
- def show
36
- if configuration(:ferret) && !params[:query].blank?
37
- perform_search
38
- end
39
- render :layout => params[:layout].blank? ? 'pages' : params[:layout] == 'none' ? false : params[:layout]
40
- end
29
+ # Showing page from params[:urlname]
30
+ # @page is fetched via before filter
31
+ # @root_page is fetched via before filter
32
+ # @language fetched via before_filter in alchemy_controller
33
+ # rendering page and querying for search results if any query is present
34
+ def show
35
+ if configuration(:ferret) && !params[:query].blank?
36
+ perform_search
37
+ end
38
+ respond_to do |format|
39
+ format.html {
40
+ render :layout => params[:layout].blank? ? 'pages' : params[:layout] == 'none' ? false : params[:layout]
41
+ }
42
+ format.rss {
43
+ if @page.contains_feed?
44
+ render :action => "show.rss.builder", :layout => false
45
+ else
46
+ render :xml => { :error => 'Not found' }, :status => 404
47
+ end
48
+ }
49
+ end
50
+ end
41
51
 
42
52
  # Renders a Google conform sitemap in xml
43
53
  def sitemap
@@ -97,6 +107,7 @@ private
97
107
  {:limit => :all},
98
108
  {:conditions => ["public = ?", true]}
99
109
  )
110
+ @search_results = (@text_search_results + @rtf_search_results).sort{ |y, x| x.ferret_score <=> y.ferret_score }
100
111
  end
101
112
 
102
113
  def find_first_public(page)
@@ -56,4 +56,59 @@ module Admin::ElementsHelper
56
56
  )
57
57
  end
58
58
 
59
+ def clipboard_select_tag(items, html_options = {})
60
+ options = [[_('Please choose'), ""]]
61
+ items.each do |item|
62
+ options << [item.class.to_s == 'Element' ? item.display_name_with_preview_text : item.name, item.id]
63
+ end
64
+ select_tag(
65
+ 'paste_from_clipboard',
66
+ @page.has_cells? ? grouped_elements_for_select(items, :id) : options_for_select(options),
67
+ {
68
+ :class => html_options[:class] || 'very_long',
69
+ :style => html_options[:style]
70
+ }
71
+ )
72
+ end
73
+
74
+ # Returns all elements that could be placed on that page because of the pages layout.
75
+ # The elements are returned as an array to be used in alchemy_selectbox form builder.
76
+ def elements_for_select(elements)
77
+ return [] if elements.nil?
78
+ options = elements.collect{ |e| [I18n.t("alchemy.element_names.#{e['name']}", :default => e['name'].capitalize), e["name"]] }
79
+ return options_for_select(options)
80
+ end
81
+
82
+ # Returns all elements that could be placed on that page because of the pages layout.
83
+ # The elements will be grouped by cell.
84
+ def grouped_elements_for_select(elements, object_method = 'name')
85
+ return [] if elements.nil?
86
+ cells_definition = Cell.definitions
87
+ return [] if cells_definition.blank?
88
+ options = {}
89
+ celled_elements = []
90
+ cells_definition.each do |cell|
91
+ cell_elements = elements.select { |e| cell['elements'].include?(e.class.name == 'Element' ? e.name : e['name']) }
92
+ celled_elements += cell_elements
93
+ optgroup_label = Cell.translated_label_for(cell['name'])
94
+ options[optgroup_label] = cell_elements.map do |e|
95
+ [
96
+ I18n.t("alchemy.element_names.#{e['name']}", :default => e['name'].capitalize),
97
+ (e.class.name == 'Element' ? e.send(object_method).to_s : e[object_method]) + "##{cell['name']}"
98
+ ]
99
+ end
100
+ end
101
+ other_elements = elements - celled_elements
102
+ unless other_elements.blank?
103
+ optgroup_label = _('other Elements')
104
+ options[optgroup_label] = other_elements.map do |e|
105
+ [
106
+ I18n.t("alchemy.element_names.#{e['name']}", :default => e['name'].capitalize),
107
+ e.class.name == 'Element' ? e.send(object_method) : e[object_method]
108
+ ]
109
+ end
110
+ end
111
+ return grouped_options_for_select(options)
112
+ end
113
+
59
114
  end
@@ -101,51 +101,6 @@ module AlchemyHelper
101
101
  filter_field << "</div>"
102
102
  filter_field.html_safe
103
103
  end
104
-
105
- def clipboard_select_tag(items, html_options = {})
106
- options = [[_('Please choose'), ""]]
107
- items.each do |item|
108
- options << [item.class.to_s == 'Element' ? item.display_name_with_preview_text : item.name, item.id]
109
- end
110
- select_tag(
111
- 'paste_from_clipboard',
112
- options_for_select(options),
113
- {
114
- :class => html_options[:class] || 'very_long',
115
- :style => html_options[:style]
116
- }
117
- )
118
- end
119
-
120
- # Returns all elements that could be placed on that page because of the pages layout.
121
- # The elements are returned as an array to be used in alchemy_selectbox form builder.
122
- def elements_for_select(elements)
123
- return [] if elements.nil?
124
- options = elements.collect{ |e| [I18n.t("alchemy.element_names.#{e['name']}", :default => e['name'].capitalize), e["name"]] }
125
- return options_for_select(options)
126
- end
127
-
128
- # Returns all elements that could be placed on that page because of the pages layout.
129
- # The elements will be grouped by cell.
130
- def grouped_elements_for_select(elements)
131
- return [] if elements.nil?
132
- cells_definition = Cell.definitions
133
- return [] if cells_definition.blank?
134
- options = {}
135
- celled_elements = []
136
- cells_definition.each do |cell|
137
- cell_elements = elements.select { |e| cell['elements'].include?(e['name']) }
138
- celled_elements += cell_elements
139
- optgroup_label = Cell.translated_label_for(cell['name'])
140
- options[optgroup_label] = cell_elements.map { |e| [I18n.t("alchemy.element_names.#{e['name']}", :default => e['name'].capitalize), e['name'] + "##{cell['name']}"] }
141
- end
142
- other_elements = elements - celled_elements
143
- unless other_elements.blank?
144
- optgroup_label = _('other Elements')
145
- options[optgroup_label] = other_elements.map { |e| [I18n.t("alchemy.element_names.#{e['name']}", :default => e['name'].capitalize), e['name']] }
146
- end
147
- return grouped_options_for_select(options)
148
- end
149
104
 
150
105
  def link_to_confirmation_window(link_string = "", message = "", url = "", html_options = {})
151
106
  title = _("please_confirm")
@@ -20,6 +20,7 @@ module ElementsHelper
20
20
  :except => [],
21
21
  :only => [],
22
22
  :from_page => "",
23
+ :from_cell => "",
23
24
  :count => nil,
24
25
  :offset => nil,
25
26
  :locals => {},
@@ -53,7 +54,7 @@ module ElementsHelper
53
54
  element_string = ""
54
55
  if options[:fallback]
55
56
  unless all_elements.detect { |e| e.name == options[:fallback][:for] }
56
- if from = Page.find_by_page_layout(options[:fallback][:from])
57
+ if from = Page.find_by_page_layout_and_language_id(options[:fallback][:from], session[:language_id])
57
58
  all_elements += from.elements.find_all_by_name(options[:fallback][:with].blank? ? options[:fallback][:for] : options[:fallback][:with])
58
59
  end
59
60
  end
@@ -143,6 +144,12 @@ module ElementsHelper
143
144
  return element
144
145
  end
145
146
 
147
+ # Renders all element partials from given cell.
148
+ def render_cell_elements(cell)
149
+ return warning("No cell given.") if cell.blank?
150
+ render_elements({:from_cell => cell})
151
+ end
152
+
146
153
  # Returns a string for the id attribute of a html element for the given element
147
154
  def element_dom_id(element)
148
155
  return "" if element.nil?