Commit d755b3b712e5a0a2d580d30c5ec1668ae3d58d43

Authored by Michel Felipe
Committed by Marcos Pereira
1 parent e58c017a

New feature archive articles

- Avoid create comments, associate children articles and perform votes on archived articles
- Update db/schema

-Signed-off-by: Michel Felipe de Oliveira Ferreira <michel.ferreira@serpro.gov.br>
-Signed-off-by: Gustavo Jaruga Cruz <darksshades@gmail.com>
-Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>

(cherry picked from commit 144d608f15e2864e8053d384e871699d314a9283)
app/helpers/article_helper.rb
... ... @@ -15,6 +15,12 @@ module ArticleHelper
15 15 topic_creation(@article) +
16 16 content_tag('h4', _('Options')) +
17 17 content_tag('div',
  18 + content_tag(
  19 + 'div',
  20 + check_box(:article, :archived) +
  21 + content_tag('label', _('Do not allow new content on this article and its children'), :for => 'article_archived_true')
  22 + ) +
  23 +
18 24 (article.profile.has_members? ?
19 25 content_tag(
20 26 'div',
... ... @@ -63,13 +69,20 @@ module ArticleHelper
63 69 content_tag('div',
64 70 content_tag('div',
65 71 radio_button(:article, :published, true) +
66   - content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true')
  72 + content_tag('span', '&nbsp;', :class => 'access-public-icon') +
  73 + content_tag('label', _('Public'), :for => 'article_published_true') +
  74 + content_tag('span', _('Visible to other people'), :class => 'access-note'),
  75 + :class => 'access-item'
67 76 ) +
68 77 content_tag('div',
69 78 radio_button(:article, :published, false) +
70   - content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private")
  79 + content_tag('span', '&nbsp;', :class => 'access-private-icon') +
  80 + content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private") +
  81 + content_tag('span', _('Limit visibility of this article'), :class => 'access-note'),
  82 + :class => 'access-item'
71 83 ) +
72   - privacity_exceptions(article, tokenized_children)
  84 + privacity_exceptions(article, tokenized_children),
  85 + :class => 'access-itens'
73 86 )
74 87 end
75 88  
... ...
app/helpers/folder_helper.rb
... ... @@ -59,7 +59,13 @@ module FolderHelper
59 59 @article = article
60 60  
61 61 visibility_options(article,tokenized_children) +
  62 + content_tag('h4', _('Options')) +
62 63 content_tag('div',
  64 + content_tag(
  65 + 'div',
  66 + check_box(:article, :archived) +
  67 + content_tag('label', _('Do not allow new content on this article and its children'), :for => 'article_archived_true')
  68 + ) +
63 69 hidden_field_tag('article[accept_comments]', 0)
64 70 )
65 71 end
... ...
app/models/article.rb
... ... @@ -8,7 +8,7 @@ class Article &lt; ActiveRecord::Base
8 8 :accept_comments, :feed, :published, :source, :source_name,
9 9 :highlighted, :notify_comments, :display_hits, :slug,
10 10 :external_feed_builder, :display_versions, :external_link,
11   - :image_builder, :show_to_followers,
  11 + :image_builder, :show_to_followers, :archived,
12 12 :author, :display_preview, :published_at, :person_followers
13 13  
14 14 acts_as_having_image
... ... @@ -152,6 +152,8 @@ class Article &lt; ActiveRecord::Base
152 152 validate :no_self_reference
153 153 validate :no_cyclical_reference, :if => 'parent_id.present?'
154 154  
  155 + validate :parent_archived?
  156 +
155 157 def no_self_reference
156 158 errors.add(:parent_id, _('self-reference is not allowed.')) if id && parent_id == id
157 159 end
... ... @@ -499,6 +501,10 @@ class Article &lt; ActiveRecord::Base
499 501 end
500 502 end
501 503  
  504 + def archived?
  505 + (self.parent && self.parent.archived) || self.archived
  506 + end
  507 +
502 508 def self.folder_types
503 509 ['Folder', 'Blog', 'Forum', 'Gallery']
504 510 end
... ... @@ -645,13 +651,21 @@ class Article &lt; ActiveRecord::Base
645 651 end
646 652  
647 653 def hit
648   - self.class.connection.execute('update articles set hits = hits + 1 where id = %d' % self.id.to_i)
649   - self.hits += 1
  654 + if !archived?
  655 + self.class.connection.execute('update articles set hits = hits + 1 where id = %d' % self.id.to_i)
  656 + self.hits += 1
  657 + end
650 658 end
651 659  
652 660 def self.hit(articles)
653   - Article.where(:id => articles.map(&:id)).update_all('hits = hits + 1')
654   - articles.each { |a| a.hits += 1 }
  661 + ids = []
  662 + articles.each do |article|
  663 + if !article.archived?
  664 + ids << article.id
  665 + article.hits += 1
  666 + end
  667 + end
  668 + Article.where(:id => ids).update_all('hits = hits + 1') if !ids.empty?
655 669 end
656 670  
657 671 def can_display_hits?
... ... @@ -861,4 +875,10 @@ class Article &lt; ActiveRecord::Base
861 875 sanitizer.sanitize(text)
862 876 end
863 877  
  878 + def parent_archived?
  879 + if self.parent_id_changed? && self.parent && self.parent.archived?
  880 + errors.add(:parent_folder, N_('is archived!!'))
  881 + end
  882 + end
  883 +
864 884 end
... ...
app/models/comment.rb
... ... @@ -36,6 +36,8 @@ class Comment &lt; ActiveRecord::Base
36 36 end
37 37 end
38 38  
  39 + validate :article_archived?
  40 +
39 41 acts_as_having_settings
40 42  
41 43 xss_terminate :only => [ :body, :title, :name ], :on => 'validation'
... ... @@ -218,4 +220,14 @@ class Comment &lt; ActiveRecord::Base
218 220 user.present? && user == author
219 221 end
220 222  
  223 + def archived?
  224 + self.source && self.source.is_a?(Article) && self.source.archived?
  225 + end
  226 +
  227 + protected
  228 +
  229 + def article_archived?
  230 + errors.add(:article, N_('associated with this comment is archived!')) if archived?
  231 + end
  232 +
221 233 end
... ...
app/views/cms/_archived_warning.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<% if !article.errors.any? %>
  2 + <div class="text-warning">
  3 + <p>
  4 + <i class="icon"></i>
  5 + <%= _("Archived article! It's read-only") %>
  6 + </p>
  7 + </div>
  8 +<% end %>
... ...
app/views/cms/edit.html.erb
1 1 <%= error_messages_for 'article' %>
2 2  
  3 +<% if @article.archived? %>
  4 + <%= render :partial => 'archived_warning', :locals => {:article => @article} %>
  5 +<% end %>
3 6 <div class='<%= (@article.display_media_panel? ? 'with_media_panel' : 'no_media_panel') %>'>
4 7 <%= labelled_form_for 'article', :html => { :multipart => true, :class => @type } do |f| %>
5 8  
... ...
app/views/content_viewer/_article_toolbar.html.erb
... ... @@ -29,7 +29,9 @@
29 29 <%= expirable_button @page, :locale, content, url %>
30 30 <% end %>
31 31  
32   - <%= modal_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : @page.parent))) unless remove_content_button(:new, @page) %>
  32 + <% if !@page.archived? %>
  33 + <%= modal_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : @page.parent))) unless remove_content_button(:new, @page) %>
  34 + <% end %>
