Commit f932e31be577cdccbf84ccd703aaa7f3780ea540

Authored by Moises Machado
1 parent 283ad2e6

ActionItem929: implemented image gallery

app/controllers/my_profile/cms_controller.rb
... ... @@ -132,7 +132,8 @@ class CmsController < MyProfileController
132 132  
133 133 def upload_files
134 134 @uploaded_files = []
135   - @parent = check_parent(params[:parent_id])
  135 + @article = @parent = check_parent(params[:parent_id])
  136 + record_coming_from_public_view if @article
136 137 if request.post? && params[:uploaded_files]
137 138 params[:uploaded_files].each do |file|
138 139 @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent) unless file == ''
... ... @@ -141,11 +142,15 @@ class CmsController &lt; MyProfileController
141 142 if @errors.any?
142 143 render :action => 'upload_files', :parent_id => @parent_id
143 144 else
144   - redirect_to( if @parent
145   - {:action => 'view', :id => @parent.id}
  145 + if params[:back_to]
  146 + redirect_back
146 147 else
147   - {:action => 'index'}
148   - end)
  148 + redirect_to( if @parent
  149 + {:action => 'view', :id => @parent.id}
  150 + else
  151 + {:action => 'index'}
  152 + end)
  153 + end
149 154 end
150 155 end
151 156 end
... ... @@ -209,7 +214,7 @@ class CmsController &lt; MyProfileController
209 214  
210 215 def record_coming_from_public_view
211 216 referer = request.referer
212   - if (maybe_ssl(url_for(@article.url)).include?(referer)) || (@article == @profile.home_page && maybe_ssl(url_for(@profile.url)).include?(referer))
  217 + if (maybe_ssl(url_for(@article.url)).include?(referer)) || (@article == profile.home_page && maybe_ssl(url_for(profile.url)).include?(referer))
213 218 @back_to = 'public_view'
214 219 @back_url = @article.url
215 220 end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -55,7 +55,7 @@ class ContentViewerController &lt; ApplicationController
55 55 # At this point the page will be showed
56 56 @page.hit
57 57  
58   - if @page.mime_type != 'text/html'
  58 + unless @page.mime_type == 'text/html' || (@page.image? && params[:view])
59 59 headers['Content-Type'] = @page.mime_type
60 60 data = @page.data
61 61  
... ... @@ -82,6 +82,10 @@ class ContentViewerController &lt; ApplicationController
82 82 @page.filter = {:year => year, :month => month}
83 83 end
84 84  
  85 + if @page.folder? && @page.view_as == 'image_gallery'
  86 + @images = @page.images.paginate(:per_page => 12, :page => params[:npage])
  87 + end
  88 +
85 89 @comments = @page.comments(true)
86 90 end
87 91  
... ...
app/helpers/application_helper.rb
... ... @@ -139,10 +139,10 @@ module ApplicationHelper
139 139 link_to text, p.admin_url.merge(url), options
140 140 end
141 141  
142   - def link_to_document(doc, text = nil)
  142 + def link_to_document(doc, text = nil, html_options = {}, url_options = {})
143 143 text ||= doc.title
144 144 path = doc.path.split(/\//)
145   - link_to text, homepage_path(:profile => doc.profile.identifier , :page => path)
  145 + link_to text, homepage_path({:profile => doc.profile.identifier , :page => path, :view => true}.merge(url_options)), html_options
146 146 end
147 147  
148 148 def link_if_permitted(link, permission = nil, target = nil)
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -12,7 +12,9 @@ module ContentViewerHelper
12 12 end
13 13  
14 14 def article_title(article, args = {})
15   - title = content_tag('h1', article.title, :class => 'title')
  15 + title = article.abstract if article.kind_of?(UploadedFile) && article.image?
  16 + title = article.title if title.blank?
  17 + title = content_tag('h1', title, :class => 'title')
16 18 if article.belongs_to_blog?
17 19 unless args[:no_link]
18 20 title = content_tag('h3', link_to(article.name, article.url), :class => 'title')
... ... @@ -39,6 +41,9 @@ module ContentViewerHelper
39 41 end
40 42  
41 43 def article_to_html(article)
  44 + content = article.to_html
  45 + return self.instance_eval(&content) if content.kind_of?(Proc)
  46 +
42 47 if article.blog?
43 48 children = if article.filter and article.filter[:year] and article.filter[:month]
44 49 filter_date = DateTime.parse("#{article.filter[:year]}-#{article.filter[:month]}-01")
... ... @@ -46,9 +51,9 @@ module ContentViewerHelper
46 51 else
47 52 article.posts.paginate :page => params[:npage], :per_page => article.posts_per_page
48 53 end
49   - article.to_html + (children.compact.empty? ? content_tag('em', _('(no posts)')) : list_posts(children))
  54 + content + (children.compact.empty? ? content_tag('em', _('(no posts)')) : list_posts(children))
50 55 else
51   - article.to_html
  56 + content
52 57 end
53 58 end
54 59  
... ...
app/helpers/folder_helper.rb
... ... @@ -11,7 +11,7 @@ module FolderHelper
11 11 def display_article_in_listing(article, recursive = false, level = 0)
12 12 result = content_tag(
13 13 'tr',
14   - content_tag('td', link_to(('&nbsp;' * (level * 4) ) + image_tag(icon_for_article(article)) + article.name, article.url))+
  14 + content_tag('td', link_to(('&nbsp;' * (level * 4) ) + image_tag(icon_for_article(article)) + article.name, article.url.merge(:view => true)))+
15 15 content_tag('td', show_date(article.updated_at), :class => 'last-update'),
16 16 :class => 'sitemap-item'
17 17 )
... ... @@ -35,4 +35,30 @@ module FolderHelper
35 35 end
36 36 end
37 37  
  38 + def custom_options_for_article(article)
  39 + @article = article
  40 + content_tag('h4', _('Options')) +
  41 + content_tag('div',
  42 + content_tag(
  43 + 'div',
  44 + check_box(:article, :published) +
  45 + content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published')
  46 + ) + (article.can_display_hits? ?
  47 + content_tag(
  48 + 'div',
  49 + check_box(:article, :display_hits) +
  50 + content_tag('label', _('I want this article to display the number of hits it received'), :for => 'article_display_hits')
  51 + ) : '') +
  52 + hidden_field_tag('article[accept_comments]', 0)
  53 + )
  54 + end
  55 +
  56 + def cms_label_for_new_children
  57 + _('New article')
  58 + end
  59 +
  60 + def cms_label_for_edit
  61 + _('Edit folder')
  62 + end
  63 +
38 64 end
... ...
app/models/article.rb
... ... @@ -29,6 +29,10 @@ class Article &lt; ActiveRecord::Base
29 29 end
30 30 end
31 31  
  32 + def css_class_name
  33 + self.class.name.underscore.dasherize
  34 + end
  35 +
32 36 def pending_categorizations
33 37 @pending_categorizations ||= []
34 38 end
... ... @@ -232,6 +236,14 @@ class Article &lt; ActiveRecord::Base
232 236 can_display_hits? && display_hits
233 237 end
234 238  
  239 + def image?
  240 + false
  241 + end
  242 +
  243 + def display_as_gallery?
  244 + false
  245 + end
  246 +
235 247 private
236 248  
237 249 def sanitize_tag_list
... ...
app/models/folder.rb
... ... @@ -2,6 +2,18 @@ class Folder &lt; Article
2 2  
3 3 acts_as_having_settings :field => :setting
4 4  
  5 + settings_items :view_as, :type => :string, :default => 'folder'
  6 +
  7 + def self.select_views
  8 + [[_('Folder'), 'folder'], [_('Image gallery'), 'image_gallery']]
  9 + end
  10 +
  11 + def self.views
  12 + select_views.map(&:last)
  13 + end
  14 +
  15 + validates_inclusion_of :view_as, :in => self.views
  16 +
5 17 def self.short_description
6 18 _('Folder')
7 19 end
... ... @@ -21,16 +33,41 @@ class Folder &lt; Article
21 33 include ActionView::Helpers::AssetTagHelper
22 34 include FolderHelper
23 35 include DatesHelper
  36 +
24 37 def to_html
  38 + send(view_as)
  39 + end
  40 +
  41 + def folder
25 42 content_tag('div', body) + tag('hr') + (children.empty? ? content_tag('em', _('(empty folder)')) : list_articles(children))
26 43 end
27 44  
  45 + def image_gallery
  46 + article = self
  47 + lambda do
  48 + render :file => 'content_viewer/image_gallery', :locals => {:article => article}
  49 + end
  50 + end
  51 +
28 52 def folder?
29 53 true
30 54 end
31 55  
  56 + def display_as_gallery?
  57 + view_as == 'image_gallery'
  58 + end
  59 +
32 60 def can_display_hits?
33 61 false
34 62 end
35 63  
  64 + def accept_comments?
  65 + false
  66 + end
  67 +
  68 + has_many :images, :class_name => 'Article',
  69 + :foreign_key => 'parent_id',
  70 + :order => 'type, name',
  71 + :conditions => ['type = ? and content_type in (?) or type = ?', 'UploadedFile', UploadedFile.content_types, 'Folder']
  72 +
36 73 end
... ...
app/models/uploaded_file.rb
... ... @@ -10,7 +10,7 @@ class UploadedFile &lt; Article
10 10 # :min_size => 2.megabytes
11 11 # :max_size => 5.megabytes
12 12 has_attachment :storage => :file_system,
13   - :thumbnails => { :icon => [24,24] },
  13 + :thumbnails => { :icon => [24,24], :thumb => '130x130>' },
14 14 :thumbnail_class => Thumbnail,
15 15 :max_size => 5.megabytes,
16 16 :resize_to => '640x480>'
... ... @@ -47,6 +47,13 @@ class UploadedFile &lt; Article
47 47 File.read(self.full_filename)
48 48 end
49 49  
  50 + # FIXME isn't this too much including just to be able to generate some HTML?
  51 + include ActionView::Helpers::TagHelper
  52 +
  53 + def to_html
  54 + tag('img', :src => public_filename, :class => css_class_name, :style => 'max-width: 100%') if image?
  55 + end
  56 +
50 57 def allow_children?
51 58 false
52 59 end
... ... @@ -55,4 +62,7 @@ class UploadedFile &lt; Article
55 62 false
56 63 end
57 64  
  65 + def display_as_gallery?
  66 + self.parent && self.parent.folder? && self.parent.display_as_gallery?
  67 + end
58 68 end
... ...
app/views/cms/_folder.rhtml
... ... @@ -4,5 +4,6 @@
4 4 <%= render :file => 'shared/tiny_mce' %>
5 5  
6 6 <%= required f.text_field('name', :size => '64') %>
  7 +<%= labelled_form_field(_('Folder type'), f.select(:view_as, Folder.select_views)) %>
7 8  
8 9 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :cols => 64)) %>
... ...
app/views/cms/upload_files.rhtml
... ... @@ -26,9 +26,15 @@
26 26  
27 27 <%= hidden_field_tag('parent_id', @parent.id) if @parent %>
28 28  
  29 + <%= hidden_field_tag('back_to', @back_to) if @back_to %>
  30 +
