Commit d9eb363116e58df526147cfdc1d529caa203484f

Authored by Antonio Terceiro
1 parent 6602995b

Adding new features to SlideshowBlock, and blocks in general

  * added option to shuffle images
  * added navigation buttons
  * other changes:
    * Removed "toggle visibility" button and action in BoxOrganizer
      Controller
    * Bock#visible? now receives an optional context so that blocks can
      decide where they must be shown or not
    * BoxesHelper now passes the context into the Block#visible? method
    * Added title to Uploaded files and displaying it instead of the
      description when viewing files

(ActionItem1358)
app/controllers/box_organizer_controller.rb
@@ -100,13 +100,6 @@ class BoxOrganizerController < ApplicationController @@ -100,13 +100,6 @@ class BoxOrganizerController < ApplicationController
100 end 100 end
101 end 101 end
102 102
103 - def toggle_visibility  
104 - @block = boxes_holder.blocks.find(params[:id])  
105 - @block.visible = !@block.visible?  
106 - @block.save  
107 - redirect_to :action => 'index'  
108 - end  
109 -  
110 protected :boxes_editor? 103 protected :boxes_editor?
111 104
112 end 105 end
app/helpers/boxes_helper.rb
@@ -59,10 +59,11 @@ module BoxesHelper @@ -59,10 +59,11 @@ module BoxesHelper
59 end 59 end
60 60
61 def display_box_content(box, main_content) 61 def display_box_content(box, main_content)
62 - box_decorator.select_blocks(box.blocks).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box) 62 + context = { :article => @page }
  63 + box_decorator.select_blocks(box.blocks, context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box)
63 end 64 end
64 65
65 - def select_blocks(arr) 66 + def select_blocks(arr, context)
66 arr 67 arr
67 end 68 end
68 69
@@ -83,7 +84,7 @@ module BoxesHelper @@ -83,7 +84,7 @@ module BoxesHelper
83 end 84 end
84 85
85 options = { 86 options = {
86 - :class => classes = ['block', block.css_classes ].uniq.join(' '), 87 + :class => classes = ['block', block_css_classes(block) ].uniq.join(' '),
87 :id => "block-#{block.id}" 88 :id => "block-#{block.id}"
88 } 89 }
89 if ( block.respond_to? 'help' ) 90 if ( block.respond_to? 'help' )
@@ -134,8 +135,8 @@ module BoxesHelper @@ -134,8 +135,8 @@ module BoxesHelper
134 def self.block_edit_buttons(block) 135 def self.block_edit_buttons(block)
135 '' 136 ''
136 end 137 end
137 - def self.select_blocks(arr)  
138 - arr.select(&:visible?) 138 + def self.select_blocks(arr, context)
  139 + arr.select { |block| block.visible?(context) }
139 end 140 end
140 end 141 end
141 142
@@ -201,7 +202,6 @@ module BoxesHelper @@ -201,7 +202,6 @@ module BoxesHelper
201 end 202 end
202 203
203 if !block.main? 204 if !block.main?
204 - buttons << icon_button(:eyes, _('Toggle block visibility'), {:action => 'toggle_visibility', :id => block.id})  
205 buttons << icon_button(:delete, _('Remove block'), { :action => 'remove', :id => block.id }, { :method => 'post', :confirm => _('Are you sure you want to remove this block?')}) 205 buttons << icon_button(:delete, _('Remove block'), { :action => 'remove', :id => block.id }, { :method => 'post', :confirm => _('Are you sure you want to remove this block?')})
206 end 206 end
207 207
@@ -217,8 +217,18 @@ module BoxesHelper @@ -217,8 +217,18 @@ module BoxesHelper
217 end 217 end
218 218
219 def import_blocks_stylesheets(options = {}) 219 def import_blocks_stylesheets(options = {})
220 - @blocks_css_files ||= current_blocks.map{|b|'blocks/' + b.css_class_name}.uniq 220 + @blocks_css_files ||= current_blocks.map{|b|'blocks/' + block_css_class_name(b)}.uniq
221 stylesheet_import(@blocks_css_files, options) 221 stylesheet_import(@blocks_css_files, options)
222 end 222 end
223 223
  224 + def block_css_class_name(block)
  225 + block.class.name.underscore.gsub('_', '-')
  226 + end
  227 + def block_css_classes(block)
  228 + classes = block_css_class_name(block)
  229 + classes += ' invisible-block' if block.display == 'never'
  230 + classes
  231 + end
  232 +
  233 +