33 35  
34 36 <% content = content_tag('span', label_for_clone_article(@page)) %>
35 37 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'new', :id => @page.id, :clone => true, :parent_id => (@page.folder? ? @page : @page.parent), :type => @page.class}) %>
... ... @@ -66,6 +68,9 @@
66 68 <% end %>
67 69 <%= button_without_text(:feed, _('RSS feed'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
68 70 <%= @plugins.dispatch(:article_header_extra_contents, @page).collect { |content| instance_exec(&content) }.join("") %>
  71 + <% if @page.archived? %>
  72 + <%= render :partial => 'cms/archived_warning', :locals => {:article => @page} %>
  73 + <% end %>
69 74 <%= render :partial => 'article_title', :locals => {:no_link => true} %>
70 75 <%= article_translations(@page) %>
71 76 </div>
... ...
app/views/content_viewer/_publishing_info.html.erb
... ... @@ -34,7 +34,7 @@
34 34 <div id='article-sub-header'>
35 35 <% if @page.display_hits? %>
36 36 <div id="article-hits">
37   - <%= n_('Viewed one time', 'Viewed %{num} times', @page.hits) % { :num => @page.hits } %>
  37 + <%= n_('Viewed one time %{desc}', 'Viewed %{num} times %{desc}', @page.hits) % { :num => @page.hits, :desc => @page.archived? ? '<b>'+_('(Not countable anymore)')+'</b>' : '' } %>
38 38 </div>
39 39 <% end %>
40 40  
... ...
app/views/content_viewer/view_page.html.erb
... ... @@ -85,7 +85,7 @@
85 85 <% end %>
86 86 </ul>
87 87  
88   - <% if @page.accept_comments? %>
  88 + <% if !@page.archived? && @page.accept_comments? %>
89 89 <div id='page-comment-form' class='page-comment-form'><%= render :partial => 'comment/comment_form', :locals =>{:url => {:controller => :comment, :action => :create}, :display_link => true, :cancel_triggers_hide => true}%></div>
90 90 <% end %>
91 91 </div><!-- end class="comments" -->
... ...
db/migrate/20151112135709_add_archived_to_articles.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddArchivedToArticles < ActiveRecord::Migration
  2 + def up
  3 + add_column :articles, :archived, :boolean, :default => false
  4 + end
  5 +
  6 + def down
  7 + remove_column :articles, :archived
  8 + end
  9 +end
... ...
db/schema.rb
... ... @@ -167,6 +167,7 @@ ActiveRecord::Schema.define(version: 20160324132518) do
167 167 t.integer "created_by_id"
168 168 t.boolean "show_to_followers", default: true
169 169 t.integer "followers_count", default: 0
  170 + t.boolean "archived", default: false
170 171 end
171 172  
172 173 add_index "articles", ["comments_count"], name: "index_articles_on_comments_count", using: :btree
... ...
plugins/vote/lib/ext/vote.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require_dependency 'models/vote'
  2 +
  3 +class Vote
  4 +
  5 + validate :verify_target_archived
  6 +
  7 + def verify_target_archived
  8 +
  9 + if voteable.kind_of?(Article) || voteable.kind_of?(Comment)
  10 + if voteable.archived?
  11 + errors.add(:base, _("The target is achived and can't accept votes"))
  12 + false
  13 + end
  14 + end
  15 +
  16 + end
  17 +
  18 +end
... ...
plugins/vote/lib/vote_plugin_helper.rb
... ... @@ -2,8 +2,10 @@ module VotePluginHelper
2 2  
3 3 def vote_partial(target, like = true, load_voters=false)
4 4 vote = like ? 1 : -1
  5 +
5 6 like_action = like ? 'like' : 'dislike'
6 7 type = target.kind_of?(Article) ? 'article' : target.kind_of?(Comment) ? 'comment' : nil
  8 + disable_vote = target.archived? ? true : false
7 9  
8 10 proc do
9 11 settings = Noosfero::Plugin::Settings.new(environment, VotePlugin)
... ... @@ -14,7 +16,7 @@ module VotePluginHelper
14 16 active = user ? (like ? user.voted_for?(target) : user.voted_against?(target)) : false
15 17 count = like ? target.votes_for : target.votes_against
16 18  
17   - render(:partial => 'vote/vote', :locals => {:target => target, :active => active, :action => like_action, :count => count, :voters => voters, :vote => vote, :model => type })
  19 + render(:partial => 'vote/vote', :locals => {:target => target, :active => active, :action => like_action, :count => count, :voters => voters, :vote => vote, :model => type, :disable_vote => disable_vote })
18 20 else
19 21 ""
20 22 end
... ...
plugins/vote/public/style.css
... ... @@ -73,3 +73,12 @@
73 73 #article .action .vote-detail img {
74 74 vertical-align: bottom;
75 75 }
  76 +
  77 +.comments-action-bar span.disabled a,
  78 +.comments-action-bar span.disabled a:hover,
  79 +.vote-actions span.disabled a,
  80 +.vote-actions span.disabled a:hover {
  81 + opacity: 0.5;
  82 + pointer-events: none;
  83 + cursor: default;
  84 +}
... ...
plugins/vote/public/vote_actions.js
... ... @@ -16,7 +16,9 @@ function openVotersDialog(div) {
16 16 clearTimeout(openEvent);
17 17 var url = $(div).data('reload_url');
18 18 hideAllVoteDetail();
19   - $.post(url);
  19 + if(url && url != '#'){
  20 + $.post(url);
  21 + }
20 22 }
21 23  
22 24 jQuery('body').live('click', function() { hideAllVoteDetail(); });
... ...
plugins/vote/test/functional/vote_plugin_profile_controller_test.rb
... ... @@ -32,6 +32,18 @@ class VotePluginProfileControllerTest &lt; ActionController::TestCase
32 32 assert profile.votes.empty?
33 33 end
34 34  
  35 + should 'not vote if a target is archived' do
  36 + article = Article.create!(:profile => profile, :name => 'Archived article', :archived => false)
  37 + comment = Comment.create!(:body => 'Comment test', :source => article, :author => profile)
  38 + xhr :post, :vote, :profile => profile.identifier, :id => article.id, :model => 'article', :vote => 1
  39 + assert !profile.votes.empty?
  40 +
  41 + article.update_attributes(:archived => true)
  42 + xhr :post, :vote, :profile => profile.identifier, :id => comment.id, :model => 'comment', :vote => 1
  43 +
  44 + assert !profile.voted_for?(comment)
  45 + end
  46 +
35 47 should 'like comment' do
36 48 xhr :post, :vote, :profile => profile.identifier, :id => comment.id, :model => 'comment', :vote => 1
37 49 assert profile.voted_for?(comment)
... ...
plugins/vote/views/vote/_vote.html.erb
1 1 <%
2   -url = url_for(:controller => 'vote_plugin_profile', :profile => profile.identifier, :action => :vote, :id => target.id, :model => model, :vote => vote)
3   -reload_url = url_for(:controller => 'vote_plugin_profile', :profile => profile.identifier, :action => :reload_vote, :id => target.id, :model => model, :vote => vote)
  2 +if disable_vote
  3 + url = reload_url = '#'
  4 + class_action = 'disabled'
  5 +else
  6 + url = url_for(:controller => 'vote_plugin_profile', :profile => profile.identifier, :action => :vote, :id => target.id, :model => model, :vote => vote)
  7 + reload_url = url_for(:controller => 'vote_plugin_profile', :profile => profile.identifier, :action => :reload_vote, :id => target.id, :model => model, :vote => vote)
  8 +end
4 9 %>
5 10  
6   -<span id="vote_<%= model %>_<%= target.id %>_<%= vote %>" data-reload_url=<%= reload_url %> class="vote-action action <%= action %>-action">
  11 +<span id="vote_<%= model %>_<%= target.id %>_<%= vote %>" data-reload_url=<%= reload_url %> class="vote-action action <%= action %>-action <%= class_action %>">
7 12  
8 13 <%= link_to content_tag(:span, count, :class=>'like-action-counter') + content_tag(:span, '', :class=>"action-icon #{action}"), url, :class => "#{active ? 'like-action-active':''} #{user ? '':'disabled'} require-login-popup" %>
9 14  
... ...
po/noosfero.pot
... ... @@ -5073,6 +5073,10 @@ msgid_plural &quot;Viewed %{num} times&quot;
5073 5073 msgstr[0] ""
5074 5074 msgstr[1] ""
5075 5075  
  5076 +#: app/views/content_viewer/_publishing_info.html.erb:19
  5077 +msgid "(Not countable anymore)"
  5078 +msgstr ""
  5079 +
5076 5080 #: app/views/content_viewer/versions_diff.html.erb:5
5077 5081 msgid "Changes on \"%s\""
5078 5082 msgstr ""
... ...
po/pt/noosfero.po
... ... @@ -5231,11 +5231,15 @@ msgid &quot;Reply&quot;
5231 5231 msgstr "Responder"
5232 5232  
5233 5233 #: app/views/content_viewer/_publishing_info.html.erb:19
5234   -msgid "Viewed one time"
5235   -msgid_plural "Viewed %{num} times"
  5234 +msgid "Viewed one time %{desc}"
  5235 +msgid_plural "Viewed %{num} times %{desc}"
5236 5236 msgstr[0] "Visualizado uma vez"
5237 5237 msgstr[1] "Visualizado %{num} vezes"
5238 5238  
  5239 +#: app/views/content_viewer/_publishing_info.html.erb:19
  5240 +msgid "(Not countable anymore)"
  5241 +msgstr "(Não mais contabilizado)"
  5242 +
5239 5243 #: app/views/content_viewer/versions_diff.html.erb:5
5240 5244 msgid "Changes on \"%s\""
5241 5245 msgstr "Mudanças em \"%s\""
... ...
public/images/icons-app/article-private-icon.png 0 → 100644

273 Bytes

public/images/icons-app/article-public-icon.png 0 → 100644

570 Bytes

public/images/icons-app/warning-icon.png 0 → 100644

509 Bytes

public/javascripts/article.js
... ... @@ -212,4 +212,10 @@ jQuery(function($) {
212 212 $("#article_published_true").click(show_hide_privacy_options);
213 213 $(".custom_privacy_option").click(show_hide_token_input);
214 214  
  215 + //Workaround to pointer-events:none CSS3
  216 + $('a.disabled').click(function(e){
  217 + e.preventDefault();
  218 + return false;
  219 + });
  220 +
215 221 });
... ...
public/stylesheets/cms.scss
1 1 @import 'cms/fetch-external-feed';
2 2 @import 'cms/media-panel';
  3 +@import 'cms/access-options';
3 4  
4 5 table.cms-articles {
5 6 table-layout: fixed;
... ...
public/stylesheets/cms/access-options.scss 0 → 100644
... ... @@ -0,0 +1,67 @@
  1 +//Variables
  2 +$icons-size: 24px;
  3 +$warning-color: #e75e40;
  4 +$description-color: #666;
  5 +$access-types: public, private;
  6 +
  7 +.text-warning {
  8 + text-align: center;
  9 + color: $warning-color;
  10 + font: {
  11 + size: 15px;
  12 + weight: bold;
  13 + }
  14 +
  15 + p i.icon {
  16 + display: inline-block;
  17 + font-size: inherit;
  18 + text-rendering: auto;
  19 + background: url('images/icons-app/warning-icon.png');
  20 + vertical-align: middle;
  21 + margin-top: -5px;
  22 + width: $icons-size;
  23 + height: $icons-size;
  24 + border: none;
  25 + }
  26 +}
  27 +
  28 +#edit-article-options {
  29 +
  30 + .access-itens .access-item {
  31 + input,label {
  32 + cursor: pointer;
  33 + }
  34 + }
  35 +
  36 + .access-item {
  37 + margin: 15px 0;
  38 + label {
  39 + font: {
  40 + size: 13px;
  41 + weight: bold;
  42 + }
  43 + }
  44 + }
  45 +
  46 + @each $access_type in $access-types {
  47 + .access-#{$access_type}-icon, .access-#{$access_type}-icon {
  48 + display: inline-block;
  49 + margin-right: 5px;
  50 + width: $icons-size;
  51 + height: $icons-size;
  52 + vertical-align: middle;
  53 + background-image: url('images/icons-app/article-#{$access_type}-icon.png');
  54 + }
  55 + }
  56 +
  57 + .access-note {
  58 + display: block;
  59 + margin: 0;
  60 + color: $description-color;
  61 + margin-left: 50px;
  62 + font: {
  63 + size: 12px;
  64 + weight: normal;
  65 + }
  66 + }
  67 +}
