Commit 825c88ca01d079a8f08773c2f8a6d53246d7e410

Authored by Aurélio A. Heckert
1 parent 36553bc6

Create FilePresenter infra

The idea is never to give a Upload File to a view. That must
always work with a File Presenter providing overwrite methods
and specific views/partials.

ActionItem2373
app/controllers/application_controller.rb
1 1 class ApplicationController < ActionController::Base
2 2  
  3 + # Preload FilePresenters to allow `FilePresenter.for()` to work
  4 + FilePresenter::Generic
  5 + FilePresenter::Image
  6 +
3 7 before_filter :setup_multitenancy
4 8 before_filter :detect_stuff_by_domain
5 9 before_filter :init_noosfero_plugins
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -55,7 +55,9 @@ class ContentViewerController &lt; ApplicationController
55 55 # At this point the page will be showed
56 56 @page.hit
57 57  
58   - unless @page.mime_type == 'text/html' || (@page.image? && params[:view])
  58 + @page = FilePresenter.for @page
  59 +
  60 + unless @page.mime_type == 'text/html' || params[:view]
59 61 headers['Content-Type'] = @page.mime_type
60 62 data = @page.data
61 63  
... ...
app/helpers/folder_helper.rb
... ... @@ -21,6 +21,7 @@ module FolderHelper
21 21 end
22 22  
23 23 def display_article_in_listing(article, recursive = false, level = 0)
  24 + article = FilePresenter.for article
24 25 article_link = if article.image?
25 26 link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true))
26 27 else
... ... @@ -40,12 +41,12 @@ module FolderHelper
40 41 end
41 42  
42 43 def icon_for_article(article)
43   - icon = article.class.icon_name(article)
  44 + icon = article.icon_name || article.class.icon_name(article)
44 45 if (icon =~ /\//)
45 46 icon
46 47 else
47   - klasses = 'icon icon-' + icon
48   - if article.kind_of?(UploadedFile)
  48 + klasses = 'icon ' + [icon].flatten.map{|name| 'icon-'+name}.join(' ')
  49 + if article.kind_of?(UploadedFile) || article.kind_of?(FilePresenter)
49 50 klasses += ' icon-upload-file'
50 51 end
51 52 klasses
... ...
app/models/article.rb
... ... @@ -133,8 +133,12 @@ class Article &lt; ActiveRecord::Base
133 133 end
134 134 end
135 135  
  136 + def css_class_list
  137 + [self.class.name.underscore.dasherize]
  138 + end
  139 +
136 140 def css_class_name
137   - self.class.name.underscore.dasherize
  141 + [css_class_list].flatten.compact.join(' ')
138 142 end
139 143  
140 144 def pending_categorizations
... ... @@ -314,7 +318,7 @@ class Article &lt; ActiveRecord::Base
314 318 end
315 319  
316 320 def view_url
317   - @view_url ||= image? ? url.merge(:view => true) : url
  321 + @view_url ||= is_a?(UploadedFile) ? url.merge(:view => true) : url
318 322 end
319 323  
320 324 def allow_children?
... ...
app/models/uploaded_file.rb
... ... @@ -61,11 +61,13 @@ class UploadedFile &lt; Article
61 61 postgresql_attachment_fu
62 62  
63 63 def self.icon_name(article = nil)
64   - if article
65   - article.image? ? article.public_filename(:icon) : (article.mime_type ? article.mime_type.gsub(/[\/+.]/, '-') : 'upload-file')
66   - else
67   - 'upload-file'
68   - end
  64 + warn = ('='*80) + "\n" +
  65 + 'The method `UploadedFile.icon_name(obj)` is deprecated. ' +
  66 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  67 + "\n" + ('='*80)
  68 + Rails.logger.warn warn if Rails.logger
  69 + puts warn
  70 + 'upload-file'
69 71 end
70 72  
71 73 def mime_type
... ... @@ -91,40 +93,26 @@ class UploadedFile &lt; Article
91 93 end
92 94  
93 95 def to_html(options = {})
  96 + warn = ('='*80) + "\n" +
  97 + 'The method `UploadedFile::to_html()` is deprecated. ' +
  98 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  99 + "\n" + ('='*80)
  100 + Rails.logger.warn warn if Rails.logger
  101 + puts warn
94 102 article = self
95 103 if image?
96 104 lambda do
97   - if article.gallery? && options[:gallery_view]
98   - images = article.parent.images
99   - current_index = images.index(article)
100   - total_of_images = images.count
101   -
102   - link_to_previous = if current_index >= 1
103   - link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'left')
104   - else
105   - content_tag('span', _('&laquo; Previous'), :class => 'left')
106   - end
107   -
108   - link_to_next = if current_index < total_of_images - 1
109   - link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'right')
110   - else
111   - content_tag('span', _('Next &raquo;'), :class => 'right')
112   - end
113   -
114   - content_tag(
115   - 'div',
116   - link_to_previous + (content_tag('span', _('image %d of %d'), :class => 'total-of-images') % [current_index + 1, total_of_images]).html_safe + link_to_next,
117   - :class => 'gallery-navigation'
118   - )
119   - end.to_s +
120   - image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') +
121   - content_tag('p', article.abstract, :class => 'uploaded-file-description')
122   -
  105 + image_tag(article.public_filename(:display),
  106 + :class => article.css_class_name,
  107 + :style => 'max-width: 100%') +
  108 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
