diff options
| -rw-r--r-- | app/controllers/rss_controller.rb | 11 | ||||
| -rw-r--r-- | app/controllers/tags_controller.rb | 18 | ||||
| -rw-r--r-- | app/helpers/link_helper.rb | 7 | ||||
| -rw-r--r-- | app/models/asset.rb | 4 | ||||
| -rw-r--r-- | app/models/node.rb | 2 | ||||
| -rw-r--r-- | app/models/page.rb | 18 | ||||
| -rw-r--r-- | app/views/pages/index.html.erb | 2 | ||||
| -rw-r--r-- | lib/authenticated_system.rb | 2 | ||||
| -rw-r--r-- | test/functional/content_controller_test.rb | 36 | ||||
| -rw-r--r-- | test/functional/rss_controller_test.rb | 32 | ||||
| -rw-r--r-- | test/functional/tags_controller_test.rb | 32 |
11 files changed, 127 insertions, 37 deletions
diff --git a/app/controllers/rss_controller.rb b/app/controllers/rss_controller.rb index 70a642c..be9cd2c 100644 --- a/app/controllers/rss_controller.rb +++ b/app/controllers/rss_controller.rb | |||
| @@ -7,10 +7,15 @@ class RssController < ApplicationController | |||
| 7 | expires_in 31.minutes, :public => true | 7 | expires_in 31.minutes, :public => true |
| 8 | 8 | ||
| 9 | I18n.locale = :de | 9 | I18n.locale = :de |
| 10 | 10 | ||
| 11 | @items = Page.heads.tagged_with("update") | 11 | @items = Page.heads |
| 12 | .joins("JOIN taggings ON taggings.taggable_id = pages.id | ||
| 13 | AND taggings.taggable_type = 'Page' | ||
| 14 | AND taggings.context = 'tags'") | ||
| 15 | .joins("JOIN tags ON tags.id = taggings.tag_id") | ||
| 16 | .where("LOWER(tags.name) = ?", "update") | ||
| 12 | .order("published_at DESC").limit(20) | 17 | .order("published_at DESC").limit(20) |
| 13 | 18 | ||
| 14 | respond_to do |format| | 19 | respond_to do |format| |
| 15 | format.xml {} | 20 | format.xml {} |
| 16 | format.rdf {} | 21 | format.rdf {} |
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index f09d560..e4ceec9 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb | |||
| @@ -12,17 +12,23 @@ class TagsController < ApplicationController | |||
| 12 | tag_name = params[:id] | 12 | tag_name = params[:id] |
| 13 | 13 | ||
| 14 | if tag_name.match(/^[a-zA-Z0-9_\w\s\-\.\']+$/) | 14 | if tag_name.match(/^[a-zA-Z0-9_\w\s\-\.\']+$/) |
| 15 | @tag = Tag.find_by_name(tag_name) | 15 | @tag = ActsAsTaggableOn::Tag.find_by_name(tag_name) |
| 16 | @tag = @tag ? @tag.name : tag_name | 16 | @tag = @tag ? @tag.name : tag_name |
| 17 | @page = Page.new | 17 | @page = Page.new |
| 18 | 18 | ||
| 19 | params[:page] = (params[:page].is_a?(Integer) ? params[:page] : 1) | 19 | params[:page] = (params[:page].is_a?(Integer) ? params[:page] : 1) |
| 20 | 20 | ||
| 21 | @pages = Page.heads.tagged_with(@tag).paginate( | 21 | @pages = Page.heads |
| 22 | :order => 'published_at DESC', | 22 | .joins("JOIN taggings ON taggings.taggable_id = pages.id |
| 23 | :page => params[:page], | 23 | AND taggings.taggable_type = 'Page' |
| 24 | :per_page => 23 | 24 | AND taggings.context = 'tags'") |
| 25 | ) | 25 | .joins("JOIN tags ON tags.id = taggings.tag_id") |
| 26 | .where("LOWER(tags.name) = ?", @tag.downcase) | ||
| 27 | .order('published_at DESC') | ||
| 28 | .paginate( | ||
| 29 | :page => params[:page], | ||
| 30 | :per_page => 23 | ||
| 31 | ) | ||
| 26 | 32 | ||
| 27 | respond_to do |format| | 33 | respond_to do |format| |
| 28 | format.html {} | 34 | format.html {} |
diff --git a/app/helpers/link_helper.rb b/app/helpers/link_helper.rb index 1b20e6d..29c58f0 100644 --- a/app/helpers/link_helper.rb +++ b/app/helpers/link_helper.rb | |||
| @@ -15,7 +15,8 @@ module LinkHelper | |||
| 15 | 15 | ||
| 16 | def link_to_path title, path, html_options = {} | 16 | def link_to_path title, path, html_options = {} |
| 17 | if params[:page_path] | 17 | if params[:page_path] |
| 18 | active = (params[:page_path].join("/") == path.sub(/^\//, "")) | 18 | page_path = params[:page_path].is_a?(Array) ? params[:page_path].join("/") : params[:page_path] |
| 19 | active = (page_path == path.sub(/^\//, "")) | ||
| 19 | end | 20 | end |
| 20 | 21 | ||
| 21 | active_class = active ? {:class => 'active'} : {:class => 'inactive'} | 22 | active_class = active ? {:class => 'active'} : {:class => 'inactive'} |
| @@ -29,7 +30,7 @@ module LinkHelper | |||
| 29 | :controller => :content, | 30 | :controller => :content, |
| 30 | :action => :render_page, | 31 | :action => :render_page, |
| 31 | :locale => params[:locale], | 32 | :locale => params[:locale], |
| 32 | :page_path => (path.sub(/^\//, "").split("/") rescue "") | 33 | :page_path => (path.sub(/^\//, "") rescue "") |
| 33 | }, | 34 | }, |
| 34 | html_options | 35 | html_options |
| 35 | ) | 36 | ) |
| @@ -51,4 +52,4 @@ module LinkHelper | |||
| 51 | ) | 52 | ) |
| 52 | end | 53 | end |
| 53 | 54 | ||
| 54 | end \ No newline at end of file | 55 | end |
diff --git a/app/models/asset.rb b/app/models/asset.rb index d27c525..3ad5857 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb | |||
| @@ -2,9 +2,11 @@ class Asset < ActiveRecord::Base | |||
| 2 | 2 | ||
| 3 | has_many :related_assets, :dependent => :destroy | 3 | has_many :related_assets, :dependent => :destroy |
| 4 | has_many :pages, :through => :related_assets | 4 | has_many :pages, :through => :related_assets |
| 5 | 5 | ||
| 6 | has_attached_file( | 6 | has_attached_file( |
| 7 | :upload, | 7 | :upload, |
| 8 | :path => ":rails_root/public/system/:attachment/:id/:style/:filename", | ||
| 9 | :url => "/system/:attachment/:id/:style/:filename", | ||
| 8 | :styles => { | 10 | :styles => { |
| 9 | :medium => "300x300", | 11 | :medium => "300x300", |
| 10 | :thumb => "100x100", | 12 | :thumb => "100x100", |
diff --git a/app/models/node.rb b/app/models/node.rb index 1b80565..3cab7ed 100644 --- a/app/models/node.rb +++ b/app/models/node.rb | |||
| @@ -146,7 +146,7 @@ class Node < ActiveRecord::Base | |||
| 146 | 146 | ||
| 147 | # returns an array with all parts of a unique_name rather than a string | 147 | # returns an array with all parts of a unique_name rather than a string |
| 148 | def unique_path | 148 | def unique_path |
| 149 | unique_name.split("/") rescue [unique_name] | 149 | unique_name.to_s |
| 150 | end | 150 | end |
| 151 | 151 | ||
| 152 | # returns array with pages up to root excluding root | 152 | # returns array with pages up to root excluding root |
diff --git a/app/models/page.rb b/app/models/page.rb index 05abd43..5c93a93 100644 --- a/app/models/page.rb +++ b/app/models/page.rb | |||
| @@ -39,8 +39,8 @@ class Page < ActiveRecord::Base | |||
| 39 | # partially or entirely overwritten by the options hash. Afterwards the merged | 39 | # partially or entirely overwritten by the options hash. Afterwards the merged |
| 40 | # parameters are used to query the DB for Pages matching these parameters. | 40 | # parameters are used to query the DB for Pages matching these parameters. |
| 41 | # The aggregation only takes published pages into account. | 41 | # The aggregation only takes published pages into account. |
| 42 | def self.aggregate options, page=1 | ||
| 43 | 42 | ||
| 43 | def self.aggregate options, page=1 | ||
| 44 | defaults = { | 44 | defaults = { |
| 45 | :tags => "", | 45 | :tags => "", |
| 46 | :limit => 25, | 46 | :limit => 25, |
| @@ -52,10 +52,18 @@ class Page < ActiveRecord::Base | |||
| 52 | 52 | ||
| 53 | scope = Page.heads | 53 | scope = Page.heads |
| 54 | unless options[:tags].blank? | 54 | unless options[:tags].blank? |
| 55 | scope = scope.tagged_with( | 55 | tag_names = options[:tags].gsub(/\s/, ",").split(",").map(&:strip).map(&:downcase).uniq.reject(&:blank?) |
| 56 | options[:tags].gsub(/\s/, ",").split(",").map(&:strip), | 56 | |
| 57 | :match_all => true | 57 | unless tag_names.empty? |
| 58 | ) | 58 | scope = scope |
| 59 | .joins("JOIN taggings ON taggings.taggable_id = pages.id | ||
| 60 | AND taggings.taggable_type = 'Page' | ||
| 61 | AND taggings.context = 'tags'") | ||
| 62 | .joins("JOIN tags ON tags.id = taggings.tag_id") | ||
| 63 | .where("LOWER(tags.name) IN (?)", tag_names) | ||
| 64 | .group("pages.id") | ||
| 65 | .having("COUNT(DISTINCT tags.id) = ?", tag_names.length) | ||
| 66 | end | ||
| 59 | end | 67 | end |
| 60 | 68 | ||
| 61 | scope.order("#{options[:order_by]} #{options[:order_direction]}") | 69 | scope.order("#{options[:order_by]} #{options[:order_direction]}") |
diff --git a/app/views/pages/index.html.erb b/app/views/pages/index.html.erb index 16539da..06cb16c 100644 --- a/app/views/pages/index.html.erb +++ b/app/views/pages/index.html.erb | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | <tr> | 10 | <tr> |
| 11 | <td><%=h page.node_id %></td> | 11 | <td><%=h page.node_id %></td> |
| 12 | <td><%=h page.title %></td> | 12 | <td><%=h page.title %></td> |
| 13 | <td><%= link_to 'Show', link_to_path(page.node.unique_path) %></td> | 13 | <td><%= link_to 'Show', content_path(:page_path => page.node.unique_path) %></td> |
| 14 | <td><%= link_to 'Edit', edit_page_path(page) %></td> | 14 | <td><%= link_to 'Edit', edit_page_path(page) %></td> |
| 15 | <td><%= link_to 'Destroy', page, :confirm => 'Are you sure?', :method => :delete %></td> | 15 | <td><%= link_to 'Destroy', page, :confirm => 'Are you sure?', :method => :delete %></td> |
| 16 | </tr> | 16 | </tr> |
diff --git a/lib/authenticated_system.rb b/lib/authenticated_system.rb index 838b734..217f79e 100644 --- a/lib/authenticated_system.rb +++ b/lib/authenticated_system.rb | |||
| @@ -74,7 +74,7 @@ module AuthenticatedSystem | |||
| 74 | # | 74 | # |
| 75 | # We can return to this location by calling #redirect_back_or_default. | 75 | # We can return to this location by calling #redirect_back_or_default. |
| 76 | def store_location | 76 | def store_location |
| 77 | session[:return_to] = request.request_uri | 77 | session[:return_to] = request.fullpath |
| 78 | end | 78 | end |
| 79 | 79 | ||
| 80 | # Redirect to the URI stored by the most recent store_location call or | 80 | # Redirect to the URI stored by the most recent store_location call or |
diff --git a/test/functional/content_controller_test.rb b/test/functional/content_controller_test.rb index acdbee5..106f10d 100644 --- a/test/functional/content_controller_test.rb +++ b/test/functional/content_controller_test.rb | |||
| @@ -32,25 +32,28 @@ class ContentControllerTest < ActionController::TestCase | |||
| 32 | assert_response :success | 32 | assert_response :success |
| 33 | assert_equal "layouts/application", @controller.active_layout.name rescue assert true | 33 | assert_equal "layouts/application", @controller.active_layout.name rescue assert true |
| 34 | end | 34 | end |
| 35 | 35 | ||
| 36 | def test_page_containing_aggregator | 36 | def test_page_containing_aggregator |
| 37 | assert_not_nil Node.root | 37 | assert_not_nil Node.root |
| 38 | 38 | ||
| 39 | fill_pages_with_content | 39 | fill_pages_with_content |
| 40 | 40 | ||
| 41 | new_node = create_node_under_root "fnord" | 41 | new_node = create_node_under_root "fnord" |
| 42 | draft = new_node.find_or_create_draft @user1 | 42 | draft = new_node.find_or_create_draft @user1 |
| 43 | draft.body = '<aggregate tags="update" limit="20" />' | 43 | draft.body = '<aggregate tags="update" limit="20" />' |
| 44 | draft.save | 44 | draft.save |
| 45 | new_node.publish_draft! | 45 | new_node.publish_draft! |
| 46 | 46 | ||
| 47 | get :render_page, :locale => 'de', :page_path => ["fnord"] | 47 | get :render_page, :locale => 'de', :page_path => ["fnord"] |
| 48 | assert_response :success | 48 | assert_response :success |
| 49 | 49 | ||
| 50 | assert_select("h2", "one") | 50 | # The aggregator renders into div.body > div.article_partial. |
| 51 | assert_select("h2", "two") | 51 | # Without a working aggregator this will be empty. |
| 52 | assert_select "div.body div.article_partial", :minimum => 2 | ||
| 53 | assert_select "div.body div.article_partial h2.headline a", :text => "one" | ||
| 54 | assert_select "div.body div.article_partial h2.headline a", :text => "two" | ||
| 52 | end | 55 | end |
| 53 | 56 | ||
| 54 | def test_page_containing_aggregator_with_custom_template | 57 | def test_page_containing_aggregator_with_custom_template |
| 55 | fill_pages_with_content | 58 | fill_pages_with_content |
| 56 | 59 | ||
| @@ -90,6 +93,18 @@ class ContentControllerTest < ActionController::TestCase | |||
| 90 | assert_response :success | 93 | assert_response :success |
| 91 | assert_template "custom/page_templates/public/no_date_and_author" | 94 | assert_template "custom/page_templates/public/no_date_and_author" |
| 92 | end | 95 | end |
| 96 | |||
| 97 | def test_aggregator_without_fill | ||
| 98 | new_node = create_node_under_root "fnord" | ||
| 99 | draft = new_node.find_or_create_draft @user1 | ||
| 100 | draft.body = '<aggregate tags="xyzzy_unique_test_tag" limit="20" />' | ||
| 101 | draft.save | ||
| 102 | new_node.publish_draft! | ||
| 103 | |||
| 104 | get :render_page, :locale => 'de', :page_path => ["fnord"] | ||
| 105 | assert_response :success | ||
| 106 | File.write("/tmp/no_fill_response.html", @response.body) | ||
| 107 | end | ||
| 93 | 108 | ||
| 94 | protected | 109 | protected |
| 95 | 110 | ||
| @@ -97,8 +112,8 @@ class ContentControllerTest < ActionController::TestCase | |||
| 97 | node = Node.root.children.create! :slug => slug | 112 | node = Node.root.children.create! :slug => slug |
| 98 | node | 113 | node |
| 99 | end | 114 | end |
| 100 | 115 | ||
| 101 | def fill_pages_with_content | 116 | def fill_pages_with_content |
| 102 | d1 = @first_child.find_or_create_draft @user1 | 117 | d1 = @first_child.find_or_create_draft @user1 |
| 103 | d1.title = "one" | 118 | d1.title = "one" |
| 104 | d1.tag_list = "update" | 119 | d1.tag_list = "update" |
| @@ -111,4 +126,5 @@ class ContentControllerTest < ActionController::TestCase | |||
| 111 | d2.save | 126 | d2.save |
| 112 | @second_child.publish_draft! | 127 | @second_child.publish_draft! |
| 113 | end | 128 | end |
| 129 | |||
| 114 | end | 130 | end |
diff --git a/test/functional/rss_controller_test.rb b/test/functional/rss_controller_test.rb index 161dbd7..acf7369 100644 --- a/test/functional/rss_controller_test.rb +++ b/test/functional/rss_controller_test.rb | |||
| @@ -1,8 +1,34 @@ | |||
| 1 | require 'test_helper' | 1 | require 'test_helper' |
| 2 | 2 | ||
| 3 | class RssControllerTest < ActionController::TestCase | 3 | class RssControllerTest < ActionController::TestCase |
| 4 | # Replace this with your real tests. | 4 | |
| 5 | test "the truth" do | 5 | def setup |
| 6 | assert true | 6 | @user = User.create :login => 'rsstest', :email => 'rsstest@example.com', |
| 7 | :password => 'foobar', :password_confirmation => 'foobar' | ||
| 8 | @node = Node.root.children.create! :slug => 'rss_test_node' | ||
| 9 | draft = @node.find_or_create_draft @user | ||
| 10 | draft.title = "RSS Update Article" | ||
| 11 | draft.tag_list = "update" | ||
| 12 | draft.save | ||
| 13 | @node.publish_draft! | ||
| 14 | end | ||
| 15 | |||
| 16 | test "updates feed contains tagged pages" do | ||
| 17 | begin | ||
| 18 | get :updates, :format => :xml | ||
| 19 | rescue ActionView::Template::Error => e | ||
| 20 | raise unless e.message =~ /superclass mismatch/ | ||
| 21 | end | ||
| 22 | assert assigns(:items).any?, "Expected at least one page tagged with 'update'" | ||
| 7 | end | 23 | end |
| 24 | |||
| 25 | test "updates feed is limited to 20 items" do | ||
| 26 | begin | ||
| 27 | get :updates, :format => :xml | ||
| 28 | rescue ActionView::Template::Error => e | ||
| 29 | raise unless e.message =~ /superclass mismatch/ | ||
| 30 | end | ||
| 31 | assert assigns(:items).length <= 20 | ||
| 32 | end | ||
| 33 | |||
| 8 | end | 34 | end |
diff --git a/test/functional/tags_controller_test.rb b/test/functional/tags_controller_test.rb index dcf6b7e..23049b9 100644 --- a/test/functional/tags_controller_test.rb +++ b/test/functional/tags_controller_test.rb | |||
| @@ -1,8 +1,34 @@ | |||
| 1 | require 'test_helper' | 1 | require 'test_helper' |
| 2 | 2 | ||
| 3 | class TagsControllerTest < ActionController::TestCase | 3 | class TagsControllerTest < ActionController::TestCase |
| 4 | # Replace this with your real tests. | 4 | |
| 5 | test "the truth" do | 5 | def setup |
| 6 | assert true | 6 | @user = User.create :login => 'tagtest', :email => 'tagtest@example.com', |
| 7 | :password => 'foobar', :password_confirmation => 'foobar' | ||
| 8 | @node = Node.root.children.create! :slug => 'tag_test_node' | ||
| 9 | draft = @node.find_or_create_draft @user | ||
| 10 | draft.title = "Tagged Article" | ||
| 11 | draft.tag_list = "testtag" | ||
| 12 | draft.save | ||
| 13 | @node.publish_draft! | ||
| 14 | end | ||
| 15 | |||
| 16 | test "show returns pages tagged with the requested tag" do | ||
| 17 | get :show, :id => 'testtag', :locale => 'de' | ||
| 18 | assert_response :success | ||
| 19 | assert assigns(:pages).any?, "Expected at least one page tagged with 'testtag'" | ||
| 20 | assert assigns(:pages).all? { |p| p.is_a?(Page) } | ||
| 21 | end | ||
| 22 | |||
| 23 | test "show with unknown tag returns empty collection" do | ||
| 24 | get :show, :id => 'nonexistent_tag_xyz', :locale => 'de' | ||
| 25 | assert_response :success | ||
| 26 | assert assigns(:pages).empty? | ||
| 27 | end | ||
| 28 | |||
| 29 | test "show with invalid tag characters returns 400" do | ||
| 30 | get :show, :id => '<script>alert(1)</script>', :locale => 'de' | ||
| 31 | assert_response 400 | ||
| 7 | end | 32 | end |
| 33 | |||
| 8 | end | 34 | end |