... ...
test/functional/cms_controller_test.rb
... ... @@ -694,6 +694,34 @@ class CmsControllerTest &lt; ActionController::TestCase
694 694 end
695 695 end
696 696  
  697 + should "marks a article like archived" do
  698 + article = create(Article, :profile => profile, :name => 'test', :published => true, :archived => false)
  699 +
  700 + post :edit, :profile => profile.identifier, :id => article.id, :article => {:archived => true}
  701 + get :edit, :profile => profile.identifier, :id => article.id
  702 + assert_tag :tag => 'input', :attributes => { :type => 'checkbox', :name => 'article[archived]', :id => 'article_archived', :checked => 'checked' }
  703 +
  704 + end
  705 +
  706 + should "try add children into archived folders" do
  707 + folder = create(Folder, :profile => profile, :name => 'test', :published => true, :archived => false)
  708 + article_child = create(Article, :profile => profile, :name => 'test child', :parent_id => folder.id, :published => true, :archived => false)
  709 +
  710 + get :edit, :profile => profile.identifier, :id => folder.id
  711 + assert_tag :tag => 'input', :attributes => { :type => 'checkbox', :name => 'article[archived]', :id => 'article_archived' }
  712 +
  713 + post :edit, :profile => profile.identifier, :id => folder.id, :article => {:archived => true}
  714 +
  715 + get :edit, :profile => profile.identifier, :id => article_child.id
  716 + assert_tag :tag => 'div', :attributes => { :class => 'text-warning'}
  717 +
  718 + err = assert_raises ActiveRecord::RecordInvalid do
  719 + another_article_child = create(Article, :profile => profile, :name => 'other test child', :parent_id => folder.id, :published => true, :archived => false)
  720 + end
  721 + assert_match 'Parent folder is archived', err.message
  722 +
  723 + end
  724 +