224 end 234 end
app/helpers/content_viewer_helper.rb
@@ -13,7 +13,7 @@ module ContentViewerHelper @@ -13,7 +13,7 @@ module ContentViewerHelper
13 end 13 end
14 14
15 def article_title(article, args = {}) 15 def article_title(article, args = {})
16 - title = article.abstract if article.kind_of?(UploadedFile) && article.image? 16 + title = article.display_title if article.kind_of?(UploadedFile) && article.image?
17 title = article.title if title.blank? 17 title = article.title if title.blank?
18 title = content_tag('h1', title, :class => 'title') 18 title = content_tag('h1', title, :class => 'title')
19 if article.belongs_to_blog? 19 if article.belongs_to_blog?
@@ -35,4 +35,9 @@ module ContentViewerHelper @@ -35,4 +35,9 @@ module ContentViewerHelper
35 link_to( number_of_comments(article), article.url.merge(:anchor => 'comments_list') ) 35 link_to( number_of_comments(article), article.url.merge(:anchor => 'comments_list') )
36 end 36 end
37 37
  38 + def image_label(image)
  39 + text = image.title || image.abstract
  40 + text && (text.first(40) + (text.size > 40 ? '…' : ''))
  41 + end
  42 +
38 end 43 end
app/models/block.rb
@@ -11,14 +11,46 @@ class Block &lt; ActiveRecord::Base @@ -11,14 +11,46 @@ class Block &lt; ActiveRecord::Base
11 belongs_to :box 11 belongs_to :box
12 12
13 acts_as_having_settings 13 acts_as_having_settings
14 - settings_items :visible, :type => :boolean, :default => true  
15 14
16 named_scope :enabled, :conditions => { :enabled => true } 15 named_scope :enabled, :conditions => { :enabled => true }
17 16
18 - def visible?  
19 - visible 17 + # Determines whether a given block must be visible. Optionally a
  18 + # <tt>context</tt> must be specified. <tt>context</tt> must be a hash, and
  19 + # may contain the following keys:
  20 + #
  21 + # * <tt>:article</tt>: the article being viewed currently
  22 + def visible?(context = nil)
  23 + if settings[:visible] == false || display == 'never'
  24 + return false
  25 + end
  26 + if context && context[:article] && display == 'home_page_only'
  27 + return context[:article] == owner.home_page
  28 + end
  29 + true
  30 + end
  31 +
  32 + # The condition for displaying a block. It can assume the following values:
  33 + #
  34 + # * <tt>'always'</tt>: the block is always displayed
  35 + # * <tt>'never'</tt>: the block is hidden (it does not appear for visitors)
  36 + # * <tt>'home_page_only'</tt> the block is displayed only when viewing the
  37 + # homepage of its owner.
  38 + def display
  39 + if settings[:visible] == false
  40 + 'never'
  41 + else
  42 + settings[:display] || 'always'
  43 + end
  44 + end
  45 +
  46 + # Sets the <tt>value</tt> attribute.
  47 + def display=(value)
  48 + settings[:display] = value
  49 + # clear the old setting
  50 + settings[:visible] = nil
20 end 51 end
21 52
  53 +
22 # returns the description of the block, used when the user sees a list of 54 # returns the description of the block, used when the user sees a list of
23 # blocks to choose one to include in the design. 55 # blocks to choose one to include in the design.
24 # 56 #
@@ -66,16 +98,6 @@ class Block &lt; ActiveRecord::Base @@ -66,16 +98,6 @@ class Block &lt; ActiveRecord::Base
66 box ? box.owner : nil 98 box ? box.owner : nil
67 end 99 end
68 100
69 - def css_class_name  
70 - self.class.name.underscore.gsub('_', '-')  
71 - end  
72 -  
73 - def css_classes  
74 - classes = css_class_name  
75 - classes += ' invisible-block' unless visible?  
76 - classes  
77 - end  
78 -  
79 def default_title 101 def default_title
80 '' 102 ''
81 end 103 end
app/models/slideshow_block.rb
@@ -2,9 +2,11 @@ class SlideshowBlock &lt; Block @@ -2,9 +2,11 @@ class SlideshowBlock &lt; Block
2 2
3 settings_items :gallery_id, :type => 'integer' 3 settings_items :gallery_id, :type => 'integer'
4 settings_items :interval, :type => 'integer', :default => 4 4 settings_items :interval, :type => 'integer', :default => 4
  5 + settings_items :shuffle, :type => 'boolean', :default => false
  6 + settings_items :navigation, :type => 'boolean', :default => false
