Commit b2833502d0184d05a577d9544379302412dc38f8
1 parent
7707a13c
Exists in
master
and in
23 other branches
ActionItem158: a better CMS
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1790 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
8 changed files
with
170 additions
and
63 deletions
Show diff stats
app/controllers/my_profile/cms_controller.rb
| @@ -19,12 +19,14 @@ class CmsController < MyProfileController | @@ -19,12 +19,14 @@ class CmsController < MyProfileController | ||
| 19 | 19 | ||
| 20 | def view | 20 | def view |
| 21 | @article = profile.articles.find(params[:id]) | 21 | @article = profile.articles.find(params[:id]) |
| 22 | - @subitems = @article.children | 22 | + @subitems = @article.children.reject {|item| item.folder? } |
| 23 | + @folders = @article.children.select {|item| item.folder? } | ||
| 23 | end | 24 | end |
| 24 | 25 | ||
| 25 | def index | 26 | def index |
| 26 | @article = nil | 27 | @article = nil |
| 27 | - @subitems = profile.top_level_articles | 28 | + @subitems = profile.top_level_articles.reject {|item| item.folder? } |
| 29 | + @folders = profile.top_level_articles.select {|item| item.folder?} | ||
| 28 | render :action => 'view' | 30 | render :action => 'view' |
| 29 | end | 31 | end |
| 30 | 32 | ||
| @@ -35,7 +37,7 @@ class CmsController < MyProfileController | @@ -35,7 +37,7 @@ class CmsController < MyProfileController | ||
| 35 | if request.post? | 37 | if request.post? |
| 36 | @article.last_changed_by = user | 38 | @article.last_changed_by = user |
| 37 | if @article.update_attributes(params[:article]) | 39 | if @article.update_attributes(params[:article]) |
| 38 | - redirect_to :action => 'view', :id => @article.id | 40 | + redirect_back |
| 39 | return | 41 | return |
| 40 | end | 42 | end |
| 41 | end | 43 | end |
| @@ -78,7 +80,7 @@ class CmsController < MyProfileController | @@ -78,7 +80,7 @@ class CmsController < MyProfileController | ||
| 78 | @article.last_changed_by = user | 80 | @article.last_changed_by = user |
| 79 | if request.post? | 81 | if request.post? |
| 80 | if @article.save | 82 | if @article.save |
| 81 | - redirect_to :action => 'view', :id => @article.id | 83 | + redirect_back |
| 82 | return | 84 | return |
| 83 | end | 85 | end |
| 84 | end | 86 | end |
| @@ -102,5 +104,15 @@ class CmsController < MyProfileController | @@ -102,5 +104,15 @@ class CmsController < MyProfileController | ||
| 102 | redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent | 104 | redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent |
| 103 | end | 105 | end |
| 104 | 106 | ||
| 107 | + protected | ||
| 108 | + | ||
| 109 | + def redirect_back | ||
| 110 | + if @article.parent | ||
| 111 | + redirect_to :action => 'view', :id => @article.parent | ||
| 112 | + else | ||
| 113 | + redirect_to :action => 'index' | ||
| 114 | + end | ||
| 115 | + end | ||
| 116 | + | ||
| 105 | end | 117 | end |
| 106 | 118 |
app/models/article.rb
| @@ -112,6 +112,10 @@ class Article < ActiveRecord::Base | @@ -112,6 +112,10 @@ class Article < ActiveRecord::Base | ||
| 112 | true | 112 | true |
| 113 | end | 113 | end |
| 114 | 114 | ||
| 115 | + def folder? | ||
| 116 | + false | ||
| 117 | + end | ||
| 118 | + | ||
| 115 | def self.find_by_initial(initial) | 119 | def self.find_by_initial(initial) |
| 116 | self.find(:all, :order => 'articles.name', :conditions => [ 'articles.name like (?) or articles.name like (?)', initial + '%', initial.upcase + '%']) | 120 | self.find(:all, :order => 'articles.name', :conditions => [ 'articles.name like (?) or articles.name like (?)', initial + '%', initial.upcase + '%']) |
| 117 | end | 121 | end |
app/models/folder.rb
| @@ -20,4 +20,8 @@ class Folder < Article | @@ -20,4 +20,8 @@ class Folder < Article | ||
| 20 | content_tag('ul', children.map { |child| content_tag('li', link_to(child.name, child.url)) }, :class => 'folder-listing') | 20 | content_tag('ul', children.map { |child| content_tag('li', link_to(child.name, child.url)) }, :class => 'folder-listing') |
| 21 | end | 21 | end |
| 22 | 22 | ||
| 23 | + def folder? | ||
| 24 | + true | ||
| 25 | + end | ||
| 26 | + | ||
| 23 | end | 27 | end |
app/views/cms/edit.rhtml
| @@ -16,6 +16,11 @@ | @@ -16,6 +16,11 @@ | ||
| 16 | %> | 16 | %> |
| 17 | 17 | ||
| 18 | <% button_bar do %> | 18 | <% button_bar do %> |
| 19 | - <%= submit_button('save', _('Save'), :cancel => (@article.parent ? { :action => 'view', :id => @article.parent.id } : { :action => 'index' } )) %> | 19 | + <%= submit_button :save, _('Save') %> |
| 20 | + <% if @parent_id || @article.parent %> | ||
| 21 | + <%= button :cancel, _('Cancel'), :action => 'view', :id => @parent_id || @article.parent %> | ||
| 22 | + <% else %> | ||
| 23 | + <%= button :cancel, _('Cancel'), :action => 'index' %> | ||
| 24 | + <% end %> | ||
| 20 | <% end %> | 25 | <% end %> |
| 21 | <% end %> | 26 | <% end %> |
app/views/cms/view.rhtml
| @@ -9,68 +9,59 @@ | @@ -9,68 +9,59 @@ | ||
| 9 | <%= icon('cms') %> | 9 | <%= icon('cms') %> |
| 10 | <%= _('Content management') %> | 10 | <%= _('Content management') %> |
| 11 | </h2> | 11 | </h2> |
| 12 | - | ||
| 13 | - <% button_bar(:style => 'margin-bottom: 1em;') do %> | ||
| 14 | - <%= lightbox_button('new', _('New article'), :action => 'new') %> | ||
| 15 | - <% end %> | ||
| 16 | - | ||
| 17 | <% end %> | 12 | <% end %> |
| 18 | 13 | ||
| 19 | -<%# subitem %> | ||
| 20 | -<% if !@subitems.empty? && @article %> | ||
| 21 | - <%= toggle_panel(_('Hide subitems'), _('Show subitems'), 'article-subitems') %> | 14 | +<% button_bar(:style => 'margin-bottom: 1em;') do %> |
| 15 | + <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %> | ||
| 16 | + <%= button :add, _('New folder'), :action => 'new', :type => 'Folder', :parent_id => parent_id %> | ||
| 17 | + <%= lightbox_button('new', _('New article'), :action => 'new', :parent_id => parent_id) %> | ||
| 22 | <% end %> | 18 | <% end %> |
| 23 | - | ||
| 24 | -<div id='article-subitems'> | ||
| 25 | - <div class='file-manager-title'><%= @article ? _('Subitems') : _('Articles') %></div> | ||
| 26 | - <div class='file-manager-small'> | ||
| 27 | - <% unless @subitems.empty? %> | ||
| 28 | - <ul> | ||
| 29 | - <% @subitems.each do |item| %> | ||
| 30 | - <li> | ||
| 31 | - <%= file_manager_button(item.name, icon_for_article(item), :action => 'view', :id => item.id) %> | ||
| 32 | - </li> | ||
| 33 | - <% end %> | ||
| 34 | - </ul> | ||
| 35 | - <% end %> | ||
| 36 | 19 | ||
| 37 | - <% if @article %> | ||
| 38 | - <% button_bar(:class => 'file-manager-controls') do %> | ||
| 39 | - <% if @article.allow_children? %> | ||
| 40 | - <%= lightbox_button('new', _('New subitem'), :action => 'new', :parent_id => @article.id) %> | ||
| 41 | - <% end %> | ||
| 42 | - <%= button('up', _('Go up one level'), :action => (@article.parent ? 'view' : 'index'), :id => @article.parent) %> | ||
| 43 | - <% end %> | ||
| 44 | - <% end %> | ||
| 45 | - </div> | ||
| 46 | -</div> <!-- article-children --> | 20 | +<table width='100%'> |
| 47 | 21 | ||
| 48 | -<%# display the article content %> | ||
| 49 | -<div id='article-contents' style='clear: left;'> | ||
| 50 | - <% if @article %> | ||
| 51 | - <h2> | ||
| 52 | - <%= @article.name %> | ||
| 53 | - <%= image_tag(icon_for_article(@article)) %> | ||
| 54 | - </h2> | ||
| 55 | - <% button_bar(:id => 'article-controls') do %> | ||
| 56 | - | ||
| 57 | - <ul> | ||
| 58 | - <li> | ||
| 59 | - <%= _('"%{article}", last changed by %{author}') % { :article => @article.name, :author => (@article.last_changed_by ? @article.last_changed_by.name : _('Unknown User')) } %> | ||
| 60 | - </li> | ||
| 61 | - <li> | ||
| 62 | - <%= _('Public address of this article: %s') % link_to(url_for(@article.url), @article.url) %> | ||
| 63 | - </li> | ||
| 64 | - <li> | ||
| 65 | - <%= _('Tags:') %> <%= @article.tag_list %> | ||
| 66 | - </li> | ||
| 67 | - <%= _('Categories:') %> <%= @article.categories.map { |item| item.name }.join(', ') %> | ||
| 68 | - </ul> | 22 | + <%# folders %> |
| 23 | + <tr> | ||
| 24 | + <th colspan='2'> | ||
| 25 | + <%= _('Folders') %> | ||
| 26 | + </th> | ||
| 27 | + </tr> | ||
| 28 | + <% if @folders.empty? %> | ||
| 29 | + <tr><td colspan='2' align='center'><em><%= _('None') %></em></td></tr> | ||
| 30 | + <% end %> | ||
| 31 | + <% for folder in @folders %> | ||
| 32 | + <tr> | ||
| 33 | + <td> | ||
| 34 | + <%= image_tag(icon_for_article(folder)) %> | ||
| 35 | + <%= link_to folder.name, :action => 'view', :id => folder.id %> | ||
| 36 | + </td> | ||
| 37 | + <td> | ||
| 38 | + <%= link_to _('Properties'), :action => 'edit', :id => folder.id %> | ||
| 39 | + <%= link_to _('View'), folder.url %> | ||
| 40 | + </td> | ||
| 41 | + </tr> | ||
| 42 | + <% end %> | ||
| 69 | 43 | ||
| 70 | - <%= button('edit', _('Edit'), { :action => 'edit', :id => @article}) %> | ||
| 71 | - <%= button('home', _('Use as homepage'), { :action => 'set_home_page', :id => @article }, { :method => :post }) %> | ||
| 72 | - <%= button('delete', _('Delete'), { :action => 'destroy', :id => @article }, :method => :post, :confirm => _('Are you sure you wan to remove this article?')) %> | ||
| 73 | - <% end %> | ||
| 74 | - <%= @article.to_html %> | 44 | + <%# non-folder subitems %> |
| 45 | + <tr> | ||
| 46 | + <th colspan='2'> | ||
| 47 | + <%= _('Articles') %> | ||
| 48 | + </th> | ||
| 49 | + </tr> | ||
| 50 | + <% if @subitems.empty? %> | ||
| 51 | + <tr><td colspan='2' align='center'><em><%= _('None') %></em></td></tr> | ||
| 52 | + <% end %> | ||
| 53 | + <% for item in @subitems %> | ||
| 54 | + <tr> | ||
| 55 | + <td> | ||
| 56 | + <%= image_tag(icon_for_article(item)) %> | ||
| 57 | + <%= link_to item.name, :id => item.id %> | ||
| 58 | + </td> | ||
| 59 | + <td> | ||
| 60 | + <%= link_to _('Edit'), :action => 'edit', :id => item.id %> | ||
| 61 | + <%= link_to _('View'), item.url %> | ||
| 62 | + </td> | ||
| 63 | + </tr> | ||
| 75 | <% end %> | 64 | <% end %> |
| 76 | -</div> | 65 | + |
| 66 | + | ||
| 67 | +</table> |
test/functional/cms_controller_test.rb
| @@ -313,6 +313,89 @@ class CmsControllerTest < Test::Unit::TestCase | @@ -313,6 +313,89 @@ class CmsControllerTest < Test::Unit::TestCase | ||
| 313 | assert_tag :tag => 'input', :attributes => { :name => 'parent_id', :value => profile.home_page.id } | 313 | assert_tag :tag => 'input', :attributes => { :name => 'parent_id', :value => profile.home_page.id } |
| 314 | end | 314 | end |
| 315 | 315 | ||
| 316 | + should 'list folders at top level' do | ||
| 317 | + f1 = Folder.new(:name => 'f1'); profile.articles << f1; f1.save! | ||
| 318 | + f2 = Folder.new(:name => 'f2'); profile.articles << f2; f2.save! | ||
| 316 | 319 | ||
| 320 | + get :index, :profile => profile.identifier | ||
| 321 | + assert_equal [f1, f2], assigns(:folders) | ||
| 322 | + assert_not_includes assigns(:subitems), f1 | ||
| 323 | + assert_not_includes assigns(:subitems), f2 | ||
| 324 | + end | ||
| 325 | + | ||
| 326 | + should 'list folders inside another folder' do | ||
| 327 | + parent = Folder.new(:name => 'parent'); profile.articles << parent; parent.save! | ||
| 328 | + f1 = Folder.new(:name => 'f1', :parent => parent); profile.articles << f1; f1.save! | ||
| 329 | + f2 = Folder.new(:name => 'f2', :parent => parent); profile.articles << f2; f2.save! | ||
| 330 | + | ||
| 331 | + get :view, :profile => profile.identifier, :id => parent.id | ||
| 332 | + assert_equal [f1, f2], assigns(:folders) | ||
| 333 | + assert_not_includes assigns(:subitems), f1 | ||
| 334 | + assert_not_includes assigns(:subitems), f2 | ||
| 335 | + end | ||
| 336 | + | ||
| 337 | + should 'offer to create new top-level folder' do | ||
| 338 | + get :index, :profile => profile.identifier | ||
| 339 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/cms/new?type=Folder"} | ||
| 340 | + end | ||
| 341 | + | ||
| 342 | + should 'offer to create sub-folder' do | ||
| 343 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 344 | + get :view, :profile => profile.identifier, :id => f.id | ||
| 345 | + | ||
| 346 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/cms/new?parent_id=#{f.id}&type=Folder" } | ||
| 347 | + end | ||
| 348 | + | ||
| 349 | + should 'redirect back to index after creating top-level article' do | ||
| 350 | + post :new, :profile => profile.identifier, :type => 'TextileArticle', :article => { :name => 'test' } | ||
| 351 | + assert_redirected_to :action => 'index' | ||
| 352 | + end | ||
| 353 | + | ||
| 354 | + should 'redirect back to folder after creating article inside it' do | ||
| 355 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 356 | + post :new, :profile => profile.identifier, :type => 'TextileArticle', :parent_id => f.id, :article => { :name => 'test' } | ||
| 357 | + assert_redirected_to :action => 'view', :id => f.id | ||
| 358 | + end | ||
| 359 | + | ||
| 360 | + should 'redirect back to index after editing top-level article' do | ||
| 361 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 362 | + post :edit, :profile => profile.identifier, :id => f.id | ||
| 363 | + assert_redirected_to :action => 'index' | ||
| 364 | + end | ||
| 365 | + | ||
| 366 | + should 'redirect back to folder after editing article inside it' do | ||
| 367 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 368 | + a = TextileArticle.create!(:parent => f, :name => 'test', :profile_id => profile.id) | ||
| 369 | + | ||
| 370 | + post :edit, :profile => profile.identifier, :id => a.id | ||
| 371 | + assert_redirected_to :action => 'view', :id => f.id | ||
| 372 | + end | ||
| 373 | + | ||
| 374 | + should 'point back to index when cancelling creation of top-level article' do | ||
| 375 | + get :new, :profile => profile.identifier, :type => 'Folder' | ||
| 376 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/cms" }, :descendant => { :content => /Cancel/ } | ||
| 377 | + end | ||
| 378 | + | ||
| 379 | + should 'point back to index when cancelling edition of top-level article' do | ||
| 380 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 381 | + get :edit, :profile => profile.identifier, :id => f.id | ||
| 382 | + | ||
| 383 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/cms" }, :descendant => { :content => /Cancel/ } | ||
| 384 | + end | ||
| 385 | + | ||
| 386 | + should 'point back to folder when cancelling creation of an article inside it' do | ||
| 387 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 388 | + get :new, :profile => profile.identifier, :type => 'Folder', :parent_id => f.id | ||
| 389 | + | ||
| 390 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/cms/view/#{f.id}" }, :descendant => { :content => /Cancel/ } | ||
| 391 | + end | ||
| 392 | + | ||
| 393 | + should 'point back to folder when cancelling edition of an article inside it' do | ||
| 394 | + f = Folder.new(:name => 'f'); profile.articles << f; f.save! | ||
| 395 | + a = TextileArticle.create!(:name => 'test', :parent => f, :profile_id => profile.id) | ||
| 396 | + get :edit, :profile => profile.identifier, :id => a.id | ||
| 397 | + | ||
| 398 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/cms/view/#{f.id}" }, :descendant => { :content => /Cancel/ } | ||
| 399 | + end | ||
| 317 | 400 | ||
| 318 | end | 401 | end |
test/unit/article_test.rb
| @@ -229,4 +229,8 @@ class ArticleTest < Test::Unit::TestCase | @@ -229,4 +229,8 @@ class ArticleTest < Test::Unit::TestCase | ||
| 229 | assert_not_includes list, a2 | 229 | assert_not_includes list, a2 |
| 230 | end | 230 | end |
| 231 | 231 | ||
| 232 | + should 'identify itself as a non-folder' do | ||
| 233 | + assert !Article.new.folder?, 'should identify itself as non-folder' | ||
| 234 | + end | ||
| 235 | + | ||
| 232 | end | 236 | end |
test/unit/folder_test.rb
| @@ -30,4 +30,8 @@ class FolderTest < ActiveSupport::TestCase | @@ -30,4 +30,8 @@ class FolderTest < ActiveSupport::TestCase | ||
| 30 | assert_match(/<li><a href=".*\/testuser\/f\/otherarticle">otherarticle<\/a><\/li>/, f.to_html) | 30 | assert_match(/<li><a href=".*\/testuser\/f\/otherarticle">otherarticle<\/a><\/li>/, f.to_html) |
| 31 | end | 31 | end |
| 32 | 32 | ||
| 33 | + should 'identify as folder' do | ||
| 34 | + assert Folder.new.folder?, 'folder must identity itself as folder' | ||
| 35 | + end | ||
| 36 | + | ||
| 33 | end | 37 | end |