697 725 should 'be able to add image with alignment' do
698 726 post :new, :type => 'TinyMceArticle', :profile => profile.identifier, :article => { :name => 'image-alignment', :body => "the text of the article with image <img src='#' align='right'/> right align..." }
699 727 saved = TinyMceArticle.find_by(name: 'image-alignment')
... ...
test/unit/article_test.rb
... ... @@ -2168,10 +2168,21 @@ class ArticleTest &lt; ActiveSupport::TestCase
2168 2168 a3 = fast_create(Article)
2169 2169 Article.hit([a1, a2, a3])
2170 2170 Article.hit([a2, a3])
  2171 +
2171 2172 assert_equal [1, 2, 2], [a1.hits, a2.hits, a3.hits]
2172 2173 assert_equal [1, 2, 2], [a1.reload.hits, a2.reload.hits, a3.reload.hits]
2173 2174 end
2174 2175  
  2176 + should 'not update hit attribute of archiveds articles' do
  2177 + a1 = fast_create(Article)
  2178 + a2 = fast_create(Article, :archived => true)
  2179 + a3 = fast_create(Article, :archived => true)
  2180 + Article.hit([a1, a2, a3])
  2181 +
  2182 + assert_equal [1, 0, 0], [a1.hits, a2.hits, a3.hits]
  2183 + assert_equal [1, 0, 0], [a1.reload.hits, a2.reload.hits, a3.reload.hits]
  2184 + end
  2185 +
