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 100 end
101 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 103 protected :boxes_editor?
111 104  
112 105 end
... ...
app/helpers/boxes_helper.rb
... ... @@ -59,10 +59,11 @@ module BoxesHelper
59 59 end
60 60  
61 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 64 end
64 65  
65   - def select_blocks(arr)
  66 + def select_blocks(arr, context)
66 67 arr
67 68 end
68 69  
... ... @@ -83,7 +84,7 @@ module BoxesHelper
83 84 end
84 85  
85 86 options = {
86   - :class => classes = ['block', block.css_classes ].uniq.join(' '),
  87 + :class => classes = ['block', block_css_classes(block) ].uniq.join(' '),
87 88 :id => "block-#{block.id}"
88 89 }
89 90 if ( block.respond_to? 'help' )
... ... @@ -134,8 +135,8 @@ module BoxesHelper
134 135 def self.block_edit_buttons(block)
135 136 ''
136 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 140 end
140 141 end
141 142  
... ... @@ -201,7 +202,6 @@ module BoxesHelper
201 202 end
202 203  
203 204 if !block.main?
204   - buttons << icon_button(:eyes, _('Toggle block visibility'), {:action => 'toggle_visibility', :id => block.id})
205 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 206 end
207 207  
... ... @@ -217,8 +217,18 @@ module BoxesHelper
217 217 end
218 218  
219 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 221 stylesheet_import(@blocks_css_files, options)
222 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 234 end
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -13,7 +13,7 @@ module ContentViewerHelper
13 13 end
14 14  
15 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 17 title = article.title if title.blank?
18 18 title = content_tag('h1', title, :class => 'title')
19 19 if article.belongs_to_blog?
... ... @@ -35,4 +35,9 @@ module ContentViewerHelper
35 35 link_to( number_of_comments(article), article.url.merge(:anchor => 'comments_list') )
36 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 43 end
... ...
app/models/block.rb
... ... @@ -11,14 +11,46 @@ class Block &lt; ActiveRecord::Base
11 11 belongs_to :box
12 12  
13 13 acts_as_having_settings
14   - settings_items :visible, :type => :boolean, :default => true
15 14  
16 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 51 end
21 52  
  53 +
22 54 # returns the description of the block, used when the user sees a list of
23 55 # blocks to choose one to include in the design.
24 56 #
... ... @@ -66,16 +98,6 @@ class Block &lt; ActiveRecord::Base
66 98 box ? box.owner : nil
67 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 101 def default_title
80 102 ''
81 103 end
... ...
app/models/slideshow_block.rb
... ... @@ -2,9 +2,11 @@ class SlideshowBlock &lt; Block
2 2  
3 3 settings_items :gallery_id, :type => 'integer'
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 8 def self.description
7   - _('Display images from gallery as slideshow')
  9 + _('Slideshow block')
8 10 end
9 11  
10 12 def gallery
... ... @@ -12,35 +14,18 @@ class SlideshowBlock &lt; Block
12 14 end
13 15  
14 16 def content
  17 + block = self
15 18 if gallery
16 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 22 end
31   - else
32 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 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 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 29 end
45 30 end
46 31 end
... ...
app/models/uploaded_file.rb
... ... @@ -4,6 +4,13 @@
4 4 # of the file itself is kept. (FIXME?)
5 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 14 def self.max_size
8 15 UploadedFile.attachment_options[:max_size]
9 16 end
... ... @@ -74,11 +81,14 @@ class UploadedFile &lt; Article
74 81 :class => 'gallery-navigation'
75 82 )
76 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 87 end
79 88 else
80 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 92 end
83 93 end
84 94 end
... ...
app/views/blocks/slideshow.rhtml 0 → 100644
... ... @@ -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 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  
7 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 21 <% button_bar do %>
10 22 <%= submit_button(:save, _('Save')) %>
11 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 3 <% if @article.image? %>
3 4 <%= f.text_field(:external_link, :size => 64) %>
4 5 <% end %>
... ...
app/views/content_viewer/_uploaded_file.rhtml
1 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 4 <% else %>
4 5 <%= render :partial => 'article', :object => uploaded_file %>
5 6 <% end %>
6   -
... ...
app/views/content_viewer/image_gallery.rhtml
... ... @@ -8,7 +8,6 @@
8 8 <% @images.each do |a| %>
9 9 <% content_tag('li', :title => a.abstract, :class => 'image-gallery-item' ) do %>
10 10 <%= render :partial => partial_for_class(a.class), :object => a %>
11   - <span><%= a.abstract && (a.abstract.first(40) + (a.abstract.size > 40 ? '…' : ''))%></span>
12 11 <% end %>
13 12 <% end %>
14 13 </ul>
... ...
app/views/content_viewer/view_page.rhtml
... ... @@ -19,7 +19,7 @@
19 19 </div>
20 20 <% end %>
21 21  
22   -<div>
  22 +<div<%= " class='logged-in'" if user %>>