5 7
6 def self.description 8 def self.description
7 - _('Display images from gallery as slideshow') 9 + _('Slideshow block')
8 end 10 end
9 11
10 def gallery 12 def gallery
@@ -12,35 +14,18 @@ class SlideshowBlock &lt; Block @@ -12,35 +14,18 @@ class SlideshowBlock &lt; Block
12 end 14 end
13 15
14 def content 16 def content
  17 + block = self
15 if gallery 18 if gallery
16 images = gallery.images 19 images = gallery.images
17 - block_id = id  
18 - block_title = title  
19 - lambda do  
20 - block_title(block_title) +  
21 - content_tag('div',  
22 - images.map do |i|  
23 - link_to(  
24 - content_tag('div', '', :style => "background-image: url(#{i.public_filename(:thumb)})"),  
25 - (i.external_link || i.view_url), :target => '_blank'  
26 - )  
27 - end.join("\n"),  
28 - :class => 'slideshow-container'  
29 - ) 20 + if shuffle
  21 + images = images.shuffle
30 end 22 end
31 - else  
32 lambda do 23 lambda do
33 - content_tag('em', _('Please select a gallery to display its images.')) 24 + render :file => 'blocks/slideshow', :locals => { :block => block, :images => images }
34 end 25 end
35 - end  
36 - end  
37 -  
38 - def footer  
39 - if gallery  
40 - block_id = id  
41 - interval_sec = interval * 1000 26 + else
42 lambda do 27 lambda do
43 - javascript_tag("jQuery('#block-#{block_id} .slideshow-container').cycle({fx: 'fade', timeout: #{interval_sec}})") 28 + render :file => 'blocks/slideshow', :locals => { :block => block, :images => nil }
44 end 29 end
45 end 30 end
46 end 31 end
app/models/uploaded_file.rb
@@ -4,6 +4,13 @@ @@ -4,6 +4,13 @@
4 # of the file itself is kept. (FIXME?) 4 # of the file itself is kept. (FIXME?)
5 class UploadedFile < Article 5 class UploadedFile < Article
6 6
  7 + settings_items :title, :type => 'string'
  8 + validates_size_of :title, :maximum => 60, :if => (lambda { |file| !file.title.blank? })
  9 +
  10 + def display_title
  11 + title.blank? ? name : title
  12 + end
  13 +
7 def self.max_size 14 def self.max_size
8 UploadedFile.attachment_options[:max_size] 15 UploadedFile.attachment_options[:max_size]
9 end 16 end
@@ -74,11 +81,14 @@ class UploadedFile &lt; Article @@ -74,11 +81,14 @@ class UploadedFile &lt; Article
74 :class => 'gallery-navigation' 81 :class => 'gallery-navigation'
75 ) 82 )
76 end.to_s + 83 end.to_s +
77 - tag('img', :src => article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') 84 + tag('img', :src => article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') +
  85 + content_tag('p', article.abstract, :class => 'uploaded-file-description')
  86 +
78 end 87 end
79 else 88 else
80 lambda do 89 lambda do
81 - content_tag('ul', content_tag('li', link_to(article.name, article.url, :class => article.css_class_name))) 90 + content_tag('ul', content_tag('li', link_to(article.name, article.url, :class => article.css_class_name))) +
  91 + content_tag('p', article.abstract, :class => 'uploaded-file-description')