2175 2186 should 'vote in a article' do
2176 2187 article = create(Article, :name => 'Test', :profile => profile, :last_changed_by => nil)
2177 2188 profile.vote(article, 5)
... ... @@ -2288,5 +2299,25 @@ class ArticleTest &lt; ActiveSupport::TestCase
2288 2299 assert_equal 10, article.reload.person_followers.count
2289 2300 end
2290 2301  
  2302 + should 'check if a article is archived' do
  2303 + folder = Folder.create!(:name => 'Parent Archived', :profile => profile)
  2304 + a1 = Article.create!(:name => 'Test', :profile => profile, :parent_id => folder.id, :archived => false)
  2305 + a2 = Article.create!(:name => 'Test 2', :profile => profile, :archived => true)
  2306 + folder.update_attributes(:archived => true)
  2307 + a1.reload
  2308 +
  2309 + assert a1.archived?
  2310 + assert a2.archived?
  2311 + end
  2312 +
  2313 + should 'try add a child article to a archived folder' do
  2314 + folder = Folder.create!(:name => 'Parent Archived', :profile => profile, :archived => true)
  2315 +
  2316 + err = assert_raises ActiveRecord::RecordInvalid do
  2317 + a1 = Article.create!(:name => 'Test', :profile => profile, :parent_id => folder.id, :archived => false)
  2318 + end
  2319 +
  2320 + assert_match 'Parent folder is archived', err.message
  2321 + end
2291 2322  
2292 2323 end
... ...
test/unit/comment_test.rb
... ... @@ -95,6 +95,17 @@ class CommentTest &lt; ActiveSupport::TestCase
95 95 assert_equal cc + 1, ActionTracker::Record.find(activity.id).comments_count
96 96 end
97 97  
  98 + should 'try add a comment to a archived article' do
  99 + person = fast_create(Person)
  100 + article = Article.create!(:name => 'Test', :profile => person, :archived => true)
  101 +
  102 + err = assert_raises ActiveRecord::RecordInvalid do
  103 + comment = create(Comment, :source => article, :author_id => person.id)
  104 + end
  105 +
  106 + assert_match 'Article associated with this comment is archived', err.message
  107 + end
  108 +
98 109 should 'provide author name for authenticated authors' do
99 110 owner = create_user('testuser').person
100 111 assert_equal 'testuser', build(Comment, :author => owner).author_name
... ...