123 109 end
124 110 else
125 111 lambda do
126   - content_tag('ul', content_tag('li', link_to(article.name, article.url, :class => article.css_class_name))) +
127   - content_tag('p', article.abstract, :class => 'uploaded-file-description')
  112 + content_tag('div',
  113 + link_to(article.name, article.url),
  114 + :class => article.css_class_name) +
  115 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
128 116 end
129 117 end
130 118 end
... ...
app/views/file_presenter/_generic.html.erb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<span class="download-link">
  2 + <span>Download</span>
  3 + <strong><%= link_to generic.filename, generic.public_filename %></strong>
  4 +</span>
  5 +
  6 +<div class="uploaded-file-description <%= 'empty' if generic.abstract.blank? %>">
  7 + <%= generic.abstract %>
  8 +</div>
  9 +
... ...
app/views/file_presenter/_image.html.erb 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +<% if image.gallery? && options[:gallery_view] %>
  2 +<%
  3 + images = image.parent.images
  4 + current_index = images.index(image.encapsulated_file)
  5 + total_of_images = images.count
  6 + link_to_previous = if current_index >= 1
  7 + link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'previous')
  8 + else
  9 + content_tag('span', _('&laquo; Previous'), :class => 'previous')
  10 + end
  11 +
  12 + link_to_next = if current_index < total_of_images - 1
  13 + link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'next')
  14 + else
  15 + content_tag('span', _('Next &raquo;'), :class => 'next')
  16 + end
  17 +%>
  18 +
  19 +<div class="gallery-navigation">
  20 + <%= link_to_previous %>
  21 + <span class="total-of-images">
  22 + <%= _('image %d of %d') % [current_index + 1, total_of_images] %>
  23 + </span>
  24 + <%= link_to_next %>
  25 +</div>
  26 +
  27 +<% end %>
  28 +
  29 +<%# image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') %>
  30 +
  31 +<img src="<%=image.public_filename(:display)%>" class="<%=image.css_class_name%>">
  32 +
  33 +<div class="uploaded-file-description <%= 'empty' if image.abstract.blank? %>">
  34 + <%= image.class %>
  35 + <%= image.abstract %>
  36 +</div>
  37 +