82 end 92 end
83 end 93 end
84 end 94 end
app/views/blocks/slideshow.rhtml 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +<%= block_title(block.title) %>
  2 +<% if images %>
  3 + <% description = images.any? { |img| !img.abstract.blank? } %>
  4 + <div class='slideshow-border<%= (description ? ' with-descriptions' : '')%>'>
  5 + <div class='slideshow-container'>
  6 + <% images.each do |img| %>
  7 + <a href="<%= url_for(img.external_link.blank? ? img.view_url: img.external_link) %>">
  8 + <%= content_tag('div', '', :style => "background-image: url(#{img.public_filename(:thumb)})", :title => (img.abstract.blank? ? '' : img.abstract)) %>
  9 + <% if !img.abstract.blank? %>
  10 + <span class='image-description'><%= img.abstract %></span>
  11 + <% end %>
  12 + </a>
  13 + <% end %>
  14 + </div>
  15 + <% if block.navigation %>
  16 + <div class='slideshow-block-navigation'>
  17 + <%= link_to _('Previous'), '#', :class => 'icon-media-prev' %>
  18 + <% if block.interval > 0 %>
  19 + <%= link_to '&nbsp;', '#', :class => 'icon-media-pause', :onclick => "togglePlayback('#block-#{block.id} .slideshow-container', this); return false;" %>
  20 + <% end %>
  21 + <%= link_to _('Next'), '#', :class => 'icon-media-next' %>
  22 + </div>
  23 + <% end %>
  24 + </div>
  25 + <script type="text/javascript">
  26 + (function($) {
  27 + var options = {fx: 'fade', pause: 1, fastOnEvent: 1, timeout: <%= block.interval * 1000 %>};
  28 + <% if block.navigation %>
  29 + options.prev = '#block-<%= block.id %> .icon-media-prev';
  30 + options.next = '#block-<%= block.id %> .icon-media-next';
  31 + <% end %>
  32 + $('#block-<%= block.id %> .slideshow-container').cycle(options);
  33 + })(jQuery);
  34 +
  35 + function togglePlayback(slideshow, button) {
  36 + var $ = jQuery;
  37 + if (button.className == 'icon-media-pause') {
  38 + button.className = 'icon-media-play';
  39 + $(slideshow).cycle('pause');
  40 + } else {
  41 + button.className = 'icon-media-pause';
  42 + $(slideshow).cycle('resume');
  43 + }
  44 + }
  45 + </script>
  46 +<% else %>
  47 + <em><%= _('Please, edit this block and select an image gallery.') %></em>
  48 +<% end %>
  49 +
app/views/box_organizer/_slideshow_block.rhtml
@@ -3,4 +3,8 @@ @@ -3,4 +3,8 @@
3 [ _('%{gallery} (%{count} images)') % {:gallery => item.path, :count => item.images.count}, item.id ] 3 [ _('%{gallery} (%{count} images)') % {:gallery => item.path, :count => item.images.count}, item.id ]
4 }) %> 4 }) %>
5 5
6 -<%= labelled_form_field _('Seconds between image transitions'), select('block', 'interval', [1, 2, 3, 4, 5, 10, 20, 30, 60] ) %> 6 +<%= labelled_form_field _('Image transition:'), select('block', 'interval', [[_('No automatic transition'), 0]] + [1, 2, 3, 4, 5, 10, 20, 30, 60].map {|item| [n_('Every 1 second', 'Every %d seconds', item) % item, item]}) %>
  7 +
  8 +<%= labelled_form_field check_box(:block, :shuffle) + _('Show images in random order'), '' %>
  9 +
  10 +<%= labelled_form_field check_box(:block, :navigation) + _('Display navigation buttons'), '' %>
app/views/box_organizer/edit.rhtml
@@ -6,6 +6,18 @@ @@ -6,6 +6,18 @@
6 6
7 <%= render :partial => partial_for_class(@block.class) %> 7 <%= render :partial => partial_for_class(@block.class) %>
8 8
  9 + <%= labelled_form_field _('Display this block:'), '' %>
  10 + <div style='margin-left: 10px'>
  11 + <%= radio_button(:block, :display, 'always') %>
  12 + <%= label_tag('block_display_always', _('In all pages')) %>
  13 + <br/>
  14 + <%= radio_button(:block, :display, 'home_page_only') %>
  15 + <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %>
  16 + <br/>
  17 + <%= radio_button(:block, :display, 'never') %>
  18 + <%= label_tag('block_display_never', _("Don't display")) %>
  19 + </div>
  20 +
9 <% button_bar do %> 21 <% button_bar do %>
10 <%= submit_button(:save, _('Save')) %> 22 <%= submit_button(:save, _('Save')) %>
11 <%= lightbox_close_button(_('Cancel')) %> 23 <%= lightbox_close_button(_('Cancel')) %>
app/views/cms/_uploaded_file.rhtml
1 -<%= labelled_form_field(_('Describe this file:'), text_area(:article, :abstract, :rows => 3, :cols => 64)) %> 1 +<%= labelled_form_field(_('Title'), text_field(:article, :title, :maxlength => 60)) %>
  2 +<%= labelled_form_field(_('Description'), text_area(:article, :abstract, :rows => 3, :cols => 64)) %>
2 <% if @article.image? %> 3 <% if @article.image? %>
3 <%= f.text_field(:external_link, :size => 64) %> 4 <%= f.text_field(:external_link, :size => 64) %>
4 <% end %> 5 <% end %>
app/views/content_viewer/_uploaded_file.rhtml
1 <% if uploaded_file.image? %> 1 <% if uploaded_file.image? %>
2 - <div> <%= link_to image_tag(uploaded_file.public_filename(:thumb), :alt => uploaded_file.abstract), uploaded_file.view_url %> </div> 2 + <div> <%= link_to image_tag(uploaded_file.public_filename(:thumb), :alt => uploaded_file.display_title), uploaded_file.view_url %> </div>
  3 + <span><%= image_label(uploaded_file) %></span>