29 31 <% button_bar do %>
30 32 <%= add_upload_file_field _('More files') %>
31   - <%= submit_button :save, _('Upload'), :cancel => {:action => (@parent ? 'view' : 'index'), :id => @parent } %>
  33 + <% if @back_url %>
  34 + <%= submit_button :save, _('Upload'), :cancel => @back_url %>
  35 + <% else %>
  36 + <%= submit_button :save, _('Upload'), :cancel => {:action => (@parent ? 'view' : 'index'), :id => @parent } %>
  37 + <% end %>
32 38 <% end %>
33 39  
34 40 <% end %>
... ...
app/views/cms/view.rhtml
... ... @@ -7,7 +7,7 @@
7 7 <h1 id='article-full-path'>
8 8 <%= icon('cms') %>
9 9 <%= link_to profile.identifier, :action => 'index' %>
10   - <%= @article.hierarchy.map {|item| " / " + ((item == @article) ? item.name : link_to(item.name, :id => item.id)) } %>
  10 + <%= @article.hierarchy.map {|item| " / " + ((item == @article) ? item.name : link_to_document(item)) } %>
11 11 </h1>
12 12 <% end %>
13 13  
... ... @@ -42,7 +42,7 @@
42 42 </td>
43 43 <td>
44 44 <%= link_to _('Properties'), :action => 'edit', :id => folder.id %>
45   - <%= link_to _('Public view'), folder.url %>
  45 + <%= link_to_document(folder, _('Public view')) %>
46 46 <%= link_to _('Use as homepage'), { :action => 'set_home_page', :id => folder.id }, :method => :post %>
47 47 <%= link_to _('Delete'), { :action => 'destroy', :id => folder.id }, :method => :post, :confirm => _('Are you sure that you want to remove this folder? Note that all the items inside it will also be removed!') %>
48 48 </td>
... ... @@ -68,7 +68,7 @@
68 68 </td>
69 69 <td>
70 70 <%= link_to _('Edit'), :action => 'edit', :id => item.id %>
71   - <%= link_to _('Public view'), item.url %>
  71 + <%= link_to_document(item, _('Public view')) %>
72 72 <%= link_to _('Spread this'), :action => 'publish', :id => item.id %>
73 73 <%= link_to _('Use as homepage'), { :action => 'set_home_page', :id => item.id }, :method => :post %>
74 74 <%= link_to _('Delete'), { :action => 'destroy', :id => item.id }, :method => :post, :confirm => _('Are you sure that you want to remove this item?') %>
... ...
app/views/content_viewer/_article.rhtml 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<%= link_to_document(article, '', :class => article.css_class_name) %>
  2 +<span><%= link_to_document(article) %></span>
... ...
app/views/content_viewer/_uploaded_file.rhtml 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<% if uploaded_file.image? %>
  2 + <div> <%= link_to_document(uploaded_file, image_tag(uploaded_file.public_filename(:thumb))) %> </div>
  3 +<% else %>
  4 + <%= render :partial => 'article', :object => uploaded_file %>
  5 +<% end %>
  6 +
... ...
app/views/content_viewer/image_gallery.rhtml 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +<div class="image-gallery">
  2 + <% if params[:slideshow] %>
  3 + <p><%= button('', _('View as gallery'), @page.url)%></p>
  4 +
  5 + <div id='slideshow' >
  6 + <% @images.each do |img| %>
  7 + <%= image_tag(url_for(img.url), :alt => (img.abstract.blank? ? img.name : img.abstract)) if img.image? %>
  8 + <% end %>
  9 + </div>
  10 +
  11 + <link href="/javascripts/jquery.aslideshow/simple/styles.css" media="screen" rel="stylesheet" type="text/css" />
  12 + <script type="text/javascript" src="/javascripts/jquery.aslideshow.js"></script>
  13 +
  14 + <script type="text/javascript">
  15 + //<![CDATA[
  16 + jQuery(document).ready(function(){
  17 + jQuery('#slideshow').slideshow({ width: 640, height: 480, playframe: false, play: true, controls : { hide: false }, imgresize:false, effect:false });
  18 + });
  19 + //]]>
  20 + </script>
  21 +
  22 + <% else %>
  23 + <p><%= button('', _('View as slideshow'), @page.url.merge(:slideshow => true))%></p>
  24 + <ul>
  25 + <% @images.each do |a| %>
  26 + <li class="image-gallery-item">
  27 + <%= render :partial => partial_for_class(a.class), :object => a %>
  28 + <span><%= a.abstract %></span>
  29 + </li>
  30 + <% end %>
  31 + </ul>
  32 + <% end %>
  33 + <br style="clear:both" />
  34 + <%= will_paginate @images, :param_name => 'npage' %>
  35 +</div>