23 23 <%= article_title(@page, :no_link => true) %>
24 24 <div id="article-actions">
25 25 <% if @page.allow_post_content?(user) %>
... ...
lib/tasks/release.rake
... ... @@ -10,7 +10,7 @@ namespace :noosfero do
10 10 end
11 11  
12 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 14 task :check_tag do
15 15 sh "git tag | grep '^#{version}$' >/dev/null" do |ok, res|
16 16 if ok
... ...
public/designs/icons/tango/style.css
... ... @@ -64,3 +64,8 @@
64 64 .icon-slideshow { background-image: url(Tango/16x16/mimetypes/x-office-presentation.png) }
65 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 16 overflow: visible;
17 17 }
18 18  
  19 +#article .logged-in h1 {
  20 + margin-top: 25px;
  21 +}
  22 +
19 23 #article-actions {
20   - top: -18px;
  24 + top: -28px;
21 25 }
22 26  
23 27 #article-actions a.button,
... ...
public/stylesheets/blocks/slideshow-block.css
1 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 14 .slideshow-block .slideshow-container div {
6   - width: 130px;
7 15 height: 130px;
8 16 background-position: 50% 50%;
9 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 34 .msie .slideshow-block .slideshow-container div {
14 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 65 #article .gallery-navigation .total-of-images {
66 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 406 p = create_user_full('test_user').person
407 407 @controller.expects(:profile).at_least_once.returns(p)
408 408 b = p.blocks[1]
409   - b.expects(:visible).returns(false)
  409 + b.expects(:visible?).returns(false)
410 410 b.save!
411 411  
412 412 get :index, :profile => p.identifier
... ...
test/functional/profile_design_controller_test.rb
... ... @@ -285,15 +285,6 @@ class ProfileDesignControllerTest &lt; Test::Unit::TestCase
285 285 assert_no_tag :tag => 'input', :attributes => { :id => 'type_blogarchivesblock', :value => 'BlogArchivesBlock' }
286 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 288 should 'offer to create feed reader block' do
298 289 get :add_block, :profile => 'designtestuser'
299 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 22 assert_nil Block.new.owner
23 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 25 should 'provide no footer by default' do
32 26 assert_nil Block.new.footer
33 27 end
... ... @@ -52,12 +46,18 @@ class BlockTest &lt; Test::Unit::TestCase
52 46 assert_equal 'my title', b.view_title
53 47 end
54 48  
55   - should 'have a visible setting' do
  49 + should 'be backwards compatible with old "visible" setting' do
56 50 b = Block.new
57   - assert b.visible?
58   - b.visible = false
59   - b.save
  51 + b.settings[:visible] = false
60 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 61 end
62 62  
63 63 should 'be cacheable' do
... ... @@ -80,4 +80,24 @@ class BlockTest &lt; Test::Unit::TestCase
80 80 assert_not_includes Block.enabled, block2
81 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 103 end
... ...
test/unit/boxes_helper_test.rb
... ... @@ -41,7 +41,7 @@ class BoxesHelperTest &lt; Test::Unit::TestCase
41 41 p = create_user_with_blocks
42 42  
43 43 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
44   - b.visible = false; b.save!
  44 + b.display = 'never'; b.save!
45 45 box = b.box
46 46 box.expects(:blocks).returns([b])
47 47 expects(:display_block).with(b, '')
... ... @@ -55,7 +55,7 @@ class BoxesHelperTest &lt; Test::Unit::TestCase
55 55 p = create_user_with_blocks
56 56  
57 57 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
58   - b.visible = false; b.save!
  58 + b.display = 'never'; b.save!
59 59 box = b.box
60 60 box.expects(:blocks).returns([b])
61 61 expects(:display_block).with(b, '').never
... ... @@ -85,4 +85,13 @@ class BoxesHelperTest &lt; Test::Unit::TestCase
85 85 assert_tag_in_string insert_boxes('main content'), :tag => "div", :attributes => { :id => 'profile-footer' }, :content => 'my custom footer'
86 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 97 end
... ...
test/unit/slideshow_block_test.rb
... ... @@ -20,15 +20,35 @@ class SlideshowBlockTest &lt; ActiveSupport::TestCase
20 20 assert_equal 4, slideshow.interval
21 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 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 52 end
33 53  
34 54 end
... ...
test/unit/uploaded_file_test.rb
... ... @@ -113,10 +113,34 @@ class UploadedFileTest &lt; Test::Unit::TestCase
113 113 p = create_user('test_user').person
114 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 117 expects(:link_to).with(file.name, file.url, :class => file.css_class_name)
118 118  
119 119 instance_eval(&file.to_html)
120 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 146 end
... ...