3 <% else %> 4 <% else %>
4 <%= render :partial => 'article', :object => uploaded_file %> 5 <%= render :partial => 'article', :object => uploaded_file %>
5 <% end %> 6 <% end %>
6 -  
app/views/content_viewer/image_gallery.rhtml
@@ -8,7 +8,6 @@ @@ -8,7 +8,6 @@
8 <% @images.each do |a| %> 8 <% @images.each do |a| %>
9 <% content_tag('li', :title => a.abstract, :class => 'image-gallery-item' ) do %> 9 <% content_tag('li', :title => a.abstract, :class => 'image-gallery-item' ) do %>
10 <%= render :partial => partial_for_class(a.class), :object => a %> 10 <%= render :partial => partial_for_class(a.class), :object => a %>
11 - <span><%= a.abstract && (a.abstract.first(40) + (a.abstract.size > 40 ? '…' : ''))%></span>  
12 <% end %> 11 <% end %>
13 <% end %> 12 <% end %>
14 </ul> 13 </ul>
app/views/content_viewer/view_page.rhtml
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 </div> 19 </div>
20 <% end %> 20 <% end %>
21 21
22 -<div> 22 +<div<%= " class='logged-in'" if user %>>
23 <%= article_title(@page, :no_link => true) %> 23 <%= article_title(@page, :no_link => true) %>
24 <div id="article-actions"> 24 <div id="article-actions">
25 <% if @page.allow_post_content?(user) %> 25 <% if @page.allow_post_content?(user) %>
lib/tasks/release.rake
@@ -10,7 +10,7 @@ namespace :noosfero do @@ -10,7 +10,7 @@ namespace :noosfero do
10 end 10 end
11 11
12 version = Noosfero::VERSION 12 version = Noosfero::VERSION
13 - desc 'checks if there is already a tag for the curren version' 13 + desc 'checks if there is already a tag for the current version'
14 task :check_tag do 14 task :check_tag do
15 sh "git tag | grep '^#{version}$' >/dev/null" do |ok, res| 15 sh "git tag | grep '^#{version}$' >/dev/null" do |ok, res|
16 if ok 16 if ok
public/designs/icons/tango/style.css
@@ -64,3 +64,8 @@ @@ -64,3 +64,8 @@
64 .icon-slideshow { background-image: url(Tango/16x16/mimetypes/x-office-presentation.png) } 64 .icon-slideshow { background-image: url(Tango/16x16/mimetypes/x-office-presentation.png) }
65 .icon-photos { background-image: url(Tango/16x16/devices/camera-photo.png) } 65 .icon-photos { background-image: url(Tango/16x16/devices/camera-photo.png) }
66 66
  67 +.icon-media-pause { background-image: url(Tango/16x16/actions/media-playback-pause.png) }
  68 +.icon-media-play { background-image: url(Tango/16x16/actions/media-playback-start.png) }
  69 +.icon-media-prev { background-image: url(Tango/16x16/actions/media-skip-backward.png) }
  70 +.icon-media-next { background-image: url(Tango/16x16/actions/media-skip-forward.png) }
  71 +
public/designs/themes/base/article.css
@@ -16,8 +16,12 @@ hr.pre-posts, hr.sep-posts { @@ -16,8 +16,12 @@ hr.pre-posts, hr.sep-posts {
16 overflow: visible; 16 overflow: visible;
17 } 17 }
18 18
  19 +#article .logged-in h1 {
  20 + margin-top: 25px;
  21 +}
  22 +