... ...
app/views/content_viewer/view_page.rhtml
... ... @@ -48,7 +48,11 @@
48 48 { :controller => 'cms', :action => 'publish', :id => @page },
49 49 :class => 'button with-text icon-spread' %>
50 50 <% end %>
51   - <%= lightbox_button(:new, label_for_new_article(@page), :controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent))) %>
  51 + <% if @page.display_as_gallery? %>
  52 + <%= button('upload-file', _('Upload files'), :controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent)) %>
  53 + <% else %>
  54 + <%= lightbox_button(:new, label_for_new_article(@page), :controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent))) %>
  55 + <% end %>
52 56 <% end %>
53 57 </div>
54 58 <% end %>
... ... @@ -79,7 +83,7 @@
79 83 </div>
80 84 <% end %>
81 85  
82   -<div class="article-body">
  86 +<div class="<%="article-body article-body-" + @page.css_class_name %>">
83 87 <%= article_to_html(@page) %>
84 88 <br style="clear:both" />
85 89 </div> <!-- end class="article-body" -->
... ...
public/images/icons-mime/empty.png 0 → 100644

3.16 KB

public/javascripts/jquery.aslideshow.js 0 → 100644
... ... @@ -0,0 +1,645 @@
  1 +/**
  2 + * jQuery (a)Slideshow plugin
  3 + *
  4 + * Copyright (c) 2008 Trent Foley
  5 + * Dual licensed under the MIT (MIT-LICENSE.txt)
  6 + * and GPL (GPL-LICENSE.txt) licenses.
  7 + *
  8 + * @author Anton Shevchuk AntonShevchuk@gmail.com
  9 + * @version 0.5.5
  10 + */
  11 +;(function($) {
  12 + defaults = {
  13 + width:320, // width in px
  14 + height:240, // height in px
  15 + index:0, // start from frame number N
  16 + time:3000, // time out beetwen slides
  17 + title:true, // show title
  18 + titleshow:false,// always show title
  19 + panel:true, // show controls panel
  20 + play:false, // play slideshow
  21 + loop:true,
  22 + effect:'fade', // aviable fade, scrollUp/Down/Left/Right, zoom, zoomFade, growX, growY
  23 + effecttime:1000,// aviable fast,slow,normal and any valid fx speed value
  24 + filter:true, // remove <br/>, empty <div>, <p> and other stuff
  25 + nextclick:false, // bind content click next slide
  26 + playclick:false, // bind content click play/stop
  27 + playhover:false, // bind content hover play/stop
  28 + playhoverr:false, // bind content hover stop/play (reverse of playhover)
  29 + playframe:true, // show frame "Play Now!"
  30 + fullscreen:false, // in full window size
  31 + imgresize:false, // resize image to slideshow window
  32 + imgcenter:true, // set image to center // TODO
  33 + imgajax:true, // load images from links
  34 + linkajax:false, // load html from links
  35 + help:'Plugin homepage: <a href="http://slideshow.hohli.com">(a)Slideshow</a><br/>'+
  36 + 'Author homepage: <a href="http://anton.shevchuk.name">Anton Shevchuk</a>',
  37 +
  38 + controls :{ // show/hide controls elements
  39 + 'hide':true, // show controls bar on mouse hover
  40 + 'first':true, // goto first frame
  41 + 'prev':true, // goto previouse frame (if it first go to last)
  42 + 'play':true, // play slideshow
  43 + 'next':true, // goto next frame (if it last go to first)
  44 + 'last':true, // goto last frame
  45 + 'help':true, // show help message
  46 + 'counter':true // show slide counter
  47 + }
  48 + };
  49 + /**
  50 + * Create a new instance of slideshow.
  51 + *
  52 + * @classDescription This class creates a new slideshow and manipulate it
  53 + *
  54 + * @return {Object} Returns a new slideshow object.
  55 + * @constructor
  56 + */
  57 + $.fn.slideshow = function(settings) {
  58 +
  59 + var _slideshow = this;
  60 +
  61 + /*
  62 + * Construct
  63 + */
  64 + this.each(function(){
  65 +
  66 + var ext = $(this);
  67 +
  68 + this.playFlag = false;
  69 + this.playId = null;
  70 + this.length = 0;
  71 + this.inited = new Array();
  72 +
  73 + /**
  74 + * Build Html
  75 + * @method
  76 + */
  77 + this.build = function () {
  78 + var _self = this;
  79 +
  80 + ext.wrapInner('<div class="slideshow"><div class="slideshow-content"></div></div>');
  81 + ext = ext.find('.slideshow');
  82 +
  83 + // filter content
  84 + if (this.options.filter) {
  85 + ext.find('.slideshow-content > br').remove();
  86 + ext.find('.slideshow-content > p:empty').remove();
  87 + ext.find('.slideshow-content > div:empty').remove();
  88 + }
  89 +
  90 +
  91 + // fullscreen
  92 + if (this.options.fullscreen) {
  93 + $('body').css({overflow:'hidden', padding:0});
  94 +
  95 + this.options.width = $(window).width();
  96 + this.options.height = ($(window).height()>$(document).height())?$(window).height():$(document).height();
  97 +
  98 +// this.options.width = this.options.width;
  99 +// this.options.height = this.options.height;
  100 +
  101 + ext.addClass('slideshow-fullscreen');
  102 + }
  103 +
  104 + this.length = ext.find('.slideshow-content > *').length;
  105 +
  106 + // build title
  107 + if (this.options.title) {
  108 + ext.prepend('<div class="slideshow-label-place"><div class="slideshow-label slideshow-opacity"></div></div>');
  109 +
  110 + if (!this.options.titleshow) {
  111 + ext.find('.slideshow-label-place').hover(function(){
  112 + $(this).find('.slideshow-label').fadeIn();
  113 + }, function() {
  114 + $(this).find('.slideshow-label').fadeOut();
  115 + });
  116 + ext.find('.slideshow-label').hide();
  117 + }
  118 +
  119 +
  120 + ext.find('.slideshow-label-place').css('width', this.options.width);
  121 + }
  122 +
  123 + // build panel
  124 + if (this.options.panel) {
  125 + ext.append('<div class="slideshow-panel-place"><div class="slideshow-panel slideshow-opacity"></div></div>');
  126 + panel = ext.find('.slideshow-panel');
  127 + if (this.options.controls.first)
  128 + panel.append('<a class="first slideshowbutton" href="#first">First</a>');
  129 +
  130 + if (this.options.controls.prev)
  131 + panel.append('<a class="prev slideshowbutton" href="#prev">Prev</a>');
  132 +
  133 + if (this.options.controls.play)
  134 + panel.append('<a class="play slideshowbutton" href="#play">Play</a>');
  135 +
  136 + if (this.options.controls.next)
  137 + panel.append('<a class="next slideshowbutton" href="#next">Next</a>');
  138 +
  139 + if (this.options.controls.last)
  140 + panel.append('<a class="last slideshowbutton" href="#last">Last</a>');
  141 +
  142 + if (this.options.controls.help) {
  143 + panel.append('<a class="help slideshowbutton" href="#help">Help</a>');
  144 + panel.prepend('<div class="slideshow-help">'+this.options.help+'</div>');
  145 +
  146 +// panel.find('.slideshow-help').css('width', this.options.width - 4 + 'px');
  147 + }
  148 +
  149 +
  150 + if (this.options.controls.counter) {
  151 + panel.append('<span class="counter">'+(this.options.index+1)+' / '+this.length+'</span>');
  152 + }
  153 +
  154 + if (this.options.controls.hide) {
  155 + ext.find('.slideshow-panel-place').hover(function(){
  156 + $(this).find('.slideshow-panel').fadeIn();
  157 + }, function() {
  158 + $(this).find('.slideshow-panel').fadeOut();
  159 + });
  160 + panel.hide();
  161 + }
  162 +
  163 +
  164 + ext.find('.slideshow-panel-place').css('width', this.options.width);
  165 + }
  166 +
  167 + /**
  168 + * Set Size Options
  169 + */
  170 + ext.css('width', this.options.width + 'px');
  171 +// ext.css('height', this.options.height + 'px');
  172 +
  173 + ext.find('.slideshow-content').css('width', this.options.width);
  174 + ext.find('.slideshow-content').css('height', this.options.height);
  175 +
  176 + /**
  177 + * Change children styles
  178 + */
  179 + ext.find('.slideshow-content > *').each(function(){
  180 + _self._build($(this));
  181 + });
  182 +
  183 + // init slide (replace by ajax etc)
  184 + this.init(this.options.index);
  185 +
  186 + // hide all slides
  187 + ext.find('.slideshow-content > *:not(:eq('+this.options.index+'))').hide();
  188 +
  189 + // show label
  190 + this.label();
  191 +
  192 + // add playframe
  193 + if (this.options.playframe) {
  194 + ext.find('.slideshow-content').append('<div class="slideshow-shadow slideshow-opacity"><div class="slideshow-frame"></div></div>');
  195 + }
  196 +
  197 + // bind all events
  198 + this.events();
  199 +
  200 + return true;
  201 + };
  202 +
  203 + /**
  204 + * Change CSS for every entity
  205 + */
  206 + this._build = function(el){
  207 + el.css({margin :0,
  208 + position: 'absolute',
  209 + left: (this.options.width/2 - el.attr('width')/2),
  210 + top : (this.options.height/2 - el.attr('height')/2),
  211 + overflow:'hidden'
  212 + });
  213 +
  214 + if (el.is('img') && this.options.imgresize || el.is(':not(img)')){
  215 + el.css({width:'100%',height:'100%'});
  216 + }
  217 + };
  218 +
  219 + /**
  220 + * Bind Events
  221 + */
  222 + this.events = function() {
  223 +
  224 + var _self = this;
  225 +
  226 + /**
  227 + * Go to next slide on content click (optional)
  228 + */
  229 + if (_self.options.nextclick)
  230 + ext.find('.slideshow-content').click(function(){
  231 + _self.stop();
  232 + _self.next();
  233 + return false;
  234 + });
  235 +
  236 + /**
  237 + * Goto first slide button
  238 + */
  239 + if (this.options.controls.first)
  240 + ext.find('a.first').click(function(){
  241 + _self.stop();
  242 + _self.goSlide(0);
  243 + return false;
  244 + });
  245 +
  246 + /**
  247 + * Goto previouse slide button
  248 + */
  249 + if (this.options.controls.prev)
  250 + ext.find('a.prev').click(function(){
  251 + _self.stop();
  252 + _self.prev();
  253 + return false;
  254 + });
  255 +
  256 + /**
  257 + * Play slideshow button
  258 + */
  259 + if (this.options.controls.play)
  260 + ext.find('a.play').click(function(){
  261 + if (_self.playFlag) {
  262 + _self.stop();
  263 + } else {
  264 + _self.play();
  265 + }
  266 + return false;
  267 + });
  268 +
  269 + /**
  270 + * Goto next slide button
  271 + */
  272 + if (this.options.controls.next)
  273 + ext.find('a.next').click(function(){
  274 + _self.stop();
  275 + _self.next();
  276 + return false;
  277 + });
  278 +
  279 + /**
  280 + * Goto last slide button
  281 + */
  282 + if (this.options.controls.last)
  283 + ext.find('a.last').click(function(){
  284 + _self.stop();
  285 + _self.goSlide(_self.length-1);
  286 + return false;
  287 + });
  288 +
  289 + /**
  290 + * Show help message
  291 + */
  292 + if (this.options.controls.help)
  293 + ext.find('a.help').click(function(){
  294 + _self.stop();
  295 + ext.find('.slideshow-help').slideToggle();
  296 + return false;
  297 + });
  298 +
  299 + /**
  300 + * Show playframe
  301 + */
  302 + if (this.options.playframe)
  303 + ext.find('.slideshow-frame').click(function(){
  304 + ext.find('.slideshow-frame').remove();
  305 + ext.find('.slideshow-shadow').remove();
  306 +
  307 + if (_self.options.playclick)
  308 + setTimeout(function(ms){ _self.play() }, _self.options.time);
  309 + return false;
  310 + });
  311 +
  312 + /**
  313 + * Play/stop on slideshow hover
  314 + */
  315 + if (this.options.playhover)
  316 + ext.hover(function(){
  317 + if (!_self.playFlag) {
  318 + _self.play();
  319 + }
  320 + }, function(){
  321 + if (_self.playFlag) {
  322 + _self.stop();
  323 + }
  324 + });
  325 +
  326 +
  327 + /**
  328 + * Stop/Play on slideshow hover
  329 + */
  330 + if (this.options.playhoverr)
  331 + ext.hover(function(){
  332 + if (_self.playFlag) {
  333 + _self.stop();
  334 + }
  335 + }, function(){
  336 + if (!_self.playFlag) {
  337 + _self.play();
  338 + }
  339 + });
  340 + };
  341 +
  342 + /**
  343 + * Find and show label of slide
  344 + * @method
  345 + */
  346 + this.label = function () {
  347 + if (!this.options.title) return false;
  348 +
  349 + label = '';
  350 +
  351 + current = ext.find('.slideshow-content > *:eq('+this.options.index +')');
  352 +
  353 + if (current.attr('alt')) {
  354 + label = current.attr('alt');
  355 + } else if (current.attr('title')) {
  356 + label = current.attr('title');
  357 + }else if (current.find('label:first').length>0) {
  358 + current.find('label:first').hide();
  359 + label = current.find('label:first').html();
  360 + }
  361 +
  362 + ext.find('.slideshow-label').html(label);
  363 + };
  364 +
  365 + /**
  366 + * Goto previous slide
  367 + * @method
  368 + */
  369 + this.prev = function () {
  370 + if (this.options.index == 0) {
  371 + i = (this.length-1);
  372 + } else {
  373 + i = this.options.index - 1;
  374 + }
  375 +
  376 + this.goSlide(i);
  377 + };
  378 +
  379 +
  380 + /**
  381 + * Play Slideshow
  382 + * @method
  383 + */
  384 + this.play = function () {
  385 + var _self = this;
  386 + this.playFlag = true;
  387 + this.playId = setTimeout(function(ms){ _self._play() }, this.options.time);
  388 + ext.find('a.play').addClass('stop');
  389 + };
  390 +
  391 + /**
  392 + * Play Slideshow
  393 + * @method
  394 + */
  395 + this._play = function () {
  396 + var _self = this;
  397 + this.next();
  398 + if (this.playFlag) {
  399 + if (this.options.index == (this.length-1) && !this.options.loop) { this.stop();return false; }
  400 + this.playId = setTimeout(function(ms){ _self._play() }, this.options.time);
  401 + }
  402 + };
  403 +
  404 + /**
  405 + * Stop Slideshow
  406 + * @method
  407 + */
  408 + this.stop = function () {
  409 + ext.find('a.play').removeClass('stop');
  410 + this.playFlag = false;
  411 + clearTimeout(this.playId);
  412 + };
  413 +
  414 + /**
  415 + * Goto next slide
  416 + * @method
  417 + */
  418 + this.next = function () {
  419 + if (this.options.index == (this.length-1)) {
  420 + i = 0;
  421 + } else {
  422 + i = this.options.index + 1;
  423 + }
  424 + this.goSlide(i);
  425 + };
  426 +
  427 + /**
  428 + * Init N-slide
  429 + * @method
  430 + * @param {Integer} n
  431 + */
  432 + this.init = function (index) {
  433 + // initialize only ones
  434 + for (var i = 0, loopCnt = this.inited.length; i < loopCnt; i++) {
  435 + if (this.inited[i] === index) {
  436 + return true;
  437 + }
  438 + }
  439 +
  440 + // index to inited stack
  441 + this.inited.push(index);
  442 +
  443 + // current slide
  444 + slide = ext.find('.slideshow-content > *:eq('+index+')');
  445 +
  446 + var _self = this;
  447 + /**
  448 + * Replace A to content from HREF
  449 + */
  450 + if (slide.get(0).tagName == 'A') {
  451 + var href = slide.attr('href');
  452 +
  453 + var title = slide.attr('title');
  454 + title = title.replace(/\"/i,'\''); // if you use single quotes for tag attribs
  455 +
  456 + var domain = document.domain;
  457 + domain = domain.replace(/\./i,"\."); // for strong check domain name
  458 +
  459 + var reimage = new RegExp("\.(png|gif|jpg|jpeg|svg)$", "i");
  460 + var relocal = new RegExp("^((https?:\/\/"+domain+")|(?!http:\/\/))", "i");
  461 +
  462 +
  463 + if (this.options.imgajax && reimage.test(href)) {
  464 + slide.replaceWith('<img src="'+href+'" alt="'+title+'"/>');
  465 + } else if (this.options.linkajax && relocal.test(href)) {
  466 + $.get(href, function(data){
  467 + slide.replaceWith('<div><label>'+title+'</label>'+data+'</div>');
  468 + });
  469 + } else { // nothing else
  470 +// slide.wrap('<p></p>');
  471 + }
  472 +
  473 + slide = ext.find('.slideshow-content > *:eq('+index+')');
  474 +
  475 + // reset css
  476 + this._build(slide);
  477 + }
  478 +
  479 + /**
  480 + * Play/stop on content click (like image and other)
  481 + */
  482 + if (this.options.playclick)
  483 + $(slide).click(function(){
  484 + if (_self.playFlag) {
  485 + _self.stop();
  486 + } else {
  487 + _self.play();
  488 + }
  489 + return false;
  490 + });
  491 + };
  492 +
  493 + /**
  494 + * Goto N-slide
  495 + * @method
  496 + * @param {Integer} n
  497 + */
  498 + this.goSlide = function (n) {
  499 +
  500 + if (this.options.index == n) return;
  501 +
  502 + this.init(n);
  503 +
  504 + var next = ext.find('.slideshow-content > *:eq('+n+')');
  505 + var prev = ext.find('.slideshow-content > *:eq('+this.options.index+')');
  506 +
  507 + // restore next slide after all effects, set z-index = 0 for prev slide
  508 + prev.css({zIndex:0});
  509 + if (this.options.imgresize) {
  510 + next.css({zIndex:1, top: 0, left: 0, opacity: 1, width: this.options.width, height: this.options.height});
  511 + } else {
  512 + next.css({zIndex:1, top: (this.options.height/2 - next.attr('height')/2), left: (this.options.width/2 - next.attr('width')/2), opacity: 1});
  513 + }
  514 +
  515 + this.options.index = n;
  516 +
  517 + if (this.options.effect == 'random' ) {
  518 + var r = Math.random();
  519 + r = Math.floor(r*12);
  520 + } else {
  521 + r = -1;
  522 + }
  523 + // effect between slides
  524 + switch (true) {
  525 + case (this.options.effect == 'scrollUp' || r == 0):
  526 + prev.css({width:'100%'});
  527 + next.css({top:0, height:0});
  528 +
  529 + prevAni = {height: 0, top:this.options.height};
  530 + break;
  531 + case (this.options.effect == 'scrollDown' || r == 1):
  532 + prev.css({width:'100%'});
  533 + next.css({top:this.options.height,height:0});
  534 +
  535 + prevAni = {height: 0, top:0};
  536 + break;
  537 + case (this.options.effect == 'scrollRight' || r == 2):
  538 + prev.css({right:0,left:'',height:'100%'});
  539 + next.css({right:'',left:0,height:'100%',width:'0%'});
  540 +
  541 + prevAni = {width: 0};
  542 + break;
  543 + case (this.options.effect == 'scrollLeft' || r == 3):
  544 + prev.css({right:'',left:0,height:'100%'});
  545 + next.css({right:0,left:'',height:'100%',width:'0%'});
  546 +
  547 + prevAni = {width: 0};
  548 + break;
  549 + case (this.options.effect == 'growX' || r == 4):
  550 + next.css({zIndex:2,opacity: 1,left: this.options.width/2, width: '0%', height:'100%'});
  551 +
  552 + prevAni = {opacity: 0.8};
  553 + break;
  554 +
  555 + case (this.options.effect == 'growY' || r == 5):
  556 + next.css({opacity: 1,top: this.options.height/2, width:'100%', height: '0%'});
  557 +
  558 + prevAni = {opacity: 0.8};
  559 + break;
  560 +
  561 + case (this.options.effect == 'zoom' || r == 6):
  562 + next.css({width: 0, height: 0, top: this.options.height/2, left: this.options.width/2});
  563 +
  564 + prevAni = {width: 0, height: 0, top: this.options.height/2, left: this.options.width/2};
  565 + break;
  566 +
  567 + case (this.options.effect == 'zoomFade' || r == 7):
  568 + next.css({zIndex:1, opacity: 0,width: 0, height: 0, top: this.options.height/2, left: this.options.width/2});
  569 +
  570 + prevAni = {opacity: 0, width: 0, height: 0, top: this.options.height/2, left: this.options.width/2};
  571 + break;
  572 +
  573 + case (this.options.effect == 'zoomTL' || r == 8):
  574 + next.css({zIndex:1, opacity: 0,width: this.options.width/2, height: this.options.height/2, top:0, left: 0});
  575 +
  576 + prevAni = {opacity: 0, width: 0, height: 0, top: this.options.height, left: this.options.width};
  577 + break;
  578 + case (this.options.effect == 'zoomBR' || r == 9):
  579 + next.css({zIndex:1, opacity: 0,width: this.options.width/2, height: this.options.height/2, top: this.options.height/2, left: this.options.width/2});
  580 +
  581 + prevAni = {opacity: 0, width: 0, height: 0, top: 0, left: 0};
  582 + break;
  583 + case (this.options.effect == 'fade' || r == 10):
  584 + default:
  585 + prev.css({zIndex:0, opacity: 1});
  586 + next.css({zIndex:1, opacity: 0});
  587 +
  588 + prevAni = {opacity: 0};
  589 + break;
  590 + }
  591 +
  592 + var _self = this;
  593 +
  594 + prev.animate(prevAni,this.options.effecttime);
  595 +
  596 + // play next slide animation, hide prev slide, update label, update counter
  597 + next.show().animate({opacity: 1}, this.options.effecttime, function () { prev.hide(); _self.label(); _self.counter(); });
  598 + };
  599 +
  600 + /**
  601 + * Update counter data
  602 + * @method
  603 + */
  604 + this.counter = function () {
  605 + if (this.options.controls.counter)
  606 + ext.find('.slideshow-panel span.counter').html((this.options.index+1) + ' / ' + this.length);
  607 +
  608 + };
  609 +
  610 + // Now initialize the slideshow
  611 + this.options = $.extend({}, defaults, settings);
  612 +
  613 + if (typeof(settings) != 'undefined') {
  614 + if (typeof(settings.controls) != 'undefined')
  615 + this.options.controls = $.extend({}, defaults.controls, settings.controls);
  616 + }
  617 +
  618 + this.build();
  619 +
  620 + /**
  621 + * Show slideshow
  622 + */
  623 + ext.show();
  624 +
  625 + /**
  626 + * Check play option
  627 + */
  628 + if (this.options.play) {
  629 + this.play();
  630 + }
  631 +
  632 + return ext;
  633 + });
  634 +
  635 + /**
  636 + * external functions - append to $
  637 + */
  638 + _slideshow.playSlide = function(){ _slideshow.each(function () { this.play(); }) };
  639 + _slideshow.stopSlide = function(){ _slideshow.each(function () { this.stop(); }) };
  640 + _slideshow.nextSlide = function(){ _slideshow.each(function () { this.next(); }) };
  641 + _slideshow.prevSlide = function(){ _slideshow.each(function () { this.prev(); }) };
  642 +
  643 + return this;
  644 + }
  645 +})(jQuery);
... ...
public/javascripts/jquery.aslideshow/simple/images/big-play.png 0 → 100644

17.3 KB

public/javascripts/jquery.aslideshow/simple/images/buttons.png 0 → 100644

4.9 KB

public/javascripts/jquery.aslideshow/simple/styles.css 0 → 100644
... ... @@ -0,0 +1,180 @@
  1 +.slideshow, .slideshow-label, .slideshow-content, .slideshow-panel {
  2 + font:12px Verdana, Tahoma, sans-serif;
  3 +}
  4 +
  5 +.slideshow {
  6 + padding:0;
  7 + border:0;
  8 + position:relative;
  9 + display:none; /* Set to "none" for not preview slideshow content */
  10 +}
  11 +
  12 +.slideshow-fullscreen {
  13 + position:absolute;
  14 + top:0;
  15 + left:0;
  16 + padding:0;
  17 + border:0;
  18 + overflow:hidden;
  19 +}
  20 +
  21 +.slideshow-label-place {
  22 + padding:0;
  23 + position:absolute;
  24 + top:0px;
  25 + left:0px;
  26 + z-index:100;
  27 + height:30px;
  28 +}
  29 + .slideshow-label {
  30 + z-index:101;
  31 + color:#fff;
  32 + width:100%;
  33 + height:100%;
  34 + line-height:30px;
  35 + text-indent:8px;
  36 + font-weight:bold
  37 + }
  38 +
  39 +.slideshow-panel-place {
  40 + padding:0;
  41 + position:absolute;
  42 + bottom:-29px;
  43 + left:0px;
  44 + z-index:100;
  45 + height:28px;
  46 +}
  47 +
  48 +
  49 + .slideshow-panel {
  50 + z-index:101;
  51 + width:100%;
  52 + height:100%;
  53 + }
  54 +
  55 + .slideshow-panel a.slideshowbutton {
  56 + display: block;
  57 + width:26px;
  58 + height:26px;
  59 + float:left;
  60 + text-indent:-99999%;
  61 + overflow:hidden;
  62 + outline: 0; /* @ Firefox, prevent dotted border after click */
  63 + background-image:url(images/buttons.png);
  64 + background-repeat:no-repeat;
  65 + border:1px solid transparent
  66 + }
  67 +
  68 + .slideshow-panel a.slideshowbutton:hover {
  69 + border:1px solid #777;
  70 + }
  71 +
  72 +
  73 + .slideshow-panel a.first {
  74 + background-position: 0 0
  75 + }
  76 +
  77 + .slideshow-panel a.prev {
  78 + background-position: -24px 0
  79 + }
  80 +
  81 + .slideshow-panel a.play {
  82 + background-position: -48px 0
  83 + }
  84 +
  85 + .slideshow-panel a.stop {
  86 + background-position: -72px 0
  87 + }
  88 +
  89 + .slideshow-panel a.next {
  90 + background-position: -96px 0
  91 + }
  92 +
  93 + .slideshow-panel a.last {
  94 + background-position: -120px 0
  95 + }
  96 +
  97 + .slideshow-panel a.help {
  98 + position:relative;
  99 + background-position: -144px 0
  100 + }
  101 +
  102 + .slideshow-panel span.counter {
  103 + float:right;
  104 + display: block;
  105 + /*width:26px;*/
  106 + height:26px;
  107 + line-height:26px;
  108 + padding:0 4px;
  109 + color: white;
  110 + }
  111 +
  112 +
  113 +.slideshow-help {
  114 + position:absolute;
  115 + bottom:28px;
  116 + left:0px;
  117 + z-index:101;
  118 + background-color:#ff9;
  119 + display:none;
  120 + opacity: 0.9;
  121 + width:100%;
  122 +}
  123 +
  124 +.slideshow-content {
  125 + padding:0;
  126 + background-color:#fff;
  127 + color:#333;
  128 + overflow:hidden;
  129 + position:relative;
  130 + width:100%;
  131 + height:100%;
  132 +}
  133 +
  134 + /* Some Content Changes */
  135 + .slideshow-content p {
  136 + padding:0;
  137 + overflow:auto;
  138 + }
  139 +
  140 +.slideshow-frame {
  141 + position:absolute;
  142 + top:0px;
  143 + left:0px;
  144 + background:url(images/big-play.png) 50% 50% no-repeat;
  145 + z-index:201;
  146 + cursor:pointer;
  147 + width:100%;
  148 + height:100%;
  149 +}
  150 +
  151 +.slideshow-shadow{
  152 + position:absolute;
  153 + top:0px;
  154 + left:0px;
  155 + z-index:200;
  156 + width:100%;
  157 + height:100%;
  158 +}
  159 +
  160 +.slideshow-opacity{
  161 + background-color: black;
  162 +}
  163 + *:first-child+html .slideshow-opacity, * html .slideshow-opacity {
  164 + zoom:1;
  165 + background:#000;
  166 + filter:alpha(opacity=50);
  167 + }
  168 + * html .slideshow-opacity {
  169 + zoom:1;
  170 + background:#000;
  171 + filter:alpha(opacity=50);
  172 + }
  173 +
  174 + *:first-child+html .slideshow-opacity *{
  175 + position:relative;
  176 + }
  177 +
  178 + * html .slideshow-opacity *{
  179 + position:relative;
  180 + }
... ...
public/stylesheets/controller_content_viewer.css
1 1 @import url('products.css');
  2 +@import url('folder.css');
2 3  
3 4 /************* enterprise homepage style *****************/
4 5  
... ... @@ -45,3 +46,7 @@ div#article-parent {
45 46 text-align: right;
46 47 font-style: italic;
47 48 }
  49 +
  50 +.article-body-uploaded-file {
  51 + text-align: center;
  52 +}