... ...
lib/file_presenter.rb 0 → 100644
... ... @@ -0,0 +1,90 @@
  1 +# All file presenters must extends `FilePresenter` not only to ensure the
  2 +# same interface, but also to make `FilePresenter.for(file)` to work.
  3 +class FilePresenter
  4 +
  5 + # Will return a encapsulated `UploadedFile` or the same object if no
  6 + # one accepts it. That behave allow to give any model to this class,
  7 + # like a Article and have no trouble with that.
  8 + def self.for(f)
  9 + return f if f.is_a? FilePresenter
  10 + klass = FilePresenter.subclasses.sort_by {|class_name|
  11 + class_name.constantize.accepts?(f) || 0
  12 + }.last.constantize
  13 + klass.accepts?(f) ? klass.new(f) : f
  14 + end
  15 +
  16 + def initialize(f)
  17 + @file = f
  18 + end
  19 +
  20 + # Allows to use the original `UploadedFile` reference.
  21 + def encapsulated_file
  22 + @file
  23 + end
  24 +
  25 + # This method must be overridden in subclasses.
  26 + #
  27 + # If the class accepts the file, return a number that represents the
  28 + # priority the class should be given to handle that file. Higher numbers
  29 + # mean higher priority.
  30 + #
  31 + # If the class does not accept the file, return false.
  32 + def self.accepts?(f)
  33 + nil
  34 + end
  35 +
  36 + # Define the css classes to style the page fragment with the file related
  37 + # content. If you want other classes to identify this area to your
  38 + # customized presenter, so do this:
  39 + # def css_class_list
  40 + # [super, 'myclass'].flatten
  41 + # end
  42 + def css_class_list
  43 + [ @file.css_class_list,
  44 + 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'),
  45 + 'content-type_' + self.content_type.split('/')[0],
  46 + 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-')
  47 + ].flatten
  48 + end
  49 +
  50 + # Enable file presenter to customize the css classes on view_page.rhtml
  51 + # You may not overwrite this method on your customized presenter.
  52 + def css_class_name
  53 + [css_class_list].flatten.compact.join(' ')
  54 + end
  55 +
  56 + # The generic icon class-name or the specific file path.
  57 + # You may replace this method on your custom FilePresenter.
  58 + # See the current used icons class-names in public/designs/icons/tango/style.css
  59 + def icon_name
  60 + if mime_type
  61 + [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ]
  62 + else
  63 + 'upload-file'
  64 + end
  65 + end
  66 +
  67 + # Automatic render `file_presenter/<custom>.html.erb` to display your
  68 + # custom presenter html content.
  69 + # You may not overwrite this method on your customized presenter.
  70 + # A variable with the same presenter name will be created to refer
  71 + # to the file object.
  72 + # Example:
  73 + # The `FilePresenter::Image` render `file_presenter/image.html.erb`
  74 + # inside the `file_presenter/image.html.erb` you can access the
  75 + # required `FilePresenter::Image` instance in the `image` variable.
  76 + def to_html(options = {})
  77 + file = self
  78 + lambda do
  79 + render :partial => file.class.to_s.underscore,
  80 + :locals => { :options => options },
  81 + :object => file
  82 + end
  83 + end
  84 +
  85 + # That makes the presenter to works like any other `UploadedFile` instance.
  86 + def method_missing(m, *args)
  87 + @file.send(m, *args)
  88 + end
  89 +
  90 +end
... ...
lib/file_presenter/generic.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +# Made to encapsulate any UploadedFile
  2 +class FilePresenter::Generic < FilePresenter
  3 + def initialize(f)
  4 + @file = f
  5 + end
  6 +
  7 + # if returns low priority, because it is generic.
  8 + def self.accepts?(f)
  9 + 1 if f.is_a? UploadedFile
  10 + end
  11 +end
... ...
lib/file_presenter/image.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class FilePresenter::Image < FilePresenter
  2 + def initialize(f)
  3 + @file = f
  4 + end
  5 +
  6 + def self.accepts?(f)
  7 + f.image? ? 10 : nil
  8 + end
  9 +
  10 + def icon_name
  11 + article.public_filename :icon
  12 + end
  13 +end
... ...
plugins/html5_video/lib/file_presenter/video.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class FilePresenter::Video < FilePresenter
  2 + def initialize(f)
  3 + @file = f
  4 + end
  5 +
  6 + def self.accepts?(f)
  7 + return nil if f.content_type.nil?
  8 + ( f.content_type[0..4] == 'video' ) ? 10 : nil
  9 + end
  10 +end
... ...
plugins/html5_video/lib/html5_video_plugin.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class Html5VideoPlugin < Noosfero::Plugin
  2 +
  3 + FilePresenter::Video
  4 +
  5 + def self.plugin_name
  6 + "HTML5 Video"
  7 + end
  8 +
  9 + def self.plugin_description
  10 + _("A plugin to enable the video suport, with auto conversion for the web.")
  11 + end
  12 +
  13 +end
... ...
plugins/html5_video/views/file_presenter/_video.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<video class="video-js vjs-default-skin" controls poster="video.jpg" preload="auto" data-setup="{}">
  2 + <source type="video/ogg" src="<%= video.public_filename %>">
  3 +</video>
  4 +
  5 +<div class="uploaded-file-description <%= 'empty' if video.abstract.blank? %>">
  6 + <%= video.abstract %>
  7 +</div>
  8 +