19 #article-actions { 23 #article-actions {
20 - top: -18px; 24 + top: -28px;
21 } 25 }
22 26
23 #article-actions a.button, 27 #article-actions a.button,
public/stylesheets/blocks/slideshow-block.css
1 .slideshow-block .slideshow-container { 1 .slideshow-block .slideshow-container {
2 - margin: auto; 2 + margin-bottom: 10px;
  3 +}
  4 +
  5 +.slideshow-block .slideshow-container a {
  6 + width: 100%;
  7 + display: block;
  8 + text-decoration: none;
  9 +}
  10 +.slideshow-block .slideshow-container a:hover {
  11 + text-decoration: none;
3 } 12 }
4 13
5 .slideshow-block .slideshow-container div { 14 .slideshow-block .slideshow-container div {
6 - width: 130px;  
7 height: 130px; 15 height: 130px;
8 background-position: 50% 50%; 16 background-position: 50% 50%;
9 background-repeat: no-repeat; 17 background-repeat: no-repeat;
10 - margin: auto; 18 +}
  19 +
  20 +.slideshow-block .with-descriptions {
  21 + border: 1px solid #ddd;
  22 +}
  23 +
  24 +.slideshow-block .image-description {
  25 + display: block;
  26 + padding: 10px;
  27 + height: 34px;
  28 + overflow: hidden;
  29 + border-top: 1px solid #ddd;
  30 + background-color: white;
  31 + color: black;
11 } 32 }
12 33
13 .msie .slideshow-block .slideshow-container div { 34 .msie .slideshow-block .slideshow-container div {
14 cursor: pointer; 35 cursor: pointer;
15 } 36 }
  37 +
  38 +.slideshow-block {
  39 + text-align: center;
  40 +}
  41 +
  42 +.slideshow-block .slideshow-block-navigation {
  43 + margin: 8px 0px;
  44 +}
  45 +.slideshow-block .slideshow-block-navigation a {
  46 + background-color: white;
  47 + background-repeat: no-repeat;
  48 + border: 1px solid #ddd;
  49 + padding: 2px;
  50 + text-decoration: none;
  51 + font-variant: small-caps;
  52 + font-size: 10px;
  53 +}
  54 +.slideshow-block .slideshow-block-navigation a:hover {
  55 + text-decoration: none;
  56 + border-color: #999;
  57 +}
  58 +
  59 +.slideshow-block .slideshow-block-navigation .icon-media-prev {
  60 + padding-left: 18px;
  61 + background-position: left;
  62 +}
  63 +.slideshow-block .slideshow-block-navigation .icon-media-next {
  64 + padding-right: 18px;
  65 + background-position: right;
  66 +
  67 +}
  68 +.slideshow-block .slideshow-block-navigation .icon-media-pause,
  69 +.slideshow-block .slideshow-block-navigation .icon-media-play {
  70 + background-position: 50% 50%;
  71 + display: inline-block;
  72 + width: 16px;
  73 +}
public/stylesheets/controller_content_viewer.css
@@ -65,3 +65,10 @@ div#article-parent { @@ -65,3 +65,10 @@ div#article-parent {
65 #article .gallery-navigation .total-of-images { 65 #article .gallery-navigation .total-of-images {
66 font-weight: bold; 66 font-weight: bold;
67 } 67 }
  68 +
  69 +#article .uploaded-file-description {
  70 + background: #f6f6f6;
  71 + border-top: 1px solid #ccc;
  72 + border-bottom: 1px solid #ccc;
  73 + padding: 1em;
  74 +}
