Commit d7fee6b905378a4d92b4c9da99b180003920b470
Committed by
Antonio Terceiro
1 parent
f5703d91
Exists in
master
and in
29 other branches
Adding articles translation support
(ActionItem894)
Showing
32 changed files
with
736 additions
and
5 deletions
Show diff stats
app/controllers/my_profile/cms_controller.rb
@@ -89,6 +89,7 @@ class CmsController < MyProfileController | @@ -89,6 +89,7 @@ class CmsController < MyProfileController | ||
89 | @article = profile.articles.find(params[:id]) | 89 | @article = profile.articles.find(params[:id]) |
90 | @parent_id = params[:parent_id] | 90 | @parent_id = params[:parent_id] |
91 | @type = params[:type] || @article.class.to_s | 91 | @type = params[:type] || @article.class.to_s |
92 | + translations if @article.translatable? | ||
92 | continue = params[:continue] | 93 | continue = params[:continue] |
93 | 94 | ||
94 | refuse_blocks | 95 | refuse_blocks |
@@ -138,6 +139,8 @@ class CmsController < MyProfileController | @@ -138,6 +139,8 @@ class CmsController < MyProfileController | ||
138 | @parent_id = parent.id | 139 | @parent_id = parent.id |
139 | end | 140 | end |
140 | 141 | ||
142 | + translations if @article.translatable? | ||
143 | + | ||
141 | @article.profile = profile | 144 | @article.profile = profile |
142 | @article.last_changed_by = user | 145 | @article.last_changed_by = user |
143 | 146 | ||
@@ -367,5 +370,11 @@ class CmsController < MyProfileController | @@ -367,5 +370,11 @@ class CmsController < MyProfileController | ||
367 | def per_page | 370 | def per_page |
368 | 10 | 371 | 10 |
369 | end | 372 | end |
373 | + | ||
374 | + def translations | ||
375 | + @locales = Noosfero.locales.invert.reject { |name, lang| !@article.possible_translations.include?(lang) } | ||
376 | + @selected_locale = @article.language || FastGettext.locale | ||
377 | + end | ||
378 | + | ||
370 | end | 379 | end |
371 | 380 |
app/controllers/public/content_viewer_controller.rb
@@ -51,6 +51,8 @@ class ContentViewerController < ApplicationController | @@ -51,6 +51,8 @@ class ContentViewerController < ApplicationController | ||
51 | return | 51 | return |
52 | end | 52 | end |
53 | 53 | ||
54 | + redirect_to_translation | ||
55 | + | ||
54 | # At this point the page will be showed | 56 | # At this point the page will be showed |
55 | @page.hit | 57 | @page.hit |
56 | 58 | ||
@@ -85,7 +87,11 @@ class ContentViewerController < ApplicationController | @@ -85,7 +87,11 @@ class ContentViewerController < ApplicationController | ||
85 | @page.posts | 87 | @page.posts |
86 | end | 88 | end |
87 | 89 | ||
90 | + posts = posts.native_translations if @page.blog? && @page.display_posts_in_current_language? | ||
91 | + | ||
88 | @posts = posts.paginate({ :page => params[:npage], :per_page => @page.posts_per_page }.merge(Article.display_filter(user, profile))) | 92 | @posts = posts.paginate({ :page => params[:npage], :per_page => @page.posts_per_page }.merge(Article.display_filter(user, profile))) |
93 | + | ||
94 | + @posts.map!{ |p| p.get_translation_to(FastGettext.locale) } if @page.blog? && @page.display_posts_in_current_language? | ||
89 | end | 95 | end |
90 | 96 | ||
91 | if @page.folder? && @page.gallery? | 97 | if @page.folder? && @page.gallery? |
@@ -125,4 +131,24 @@ class ContentViewerController < ApplicationController | @@ -125,4 +131,24 @@ class ContentViewerController < ApplicationController | ||
125 | def per_page | 131 | def per_page |
126 | 12 | 132 | 12 |
127 | end | 133 | end |
134 | + | ||
135 | + def redirect_to_translation | ||
136 | + locale = FastGettext.locale | ||
137 | + if !@page.language.nil? && @page.language != locale | ||
138 | + translations = [@page.native_translation] + @page.native_translation.translations | ||
139 | + urls = translations.map{ |t| URI.parse(url_for(t.url)).path } | ||
140 | + urls << URI.parse(url_for(profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }))).path | ||
141 | + urls << URI.parse(url_for(profile.admin_url.merge(:controller => 'cms', :action => 'new'))).path | ||
142 | + referer = URI.parse(url_for(request.referer)).path unless request.referer.blank? | ||
143 | + unless urls.include?(referer) | ||
144 | + translations.each do |translation| | ||
145 | + if translation.language == locale | ||
146 | + @page = translation | ||
147 | + redirect_to :profile => @page.profile.identifier, :page => @page.explode_path | ||
148 | + end | ||
149 | + end | ||
150 | + end | ||
151 | + end | ||
152 | + end | ||
153 | + | ||
128 | end | 154 | end |
app/helpers/application_helper.rb
@@ -27,7 +27,7 @@ module ApplicationHelper | @@ -27,7 +27,7 @@ module ApplicationHelper | ||
27 | include AccountHelper | 27 | include AccountHelper |
28 | 28 | ||
29 | def locale | 29 | def locale |
30 | - FastGettext.locale | 30 | + (@page && !@page.language.blank?) ? @page.language : FastGettext.locale |
31 | end | 31 | end |
32 | 32 | ||
33 | def load_web2_conf | 33 | def load_web2_conf |
app/helpers/content_viewer_helper.rb
@@ -35,4 +35,16 @@ module ContentViewerHelper | @@ -35,4 +35,16 @@ module ContentViewerHelper | ||
35 | text && (text.first(40) + (text.size > 40 ? '…' : '')) | 35 | text && (text.first(40) + (text.size > 40 ? '…' : '')) |
36 | end | 36 | end |
37 | 37 | ||
38 | + def article_translations(article) | ||
39 | + unless article.native_translation.translations.empty? | ||
40 | + links = (article.native_translation.translations + [article.native_translation]).map do |translation| | ||
41 | + { Noosfero.locales[translation.language] => { :href => url_for(translation.url) } } | ||
42 | + end | ||
43 | + content_tag(:div, link_to(_('Translations'), '#', | ||
44 | + :onclick => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false", | ||
45 | + :class => 'article-translations-menu simplemenu-trigger up'), | ||
46 | + :class => 'article-translations') | ||
47 | + end | ||
48 | + end | ||
49 | + | ||
38 | end | 50 | end |
app/models/article.rb
@@ -28,6 +28,10 @@ class Article < ActiveRecord::Base | @@ -28,6 +28,10 @@ class Article < ActiveRecord::Base | ||
28 | 28 | ||
29 | belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' | 29 | belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' |
30 | 30 | ||
31 | + has_many :translations, :class_name => 'Article', :foreign_key => :translation_of_id | ||
32 | + belongs_to :translation_of, :class_name => 'Article', :foreign_key => :translation_of_id | ||
33 | + before_destroy :rotate_translations | ||
34 | + | ||
31 | before_create do |article| | 35 | before_create do |article| |
32 | article.published_at = article.created_at if article.published_at.nil? | 36 | article.published_at = article.created_at if article.published_at.nil? |
33 | if article.reference_article && !article.parent | 37 | if article.reference_article && !article.parent |
@@ -53,6 +57,10 @@ class Article < ActiveRecord::Base | @@ -53,6 +57,10 @@ class Article < ActiveRecord::Base | ||
53 | URL_FORMAT = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\Z/ix | 57 | URL_FORMAT = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\Z/ix |
54 | 58 | ||
55 | validates_format_of :external_link, :with => URL_FORMAT, :if => lambda { |article| !article.external_link.blank? } | 59 | validates_format_of :external_link, :with => URL_FORMAT, :if => lambda { |article| !article.external_link.blank? } |
60 | + validate :known_language | ||
61 | + validate :used_translation | ||
62 | + validate :native_translation_must_have_language | ||
63 | + validate :translation_must_have_language | ||
56 | 64 | ||
57 | def is_trackable? | 65 | def is_trackable? |
58 | self.published? && self.notifiable? && self.advertise? | 66 | self.published? && self.notifiable? && self.advertise? |
@@ -251,6 +259,65 @@ class Article < ActiveRecord::Base | @@ -251,6 +259,65 @@ class Article < ActiveRecord::Base | ||
251 | false | 259 | false |
252 | end | 260 | end |
253 | 261 | ||
262 | + named_scope :native_translations, :conditions => { :translation_of_id => nil } | ||
263 | + | ||
264 | + def translatable? | ||
265 | + false | ||
266 | + end | ||
267 | + | ||
268 | + def native_translation | ||
269 | + self.translation_of.nil? ? self : self.translation_of | ||
270 | + end | ||
271 | + | ||
272 | + def possible_translations | ||
273 | + possibilities = Noosfero.locales.keys - self.native_translation.translations(:select => :language).map(&:language) - [self.native_translation.language] | ||
274 | + possibilities << self.language unless self.language_changed? | ||
275 | + possibilities | ||
276 | + end | ||
277 | + | ||
278 | + def known_language | ||
279 | + unless self.language.blank? | ||
280 | + errors.add(:language, N_('Language not supported by Noosfero')) unless Noosfero.locales.key?(self.language) | ||
281 | + end | ||
282 | + end | ||
283 | + | ||
284 | + def used_translation | ||
285 | + unless self.language.blank? or self.translation_of.nil? | ||
286 | + errors.add(:language, N_('Language is already used')) unless self.possible_translations.include?(self.language) | ||
287 | + end | ||
288 | + end | ||
289 | + | ||
290 | + def translation_must_have_language | ||
291 | + unless self.translation_of.nil? | ||
292 | + errors.add(:language, N_('Language must be choosen')) if self.language.blank? | ||
293 | + end | ||
294 | + end | ||
295 | + | ||
296 | + def native_translation_must_have_language | ||
297 | + unless self.translation_of.nil? | ||
298 | + errors.add_to_base(N_('A language must be choosen for the native article')) if self.translation_of.language.blank? | ||
299 | + end | ||
300 | + end | ||
301 | + | ||
302 | + def rotate_translations | ||
303 | + unless self.translations.empty? | ||
304 | + rotate = self.translations | ||
305 | + root = rotate.shift | ||
306 | + root.update_attribute(:translation_of_id, nil) | ||
307 | + root.translations = rotate | ||
308 | + end | ||
309 | + end | ||
310 | + | ||
311 | + def get_translation_to(locale) | ||
312 | + if self.language.nil? || self.language == locale | ||
313 | + self | ||
314 | + elsif self.native_translation.language == locale | ||
315 | + self.native_translation | ||
316 | + else | ||
317 | + self.native_translation.translations.first(:conditions => { :language => locale }) || self | ||
318 | + end | ||
319 | + end | ||
320 | + | ||
254 | def published? | 321 | def published? |
255 | if self.published | 322 | if self.published |
256 | if self.parent && !self.parent.published? | 323 | if self.parent && !self.parent.published? |
app/models/blog.rb
@@ -57,4 +57,8 @@ class Blog < Folder | @@ -57,4 +57,8 @@ class Blog < Folder | ||
57 | settings_items :visualization_format, :type => :string, :default => 'full' | 57 | settings_items :visualization_format, :type => :string, :default => 'full' |
58 | validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format | 58 | validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format |
59 | 59 | ||
60 | + settings_items :display_posts_in_current_language, :type => :boolean, :default => true | ||
61 | + | ||
62 | + alias :display_posts_in_current_language? :display_posts_in_current_language | ||
63 | + | ||
60 | end | 64 | end |
app/models/blog_archives_block.rb
@@ -24,7 +24,7 @@ class BlogArchivesBlock < Block | @@ -24,7 +24,7 @@ class BlogArchivesBlock < Block | ||
24 | owner_blog = self.blog | 24 | owner_blog = self.blog |
25 | return nil unless owner_blog | 25 | return nil unless owner_blog |
26 | results = '' | 26 | results = '' |
27 | - owner_blog.posts.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| | 27 | + owner_blog.posts.native_translations.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| |
28 | results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})")) | 28 | results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})")) |
29 | results << "<ul class='#{year}-archive'>" | 29 | results << "<ul class='#{year}-archive'>" |
30 | results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month| | 30 | results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month| |
app/models/environment.rb
@@ -108,7 +108,7 @@ class Environment < ActiveRecord::Base | @@ -108,7 +108,7 @@ class Environment < ActiveRecord::Base | ||
108 | 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"), | 108 | 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"), |
109 | 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'), | 109 | 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'), |
110 | 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'), | 110 | 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'), |
111 | - 'xmpp_chat' => _('XMPP/Jabber based chat'), | 111 | + 'xmpp_chat' => _('XMPP/Jabber based chat') |
112 | } | 112 | } |
113 | end | 113 | end |
114 | 114 |
app/models/event.rb
app/models/rss_feed.rb
@@ -62,7 +62,8 @@ class RssFeed < Article | @@ -62,7 +62,8 @@ class RssFeed < Article | ||
62 | include ActionController::UrlWriter | 62 | include ActionController::UrlWriter |
63 | def fetch_articles | 63 | def fetch_articles |
64 | if parent && parent.has_posts? | 64 | if parent && parent.has_posts? |
65 | - return parent.posts.find(:all, :conditions => ['published = ?', true], :limit => self.limit, :order => 'id desc') | 65 | + language = self.language.blank? ? {} : { :language => self.language } |
66 | + return parent.posts.find(:all, :conditions => { :published => true }.merge(language), :limit => self.limit, :order => 'id desc') | ||
66 | end | 67 | end |
67 | 68 | ||
68 | articles = | 69 | articles = |
app/models/textile_article.rb
app/models/tiny_mce_article.rb
app/views/cms/_blog.rhtml
@@ -56,8 +56,11 @@ | @@ -56,8 +56,11 @@ | ||
56 | 56 | ||
57 | <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, [5, 10, 20, 50, 100])) %> | 57 | <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, [5, 10, 20, 50, 100])) %> |
58 | 58 | ||
59 | +<%= labelled_check_box(_('Display posts in current language, if available'), 'article[display_posts_in_current_language]', '1', @article.display_posts_in_current_language?) %> | ||
60 | + | ||
59 | <% f.fields_for 'feed', @article.feed do |feed| %> | 61 | <% f.fields_for 'feed', @article.feed do |feed| %> |
60 | <%= labelled_form_field(_('Limit of posts in RSS Feed'), feed.select(:limit, [5, 10, 20, 50])) %> | 62 | <%= labelled_form_field(_('Limit of posts in RSS Feed'), feed.select(:limit, [5, 10, 20, 50])) %> |
63 | + <%= labelled_form_field(_('Include in RSS Feed only posts from language:'), feed.select(:language, { _('All') => nil }.merge(Noosfero.locales.invert))) %> | ||
61 | <% end %> | 64 | <% end %> |
62 | 65 | ||
63 | <% f.fields_for 'external_feed_builder', @article.external_feed do |efeed| %> | 66 | <% f.fields_for 'external_feed_builder', @article.external_feed do |efeed| %> |
app/views/cms/_event.rhtml
@@ -5,6 +5,8 @@ | @@ -5,6 +5,8 @@ | ||
5 | 5 | ||
6 | <%= required f.text_field('name', :size => '64') %> | 6 | <%= required f.text_field('name', :size => '64') %> |
7 | 7 | ||
8 | +<%= render :partial => 'translatable' %> | ||
9 | + | ||
8 | <%= labelled_form_field(_('Start date'), pick_date(:article, :start_date)) %> | 10 | <%= labelled_form_field(_('Start date'), pick_date(:article, :start_date)) %> |
9 | 11 | ||
10 | <%= labelled_form_field(_('End date'), pick_date(:article, :end_date)) %> | 12 | <%= labelled_form_field(_('End date'), pick_date(:article, :end_date)) %> |
app/views/cms/_textile_article.rhtml
@@ -4,6 +4,8 @@ | @@ -4,6 +4,8 @@ | ||
4 | 4 | ||
5 | <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64')) %> | 5 | <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64')) %> |
6 | 6 | ||
7 | +<%= render :partial => 'translatable' %> | ||
8 | + | ||
7 | <br style="clear: both;"/> | 9 | <br style="clear: both;"/> |
8 | <%= button :add, _("Lead"), '#', :id => "lead-button", :style => "margin-left: 0px;" %> | 10 | <%= button :add, _("Lead"), '#', :id => "lead-button", :style => "margin-left: 0px;" %> |
9 | <em><%= _('Used when a short version of your text is needed.') %></em> | 11 | <em><%= _('Used when a short version of your text is needed.') %></em> |
app/views/cms/_tiny_mce_article.rhtml
@@ -11,6 +11,8 @@ | @@ -11,6 +11,8 @@ | ||
11 | <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64')) %> | 11 | <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64')) %> |
12 | <% end %> | 12 | <% end %> |
13 | 13 | ||
14 | + <%= render :partial => 'translatable' %> | ||
15 | + | ||
14 | <br style="clear: both;"/> | 16 | <br style="clear: both;"/> |
15 | <%= button :add, _("Lead"), '#', :id => "lead-button", :style => "margin-left: 0px;" %> | 17 | <%= button :add, _("Lead"), '#', :id => "lead-button", :style => "margin-left: 0px;" %> |
16 | <em><%= _('Used when a short version of your text is needed.') %></em> | 18 | <em><%= _('Used when a short version of your text is needed.') %></em> |
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +<% if @article.translatable? %> | ||
2 | + <div class="article-translation-field"> | ||
3 | + <%= label :article, :language, _('Language') %> <br /> | ||
4 | + <%= select :article, :language, @locales, { :selected => @selected_locale, :include_blank => true } %> | ||
5 | + <%= hidden_field(:article, :translation_of_id) %> | ||
6 | + </div> | ||
7 | +<% end %> |
app/views/content_viewer/view_page.rhtml
@@ -35,6 +35,11 @@ | @@ -35,6 +35,11 @@ | ||
35 | <% end %> | 35 | <% end %> |
36 | <% if !(profile.kind_of?(Enterprise) && environment.enabled?('disable_cms')) %> | 36 | <% if !(profile.kind_of?(Enterprise) && environment.enabled?('disable_cms')) %> |
37 | <% if !@page.gallery? %> | 37 | <% if !@page.gallery? %> |
38 | + <%= link_to _('Add translation'), | ||
39 | + profile.admin_url.merge(:controller => 'cms', :action => 'new', | ||
40 | + :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)), | ||
41 | + :type => @page.type, :article => { :translation_of_id => @page.native_translation.id }), | ||
42 | + :class => 'button with-text icon-locale' if @page.translatable? && !@page.native_translation.language.blank? %> | ||
38 | <%= lightbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) %> | 43 | <%= lightbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) %> |
39 | <% end %> | 44 | <% end %> |
40 | <% if (@page.folder? && !@page.has_posts?) || (@page.parent && @page.parent.folder? && !@page.parent.has_posts?) %> | 45 | <% if (@page.folder? && !@page.has_posts?) || (@page.parent && @page.parent.folder? && !@page.parent.has_posts?) %> |
@@ -50,6 +55,7 @@ | @@ -50,6 +55,7 @@ | ||
50 | <%= link_to content_tag( 'span', _('Suggest an article') ), profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}), :class => 'button with-text icon-new' %> | 55 | <%= link_to content_tag( 'span', _('Suggest an article') ), profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}), :class => 'button with-text icon-new' %> |
51 | <% end %> | 56 | <% end %> |
52 | <% end %> | 57 | <% end %> |
58 | + <%= article_translations(@page) %> | ||
53 | <div id="article-header"> | 59 | <div id="article-header"> |
54 | <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %> | 60 | <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %> |
55 | <%= article_title(@page, :no_link => true) %> | 61 | <%= article_title(@page, :no_link => true) %> |
db/migrate/20101205034144_add_language_and_translation_of_id_to_article.rb
0 → 100644
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +class AddLanguageAndTranslationOfIdToArticle < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + add_column :articles, :translation_of_id, :interger | ||
4 | + add_column :articles, :language, :string | ||
5 | + | ||
6 | + add_column :article_versions, :translation_of_id, :interger | ||
7 | + add_column :article_versions, :language, :string | ||
8 | + | ||
9 | + add_index :articles, :translation_of_id | ||
10 | + | ||
11 | + select_all("select id, setting from articles where type = 'Blog'").each do |blog| | ||
12 | + settings = YAML.load(blog['setting']) | ||
13 | + settings[:display_posts_in_current_language] = true | ||
14 | + assignments = ActiveRecord::Base.sanitize_sql_for_assignment(:setting => settings.to_yaml) | ||
15 | + update("update articles set %s where id = %d" % [assignments, blog['id']]) | ||
16 | + end | ||
17 | + | ||
18 | + end | ||
19 | + | ||
20 | + def self.down | ||
21 | + remove_index :articles, :translation_of_id | ||
22 | + | ||
23 | + remove_column :article_versions, :translation_of_id | ||
24 | + remove_column :article_versions, :language | ||
25 | + | ||
26 | + remove_column :articles, :language | ||
27 | + remove_column :articles, :translation_of_id | ||
28 | + end | ||
29 | +end |
features/edit_article.feature
@@ -133,3 +133,32 @@ Feature: edit article | @@ -133,3 +133,32 @@ Feature: edit article | ||
133 | Then I should be on "My new article" edit page | 133 | Then I should be on "My new article" edit page |
134 | And the "Title" field should contain "My new article" | 134 | And the "Title" field should contain "My new article" |
135 | And the "Text" field should contain "text for the new article" | 135 | And the "Text" field should contain "text for the new article" |
136 | + | ||
137 | + Scenario: add a translation to an article | ||
138 | + Given I am on Joao Silva's sitemap | ||
139 | + And I follow "Save the whales" | ||
140 | + Then I should not see "Add translation" | ||
141 | + And I follow "Edit" | ||
142 | + And I select "English" from "Language" | ||
143 | + Then I press "Save" | ||
144 | + And I follow "Add translation" | ||
145 | + And I fill in "Title" with "Mi neuvo artículo" | ||
146 | + And I select "Español" from "Language" | ||
147 | + When I press "Save" | ||
148 | + Then I should be on /joaosilva/save-the-whales | ||
149 | + And I should see "Translations" | ||
150 | + | ||
151 | + Scenario: not add a translation without a language | ||
152 | + Given the following articles | ||
153 | + | owner | name | language | | ||
154 | + | joaosilva | Article in English | en | | ||
155 | + And I am on Joao Silva's sitemap | ||
156 | + And I follow "Article in English" | ||
157 | + And I follow "Add translation" | ||
158 | + And I fill in "Title" with "Article in Portuguese" | ||
159 | + When I press "Save" | ||
160 | + Then I should see "Language must be choosen" | ||
161 | + And I select "Português" from "Language" | ||
162 | + When I press "Save" | ||
163 | + Then I should not see "Language must be choosen" | ||
164 | + And I should be on /joaosilva/article-in-english |
public/designs/icons/tango/style.css
@@ -75,3 +75,4 @@ | @@ -75,3 +75,4 @@ | ||
75 | .icon-reply { background-image: url(Tango/16x16/actions/mail-reply-sender.png) } | 75 | .icon-reply { background-image: url(Tango/16x16/actions/mail-reply-sender.png) } |
76 | .icon-newforum { background-image: url(Tango/16x16/apps/system-users.png) } | 76 | .icon-newforum { background-image: url(Tango/16x16/apps/system-users.png) } |
77 | .icon-newgallery { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) } | 77 | .icon-newgallery { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) } |
78 | +.icon-locale { background-image: url(Tango/16x16/apps/preferences-desktop-locale.png) } | ||
78 | \ No newline at end of file | 79 | \ No newline at end of file |
public/stylesheets/application.css
@@ -5232,6 +5232,49 @@ h1#agenda-title { | @@ -5232,6 +5232,49 @@ h1#agenda-title { | ||
5232 | margin-top: 10px; | 5232 | margin-top: 10px; |
5233 | } | 5233 | } |
5234 | 5234 | ||
5235 | + | ||
5236 | +.article-translations-menu { | ||
5237 | + float: right; | ||
5238 | + bottom: -15px; | ||
5239 | +} | ||
5240 | + | ||
5241 | +.opera .article-translations-menu { | ||
5242 | + bottom: -8px; | ||
5243 | +} | ||
5244 | + | ||
5245 | +.webkit .article-translations-menu { | ||
5246 | + bottom: -10px; | ||
5247 | +} | ||
5248 | + | ||
5249 | +.article-translations-menu { | ||
5250 | + text-decoration: none !important; | ||
5251 | + height: 0; | ||
5252 | +} | ||
5253 | + | ||
5254 | +.article-translations .menu-submenu-header, | ||
5255 | +.article-translations .menu-submenu-content, | ||
5256 | +.article-translations .menu-submenu-footer { | ||
5257 | + background: none; | ||
5258 | +} | ||
5259 | + | ||
5260 | +.article-translations .menu-submenu { | ||
5261 | + bottom: auto; | ||
5262 | + top: 60px; | ||
5263 | + right: 0; | ||
5264 | + border: 1px solid; | ||
5265 | + background: #fff; | ||
5266 | +} | ||
5267 | + | ||
5268 | +.msie7 .article-translations .menu-submenu, | ||
5269 | +.webkit .article-translations .menu-submenu, | ||
5270 | +.opera .article-translations .menu-submenu { | ||
5271 | + top: 30px; | ||
5272 | +} | ||
5273 | + | ||
5274 | +.article-translations .menu-submenu-list { | ||
5275 | + list-style: none; | ||
5276 | +} | ||
5277 | + | ||
5235 | /* Forum */ | 5278 | /* Forum */ |
5236 | 5279 | ||
5237 | .forum-posts .pagination { | 5280 | .forum-posts .pagination { |
test/functional/application_controller_test.rb
@@ -419,4 +419,26 @@ class ApplicationControllerTest < Test::Unit::TestCase | @@ -419,4 +419,26 @@ class ApplicationControllerTest < Test::Unit::TestCase | ||
419 | assert_tag :tag => 'meta', :attributes => { :name => 'description', :content => assigns(:environment).name } | 419 | assert_tag :tag => 'meta', :attributes => { :name => 'description', :content => assigns(:environment).name } |
420 | end | 420 | end |
421 | 421 | ||
422 | + should 'set html lang as the article language if an article is present and has a language' do | ||
423 | + a = fast_create(Article, :name => 'test article', :language => 'fr') | ||
424 | + @controller.instance_variable_set('@page', a) | ||
425 | + FastGettext.stubs(:locale).returns('es') | ||
426 | + get :index | ||
427 | + assert_tag :html, :attributes => { :lang => 'fr' } | ||
428 | + end | ||
429 | + | ||
430 | + should 'set html lang as locale if no page present' do | ||
431 | + FastGettext.stubs(:locale).returns('es') | ||
432 | + get :index | ||
433 | + assert_tag :html, :attributes => { :lang => 'es' } | ||
434 | + end | ||
435 | + | ||
436 | + should 'set html lang as locale if page has no language' do | ||
437 | + a = fast_create(Article, :name => 'test article', :language => nil) | ||
438 | + @controller.instance_variable_set('@page', a) | ||
439 | + FastGettext.stubs(:locale).returns('es') | ||
440 | + get :index | ||
441 | + assert_tag :html, :attributes => { :lang => 'es' } | ||
442 | + end | ||
443 | + | ||
422 | end | 444 | end |
test/functional/cms_controller_test.rb
@@ -1440,4 +1440,63 @@ class CmsControllerTest < Test::Unit::TestCase | @@ -1440,4 +1440,63 @@ class CmsControllerTest < Test::Unit::TestCase | ||
1440 | assert_response :success | 1440 | assert_response :success |
1441 | end | 1441 | end |
1442 | 1442 | ||
1443 | + should 'article language should be selected' do | ||
1444 | + textile = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'ru') | ||
1445 | + get :edit, :profile => @profile.identifier, :id => textile.id | ||
1446 | + assert_tag :option, :attributes => { :selected => 'selected', :value => 'ru' }, :parent => { | ||
1447 | + :tag => 'select', :attributes => { :id => 'article_language'} } | ||
1448 | + end | ||
1449 | + | ||
1450 | + should 'list possible languages and include blank option' do | ||
1451 | + get :new, :profile => @profile.identifier, :type => 'TextileArticle' | ||
1452 | + assert_equal Noosfero.locales.invert, assigns(:locales) | ||
1453 | + assert_tag :option, :attributes => { :value => '' }, :parent => { | ||
1454 | + :tag => 'select', :attributes => { :id => 'article_language'} } | ||
1455 | + end | ||
1456 | + | ||
1457 | + should 'add translation to an article' do | ||
1458 | + textile = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'ru') | ||
1459 | + assert_difference Article, :count do | ||
1460 | + post :new, :profile => @profile.identifier, :type => 'TextileArticle', :article => { :name => 'english translation', :translation_of_id => textile.id, :language => 'en' } | ||
1461 | + end | ||
1462 | + end | ||
1463 | + | ||
1464 | + should 'not display language selection if article is not translatable' do | ||
1465 | + blog = fast_create(Blog, :name => 'blog', :profile_id => @profile.id) | ||
1466 | + get :edit, :profile => @profile.identifier, :id => blog.id | ||
1467 | + assert_no_tag :select, :attributes => { :id => 'article_language'} | ||
1468 | + end | ||
1469 | + | ||
1470 | + should 'display display posts in current language input checked on edit blog' do | ||
1471 | + get :new, :profile => profile.identifier, :type => 'Blog' | ||
1472 | + assert_tag :tag => 'input', :attributes => { :type => 'checkbox', :name => 'article[display_posts_in_current_language]', :checked => 'checked' } | ||
1473 | + end | ||
1474 | + | ||
1475 | + should 'update to false blog display posts in current language setting' do | ||
1476 | + profile.articles << Blog.new(:name => 'Blog for test', :profile => profile, :display_posts_in_current_language => true) | ||
1477 | + post :edit, :profile => profile.identifier, :id => profile.blog.id, :article => { :display_posts_in_current_language => false } | ||
1478 | + profile.blog.reload | ||
1479 | + assert !profile.blog.display_posts_in_current_language? | ||
1480 | + end | ||
1481 | + | ||
1482 | + should 'update to true blog display posts in current language setting' do | ||
1483 | + profile.articles << Blog.new(:name => 'Blog for test', :profile => profile, :display_posts_in_current_language => false) | ||
1484 | + post :edit, :profile => profile.identifier, :id => profile.blog.id, :article => { :display_posts_in_current_language => true } | ||
1485 | + profile.blog.reload | ||
1486 | + assert profile.blog.display_posts_in_current_language? | ||
1487 | + end | ||
1488 | + | ||
1489 | + should 'be checked display posts in current language checkbox' do | ||
1490 | + profile.articles << Blog.new(:name => 'Blog for test', :profile => profile, :display_posts_in_current_language => true) | ||
1491 | + get :edit, :profile => profile.identifier, :id => profile.blog.id | ||
1492 | + assert_tag :tag => 'input', :attributes => { :type => 'checkbox', :name => 'article[display_posts_in_current_language]', :checked => 'checked' } | ||
1493 | + end | ||
1494 | + | ||
1495 | + should 'be unchecked display posts in current language checkbox' do | ||
1496 | + profile.articles << Blog.new(:name => 'Blog for test', :profile => profile, :display_posts_in_current_language => false) | ||
1497 | + get :edit, :profile => profile.identifier, :id => profile.blog.id | ||
1498 | + assert_tag :tag => 'input', :attributes => { :type => 'checkbox', :name => 'article[display_posts_in_current_language]' } | ||
1499 | + assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox', :name => 'article[display_posts_in_current_language]', :checked => 'checked' } | ||
1500 | + end | ||
1501 | + | ||
1443 | end | 1502 | end |
test/functional/content_viewer_controller_test.rb
@@ -1095,4 +1095,147 @@ class ContentViewerControllerTest < Test::Unit::TestCase | @@ -1095,4 +1095,147 @@ class ContentViewerControllerTest < Test::Unit::TestCase | ||
1095 | assert_tag :tag => 'div', :attributes => { :class => /main-block/ }, :descendant => { :tag => 'a', :attributes => { :href => "/myprofile/testinguser/cms/edit/#{forum.id}" }, :content => 'Configure forum' } | 1095 | assert_tag :tag => 'div', :attributes => { :class => /main-block/ }, :descendant => { :tag => 'a', :attributes => { :href => "/myprofile/testinguser/cms/edit/#{forum.id}" }, :content => 'Configure forum' } |
1096 | end | 1096 | end |
1097 | 1097 | ||
1098 | + should 'display add translation link if article is translatable' do | ||
1099 | + login_as @profile.identifier | ||
1100 | + textile = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'en') | ||
1101 | + get :view_page, :profile => @profile.identifier, :page => textile.explode_path | ||
1102 | + assert_tag :a, :attributes => { :href => "/myprofile/#{profile.identifier}/cms/new?article%5Btranslation_of_id%5D=#{textile.id}&type=#{TextileArticle}" } | ||
1103 | + end | ||
1104 | + | ||
1105 | + should 'not display add translation link if article is not translatable' do | ||
1106 | + login_as @profile.identifier | ||
1107 | + blog = fast_create(Blog, :profile_id => @profile.id, :path => 'blog') | ||
1108 | + get :view_page, :profile => @profile.identifier, :page => blog.explode_path | ||
1109 | + assert_no_tag :a, :attributes => { :content => 'Add translation', :class => /icon-locale/ } | ||
1110 | + end | ||
1111 | + | ||
1112 | + should 'not display add translation link if article hasnt a language defined' do | ||
1113 | + login_as @profile.identifier | ||
1114 | + textile = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile') | ||
1115 | + get :view_page, :profile => @profile.identifier, :page => textile.explode_path | ||
1116 | + assert_no_tag :a, :attributes => { :content => 'Add translation', :class => /icon-locale/ } | ||
1117 | + end | ||
1118 | + | ||
1119 | + should 'diplay translations link if article has translations' do | ||
1120 | + login_as @profile.identifier | ||
1121 | + textile = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'en') | ||
1122 | + translation = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'translation', :language => 'es', :translation_of_id => textile) | ||
1123 | + get :view_page, :profile => @profile.identifier, :page => textile.explode_path | ||
1124 | + assert_tag :a, :attributes => { :class => /article-translations-menu/, :onclick => /toggleSubmenu/ } | ||
1125 | + end | ||
1126 | + | ||
1127 | + should 'be redirected to translation if article is a root' do | ||
1128 | + @request.env['HTTP_REFERER'] = 'http://some.path' | ||
1129 | + FastGettext.stubs(:locale).returns('es') | ||
1130 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1131 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1132 | + get :view_page, :profile => @profile.identifier, :page => en_article.explode_path | ||
1133 | + assert_redirected_to :profile => @profile.identifier, :page => es_article.explode_path | ||
1134 | + assert_equal es_article, assigns(:page) | ||
1135 | + end | ||
1136 | + | ||
1137 | + should 'be redirected to translation' do | ||
1138 | + @request.env['HTTP_REFERER'] = 'http://some.path' | ||
1139 | + FastGettext.stubs(:locale).returns('en') | ||
1140 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1141 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1142 | + get :view_page, :profile => @profile.identifier, :page => es_article.explode_path | ||
1143 | + assert_redirected_to :profile => @profile.identifier, :page => en_article.explode_path | ||
1144 | + assert_equal en_article, assigns(:page) | ||
1145 | + end | ||
1146 | + | ||
1147 | + should 'not be redirected if already in translation' do | ||
1148 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1149 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1150 | + @request.env['HTTP_REFERER'] = "http://localhost:3000/#{@profile.identifier}/#{es_article.path}" | ||
1151 | + FastGettext.stubs(:locale).returns('es') | ||
1152 | + get :view_page, :profile => @profile.identifier, :page => es_article.explode_path | ||
1153 | + assert_response :success | ||
1154 | + assert_equal es_article, assigns(:page) | ||
1155 | + end | ||
1156 | + | ||
1157 | + should 'not be redirected if article does not have a language' do | ||
1158 | + FastGettext.stubs(:locale).returns('es') | ||
1159 | + article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'article') | ||
1160 | + get :view_page, :profile => @profile.identifier, :page => article.explode_path | ||
1161 | + assert_response :success | ||
1162 | + assert_equal article, assigns(:page) | ||
1163 | + end | ||
1164 | + | ||
1165 | + should 'not be redirected if http_referer is a translation' do | ||
1166 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1167 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1168 | + @request.env['HTTP_REFERER'] = "http://localhost:3000/#{@profile.identifier}/#{es_article.path}" | ||
1169 | + FastGettext.stubs(:locale).returns('es') | ||
1170 | + get :view_page, :profile => @profile.identifier, :page => en_article.explode_path | ||
1171 | + assert_response :success | ||
1172 | + assert_equal en_article, assigns(:page) | ||
1173 | + end | ||
1174 | + | ||
1175 | + should 'be redirected if http_referer is nil' do | ||
1176 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1177 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1178 | + @request.env['HTTP_REFERER'] = nil | ||
1179 | + FastGettext.stubs(:locale).returns('es') | ||
1180 | + get :view_page, :profile => @profile.identifier, :page => en_article.explode_path | ||
1181 | + assert_redirected_to :profile => @profile.identifier, :page => es_article.explode_path | ||
1182 | + assert_equal es_article, assigns(:page) | ||
1183 | + end | ||
1184 | + | ||
1185 | + should 'not be redirected to transition if came from edit' do | ||
1186 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1187 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1188 | + FastGettext.stubs(:locale).returns('es') | ||
1189 | + @request.env['HTTP_REFERER'] = "http://localhost/myprofile/#{@profile.identifier}/cms/edit/#{en_article.id}" | ||
1190 | + get :view_page, :profile => @profile.identifier, :page => es_article.explode_path | ||
1191 | + assert_response :success | ||
1192 | + assert_equal es_article, assigns(:page) | ||
1193 | + end | ||
1194 | + | ||
1195 | + should 'not be redirected to transition if came from new' do | ||
1196 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1197 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1198 | + FastGettext.stubs(:locale).returns('es') | ||
1199 | + @request.env['HTTP_REFERER'] = "http://localhost/myprofile/#{@profile.identifier}/cms/new" | ||
1200 | + get :view_page, :profile => @profile.identifier, :page => es_article.explode_path | ||
1201 | + assert_response :success | ||
1202 | + assert_equal es_article, assigns(:page) | ||
1203 | + end | ||
1204 | + | ||
1205 | + should 'replace article for his translation at blog listing if blog option is enabled' do | ||
1206 | + FastGettext.stubs(:locale).returns('es') | ||
1207 | + blog = fast_create(Blog, :profile_id => profile.id, :path => 'blog') | ||
1208 | + blog.stubs(:display_posts_in_current_language).returns(true) | ||
1209 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1210 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1211 | + blog.posts = [en_article, es_article] | ||
1212 | + | ||
1213 | + get :view_page, :profile => @profile.identifier, :page => blog.explode_path | ||
1214 | + assert_tag :div, :attributes => { :id => "post-#{es_article.id}" } | ||
1215 | + assert_no_tag :div, :attributes => { :id => "post-#{en_article.id}" } | ||
1216 | + end | ||
1217 | + | ||
1218 | + should 'list all posts at blog listing if blog option is disabled' do | ||
1219 | + FastGettext.stubs(:locale).returns('es') | ||
1220 | + blog = Blog.create!(:name => 'A blog test', :profile => profile, :display_posts_in_current_language => false) | ||
1221 | + blog.posts << es_post = TextileArticle.create!(:name => 'Spanish Post', :profile => profile, :parent => blog, :language => 'es') | ||
1222 | + blog.posts << en_post = TextileArticle.create!(:name => 'English Post', :profile => profile, :parent => blog, :language => 'en', :translation_of_id => es_post.id) | ||
1223 | + get :view_page, :profile => profile.identifier, :page => [blog.path] | ||
1224 | + assert_equal 2, assigns(:posts).size | ||
1225 | + assert_tag :div, :attributes => { :id => "post-#{es_post.id}" } | ||
1226 | + assert_tag :div, :attributes => { :id => "post-#{en_post.id}" } | ||
1227 | + end | ||
1228 | + | ||
1229 | + should 'display only native translations at blog listing if blog option is enabled' do | ||
1230 | + FastGettext.stubs(:locale).returns('es') | ||
1231 | + blog = fast_create(Blog, :profile_id => profile.id, :path => 'blog') | ||
1232 | + blog.stubs(:display_posts_in_current_language).returns(true) | ||
1233 | + en_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'en_article', :language => 'en') | ||
1234 | + es_article = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'es_article', :language => 'es', :translation_of_id => en_article) | ||
1235 | + blog.posts = [en_article, es_article] | ||
1236 | + | ||
1237 | + get :view_page, :profile => @profile.identifier, :page => blog.explode_path | ||
1238 | + assert_equal [es_article], assigns(:posts) | ||
1239 | + end | ||
1240 | + | ||
1098 | end | 1241 | end |
test/unit/article_test.rb
@@ -1229,4 +1229,174 @@ class ArticleTest < Test::Unit::TestCase | @@ -1229,4 +1229,174 @@ class ArticleTest < Test::Unit::TestCase | ||
1229 | assert_equal [g], p.articles.galleries | 1229 | assert_equal [g], p.articles.galleries |
1230 | end | 1230 | end |
1231 | 1231 | ||
1232 | + should 'has many translations' do | ||
1233 | + a = build(Article) | ||
1234 | + assert_raises(ActiveRecord::AssociationTypeMismatch) { a.translations << 1 } | ||
1235 | + assert_nothing_raised { a.translations << build(Article) } | ||
1236 | + end | ||
1237 | + | ||
1238 | + should 'belongs to translation of' do | ||
1239 | + a = build(Article) | ||
1240 | + assert_raises(ActiveRecord::AssociationTypeMismatch) { a.translation_of = 1 } | ||
1241 | + assert_nothing_raised { a.translation_of = build(Article) } | ||
1242 | + end | ||
1243 | + | ||
1244 | + should 'has language' do | ||
1245 | + a = build(Article) | ||
1246 | + assert_nothing_raised { a.language = 'en' } | ||
1247 | + end | ||
1248 | + | ||
1249 | + should 'validade inclusion of language' do | ||
1250 | + a = build(Article) | ||
1251 | + a.language = '12' | ||
1252 | + a.valid? | ||
1253 | + assert a.errors.invalid?(:language) | ||
1254 | + a.language = 'en' | ||
1255 | + a.valid? | ||
1256 | + assert !a.errors.invalid?(:language) | ||
1257 | + end | ||
1258 | + | ||
1259 | + should 'language can be blank' do | ||
1260 | + a = build(Article) | ||
1261 | + a.valid? | ||
1262 | + assert !a.errors.invalid?(:language) | ||
1263 | + a.language = '' | ||
1264 | + a.valid? | ||
1265 | + assert !a.errors.invalid?(:language) | ||
1266 | + end | ||
1267 | + | ||
1268 | + should 'article is not translatable' do | ||
1269 | + a = build(Article) | ||
1270 | + assert !a.translatable? | ||
1271 | + end | ||
1272 | + | ||
1273 | + should 'get native translation' do | ||
1274 | + native_article = fast_create(Article) | ||
1275 | + article_translation = fast_create(Article) | ||
1276 | + native_article.translations << article_translation | ||
1277 | + assert_equal native_article, native_article.native_translation | ||
1278 | + assert_equal native_article, article_translation.native_translation | ||
1279 | + end | ||
1280 | + | ||
1281 | + should 'list possible translations' do | ||
1282 | + native_article = fast_create(Article, :language => 'pt') | ||
1283 | + article_translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id) | ||
1284 | + possible_translations = native_article.possible_translations | ||
1285 | + assert !possible_translations.include?('en') | ||
1286 | + assert possible_translations.include?('pt') | ||
1287 | + end | ||
1288 | + | ||
1289 | + should 'verify if translation is already in use' do | ||
1290 | + native_article = fast_create(Article, :language => 'pt') | ||
1291 | + article_translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id) | ||
1292 | + a = build(Article) | ||
1293 | + a.language = 'en' | ||
1294 | + a.translation_of = native_article | ||
1295 | + a.valid? | ||
1296 | + assert a.errors.invalid?(:language) | ||
1297 | + a.language = 'es' | ||
1298 | + a.valid? | ||
1299 | + assert !a.errors.invalid?(:language) | ||
1300 | + end | ||
1301 | + | ||
1302 | + should 'verify if native translation is already in use' do | ||
1303 | + native_article = fast_create(Article, :language => 'pt') | ||
1304 | + a = build(Article) | ||
1305 | + a.language = 'pt' | ||
1306 | + a.translation_of = native_article | ||
1307 | + a.valid? | ||
1308 | + assert a.errors.invalid?(:language) | ||
1309 | + a.language = 'es' | ||
1310 | + a.valid? | ||
1311 | + assert !a.errors.invalid?(:language) | ||
1312 | + end | ||
1313 | + | ||
1314 | + should 'translation have a language' do | ||
1315 | + native_article = fast_create(Article, :language => 'pt') | ||
1316 | + a = build(Article) | ||
1317 | + a.translation_of = native_article | ||
1318 | + a.valid? | ||
1319 | + assert a.errors.invalid?(:language) | ||
1320 | + a.language = 'en' | ||
1321 | + a.valid? | ||
1322 | + assert !a.errors.invalid?(:language) | ||
1323 | + end | ||
1324 | + | ||
1325 | + should 'native translation have a language' do | ||
1326 | + native_article = fast_create(Article) | ||
1327 | + a = build(Article) | ||
1328 | + a.language = 'en' | ||
1329 | + a.translation_of = native_article | ||
1330 | + a.valid? | ||
1331 | + n = a.errors.count | ||
1332 | + native_article.language = 'pt' | ||
1333 | + native_article.save | ||
1334 | + a.valid? | ||
1335 | + assert_equal n - 1, a.errors.count | ||
1336 | + end | ||
1337 | + | ||
1338 | + should 'rotate translations when root article is destroyed' do | ||
1339 | + native_article = fast_create(Article, :language => 'pt', :profile_id => @profile.id) | ||
1340 | + translation1 = fast_create(Article, :language => 'en', :translation_of_id => native_article.id, :profile_id => @profile.id) | ||
1341 | + translation2 = fast_create(Article, :language => 'es', :translation_of_id => native_article.id, :profile_id => @profile.id) | ||
1342 | + native_article.destroy | ||
1343 | + assert translation1.translation_of.nil? | ||
1344 | + assert translation1.translations.include?(translation2) | ||
1345 | + end | ||
1346 | + | ||
1347 | + should 'rotate one translation when root article is destroyed' do | ||
1348 | + native_article = fast_create(Article, :language => 'pt', :profile_id => @profile.id) | ||
1349 | + translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id, :profile_id => @profile.id) | ||
1350 | + native_article.destroy | ||
1351 | + assert translation.translation_of.nil? | ||
1352 | + assert translation.translations.empty? | ||
1353 | + end | ||
1354 | + | ||
1355 | + should 'get self if article does not a language' do | ||
1356 | + article = fast_create(Article, :profile_id => @profile.id) | ||
1357 | + assert_equal article, article.get_translation_to('en') | ||
1358 | + end | ||
1359 | + | ||
1360 | + should 'get self if article is the translation' do | ||
1361 | + article = fast_create(Article, :language => 'pt', :profile_id => @profile.id) | ||
1362 | + assert_equal article, article.get_translation_to('pt') | ||
1363 | + end | ||
1364 | + | ||
1365 | + should 'get the native translation if it is the translation' do | ||
1366 | + native_article = fast_create(Article, :language => 'pt', :profile_id => @profile.id) | ||
1367 | + translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id, :profile_id => @profile.id) | ||
1368 | + assert_equal native_article, translation.get_translation_to('pt') | ||
1369 | + end | ||
1370 | + | ||
1371 | + should 'get the translation if article has translation' do | ||
1372 | + native_article = fast_create(Article, :language => 'pt', :profile_id => @profile.id) | ||
1373 | + translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id, :profile_id => @profile.id) | ||
1374 | + assert_equal translation, native_article.get_translation_to('en') | ||
1375 | + end | ||
1376 | + | ||
1377 | + should 'get self if article does not has a translation' do | ||
1378 | + native_article = fast_create(Article, :language => 'pt', :profile_id => @profile.id) | ||
1379 | + assert_equal native_article, native_article.get_translation_to('en') | ||
1380 | + end | ||
1381 | + | ||
1382 | + should 'get only non translated articles' do | ||
1383 | + p = fast_create(Profile) | ||
1384 | + native = fast_create(Article, :language => 'pt', :profile_id => p.id) | ||
1385 | + translation = fast_create(Article, :language => 'en', :translation_of_id => native.id, :profile_id => p.id) | ||
1386 | + assert_equal [native], p.articles.native_translations | ||
1387 | + end | ||
1388 | + | ||
1389 | + should 'not list own language as a possible translation if language has changed' do | ||
1390 | + a = build(Article, :language => 'pt') | ||
1391 | + assert !a.possible_translations.include?('pt') | ||
1392 | + a = fast_create(Article, :language => 'pt') | ||
1393 | + a.language = 'en' | ||
1394 | + assert !a.possible_translations.include?('en') | ||
1395 | + end | ||
1396 | + | ||
1397 | + should 'list own language as a possible translation if language has not changed' do | ||
1398 | + a = fast_create(Article, :language => 'pt') | ||
1399 | + assert a.possible_translations.include?('pt') | ||
1400 | + end | ||
1401 | + | ||
1232 | end | 1402 | end |
test/unit/blog_archives_block_test.rb
@@ -102,4 +102,36 @@ class BlogArchivesBlockTest < ActiveSupport::TestCase | @@ -102,4 +102,36 @@ class BlogArchivesBlockTest < ActiveSupport::TestCase | ||
102 | assert_no_match(/blog-two/m, block.content) | 102 | assert_no_match(/blog-two/m, block.content) |
103 | end | 103 | end |
104 | 104 | ||
105 | + should 'list amount native posts by year' do | ||
106 | + date = DateTime.parse('2008-01-01') | ||
107 | + blog = profile.blog | ||
108 | + 2.times do |i| | ||
109 | + post = fast_create(TextileArticle, :name => "post #{i} test", :profile_id => profile.id, | ||
110 | + :parent_id => blog.id, :language => 'en') | ||
111 | + post.update_attribute(:published_at, date) | ||
112 | + translation = fast_create(TextileArticle, :name => "post #{i} test", :profile_id => profile.id, | ||
113 | + :parent_id => blog.id, :language => 'en', :translation_of_id => post.id) | ||
114 | + translation.update_attribute(:published_at, date) | ||
115 | + end | ||
116 | + block = BlogArchivesBlock.new | ||
117 | + block.stubs(:owner).returns(profile) | ||
118 | + assert_tag_in_string block.content, :tag => 'li', :content => '2008 (2)' | ||
119 | + end | ||
120 | + | ||
121 | + should 'list amount native posts by month' do | ||
122 | + date = DateTime.parse('2008-01-01') | ||
123 | + blog = profile.blog | ||
124 | + 2.times do |i| | ||
125 | + post = fast_create(TextileArticle, :name => "post #{i} test", :profile_id => profile.id, | ||
126 | + :parent_id => blog.id, :language => 'en') | ||
127 | + post.update_attribute(:published_at, date) | ||
128 | + translation = fast_create(TextileArticle, :name => "post #{i} test", :profile_id => profile.id, | ||
129 | + :parent_id => blog.id, :language => 'en', :translation_of_id => post.id) | ||
130 | + translation.update_attribute(:published_at, date) | ||
131 | + end | ||
132 | + block = BlogArchivesBlock.new | ||
133 | + block.stubs(:owner).returns(profile) | ||
134 | + assert_tag_in_string block.content, :tag => 'a', :content => 'January (2)', :attributes => {:href => /^http:\/\/.*\/flatline\/blog-one\?month=01&year=2008$/ } | ||
135 | + end | ||
136 | + | ||
105 | end | 137 | end |
test/unit/blog_test.rb
@@ -175,4 +175,20 @@ class BlogTest < ActiveSupport::TestCase | @@ -175,4 +175,20 @@ class BlogTest < ActiveSupport::TestCase | ||
175 | assert Blog.new.has_posts? | 175 | assert Blog.new.has_posts? |
176 | end | 176 | end |
177 | 177 | ||
178 | + should 'display posts in current language by default' do | ||
179 | + blog = Blog.new | ||
180 | + assert blog.display_posts_in_current_language | ||
181 | + assert blog.display_posts_in_current_language? | ||
182 | + end | ||
183 | + | ||
184 | + should 'update display posts in current language setting' do | ||
185 | + p = create_user('testuser').person | ||
186 | + p.articles << Blog.new(:profile => p, :name => 'Blog test') | ||
187 | + blog = p.blog | ||
188 | + blog.display_posts_in_current_language = false | ||
189 | + assert blog.save! && blog.reload | ||
190 | + assert !blog.reload.display_posts_in_current_language | ||
191 | + assert !blog.reload.display_posts_in_current_language? | ||
192 | + end | ||
193 | + | ||
178 | end | 194 | end |
test/unit/event_test.rb
@@ -265,4 +265,10 @@ class EventTest < ActiveSupport::TestCase | @@ -265,4 +265,10 @@ class EventTest < ActiveSupport::TestCase | ||
265 | assert_match /<!-- .* --> <h1> Wellformed html code <\/h1>/, event.description | 265 | assert_match /<!-- .* --> <h1> Wellformed html code <\/h1>/, event.description |
266 | assert_match /<!-- .* --> <h1> Wellformed html code <\/h1>/, event.address | 266 | assert_match /<!-- .* --> <h1> Wellformed html code <\/h1>/, event.address |
267 | end | 267 | end |
268 | + | ||
269 | + should 'be translatable' do | ||
270 | + e = Event.new | ||
271 | + assert e.translatable? | ||
272 | + end | ||
273 | + | ||
268 | end | 274 | end |
test/unit/rss_feed_test.rb
@@ -240,4 +240,24 @@ class RssFeedTest < Test::Unit::TestCase | @@ -240,4 +240,24 @@ class RssFeedTest < Test::Unit::TestCase | ||
240 | assert_not_nil RssFeed.new.to_html | 240 | assert_not_nil RssFeed.new.to_html |
241 | end | 241 | end |
242 | 242 | ||
243 | + should 'include posts from all languages' do | ||
244 | + profile = create_user('testuser').person | ||
245 | + blog = Blog.create!(:name => 'blog-test', :profile => profile, :language => nil) | ||
246 | + blog.posts << en_post = fast_create(TextArticle, :name => "English", :profile_id => profile.id, :parent_id => blog.id, :published => true, :language => 'en') | ||
247 | + blog.posts << es_post = fast_create(TextArticle, :name => "Spanish", :profile_id => profile.id, :parent_id => blog.id, :published => true, :language => 'es') | ||
248 | + | ||
249 | + assert blog.feed.fetch_articles.include?(en_post) | ||
250 | + assert blog.feed.fetch_articles.include?(es_post) | ||
251 | + end | ||
252 | + | ||
253 | + should 'include only posts from some language' do | ||
254 | + profile = create_user('testuser').person | ||
255 | + blog = Blog.create!(:name => 'blog-test', :profile => profile) | ||
256 | + blog.feed.update_attributes! :language => 'es' | ||
257 | + blog.posts << en_post = fast_create(TextArticle, :name => "English", :profile_id => profile.id, :parent_id => blog.id, :published => true, :language => 'en') | ||
258 | + blog.posts << es_post = fast_create(TextArticle, :name => "Spanish", :profile_id => profile.id, :parent_id => blog.id, :published => true, :language => 'es') | ||
259 | + | ||
260 | + assert_equal [es_post], blog.feed.fetch_articles | ||
261 | + end | ||
262 | + | ||
243 | end | 263 | end |
test/unit/textile_article_test.rb
@@ -146,4 +146,9 @@ class TextileArticleTest < Test::Unit::TestCase | @@ -146,4 +146,9 @@ class TextileArticleTest < Test::Unit::TestCase | ||
146 | assert_equal false, a.is_trackable? | 146 | assert_equal false, a.is_trackable? |
147 | end | 147 | end |
148 | 148 | ||
149 | + should 'be translatable' do | ||
150 | + a = TextileArticle.new | ||
151 | + assert a.translatable? | ||
152 | + end | ||
153 | + | ||
149 | end | 154 | end |
test/unit/tiny_mce_article_test.rb
@@ -237,5 +237,8 @@ class TinyMceArticleTest < Test::Unit::TestCase | @@ -237,5 +237,8 @@ class TinyMceArticleTest < Test::Unit::TestCase | ||
237 | assert_equal false, a.is_trackable? | 237 | assert_equal false, a.is_trackable? |
238 | end | 238 | end |
239 | 239 | ||
240 | - | 240 | + should 'be translatable' do |
241 | + a = TinyMceArticle.new | ||
242 | + assert a.translatable? | ||
243 | + end | ||
241 | end | 244 | end |