... ...
public/designs/icons/tango/style.css
... ... @@ -80,7 +80,7 @@
80 80 .icon-application-postscript { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-postscript.png) }
81 81 .icon-application-pdf { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-pdf.png) }
82 82 .icon-application-ogg { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-ogg.png) }
83   -.icon-video-mpeg { background-image: url(Tango/16x16/mimetypes/video-x-generic.png) }
  83 +.icon-video, .icon-video-mpeg { background-image: url(Tango/16x16/mimetypes/video-x-generic.png) }
84 84 .icon-application-vnd-oasis-opendocument-text { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.text.png) }
85 85 .icon-application-vnd-oasis-opendocument-spreadsheet { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.spreadsheet.png) }
86 86 .icon-application-vnd-oasis-opendocument-presentation { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.presentation.png) }
... ...
public/designs/themes/base/style.css
... ... @@ -822,12 +822,6 @@ div#notice {
822 822 margin-bottom: 0px;
823 823 }
824 824  
825   -X.sep {
826   - background: url(imgs/blog-sep.png) 50% 50% repeat-x;
827   - padding: 10px 20px;
828   - margin: 10px -19px;
829   -}
830   -
831 825 /* ==> search-results.css <== */
832 826  
833 827  
... ... @@ -1019,6 +1013,46 @@ hr.pre-posts, hr.sep-posts {
1019 1013 background: #FFF;
1020 1014 }
1021 1015  
  1016 +/************* uploaded file *****************/
  1017 +
  1018 +#article .gallery-navigation {
  1019 + padding: 10px 0;
  1020 +}
  1021 +
  1022 +#article .gallery-navigation .previous {
  1023 + margin-right: 10px;
  1024 +}
  1025 +
  1026 +#article .gallery-navigation .next {
  1027 + margin-left: 10px;
  1028 +}
  1029 +
  1030 +#article .gallery-navigation .total-of-images {
  1031 + font-weight: bold;
  1032 +}
  1033 +
  1034 +#article .uploaded-file-description {
  1035 + background: #f6f6f6;
  1036 + border-top: 1px solid #ccc;
  1037 + border-bottom: 1px solid #ccc;
  1038 + padding: 1em;
  1039 +}
  1040 +#article .uploaded-file-description.empty {
  1041 + display: none;
  1042 +}
  1043 +
  1044 +#article.file-generic .download-link {
  1045 + display: block;
  1046 + margin-bottom: 10px;
  1047 +}
  1048 +#article.file-generic .download-link span {
  1049 + font-size: 150%;
  1050 + padding-right: 5px;
  1051 +}
  1052 +#article.file-generic .download-link a {
  1053 + font-size: 180%;
  1054 + text-decoration: none;
  1055 +}
1022 1056  
1023 1057 /**************************** Comments *******************************/
1024 1058  
... ...
public/stylesheets/application.css
... ... @@ -1534,14 +1534,15 @@ a.button.disabled, input.disabled {
1534 1534 .map {
1535 1535 clear: both;
1536 1536 }
  1537 +
1537 1538 /***********************************************************
1538 1539 * style for blocks
1539 1540 ***********************************************************/
1540 1541  
1541   - #content .communities-block .profile-image {
1542   - float: none;
1543   - padding-left: 0px;
1544   - max-width: none;
  1542 +#content .communities-block .profile-image {
  1543 + float: none;
  1544 + padding-left: 0px;
  1545 + max-width: none;
1545 1546 }
1546 1547 #content .communities-block .vcard .profile_link {
1547 1548 text-align: center;
... ... @@ -3313,26 +3314,7 @@ div#article-parent {
3313 3314 .article-body-uploaded-file {
3314 3315 text-align: center;
3315 3316 }
3316   -/************* uploaded file *****************/
3317 3317  
3318   -#article .gallery-navigation {
3319   - padding: 10px 0;
3320   -}
3321   -#article .gallery-navigation .left {
3322   - margin-right: 10px;
3323   -}
3324   -#article .gallery-navigation .right {
3325   - margin-left: 10px;
3326   -}
3327   -#article .gallery-navigation .total-of-images {
3328   - font-weight: bold;
3329   -}
3330   -#article .uploaded-file-description {
3331   - background: #f6f6f6;
3332   - border-top: 1px solid #ccc;
3333   - border-bottom: 1px solid #ccc;
3334   - padding: 1em;
3335   -}
3336 3318 /* ==> public/stylesheets/controller_events.css <== */
3337 3319 #agenda {
3338 3320 position: relative;
... ...