test/functional/application_controller_test.rb
@@ -406,7 +406,7 @@ class ApplicationControllerTest &lt; Test::Unit::TestCase @@ -406,7 +406,7 @@ class ApplicationControllerTest &lt; Test::Unit::TestCase
406 p = create_user_full('test_user').person 406 p = create_user_full('test_user').person
407 @controller.expects(:profile).at_least_once.returns(p) 407 @controller.expects(:profile).at_least_once.returns(p)
408 b = p.blocks[1] 408 b = p.blocks[1]
409 - b.expects(:visible).returns(false) 409 + b.expects(:visible?).returns(false)
410 b.save! 410 b.save!
411 411
412 get :index, :profile => p.identifier 412 get :index, :profile => p.identifier
test/functional/profile_design_controller_test.rb
@@ -285,15 +285,6 @@ class ProfileDesignControllerTest &lt; Test::Unit::TestCase @@ -285,15 +285,6 @@ class ProfileDesignControllerTest &lt; Test::Unit::TestCase
285 assert_no_tag :tag => 'input', :attributes => { :id => 'type_blogarchivesblock', :value => 'BlogArchivesBlock' } 285 assert_no_tag :tag => 'input', :attributes => { :id => 'type_blogarchivesblock', :value => 'BlogArchivesBlock' }
286 end 286 end
287 287
288 - should 'toggle block visibility' do  
289 - state = @b1.visible?  
290 - get :toggle_visibility, :id => @b1.id, :profile => holder.identifier  
291 - block = Block.find(@b1.id)  
292 -  
293 - assert_equal block, assigns(:block)  
294 - assert_equal !state, block.visible?  
295 - end  
296 -  
297 should 'offer to create feed reader block' do 288 should 'offer to create feed reader block' do
298 get :add_block, :profile => 'designtestuser' 289 get :add_block, :profile => 'designtestuser'
299 assert_tag :tag => 'input', :attributes => { :id => 'type_feedreaderblock', :value => 'FeedReaderBlock' } 290 assert_tag :tag => 'input', :attributes => { :id => 'type_feedreaderblock', :value => 'FeedReaderBlock' }
test/unit/block_test.rb
@@ -22,12 +22,6 @@ class BlockTest &lt; Test::Unit::TestCase @@ -22,12 +22,6 @@ class BlockTest &lt; Test::Unit::TestCase
22 assert_nil Block.new.owner 22 assert_nil Block.new.owner
23 end 23 end
24 24
25 - should 'generate CSS class name' do  
26 - block = Block.new  
27 - block.class.expects(:name).returns('SomethingBlock')  
28 - assert_equal 'something-block', block.css_class_name  
29 - end  
30 -  
31 should 'provide no footer by default' do 25 should 'provide no footer by default' do
32 assert_nil Block.new.footer 26 assert_nil Block.new.footer
33 end 27 end
@@ -52,12 +46,18 @@ class BlockTest &lt; Test::Unit::TestCase @@ -52,12 +46,18 @@ class BlockTest &lt; Test::Unit::TestCase
52 assert_equal 'my title', b.view_title 46 assert_equal 'my title', b.view_title
53 end 47 end
54 48
55 - should 'have a visible setting' do 49 + should 'be backwards compatible with old "visible" setting' do
56 b = Block.new 50 b = Block.new
57 - assert b.visible?  
58 - b.visible = false  
59 - b.save 51 + b.settings[:visible] = false
60 assert !b.visible? 52 assert !b.visible?
  53 + assert_equal 'never', b.display
  54 + end
  55 +
  56 + should 'clean old "visible setting" when display is set' do
  57 + b = Block.new
  58 + b.settings[:visible] = false
  59 + b.display = 'never'
  60 + assert_nil b.settings[:visible]
61 end 61 end
62 62
63 should 'be cacheable' do 63 should 'be cacheable' do
@@ -80,4 +80,24 @@ class BlockTest &lt; Test::Unit::TestCase @@ -80,4 +80,24 @@ class BlockTest &lt; Test::Unit::TestCase
80 assert_not_includes Block.enabled, block2 80 assert_not_includes Block.enabled, block2
81 end 81 end
82 82
  83 + should 'be displayed everywhere by default' do
  84 + assert_equal true, Block.new.visible?
  85 + end
  86 +
  87 + should 'not display when set to hidden' do
  88 + assert_equal false, Block.new(:display => 'never').visible?
  89 + assert_equal false, Block.new(:display => 'never').visible?(:article => Article.new)
  90 + end
  91 +
  92 + should 'be able to be displayed only in the homepage' do
  93 + profile = Profile.new
  94 + home_page = Article.new
  95 + profile.home_page = home_page
  96 + block = Block.new(:display => 'home_page_only')
  97 + block.stubs(:owner).returns(profile)
  98 +
  99 + assert_equal true, block.visible?(:article => home_page)
  100 + assert_equal false, block.visible?(:article => Article.new)
  101 + end
  102 +
83 end 103 end
test/unit/boxes_helper_test.rb
@@ -41,7 +41,7 @@ class BoxesHelperTest &lt; Test::Unit::TestCase @@ -41,7 +41,7 @@ class BoxesHelperTest &lt; Test::Unit::TestCase
41 p = create_user_with_blocks 41 p = create_user_with_blocks
42 42
43 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0] 43 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
44 - b.visible = false; b.save! 44 + b.display = 'never'; b.save!
45 box = b.box 45 box = b.box
46 box.expects(:blocks).returns([b]) 46 box.expects(:blocks).returns([b])
47 expects(:display_block).with(b, '') 47 expects(:display_block).with(b, '')
@@ -55,7 +55,7 @@ class BoxesHelperTest &lt; Test::Unit::TestCase @@ -55,7 +55,7 @@ class BoxesHelperTest &lt; Test::Unit::TestCase
55 p = create_user_with_blocks 55 p = create_user_with_blocks
56 56
57 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0] 57 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
58 - b.visible = false; b.save! 58 + b.display = 'never'; b.save!
59 box = b.box 59 box = b.box
60 box.expects(:blocks).returns([b]) 60 box.expects(:blocks).returns([b])
61 expects(:display_block).with(b, '').never 61 expects(:display_block).with(b, '').never
@@ -85,4 +85,13 @@ class BoxesHelperTest &lt; Test::Unit::TestCase @@ -85,4 +85,13 @@ class BoxesHelperTest &lt; Test::Unit::TestCase
85 assert_tag_in_string insert_boxes('main content'), :tag => "div", :attributes => { :id => 'profile-footer' }, :content => 'my custom footer' 85 assert_tag_in_string insert_boxes('main content'), :tag => "div", :attributes => { :id => 'profile-footer' }, :content => 'my custom footer'
86 end 86 end
87 87
  88 + should 'calculate CSS class names correctly' do
  89 + assert_equal 'slideshow-block', block_css_class_name(SlideshowBlock.new)
  90 + assert_equal 'main-block', block_css_class_name(MainBlock.new)
  91 + end
  92 +
  93 + should 'add invisible CSS class name for invisible blocks' do
  94 + assert !block_css_classes(Block.new(:display => 'always')).split.any? { |item| item == 'invisible-block'}
  95 + assert block_css_classes(Block.new(:display => 'never')).split.any? { |item| item == 'invisible-block'}
  96 + end