... ...
public/stylesheets/controller_search.css
  1 +@import url(pagination.css);
  2 +
1 3 #search-page {
2 4 position: relative; /* to the text appear on MSIE 6 */
3 5 }
... ... @@ -280,27 +282,3 @@
280 282 height: 150px;
281 283 overflow: auto;
282 284 }
283   -
284   -/* * * Pagination * * * * * * * * * * * */
285   -
286   -.pagination {
287   - text-align: center;
288   - clear: both;
289   -}
290   -
291   -.pagination .disabled {
292   - color: #888;
293   -}
294   -
295   -.pagination .current {
296   - font-weight: bold;
297   -}
298   -
299   -.pagination a {
300   - text-decoration: none;
301   -}
302   -
303   -.pagination a:hover {
304   - color: #026;
305   -}
306   -
... ...
public/stylesheets/folder.css 0 → 100644
... ... @@ -0,0 +1,68 @@
  1 +@import url(pagination.css);
  2 +
  3 +.image-gallery {
  4 + text-align: center;
  5 +}
  6 +
  7 +.image-gallery ul {
  8 + padding: 0px;
  9 +}
  10 +
  11 +.image-gallery-item {
  12 + width: 142px;
  13 + height: 154px;
  14 + list-style: none;
  15 + margin: 5px;
  16 + float: left;
  17 + overflow: hidden;
  18 +}
  19 +
  20 +.image-gallery-item span {
  21 + text-align: center;
  22 + font-size: 11px;
  23 +}
  24 +
  25 +.image-gallery-item span a {
  26 + text-decoration: none;
  27 + color: #000
  28 +}
  29 +
  30 +.image-gallery-item img {
  31 + padding: 2px;
  32 + border: 1px solid;
  33 +}
  34 +
  35 +.image-gallery-item .folder, .image-gallery-item .uploaded-file {
  36 + width: 120px;
  37 + height: 120px;
  38 + border: 1px solid;
  39 +}
  40 +
  41 +.image-gallery-item .folder {
  42 + background: transparent url('../images/icons-app/gtk-folder.png') no-repeat 50% 43%;
  43 + display: block;
  44 + color: #DB8;
  45 +}
  46 +
  47 +.image-gallery-item .folder:hover {
  48 + border: 4px solid;
  49 + width: 114px;
  50 + height: 114px;
  51 + background-color: #FF9
  52 +}
  53 +
  54 +.image-gallery-item .uploaded-file {
  55 + background: transparent url('../images/icons-mime/empty.png') no-repeat 50% 43%;
  56 +}
  57 +
  58 +#content #article .image-gallery .pagination .prev_page {
  59 + position: relative;
  60 + left: auto;
  61 + right: auto;
  62 +}
  63 +
  64 +#content #article .image-gallery .pagination .next_page {
  65 + position: relative;
  66 + right: auto;
  67 + left: auto;
  68 +}