88 end 97 end
test/unit/slideshow_block_test.rb
@@ -20,15 +20,35 @@ class SlideshowBlockTest &lt; ActiveSupport::TestCase @@ -20,15 +20,35 @@ class SlideshowBlockTest &lt; ActiveSupport::TestCase
20 assert_equal 4, slideshow.interval 20 assert_equal 4, slideshow.interval
21 end 21 end
22 22
23 - should 'not invoke javascript when has no gallery' do  
24 - slideshow_block = SlideshowBlock.new()  
25 - assert_nil slideshow_block.footer 23 + should 'list in the same order' do
  24 + gallery = mock
  25 + images = []
  26 + images.expects(:shuffle).never
  27 + gallery.stubs(:images).returns(images)
  28 +
  29 + block = SlideshowBlock.new
  30 + block.stubs(:gallery).returns(gallery)
  31 + block.content
26 end 32 end
27 33
28 - should 'invoke javascript when has gallery' do  
29 - gallery = fast_create(Folder, :profile_id => profile.id)  
30 - slideshow_block = SlideshowBlock.new(:gallery_id => gallery.id)  
31 - assert_not_nil slideshow_block.footer 34 + should 'list in random order' do
  35 + gallery = mock
  36 + images = []
  37 + shuffled = []
  38 + gallery.stubs(:images).returns(images)
  39 + images.expects(:shuffle).once.returns(shuffled)
  40 +
  41 + block = SlideshowBlock.new(:shuffle => true)
  42 + block.stubs(:gallery).returns(gallery)
  43 + block.content
  44 + end
  45 +
  46 + should 'not shuffle by default' do
  47 + assert_equal false, SlideshowBlock.new.shuffle
  48 + end
  49 +
  50 + should 'not display navigation by default' do
  51 + assert_equal false, SlideshowBlock.new.navigation
32 end 52 end
33 53
34 end 54 end
test/unit/uploaded_file_test.rb
@@ -113,10 +113,34 @@ class UploadedFileTest &lt; Test::Unit::TestCase @@ -113,10 +113,34 @@ class UploadedFileTest &lt; Test::Unit::TestCase
113 p = create_user('test_user').person 113 p = create_user('test_user').person
114 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :profile => p) 114 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :profile => p)
115 115
116 - stubs(:content_tag) 116 + stubs(:content_tag).returns('link')
117 expects(:link_to).with(file.name, file.url, :class => file.css_class_name) 117 expects(:link_to).with(file.name, file.url, :class => file.css_class_name)
118 118
119 instance_eval(&file.to_html) 119 instance_eval(&file.to_html)
120 end 120 end
121 121
  122 + should 'have title' do
  123 + assert_equal 'my title', UploadedFile.new(:title => 'my title').title
  124 + end
  125 +
  126 + should 'limit title to 140 characters' do
  127 + upload = UploadedFile.new
  128 +
  129 + upload.title = '+' * 61; upload.valid?
  130 + assert upload.errors[:title]
  131 +
  132 + upload.title = '+' * 60; upload.valid?
  133 + assert !upload.errors[:title]
  134 +
  135 + end
  136 +
  137 + should 'always provide a display title' do
  138 + upload = UploadedFile.new(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'))
  139 + assert_equal 'test.txt', upload.display_title
  140 + upload.title = 'My text file'
  141 + assert_equal 'My text file', upload.display_title
  142 + upload.title = ''
  143 + assert_equal 'test.txt', upload.display_title
  144 + end
  145 +
122 end 146 end