... ...
public/stylesheets/pagination.css 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +.pagination {
  2 + text-align: center;
  3 + clear: both;
  4 +}
  5 +
  6 +.pagination .disabled {
  7 + color: #888;
  8 +}
  9 +
  10 +.pagination .current {
  11 + font-weight: bold;
  12 +}
  13 +
  14 +.pagination a {
  15 + text-decoration: none;
  16 +}
  17 +
  18 +.pagination a:hover {
  19 + color: #026;
  20 +}
... ...
test/functional/cms_controller_test.rb
... ... @@ -190,6 +190,7 @@ class CmsControllerTest &lt; Test::Unit::TestCase
190 190 post :new, :type => UploadedFile.name, :profile => profile.identifier, :article => { :uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain')}
191 191 end
192 192 assert_not_nil profile.articles.find_by_path('test.txt')
  193 + assigns(:article).destroy
193 194 end
194 195  
195 196 should 'be able to update an uploaded file' do
... ... @@ -816,4 +817,53 @@ class CmsControllerTest &lt; Test::Unit::TestCase
816 817 get :edit, :profile => profile.identifier, :id => profile.blog.id
817 818 assert_tag :tag => 'a', :content => 'Cancel', :attributes => { :href => /\/myprofile\/#{profile.identifier}/ }
818 819 end
  820 +
  821 + should 'create icon upload file in folder' do
  822 + f = Folder.create!(:name => 'test_folder', :profile => profile, :view_as => 'image_gallery')
  823 + post :new, :profile => profile.identifier,
  824 + :type => UploadedFile.name,
  825 + :parent_id => f.id,
  826 + :article => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')}
  827 +
  828 + assert File.exists?(assigns(:article).icon_name)
  829 + assigns(:article).destroy
  830 + end
  831 +
  832 + should 'create icon upload file' do
  833 + post :new, :profile => profile.identifier,
  834 + :type => UploadedFile.name,
  835 + :article => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')}
  836 +
  837 + assert File.exists?(assigns(:article).icon_name)
  838 + assigns(:article).destroy
  839 + end
  840 +
  841 + should 'record when coming from public view on upload files' do
  842 + folder = Folder.create!(:name => 'testfolder', :profile => profile)
  843 +
  844 + @request.expects(:referer).returns("http://colivre.net/#{profile.identifier}/#{folder.slug}")
  845 +
  846 + get :upload_files, :profile => profile.identifier, :parent_id => folder.id
  847 + assert_tag :tag => 'input', :attributes => { :type => 'hidden', :name => 'back_to', :value => 'public_view' }
  848 + assert_tag :tag => 'a', :descendant => { :content => 'Cancel' }, :attributes => { :href => /^https?:\/\/colivre.net\/#{profile.identifier}\/#{folder.slug}/ }
  849 + end
  850 +
  851 + should 'detect when comming from home page to upload files' do
  852 + folder = Folder.create!(:name => 'testfolder', :profile => profile)
  853 + profile.expects(:home_page).returns(folder).at_least_once
  854 +
  855 + @request.expects(:referer).returns("http://colivre.net/#{profile.identifier}").at_least_once
  856 + @controller.stubs(:profile).returns(profile)
  857 + get :upload_files, :profile => profile.identifier, :parent_id => folder.id
  858 + assert_tag :tag => 'input', :attributes => { :type => 'hidden', :name => 'back_to', :value => 'public_view' }
  859 + assert_tag :tag => 'a', :descendant => { :content => 'Cancel' }, :attributes => { :href => /^https?:\/\/colivre.net\/#{profile.identifier}\/#{profile.home_page.slug}$/ }
  860 + end
  861 +
  862 + should 'go back to public view when upload files coming from there' do
  863 + folder = Folder.create!(:name => 'test_folder', :profile => profile)
  864 +
  865 + post :upload_files, :profile => profile.identifier, :parent_id => folder.id, :back_to => 'public_view', :uploaded_files => [fixture_file_upload('files/rails.png', 'image/png')]
  866 + assert_template nil
  867 + assert_redirected_to folder.url
  868 + end
819 869 end
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -658,4 +658,45 @@ class ContentViewerControllerTest &lt; Test::Unit::TestCase
658 658 assert_equal 1, a.hits
659 659 end
660 660  
  661 + should 'render html for image when view' do
  662 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => profile)
  663 + get :view_page, :profile => profile.identifier, :page => file.explode_path, :view => true
  664 +
  665 + assert_response :success
  666 + assert_template 'view_page'
  667 + end
  668 +
  669 + should 'download data for image when not view' do
  670 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => profile)
  671 + get :view_page, :profile => profile.identifier, :page => file.explode_path
  672 +
  673 + assert_response :success
  674 + assert_template nil
  675 + end
  676 +
  677 + should "display 'Upload files' when create children of image gallery" do
  678 + login_as(profile.identifier)
  679 + f = Folder.create!(:name => 'gallery', :profile => profile, :view_as => 'image_gallery')
  680 + get :view_page, :profile => profile.identifier, :page => f.explode_path
  681 + assert_tag :tag => 'a', :content => 'Upload files', :attributes => {:href => /parent_id=#{f.id}/}
  682 + end
  683 +
  684 + should "display 'New article' when showing folder child of image gallery" do
  685 + login_as(profile.identifier)
  686 + folder1 = Folder.create!(:name => 'gallery1', :profile => profile, :view_as => 'image_gallery')
  687 + folder1.children << folder2 = Folder.new(:name => 'gallery2', :profile => profile)
  688 +
  689 + get :view_page, :profile => profile.identifier, :page => folder2.explode_path
  690 + assert_no_tag :tag => 'a', :content => 'Upload files', :attributes => {:href => /parent_id=#{folder2.id}/}
  691 + assert_tag :tag => 'a', :content => 'New article', :attributes => {:href =>/parent_id=#{folder2.id}/}
  692 + end
  693 +
  694 + should "display 'Upload files' to image gallery when showing its children" do
  695 + login_as(profile.identifier)
  696 + folder = Folder.create!(:name => 'gallery', :profile => profile, :view_as => 'image_gallery')
  697 + file = UploadedFile.create!(:profile => profile, :parent => folder, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  698 + get :view_page, :profile => profile.identifier, :page => file.explode_path, :view => true
  699 +
  700 + assert_tag :tag => 'a', :content => 'Upload files', :attributes => {:href => /parent_id=#{folder.id}/}
  701 + end
661 702 end
... ...
test/unit/folder_test.rb
... ... @@ -25,8 +25,8 @@ class FolderTest &lt; ActiveSupport::TestCase
25 25 f.children.create!(:profile => p, :name => 'otherarticle')
26 26 f.reload
27 27  
28   - assert_tag_in_string f.to_html, :tag => 'td', :descendant => { :tag => 'a', :attributes => { :href => /.*\/testuser\/f\/onearticle$/ } }, :content => /onearticle/
29   - assert_tag_in_string f.to_html, :tag => 'td', :descendant => { :tag => 'a', :attributes => { :href => /.*\/testuser\/f\/otherarticle$/ } }, :content => /otherarticle/
  28 + assert_tag_in_string f.to_html, :tag => 'td', :descendant => { :tag => 'a', :attributes => { :href => /.*\/testuser\/f\/onearticle(\?|$)/ } }, :content => /onearticle/
  29 + assert_tag_in_string f.to_html, :tag => 'td', :descendant => { :tag => 'a', :attributes => { :href => /.*\/testuser\/f\/otherarticle(\?|$)/ } }, :content => /otherarticle/
30 30 end
31 31  
32 32 should 'explictly advise if empty' do
... ... @@ -52,4 +52,59 @@ class FolderTest &lt; ActiveSupport::TestCase
52 52 assert_equal false, a.can_display_hits?
53 53 end
54 54  
  55 + should 'be viewed as image gallery' do
  56 + p = create_user('test_user').person
  57 + f = Folder.create!(:name => 'Test folder', :profile => p)
  58 + f.view_as = 'image_gallery'; f.save!
  59 + f.reload
  60 +
  61 + assert_equal 'image_gallery', f.view_as
  62 + end
  63 +
  64 + should 'not allow view as bogus' do
  65 + p = create_user('test_user').person
  66 + f = Folder.create!(:name => 'Test folder', :profile => p)
  67 + f.view_as = 'bogus'
  68 + assert !f.save
  69 + end
  70 +
  71 + should 'view as folder by default' do
  72 + p = create_user('test_user').person
  73 + f = Folder.create!(:name => 'Test folder', :profile => p)
  74 + f.expects(:folder)
  75 + f.to_html
  76 +
  77 + assert_equal 'folder', f.view_as
  78 + end
  79 +
  80 + should 'have images that are only images or other folders' do
  81 + p = create_user('test_user').person
  82 + f = Folder.create!(:name => 'Test folder', :profile => p)
  83 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :parent => f, :profile => p)
  84 + image = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => f, :profile => p)
  85 + folder = Folder.create!(:name => 'child test folder', :parent => f, :profile => p)
  86 +
  87 + assert_equivalent [folder, image], f.images
  88 + end
  89 +
  90 + should 'bring folders first in alpha order in images listing' do
  91 + p = create_user('test_user').person
  92 + f = Folder.create!(:name => 'Test folder', :profile => p)
  93 + folder1 = Folder.create!(:name => 'child test folder 1', :parent => f, :profile => p)
  94 + image = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => f, :profile => p)
  95 + folder2 = Folder.create!(:name => 'child test folder 2', :parent => f, :profile => p)
  96 + folder3 = Folder.create!(:name => 'another child test folder', :parent => f, :profile => p)
  97 +
  98 + assert_equal [folder3.id, folder1.id, folder2.id, image.id], f.images.map(&:id)
  99 + end
  100 +
  101 + should 'images support pagination' do
  102 + p = create_user('test_user').person
  103 + f = Folder.create!(:name => 'Test folder', :profile => p)
  104 + folder = Folder.create!(:name => 'child test folder', :parent => f, :profile => p)
  105 + image = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => f, :profile => p)
  106 +
  107 + assert_equal [image], f.images.paginate(:page => 2, :per_page => 1)
  108 + end
  109 +
55 110 end
... ...
test/unit/uploaded_file_test.rb
... ... @@ -85,4 +85,21 @@ class UploadedFileTest &lt; Test::Unit::TestCase
85 85 assert f.valid?
86 86 end
87 87  
  88 + should 'create icon when created in folder' do
  89 + p = create_user('test_user').person
  90 + f = Folder.create!(:name => 'test_folder', :profile => p)
  91 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent_id => f.id, :profile => p)
  92 +
  93 + assert File.exists?(file.public_filename(:icon))
  94 + file.destroy
  95 + end
  96 +
  97 + should 'create icon when not created in folder' do
  98 + p = create_user('test_user').person
  99 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => p)
  100 +
  101 + assert File.exists?(file.public_filename(:icon))
  102 + file.destroy
  103 + end
  104 +
88 105 end
... ...
vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb
... ... @@ -220,7 +220,11 @@ module Technoweenie # :nodoc:
220 220  
221 221 # Returns true/false if an attachment is thumbnailable. A thumbnailable attachment has an image content type and the parent_id attribute.
222 222 def thumbnailable?
223   - image? && respond_to?(:parent_id) && parent_id.nil?
  223 + image? && !is_thumbnail?
  224 + end
  225 +
  226 + def is_thumbnail?
  227 + (thumbnail_class == self.class) && !(respond_to?(:parent_id) && parent_id.nil?)
224 228 end
225 229  
226 230 # Returns the class used to create new thumbnails for this attachment.
... ... @@ -389,7 +393,7 @@ module Technoweenie # :nodoc:
389 393  
390 394 # Initializes a new thumbnail with the given suffix.
391 395 def find_or_initialize_thumbnail(file_name_suffix)
392   - respond_to?(:parent_id) ?
  396 + thumbnail_class.columns.map(&:name).include?('parent_id') ?
393 397 thumbnail_class.find_or_initialize_by_thumbnail_and_parent_id(file_name_suffix.to_s, id) :
394 398 thumbnail_class.find_or_initialize_by_thumbnail(file_name_suffix.to_s)
395 399 end
... ... @@ -402,7 +406,7 @@ module Technoweenie # :nodoc:
402 406 # Cleans up after processing. Thumbnails are created, the attachment is stored to the backend, and the temp_paths are cleared.
403 407 def after_process_attachment
404 408 if @saved_attachment
405   - if respond_to?(:process_attachment_with_processing) && thumbnailable? && !attachment_options[:thumbnails].blank? && parent_id.nil?
  409 + if respond_to?(:process_attachment_with_processing) && thumbnailable? && !attachment_options[:thumbnails].blank?
406 410 temp_file = temp_path || create_temp_file
407 411 attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(temp_file, suffix, *size) }
408 412 end
... ...
vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/file_system_backend.rb
... ... @@ -28,7 +28,7 @@ module Technoweenie # :nodoc:
28 28  
29 29 # The attachment ID used in the full path of a file
30 30 def attachment_path_id
31   - ((respond_to?(:parent_id) && parent_id) || id).to_i
  31 + (is_thumbnail? && respond_to?(:parent_id)) ? parent_id : id
32 32 end
33 33  
34 34 # overrwrite this to do your own app-specific partitioning.
... ... @@ -94,4 +94,4 @@ module Technoweenie # :nodoc:
94 94 end
95 95 end
96 96 end
97   -end
98 97 \ No newline at end of file
  98 +end
... ...