Commit 0d7f79273bf29965639763a0987388b55a67f98a
Exists in
master
and in
29 other branches
Merge branch 'ActionItem2892' of github.com:SerproLivre/noosfero into ActionItem2892
Showing
213 changed files
with
4328 additions
and
594 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 213 files displayed.
COPYRIGHT
@@ -4,8 +4,9 @@ Copyright (c) 2007-2009, | @@ -4,8 +4,9 @@ Copyright (c) 2007-2009, | ||
4 | Cáritas Brasileira <http://www.caritasbrasileira.org/> | 4 | Cáritas Brasileira <http://www.caritasbrasileira.org/> |
5 | Copyright (c) 2007-2009, | 5 | Copyright (c) 2007-2009, |
6 | Ynternet.org Foundation <http://www.ynternet.org/> | 6 | Ynternet.org Foundation <http://www.ynternet.org/> |
7 | -Copyright (c) 2008-2009, | 7 | +Copyright (c) 2008-2013, |
8 | Colivre <http://www.colivre.coop.br/> | 8 | Colivre <http://www.colivre.coop.br/> |
9 | +Copyright (c) the Noosfero contributors. See AUTHORS | ||
9 | 10 | ||
10 | This program is free software: you can redistribute it and/or modify | 11 | This program is free software: you can redistribute it and/or modify |
11 | it under the terms of the GNU Affero General Public License as published by | 12 | it under the terms of the GNU Affero General Public License as published by |
HACKING
@@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the | @@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the | ||
52 | development mailing list so that we can include it in the official repository. | 52 | development mailing list so that we can include it in the official repository. |
53 | This way other people using the same OS will have to put less effort to develop | 53 | This way other people using the same OS will have to put less effort to develop |
54 | Noosfero. | 54 | Noosfero. |
55 | + | ||
56 | +== Submitting your changes back | ||
57 | + | ||
58 | +For now please read: | ||
59 | + | ||
60 | +- Coding conventions | ||
61 | + http://noosfero.org/Development/CodingConventions | ||
62 | +- Patch guidelines | ||
63 | + http://noosfero.org/Development/PatchGuidelines |
HACKING.rails235
@@ -1,13 +0,0 @@ | @@ -1,13 +0,0 @@ | ||
1 | -This is a draft of how to create a environment to Rails 2.3.5 to Noosfero | ||
2 | -development. | ||
3 | - | ||
4 | -Install dependencies: | ||
5 | - | ||
6 | -gem install rails -v 2.3.5 | ||
7 | -gem install i18n | ||
8 | -gem install will_paginate -v 2.3.12 | ||
9 | -gem install cucumber | ||
10 | - | ||
11 | -Creating initial environment: | ||
12 | - | ||
13 | -rake db:schema:load |
INSTALL.chat
@@ -6,7 +6,7 @@ To configure XMPP/BOSH in Noosfero you need: | @@ -6,7 +6,7 @@ To configure XMPP/BOSH in Noosfero you need: | ||
6 | * SystemTimer - http://ph7spot.com/musings/system-timer | 6 | * SystemTimer - http://ph7spot.com/musings/system-timer |
7 | * Pidgin data files - http://www.pidgin.im/ | 7 | * Pidgin data files - http://www.pidgin.im/ |
8 | 8 | ||
9 | -If you use Debian Wheezy: | 9 | +If you use Debian 6.0 (squeeze): |
10 | 10 | ||
11 | # apt-get install librestclient-ruby pidgin-data ruby1.8-dev | 11 | # apt-get install librestclient-ruby pidgin-data ruby1.8-dev |
12 | # gem install SystemTimer | 12 | # gem install SystemTimer |
README
1 | -noosfero - a web-based social platform | 1 | +Noosfero - a web-based social platform |
2 | ====================================== | 2 | ====================================== |
3 | 3 | ||
4 | -:: About the project | 4 | +http://www.noosfero.org/ |
5 | 5 | ||
6 | -Homepage: http://www.noosfero.org/ | 6 | +Documentation |
7 | +------------- | ||
7 | 8 | ||
8 | -:: Authors and copyright | 9 | +The following documentation is available: |
9 | 10 | ||
10 | -Authors: see file AUTHORS | ||
11 | -Copyright information: see file COPYRIGHT | ||
12 | -Full license text; see file COPYING | 11 | +File Purpose |
12 | +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
13 | +INSTALL install instructions | ||
14 | +INSTALL.awstats install instructions - access statistics service | ||
15 | +INSTALL.chat install instructions - chat service | ||
16 | +INSTALL.email install instructions - email service | ||
17 | +INSTALL.multitenancy install instructions - multiple sites | ||
18 | +INSTALL.varnish install instructions - varnish HTTP caching (recommended) | ||
19 | +HACKING development instruction | ||
20 | +RELEASING instructions for doing releases | ||
21 | +doc/noosfero/* user documentation (available through the app itself) | ||
22 | + | ||
23 | + | ||
24 | +Authors and copyright | ||
25 | +--------------------- | ||
26 | + | ||
27 | +Authorship and copyright information is available in the files listed below. | ||
28 | + | ||
29 | +File Purpose | ||
30 | +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
31 | +AUTHORS list of authors (updated at each release) | ||
32 | +COPYRIGHT Copyright statement for the project | ||
33 | +COPYING Full text of the project license |
app/controllers/my_profile/cms_controller.rb
@@ -267,7 +267,10 @@ class CmsController < MyProfileController | @@ -267,7 +267,10 @@ class CmsController < MyProfileController | ||
267 | @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) | 267 | @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) |
268 | @task = SuggestArticle.new(params[:task]) | 268 | @task = SuggestArticle.new(params[:task]) |
269 | if request.post? | 269 | if request.post? |
270 | - @task.target = profile | 270 | + @task.target = profile |
271 | + @task.ip_address = request.remote_ip | ||
272 | + @task.user_agent = request.user_agent | ||
273 | + @task.referrer = request.referrer | ||
271 | if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save | 274 | if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save |
272 | session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') | 275 | session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') |
273 | redirect_to @back_to | 276 | redirect_to @back_to |
app/controllers/my_profile/memberships_controller.rb
@@ -9,9 +9,10 @@ class MembershipsController < MyProfileController | @@ -9,9 +9,10 @@ class MembershipsController < MyProfileController | ||
9 | def new_community | 9 | def new_community |
10 | @community = Community.new(params[:community]) | 10 | @community = Community.new(params[:community]) |
11 | @community.environment = environment | 11 | @community.environment = environment |
12 | + @back_to = params[:back_to] || url_for(:action => 'index') | ||
12 | if request.post? && @community.valid? | 13 | if request.post? && @community.valid? |
13 | @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) | 14 | @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) |
14 | - redirect_to :action => 'index' | 15 | + redirect_to @back_to |
15 | return | 16 | return |
16 | end | 17 | end |
17 | end | 18 | end |
app/controllers/my_profile/profile_editor_controller.rb
@@ -4,7 +4,7 @@ class ProfileEditorController < MyProfileController | @@ -4,7 +4,7 @@ class ProfileEditorController < MyProfileController | ||
4 | protect 'destroy_profile', :profile, :only => [:destroy_profile] | 4 | protect 'destroy_profile', :profile, :only => [:destroy_profile] |
5 | 5 | ||
6 | def index | 6 | def index |
7 | - @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)} | 7 | + @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)} |
8 | end | 8 | end |
9 | 9 | ||
10 | helper :profile | 10 | helper :profile |
app/controllers/my_profile/spam_controller.rb
@@ -14,9 +14,15 @@ class SpamController < MyProfileController | @@ -14,9 +14,15 @@ class SpamController < MyProfileController | ||
14 | if params[:remove_comment] | 14 | if params[:remove_comment] |
15 | profile.comments_received.find(params[:remove_comment]).destroy | 15 | profile.comments_received.find(params[:remove_comment]).destroy |
16 | end | 16 | end |
17 | + if params[:remove_task] | ||
18 | + Task.to(profile).find_by_id(params[:remove_task]).destroy | ||
19 | + end | ||
17 | if params[:mark_comment_as_ham] | 20 | if params[:mark_comment_as_ham] |
18 | profile.comments_received.find(params[:mark_comment_as_ham]).ham! | 21 | profile.comments_received.find(params[:mark_comment_as_ham]).ham! |
19 | end | 22 | end |
23 | + if params[:mark_task_as_ham] && (t = Task.to(profile).find_by_id(params[:mark_task_as_ham])) | ||
24 | + t.ham! | ||
25 | + end | ||
20 | if request.xhr? | 26 | if request.xhr? |
21 | json_response(true) | 27 | json_response(true) |
22 | else | 28 | else |
@@ -28,7 +34,8 @@ class SpamController < MyProfileController | @@ -28,7 +34,8 @@ class SpamController < MyProfileController | ||
28 | return | 34 | return |
29 | end | 35 | end |
30 | 36 | ||
31 | - @spam = profile.comments_received.spam.paginate({:page => params[:page]}) | 37 | + @comment_spam = profile.comments_received.spam.paginate({:page => params[:comments_page]}) |
38 | + @task_spam = Task.to(profile).spam.paginate({:page => params[:tasks_page]}) | ||
32 | end | 39 | end |
33 | 40 | ||
34 | protected | 41 | protected |
app/controllers/my_profile/tasks_controller.rb
@@ -4,12 +4,12 @@ class TasksController < MyProfileController | @@ -4,12 +4,12 @@ class TasksController < MyProfileController | ||
4 | 4 | ||
5 | def index | 5 | def index |
6 | @filter = params[:filter_type].blank? ? nil : params[:filter_type] | 6 | @filter = params[:filter_type].blank? ? nil : params[:filter_type] |
7 | - @tasks = Task.to(profile).pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page]) | 7 | + @tasks = Task.to(profile).without_spam.pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page]) |
8 | @failed = params ? params[:failed] : {} | 8 | @failed = params ? params[:failed] : {} |
9 | end | 9 | end |
10 | 10 | ||
11 | def processed | 11 | def processed |
12 | - @tasks = Task.to(profile).closed.sort_by(&:created_at) | 12 | + @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) |
13 | end | 13 | end |
14 | 14 | ||
15 | VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] | 15 | VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] |
@@ -57,7 +57,7 @@ class TasksController < MyProfileController | @@ -57,7 +57,7 @@ class TasksController < MyProfileController | ||
57 | end | 57 | end |
58 | 58 | ||
59 | def list_requested | 59 | def list_requested |
60 | - @tasks = Task.find_all_by_requestor_id(profile.id) | 60 | + @tasks = Task.without_spam.find_all_by_requestor_id(profile.id) |
61 | end | 61 | end |
62 | 62 | ||
63 | def ticket_details | 63 | def ticket_details |
app/controllers/public/content_viewer_controller.rb
@@ -53,7 +53,9 @@ class ContentViewerController < ApplicationController | @@ -53,7 +53,9 @@ class ContentViewerController < ApplicationController | ||
53 | # At this point the page will be showed | 53 | # At this point the page will be showed |
54 | @page.hit | 54 | @page.hit |
55 | 55 | ||
56 | - unless @page.mime_type == 'text/html' || (@page.image? && params[:view]) | 56 | + @page = FilePresenter.for @page |
57 | + | ||
58 | + unless @page.mime_type == 'text/html' || params[:view] | ||
57 | headers['Content-Type'] = @page.mime_type | 59 | headers['Content-Type'] = @page.mime_type |
58 | data = @page.data | 60 | data = @page.data |
59 | 61 |
app/helpers/application_helper.rb
@@ -558,6 +558,9 @@ module ApplicationHelper | @@ -558,6 +558,9 @@ module ApplicationHelper | ||
558 | # displays a link to the profile homepage with its image (as generated by | 558 | # displays a link to the profile homepage with its image (as generated by |
559 | # #profile_image) and its name below it. | 559 | # #profile_image) and its name below it. |
560 | def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil ) | 560 | def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil ) |
561 | + if content = @plugins.dispatch_first(:profile_image_link, profile, size, tag, extra_info) | ||
562 | + return instance_eval(&content) | ||
563 | + end | ||
561 | name = profile.short_name | 564 | name = profile.short_name |
562 | if profile.person? | 565 | if profile.person? |
563 | url = url_for(profile.check_friendship_url) | 566 | url = url_for(profile.check_friendship_url) |
@@ -574,16 +577,16 @@ module ApplicationHelper | @@ -574,16 +577,16 @@ module ApplicationHelper | ||
574 | extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) | 577 | extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) |
575 | links = links_for_balloon(profile) | 578 | links = links_for_balloon(profile) |
576 | content_tag('div', content_tag(tag, | 579 | content_tag('div', content_tag(tag, |
577 | - (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") + | ||
578 | - link_to( | ||
579 | - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + | ||
580 | - content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + | ||
581 | - extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ), | ||
582 | - profile.url, | ||
583 | - :class => 'profile_link url', | ||
584 | - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, | ||
585 | - :title => profile.name ), | ||
586 | - :class => 'vcard'), :class => 'common-profile-list-block') | 580 | + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") + |
581 | + link_to( | ||
582 | + content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + | ||
583 | + content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + | ||
584 | + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ), | ||
585 | + profile.url, | ||
586 | + :class => 'profile_link url', | ||
587 | + :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, | ||
588 | + :title => profile.name ), | ||
589 | + :class => 'vcard'), :class => 'common-profile-list-block') | ||
587 | end | 590 | end |
588 | 591 | ||
589 | def gravatar_url_for(email, options = {}) | 592 | def gravatar_url_for(email, options = {}) |
@@ -1113,15 +1116,34 @@ module ApplicationHelper | @@ -1113,15 +1116,34 @@ module ApplicationHelper | ||
1113 | result | 1116 | result |
1114 | end | 1117 | end |
1115 | 1118 | ||
1116 | - def manage_enterprises | ||
1117 | - if user && !user.enterprises.empty? | ||
1118 | - enterprises_link = user.enterprises.map do |enterprise| | ||
1119 | - link_to(content_tag('strong', [_('<span>Manage</span> %s') % enterprise.short_name(25)]), @environment.top_url + "/myprofile/#{enterprise.identifier}", :class => "icon-menu-"+enterprise.class.identification.underscore, :title => [_('Manage %s') % enterprise.short_name]) | 1119 | + def manage_link(list, kind) |
1120 | + if list.present? | ||
1121 | + link_to_all = nil | ||
1122 | + if list.count > 5 | ||
1123 | + list = list.first(5) | ||
1124 | + link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => current_user.login) | ||
1125 | + end | ||
1126 | + link = list.map do |element| | ||
1127 | + link_to(content_tag('strong', [_('<span>Manage</span> %s') % element.short_name(25)]), @environment.top_url + "/myprofile/#{element.identifier}", :class => "icon-menu-"+element.class.identification.underscore, :title => [_('Manage %s') % element.short_name]) | ||
1120 | end | 1128 | end |
1121 | - render :partial => 'shared/manage_enterprises', :locals => {:enterprises_link => enterprises_link} | 1129 | + if link_to_all |
1130 | + link << link_to_all | ||
1131 | + end | ||
1132 | + render :partial => "shared/manage_link", :locals => {:link => link, :kind => kind.to_s} | ||
1122 | end | 1133 | end |
1123 | end | 1134 | end |
1124 | 1135 | ||
1136 | + def manage_enterprises | ||
1137 | + return unless user && user.environment.enabled?(:display_my_enterprises_on_user_menu) | ||
1138 | + manage_link(user.enterprises, :enterprises) | ||
1139 | + end | ||
1140 | + | ||
1141 | + def manage_communities | ||
1142 | + return unless user && user.environment.enabled?(:display_my_communities_on_user_menu) | ||
1143 | + administered_communities = user.communities.more_popular.select {|c| c.admins.include? user} | ||
1144 | + manage_link(administered_communities, :communities) | ||
1145 | + end | ||
1146 | + | ||
1125 | def usermenu_logged_in | 1147 | def usermenu_logged_in |
1126 | pending_tasks_count = '' | 1148 | pending_tasks_count = '' |
1127 | count = user ? Task.to(user).pending.count : -1 | 1149 | count = user ? Task.to(user).pending.count : -1 |
@@ -1133,6 +1155,7 @@ module ApplicationHelper | @@ -1133,6 +1155,7 @@ module ApplicationHelper | ||
1133 | render_environment_features(:usermenu) + | 1155 | render_environment_features(:usermenu) + |
1134 | link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', @environment.top_url + '/admin', :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') + | 1156 | link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', @environment.top_url + '/admin', :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') + |
1135 | manage_enterprises.to_s + | 1157 | manage_enterprises.to_s + |
1158 | + manage_communities.to_s + | ||
1136 | link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', @environment.top_url + '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) + | 1159 | link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', @environment.top_url + '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) + |
1137 | pending_tasks_count + | 1160 | pending_tasks_count + |
1138 | link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) | 1161 | link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) |
@@ -1401,7 +1424,7 @@ module ApplicationHelper | @@ -1401,7 +1424,7 @@ module ApplicationHelper | ||
1401 | end | 1424 | end |
1402 | 1425 | ||
1403 | def filter_html(html, source) | 1426 | def filter_html(html, source) |
1404 | - if @plugins | 1427 | + if @plugins && source.has_macro? |
1405 | html = convert_macro(html, source) | 1428 | html = convert_macro(html, source) |
1406 | #TODO This parse should be done through the macro infra, but since there | 1429 | #TODO This parse should be done through the macro infra, but since there |
1407 | # are old things that do not support it we are keeping this hot spot. | 1430 | # are old things that do not support it we are keeping this hot spot. |
app/helpers/blog_helper.rb
@@ -42,7 +42,7 @@ module BlogHelper | @@ -42,7 +42,7 @@ module BlogHelper | ||
42 | 42 | ||
43 | def display_post(article, format = 'full') | 43 | def display_post(article, format = 'full') |
44 | no_comments = (format == 'full') ? false : true | 44 | no_comments = (format == 'full') ? false : true |
45 | - html = send("display_#{format}_format", article).html_safe | 45 | + html = send("display_#{format}_format", FilePresenter.for(article)).html_safe |
46 | 46 | ||
47 | article_title(article, :no_comments => no_comments) + html | 47 | article_title(article, :no_comments => no_comments) + html |
48 | end | 48 | end |
app/helpers/boxes_helper.rb
@@ -100,9 +100,7 @@ module BoxesHelper | @@ -100,9 +100,7 @@ module BoxesHelper | ||
100 | options[:title] = _("This block is invisible. Your visitors will not see it.") | 100 | options[:title] = _("This block is invisible. Your visitors will not see it.") |
101 | end | 101 | end |
102 | 102 | ||
103 | - if @controller.send(:content_editor?) | ||
104 | - result = filter_html(result, block) | ||
105 | - end | 103 | + result = filter_html(result, block) |
106 | 104 | ||
107 | box_decorator.block_target(block.box, block) + | 105 | box_decorator.block_target(block.box, block) + |
108 | content_tag('div', | 106 | content_tag('div', |
app/helpers/categories_helper.rb
@@ -3,10 +3,21 @@ module CategoriesHelper | @@ -3,10 +3,21 @@ module CategoriesHelper | ||
3 | 3 | ||
4 | COLORS = [ | 4 | COLORS = [ |
5 | [ N_('Do not display at the menu'), nil ], | 5 | [ N_('Do not display at the menu'), nil ], |
6 | - [ N_('Orange'), 1 ], | ||
7 | - [ N_('Green'), 2 ], | ||
8 | - [ N_('Purple'), 3 ], | ||
9 | - [ N_('Red'), 4 ], | 6 | + [ N_('Orange'), 1], |
7 | + [ N_('Green'), 2], | ||
8 | + [ N_('Purple'), 3], | ||
9 | + [ N_('Red'), 4], | ||
10 | + [ N_('Dark Green'), 5], | ||
11 | + [ N_('Blue Oil'), 6], | ||
12 | + [ N_('Blue'), 7], | ||
13 | + [ N_('Brown'), 8], | ||
14 | + [ N_('Light Green'), 9], | ||
15 | + [ N_('Light Blue'), 10], | ||
16 | + [ N_('Dark Blue'), 11], | ||
17 | + [ N_('Blue Pool'), 12], | ||
18 | + [ N_('Beige'), 13], | ||
19 | + [ N_('Yellow'), 14], | ||
20 | + [ N_('Light Brown'), 15] | ||
10 | ] | 21 | ] |
11 | 22 | ||
12 | TYPES = [ | 23 | TYPES = [ |
app/helpers/cms_helper.rb
@@ -33,7 +33,7 @@ module CmsHelper | @@ -33,7 +33,7 @@ module CmsHelper | ||
33 | link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article) | 33 | link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article) |
34 | else | 34 | else |
35 | if article.image? | 35 | if article.image? |
36 | - image_tag(icon_for_article(article)) + link_to(article_name, article.url) | 36 | + image_tag(icon_for_article(article)) + link_to(article_name, article.url) |
37 | else | 37 | else |
38 | link_to article_name, article.url, :class => icon_for_article(article) | 38 | link_to article_name, article.url, :class => icon_for_article(article) |
39 | end | 39 | end |
app/helpers/content_viewer_helper.rb
@@ -17,7 +17,7 @@ module ContentViewerHelper | @@ -17,7 +17,7 @@ module ContentViewerHelper | ||
17 | title = article.display_title if article.kind_of?(UploadedFile) && article.image? | 17 | title = article.display_title if article.kind_of?(UploadedFile) && article.image? |
18 | title = article.title if title.blank? | 18 | title = article.title if title.blank? |
19 | title = content_tag('h1', h(title), :class => 'title') | 19 | title = content_tag('h1', h(title), :class => 'title') |
20 | - if article.belongs_to_blog? | 20 | + if article.belongs_to_blog? || article.belongs_to_forum? |
21 | unless args[:no_link] | 21 | unless args[:no_link] |
22 | title = content_tag('h1', link_to(article.name, article.url), :class => 'title') | 22 | title = content_tag('h1', link_to(article.name, article.url), :class => 'title') |
23 | end | 23 | end |
app/helpers/folder_helper.rb
@@ -21,6 +21,7 @@ module FolderHelper | @@ -21,6 +21,7 @@ module FolderHelper | ||
21 | end | 21 | end |
22 | 22 | ||
23 | def display_article_in_listing(article, recursive = false, level = 0) | 23 | def display_article_in_listing(article, recursive = false, level = 0) |
24 | + article = FilePresenter.for article | ||
24 | article_link = if article.image? | 25 | article_link = if article.image? |
25 | link_to(' ' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)) | 26 | link_to(' ' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)) |
26 | else | 27 | else |
@@ -40,12 +41,15 @@ module FolderHelper | @@ -40,12 +41,15 @@ module FolderHelper | ||
40 | end | 41 | end |
41 | 42 | ||
42 | def icon_for_article(article) | 43 | def icon_for_article(article) |
43 | - icon = article.class.icon_name(article) | 44 | + article = FilePresenter.for article |
45 | + icon = article.respond_to?(:icon_name) ? | ||
46 | + article.icon_name : | ||
47 | + article.class.icon_name(article) | ||
44 | if (icon =~ /\//) | 48 | if (icon =~ /\//) |
45 | icon | 49 | icon |
46 | else | 50 | else |
47 | - klasses = 'icon icon-' + icon | ||
48 | - if article.kind_of?(UploadedFile) | 51 | + klasses = 'icon ' + [icon].flatten.map{|name| 'icon-'+name}.join(' ') |
52 | + if article.kind_of?(UploadedFile) || article.kind_of?(FilePresenter) | ||
49 | klasses += ' icon-upload-file' | 53 | klasses += ' icon-upload-file' |
50 | end | 54 | end |
51 | klasses | 55 | klasses |
app/helpers/sweeper_helper.rb
@@ -44,4 +44,30 @@ module SweeperHelper | @@ -44,4 +44,30 @@ module SweeperHelper | ||
44 | def expire_profile_index(profile) | 44 | def expire_profile_index(profile) |
45 | expire_timeout_fragment(profile.relationships_cache_key) | 45 | expire_timeout_fragment(profile.relationships_cache_key) |
46 | end | 46 | end |
47 | + | ||
48 | + def expire_blocks_cache(context, causes) | ||
49 | + if context.kind_of?(Profile) | ||
50 | + profile = context | ||
51 | + environment = profile.environment | ||
52 | + else | ||
53 | + environment = context | ||
54 | + profile = nil | ||
55 | + end | ||
56 | + | ||
57 | + blocks_to_expire = [] | ||
58 | + if profile | ||
59 | + profile.blocks.each {|block| | ||
60 | + conditions = block.class.expire_on | ||
61 | + blocks_to_expire << block unless (conditions[:profile] & causes).empty? | ||
62 | + } | ||
63 | + end | ||
64 | + environment.blocks.each {|block| | ||
65 | + conditions = block.class.expire_on | ||
66 | + blocks_to_expire << block unless (conditions[:environment] & causes).empty? | ||
67 | + } | ||
68 | + | ||
69 | + blocks_to_expire.uniq! | ||
70 | + BlockSweeper.expire_blocks(blocks_to_expire) | ||
71 | + end | ||
72 | + | ||
47 | end | 73 | end |
app/models/article.rb
@@ -156,8 +156,12 @@ class Article < ActiveRecord::Base | @@ -156,8 +156,12 @@ class Article < ActiveRecord::Base | ||
156 | end | 156 | end |
157 | end | 157 | end |
158 | 158 | ||
159 | + def css_class_list | ||
160 | + [self.class.name.underscore.dasherize] | ||
161 | + end | ||
162 | + | ||
159 | def css_class_name | 163 | def css_class_name |
160 | - self.class.name.underscore.dasherize | 164 | + [css_class_list].flatten.compact.join(' ') |
161 | end | 165 | end |
162 | 166 | ||
163 | def pending_categorizations | 167 | def pending_categorizations |
@@ -312,6 +316,10 @@ class Article < ActiveRecord::Base | @@ -312,6 +316,10 @@ class Article < ActiveRecord::Base | ||
312 | def belongs_to_blog? | 316 | def belongs_to_blog? |
313 | self.parent and self.parent.blog? | 317 | self.parent and self.parent.blog? |
314 | end | 318 | end |
319 | + | ||
320 | + def belongs_to_forum? | ||
321 | + self.parent and self.parent.forum? | ||
322 | + end | ||
315 | 323 | ||
316 | def info_from_last_update | 324 | def info_from_last_update |
317 | last_comment = comments.last | 325 | last_comment = comments.last |
@@ -327,7 +335,7 @@ class Article < ActiveRecord::Base | @@ -327,7 +335,7 @@ class Article < ActiveRecord::Base | ||
327 | end | 335 | end |
328 | 336 | ||
329 | def view_url | 337 | def view_url |
330 | - @view_url ||= image? ? url.merge(:view => true) : url | 338 | + @view_url ||= is_a?(UploadedFile) ? url.merge(:view => true) : url |
331 | end | 339 | end |
332 | 340 | ||
333 | def comment_url_structure(comment, action = :edit) | 341 | def comment_url_structure(comment, action = :edit) |
@@ -675,6 +683,10 @@ class Article < ActiveRecord::Base | @@ -675,6 +683,10 @@ class Article < ActiveRecord::Base | ||
675 | 683 | ||
676 | delegate :region, :region_id, :environment, :environment_id, :to => :profile, :allow_nil => true | 684 | delegate :region, :region_id, :environment, :environment_id, :to => :profile, :allow_nil => true |
677 | 685 | ||
686 | + def has_macro? | ||
687 | + true | ||
688 | + end | ||
689 | + | ||
678 | private | 690 | private |
679 | 691 | ||
680 | def sanitize_tag_list | 692 | def sanitize_tag_list |
app/models/article_block.rb
@@ -12,7 +12,7 @@ class ArticleBlock < Block | @@ -12,7 +12,7 @@ class ArticleBlock < Block | ||
12 | block = self | 12 | block = self |
13 | lambda do | 13 | lambda do |
14 | block_title(block.title) + | 14 | block_title(block.title) + |
15 | - (block.article ? article_to_html(block.article, | 15 | + (block.article ? article_to_html(FilePresenter.for(block.article), |
16 | :gallery_view => false, | 16 | :gallery_view => false, |
17 | :inside_block => block, # For Blogs and folders | 17 | :inside_block => block, # For Blogs and folders |
18 | :format => block.visualization_format # For Articles and contents | 18 | :format => block.visualization_format # For Articles and contents |
@@ -23,7 +23,7 @@ class ArticleBlock < Block | @@ -23,7 +23,7 @@ class ArticleBlock < Block | ||
23 | def article_id | 23 | def article_id |
24 | self.settings[:article_id] | 24 | self.settings[:article_id] |
25 | end | 25 | end |
26 | - | 26 | + |
27 | def article_id= value | 27 | def article_id= value |
28 | self.settings[:article_id] = value.blank? ? nil : value.to_i | 28 | self.settings[:article_id] = value.blank? ? nil : value.to_i |
29 | end | 29 | end |
@@ -63,4 +63,9 @@ class ArticleBlock < Block | @@ -63,4 +63,9 @@ class ArticleBlock < Block | ||
63 | end | 63 | end |
64 | 64 | ||
65 | settings_items :visualization_format, :type => :string, :default => 'short' | 65 | settings_items :visualization_format, :type => :string, :default => 'short' |
66 | + | ||
67 | + def self.expire_on | ||
68 | + { :profile => [:article], :environment => [:article] } | ||
69 | + end | ||
70 | + | ||
66 | end | 71 | end |
app/models/block.rb
@@ -138,4 +138,19 @@ class Block < ActiveRecord::Base | @@ -138,4 +138,19 @@ class Block < ActiveRecord::Base | ||
138 | 4.hours | 138 | 4.hours |
139 | end | 139 | end |
140 | 140 | ||
141 | + def has_macro? | ||
142 | + false | ||
143 | + end | ||
144 | + | ||
145 | + # Override in your subclasses. | ||
146 | + # Define which events and context should cause the block cache to expire | ||
147 | + # Possible events are: :article, :profile, :friendship, :category | ||
148 | + # Possible contexts are: :profile, :environment | ||
149 | + def self.expire_on | ||
150 | + { | ||
151 | + :profile => [], | ||
152 | + :environment => [] | ||
153 | + } | ||
154 | + end | ||
155 | + | ||
141 | end | 156 | end |
app/models/blog_archives_block.rb
@@ -45,4 +45,7 @@ class BlogArchivesBlock < Block | @@ -45,4 +45,7 @@ class BlogArchivesBlock < Block | ||
45 | content_tag('div', link_to(_('Subscribe RSS Feed'), owner_blog.feed.url), :class => 'subscribe-feed') | 45 | content_tag('div', link_to(_('Subscribe RSS Feed'), owner_blog.feed.url), :class => 'subscribe-feed') |
46 | end | 46 | end |
47 | 47 | ||
48 | + def self.expire_on | ||
49 | + { :profile => [:article], :environment => [:article] } | ||
50 | + end | ||
48 | end | 51 | end |
app/models/categories_block.rb
app/models/category.rb
@@ -12,7 +12,7 @@ class Category < ActiveRecord::Base | @@ -12,7 +12,7 @@ class Category < ActiveRecord::Base | ||
12 | validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n | 12 | validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n |
13 | belongs_to :environment | 13 | belongs_to :environment |
14 | 14 | ||
15 | - validates_inclusion_of :display_color, :in => [ 1, 2, 3, 4, nil ] | 15 | + validates_inclusion_of :display_color, :in => 1..15, :allow_nil => true |
16 | validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.').fix_i18n | 16 | validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.').fix_i18n |
17 | 17 | ||
18 | # Finds all top level categories for a given environment. | 18 | # Finds all top level categories for a given environment. |
app/models/comment.rb
@@ -16,9 +16,7 @@ class Comment < ActiveRecord::Base | @@ -16,9 +16,7 @@ class Comment < ActiveRecord::Base | ||
16 | has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy | 16 | has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy |
17 | belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' | 17 | belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' |
18 | 18 | ||
19 | - named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false] | ||
20 | named_scope :without_reply, :conditions => ['reply_of_id IS NULL'] | 19 | named_scope :without_reply, :conditions => ['reply_of_id IS NULL'] |
21 | - named_scope :spam, :conditions => ['spam = ?', true] | ||
22 | 20 | ||
23 | # unauthenticated authors: | 21 | # unauthenticated authors: |
24 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) | 22 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) |
@@ -108,6 +106,17 @@ class Comment < ActiveRecord::Base | @@ -108,6 +106,17 @@ class Comment < ActiveRecord::Base | ||
108 | 106 | ||
109 | include Noosfero::Plugin::HotSpot | 107 | include Noosfero::Plugin::HotSpot |
110 | 108 | ||
109 | + include Spammable | ||
110 | + | ||
111 | + def after_spam! | ||
112 | + SpammerLogger.log(ip_address, self) | ||
113 | + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam)) | ||
114 | + end | ||
115 | + | ||
116 | + def after_ham! | ||
117 | + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham)) | ||
118 | + end | ||
119 | + | ||
111 | def verify_and_notify | 120 | def verify_and_notify |
112 | check_for_spam | 121 | check_for_spam |
113 | unless spam? | 122 | unless spam? |
@@ -115,10 +124,6 @@ class Comment < ActiveRecord::Base | @@ -115,10 +124,6 @@ class Comment < ActiveRecord::Base | ||
115 | end | 124 | end |
116 | end | 125 | end |
117 | 126 | ||
118 | - def check_for_spam | ||
119 | - plugins.dispatch(:check_comment_for_spam, self) | ||
120 | - end | ||
121 | - | ||
122 | def notify_by_mail | 127 | def notify_by_mail |
123 | if source.kind_of?(Article) && article.notify_comments? | 128 | if source.kind_of?(Article) && article.notify_comments? |
124 | if !notification_emails.empty? | 129 | if !notification_emails.empty? |
@@ -205,37 +210,6 @@ class Comment < ActiveRecord::Base | @@ -205,37 +210,6 @@ class Comment < ActiveRecord::Base | ||
205 | @rejected = true | 210 | @rejected = true |
206 | end | 211 | end |
207 | 212 | ||
208 | - def spam? | ||
209 | - !spam.nil? && spam | ||
210 | - end | ||
211 | - | ||
212 | - def ham? | ||
213 | - !spam.nil? && !spam | ||
214 | - end | ||
215 | - | ||
216 | - def spam! | ||
217 | - self.spam = true | ||
218 | - self.save! | ||
219 | - SpammerLogger.log(ip_address, self) | ||
220 | - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam)) | ||
221 | - self | ||
222 | - end | ||
223 | - | ||
224 | - def ham! | ||
225 | - self.spam = false | ||
226 | - self.save! | ||
227 | - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham)) | ||
228 | - self | ||
229 | - end | ||
230 | - | ||
231 | - def marked_as_spam | ||
232 | - plugins.dispatch(:comment_marked_as_spam, self) | ||
233 | - end | ||
234 | - | ||
235 | - def marked_as_ham | ||
236 | - plugins.dispatch(:comment_marked_as_ham, self) | ||
237 | - end | ||
238 | - | ||
239 | def need_moderation? | 213 | def need_moderation? |
240 | article.moderate_comments? && (author.nil? || article.author != author) | 214 | article.moderate_comments? && (author.nil? || article.author != author) |
241 | end | 215 | end |
app/models/environment.rb
@@ -127,7 +127,9 @@ class Environment < ActiveRecord::Base | @@ -127,7 +127,9 @@ class Environment < ActiveRecord::Base | ||
127 | 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'), | 127 | 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'), |
128 | 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'), | 128 | 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'), |
129 | 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'), | 129 | 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'), |
130 | - 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login') | 130 | + 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login'), |
131 | + 'display_my_communities_on_user_menu' => _('Display on menu the list of communities the user can manage'), | ||
132 | + 'display_my_enterprises_on_user_menu' => _('Display on menu the list of enterprises the user can manage') | ||
131 | } | 133 | } |
132 | end | 134 | end |
133 | 135 |
app/models/link_list_block.rb
@@ -33,6 +33,12 @@ class LinkListBlock < Block | @@ -33,6 +33,12 @@ class LinkListBlock < Block | ||
33 | ['chat', N_('Chat')] | 33 | ['chat', N_('Chat')] |
34 | ] | 34 | ] |
35 | 35 | ||
36 | + TARGET_OPTIONS = [ | ||
37 | + [N_('Same page'), '_self'], | ||
38 | + [N_('New tab'), '_blank'], | ||
39 | + [N_('New window'), '_new'], | ||
40 | + ] | ||
41 | + | ||
36 | settings_items :links, Array, :default => [] | 42 | settings_items :links, Array, :default => [] |
37 | 43 | ||
38 | before_save do |block| | 44 | before_save do |block| |
@@ -57,7 +63,7 @@ class LinkListBlock < Block | @@ -57,7 +63,7 @@ class LinkListBlock < Block | ||
57 | def link_html(link) | 63 | def link_html(link) |
58 | klass = 'icon-' + link[:icon] if link[:icon] | 64 | klass = 'icon-' + link[:icon] if link[:icon] |
59 | sanitize_link( | 65 | sanitize_link( |
60 | - link_to(link[:name], expand_address(link[:address]), :class => klass) | 66 | + link_to(link[:name], expand_address(link[:address]), :target => link[:target], :class => klass) |
61 | ) | 67 | ) |
62 | end | 68 | end |
63 | 69 |
app/models/raw_html_block.rb
@@ -10,4 +10,7 @@ class RawHTMLBlock < Block | @@ -10,4 +10,7 @@ class RawHTMLBlock < Block | ||
10 | (title.blank? ? '' : block_title(title)).html_safe + html.to_s.html_safe | 10 | (title.blank? ? '' : block_title(title)).html_safe + html.to_s.html_safe |
11 | end | 11 | end |
12 | 12 | ||
13 | + def has_macro? | ||
14 | + true | ||
15 | + end | ||
13 | end | 16 | end |
app/models/recent_documents_block.rb
app/models/spammer_logger.rb
@@ -6,6 +6,8 @@ class SpammerLogger < Logger | @@ -6,6 +6,8 @@ class SpammerLogger < Logger | ||
6 | if object | 6 | if object |
7 | if object.kind_of?(Comment) | 7 | if object.kind_of?(Comment) |
8 | @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n" | 8 | @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n" |
9 | + elsif object.kind_of?(SuggestArticle) | ||
10 | + @logger << "[#{Time.now.strftime('%F %T %z')}] SuggestArticle-id: #{object.id} IP: #{spammer_ip}\n" | ||
9 | end | 11 | end |
10 | else | 12 | else |
11 | @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n" | 13 | @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n" |
app/models/suggest_article.rb
@@ -11,6 +11,17 @@ class SuggestArticle < Task | @@ -11,6 +11,17 @@ class SuggestArticle < Task | ||
11 | settings_items :source, :type => String | 11 | settings_items :source, :type => String |
12 | settings_items :source_name, :type => String | 12 | settings_items :source_name, :type => String |
13 | settings_items :highlighted, :type => :boolean, :default => false | 13 | settings_items :highlighted, :type => :boolean, :default => false |
14 | + settings_items :ip_address, :type => String | ||
15 | + settings_items :user_agent, :type => String | ||
16 | + settings_items :referrer, :type => String | ||
17 | + | ||
18 | + after_create :schedule_spam_checking | ||
19 | + | ||
20 | + def schedule_spam_checking | ||
21 | + self.delay.check_for_spam | ||
22 | + end | ||
23 | + | ||
24 | + include Noosfero::Plugin::HotSpot | ||
14 | 25 | ||
15 | def sender | 26 | def sender |
16 | "#{name} (#{email})" | 27 | "#{name} (#{email})" |
@@ -61,4 +72,12 @@ class SuggestArticle < Task | @@ -61,4 +72,12 @@ class SuggestArticle < Task | ||
61 | _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } | 72 | _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } |
62 | end | 73 | end |
63 | 74 | ||
75 | + def after_spam! | ||
76 | + SpammerLogger.log(ip_address, self) | ||
77 | + self.delay.marked_as_spam | ||
78 | + end | ||
79 | + | ||
80 | + def after_ham! | ||
81 | + self.delay.marked_as_ham | ||
82 | + end | ||
64 | end | 83 | end |
app/models/tags_block.rb
app/models/task.rb
@@ -235,6 +235,8 @@ class Task < ActiveRecord::Base | @@ -235,6 +235,8 @@ class Task < ActiveRecord::Base | ||
235 | end | 235 | end |
236 | end | 236 | end |
237 | 237 | ||
238 | + include Spammable | ||
239 | + | ||
238 | protected | 240 | protected |
239 | 241 | ||
240 | # This method must be overrided in subclasses, and its implementation must do | 242 | # This method must be overrided in subclasses, and its implementation must do |
@@ -275,6 +277,7 @@ class Task < ActiveRecord::Base | @@ -275,6 +277,7 @@ class Task < ActiveRecord::Base | ||
275 | named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } | 277 | named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } |
276 | named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } | 278 | named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } |
277 | 279 | ||
280 | + | ||
278 | named_scope :to, lambda { |profile| | 281 | named_scope :to, lambda { |profile| |
279 | environment_condition = nil | 282 | environment_condition = nil |
280 | if profile.person? | 283 | if profile.person? |
app/models/uploaded_file.rb
@@ -41,7 +41,25 @@ class UploadedFile < Article | @@ -41,7 +41,25 @@ class UploadedFile < Article | ||
41 | end | 41 | end |
42 | 42 | ||
43 | def self.max_size | 43 | def self.max_size |
44 | - UploadedFile.attachment_options[:max_size] | 44 | + default = 5.megabytes |
45 | + | ||
46 | + multipliers = { | ||
47 | + :KB => :kilobytes, | ||
48 | + :MB => :megabytes, | ||
49 | + :GB => :gigabytes, | ||
50 | + :TB => :terabytes, | ||
51 | + } | ||
52 | + max_upload_size = NOOSFERO_CONF['max_upload_size'] | ||
53 | + | ||
54 | + if max_upload_size =~ /^(\d+(\.\d+)?)\s*(KB|MB|GB|TB)?$/ | ||
55 | + number = $1.to_f | ||
56 | + unit = $3 || :MB | ||
57 | + multiplier = multipliers[unit.to_sym] | ||
58 | + | ||
59 | + number.send(multiplier).to_i | ||
60 | + else | ||
61 | + default | ||
62 | + end | ||
45 | end | 63 | end |
46 | 64 | ||
47 | # FIXME need to define min/max file size | 65 | # FIXME need to define min/max file size |
@@ -52,20 +70,28 @@ class UploadedFile < Article | @@ -52,20 +70,28 @@ class UploadedFile < Article | ||
52 | has_attachment :storage => :file_system, | 70 | has_attachment :storage => :file_system, |
53 | :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' }, | 71 | :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' }, |
54 | :thumbnail_class => Thumbnail, | 72 | :thumbnail_class => Thumbnail, |
55 | - :max_size => 5.megabytes # remember to update validate message below | 73 | + :max_size => self.max_size |
56 | 74 | ||
57 | - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n | 75 | + validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of %{size}").sub('%{size}', self.max_size.to_humanreadable).fix_i18n |
58 | 76 | ||
59 | delay_attachment_fu_thumbnails | 77 | delay_attachment_fu_thumbnails |
60 | 78 | ||
61 | postgresql_attachment_fu | 79 | postgresql_attachment_fu |
62 | 80 | ||
81 | + # Use this method only to get the generic icon for this kind of content. | ||
82 | + # If you want the specific icon for a file type or the iconified version | ||
83 | + # of an image, use FilePresenter.for(uploaded_file).icon_name | ||
63 | def self.icon_name(article = nil) | 84 | 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' | 85 | + unless article.nil? |
86 | + warn = ('='*80) + "\n" + | ||
87 | + 'The method `UploadedFile.icon_name(obj)` is deprecated. ' + | ||
88 | + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' + | ||
89 | + "\n" + ('='*80) | ||
90 | + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test' | ||
91 | + Rails.logger.warn warn if Rails.logger | ||
92 | + puts warn if ENV['RAILS_ENV'] == 'development' | ||
68 | end | 93 | end |
94 | + 'upload-file' | ||
69 | end | 95 | end |
70 | 96 | ||
71 | def mime_type | 97 | def mime_type |
@@ -91,40 +117,27 @@ class UploadedFile < Article | @@ -91,40 +117,27 @@ class UploadedFile < Article | ||
91 | end | 117 | end |
92 | 118 | ||
93 | def to_html(options = {}) | 119 | def to_html(options = {}) |
120 | + warn = ('='*80) + "\n" + | ||
121 | + 'The method `UploadedFile#to_html()` is deprecated. ' + | ||
122 | + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' + | ||
123 | + "\n" + ('='*80) | ||
124 | + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test' | ||
125 | + Rails.logger.warn warn if Rails.logger | ||
126 | + puts warn if ENV['RAILS_ENV'] == 'development' | ||
94 | article = self | 127 | article = self |
95 | if image? | 128 | if image? |
96 | lambda do | 129 | 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(_('« Previous'), images[current_index - 1].view_url, :class => 'left') | ||
104 | - else | ||
105 | - content_tag('span', _('« Previous'), :class => 'left') | ||
106 | - end | ||
107 | - | ||
108 | - link_to_next = if current_index < total_of_images - 1 | ||
109 | - link_to(_('Next »'), images[current_index + 1].view_url, :class => 'right') | ||
110 | - else | ||
111 | - content_tag('span', _('Next »'), :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 | - | 130 | + image_tag(article.public_filename(:display), |
131 | + :class => article.css_class_name, | ||
132 | + :style => 'max-width: 100%') + | ||
133 | + content_tag('div', article.abstract, :class => 'uploaded-file-description') | ||
123 | end | 134 | end |
124 | else | 135 | else |
125 | lambda do | 136 | 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') | 137 | + content_tag('div', |
138 | + link_to(article.name, article.url), | ||
139 | + :class => article.css_class_name) + | ||
140 | + content_tag('div', article.abstract, :class => 'uploaded-file-description') | ||
128 | end | 141 | end |
129 | end | 142 | end |
130 | end | 143 | end |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +class FilePresenter::Image < FilePresenter | ||
2 | + def self.accepts?(f) | ||
3 | + return nil unless f.respond_to? :image? | ||
4 | + f.image? ? 10 : nil | ||
5 | + end | ||
6 | + | ||
7 | + def icon_name | ||
8 | + public_filename :icon | ||
9 | + end | ||
10 | + | ||
11 | + def short_description | ||
12 | + _('Image (%s)') % content_type.split('/')[1].upcase | ||
13 | + end | ||
14 | +end |
app/sweepers/article_sweeper.rb
@@ -16,15 +16,15 @@ class ArticleSweeper < ActiveRecord::Observer | @@ -16,15 +16,15 @@ class ArticleSweeper < ActiveRecord::Observer | ||
16 | end | 16 | end |
17 | end | 17 | end |
18 | 18 | ||
19 | + | ||
19 | protected | 20 | protected |
20 | 21 | ||
21 | def expire_caches(article) | 22 | def expire_caches(article) |
23 | + expire_blocks_cache(article.profile, [:article]) | ||
24 | + | ||
22 | return if !article.environment | 25 | return if !article.environment |
26 | + | ||
23 | article.hierarchy(true).each { |a| a.touch if a != article } | 27 | article.hierarchy(true).each { |a| a.touch if a != article } |
24 | - blocks = article.profile.blocks | ||
25 | - blocks += article.profile.environment.blocks if article.profile.environment | ||
26 | - blocks = blocks.select{|b|[RecentDocumentsBlock, BlogArchivesBlock].any?{|c| b.kind_of?(c)}} | ||
27 | - BlockSweeper.expire_blocks(blocks) | ||
28 | env = article.profile.environment | 28 | env = article.profile.environment |
29 | if env && (env.portal_community == article.profile) | 29 | if env && (env.portal_community == article.profile) |
30 | article.environment.locales.keys.each do |locale| | 30 | article.environment.locales.keys.each do |locale| |
app/sweepers/category_sweeper.rb
@@ -3,7 +3,13 @@ class CategorySweeper < ActiveRecord::Observer | @@ -3,7 +3,13 @@ class CategorySweeper < ActiveRecord::Observer | ||
3 | include SweeperHelper | 3 | include SweeperHelper |
4 | 4 | ||
5 | def after_save(category) | 5 | def after_save(category) |
6 | + expire_blocks_cache(category.environment, [:category]) | ||
7 | + | ||
8 | + # Needed for environments with application layout | ||
6 | expire_fragment(category.environment.id.to_s + "_categories_menu") | 9 | expire_fragment(category.environment.id.to_s + "_categories_menu") |
7 | end | 10 | end |
8 | 11 | ||
12 | + def after_destroy(category) | ||
13 | + expire_blocks_cache(category.environment, [:category]) | ||
14 | + end | ||
9 | end | 15 | end |
app/views/box_organizer/_link_list_block.rhtml
1 | <strong><%= _('Links') %></strong> | 1 | <strong><%= _('Links') %></strong> |
2 | <div id='edit-link-list-block' style='width:450px'> | 2 | <div id='edit-link-list-block' style='width:450px'> |
3 | <table id='links' class='noborder'> | 3 | <table id='links' class='noborder'> |
4 | - <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr> | 4 | + <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th><th><%= _('Target') %></th></tr> |
5 | <% for link in @block.links do %> | 5 | <% for link in @block.links do %> |
6 | <tr> | 6 | <tr> |
7 | <td> | 7 | <td> |
@@ -9,6 +9,9 @@ | @@ -9,6 +9,9 @@ | ||
9 | </td> | 9 | </td> |
10 | <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td> | 10 | <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td> |
11 | <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td> | 11 | <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td> |
12 | + <td> | ||
13 | + <%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %> | ||
14 | + </td> | ||
12 | </tr> | 15 | </tr> |
13 | <% end %> | 16 | <% end %> |
14 | </table> | 17 | </table> |
@@ -18,7 +21,9 @@ | @@ -18,7 +21,9 @@ | ||
18 | page.insert_html :bottom, 'links', content_tag('tr', | 21 | page.insert_html :bottom, 'links', content_tag('tr', |
19 | content_tag('td', icon_selector('ok')) + | 22 | content_tag('td', icon_selector('ok')) + |
20 | content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) + | 23 | content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) + |
21 | - content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'cel-address')) | 24 | + content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') + |
25 | + content_tag('td', select_tag('block[links][][target]', | ||
26 | +options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target]))) | ||
22 | ) + | 27 | ) + |
23 | javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight") | 28 | javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight") |
24 | end %> | 29 | end %> |
app/views/cms/view.rhtml
@@ -40,13 +40,16 @@ | @@ -40,13 +40,16 @@ | ||
40 | </tr> | 40 | </tr> |
41 | <% end %> | 41 | <% end %> |
42 | 42 | ||
43 | - <% @articles.each do |article| %> | 43 | + <% @articles.each do |article| article = FilePresenter.for article %> |
44 | <tr title="<%= article.title%>" > | 44 | <tr title="<%= article.title%>" > |
45 | - <td> | 45 | + <td class="article-name"> |
46 | <%= link_to_article(article) %> | 46 | <%= link_to_article(article) %> |
47 | </td> | 47 | </td> |
48 | - <td> | ||
49 | - <%= article.class.short_description %> | 48 | + <% short_description = article.respond_to?(:short_description) ? |
49 | + article.short_description : | ||
50 | + article.class.short_description %> | ||
51 | + <td class="article-mime" title=<%= short_description.to_json %>> | ||
52 | + <%= short_description %> | ||
50 | </td> | 53 | </td> |
51 | <td class="article-controls"> | 54 | <td class="article-controls"> |
52 | <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %> | 55 | <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %> |
@@ -0,0 +1,9 @@ | @@ -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 | + |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
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(_('« Previous'), images[current_index - 1].view_url, :class => 'previous') | ||
8 | + else | ||
9 | + content_tag('span', _('« Previous'), :class => 'previous') | ||
10 | + end | ||
11 | + | ||
12 | + link_to_next = if current_index < total_of_images - 1 | ||
13 | + link_to(_('Next »'), images[current_index + 1].view_url, :class => 'next') | ||
14 | + else | ||
15 | + content_tag('span', _('Next »'), :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.abstract %> | ||
35 | +</div> | ||
36 | + |
app/views/memberships/new_community.rhtml
@@ -46,9 +46,11 @@ | @@ -46,9 +46,11 @@ | ||
46 | 46 | ||
47 | <%= template_options(Community, 'community')%> | 47 | <%= template_options(Community, 'community')%> |
48 | 48 | ||
49 | + <%= hidden_field_tag('back_to', @back_to) %> | ||
50 | + | ||
49 | <% button_bar do %> | 51 | <% button_bar do %> |
50 | <%= submit_button(:save, _('Create')) %> | 52 | <%= submit_button(:save, _('Create')) %> |
51 | - <%= button(:cancel, _('Cancel'), :action => 'index') %> | 53 | + <%= button(:cancel, _('Cancel'), @back_to ) %> |
52 | <% end %> | 54 | <% end %> |
53 | 55 | ||
54 | <% end %> | 56 | <% end %> |
app/views/shared/_manage_enterprises.rhtml
@@ -1,8 +0,0 @@ | @@ -1,8 +0,0 @@ | ||
1 | -<div id='manage-enterprises'> | ||
2 | - <a href="#" id='manage-enterprises-link' class='simplemenu-trigger' title='<%= _('Manage enterprises') %>'><i class="icon-menu-enterprise"></i><strong><%= ui_icon('ui-icon-triangle-1-s') + _('My enterprises') %></strong></a> | ||
3 | - <ul class='simplemenu-submenu'> | ||
4 | - <% enterprises_link.each do |link| %> | ||
5 | - <li class='simplemenu-item'><%= link %></li> | ||
6 | - <% end %> | ||
7 | - </ul> | ||
8 | -</div> |
@@ -0,0 +1,8 @@ | @@ -0,0 +1,8 @@ | ||
1 | +<div id=<%= "manage-#{kind}" %> class="manage-groups"> | ||
2 | + <a href="#" id=<%= "manage-#{kind}-link" %> class="simplemenu-trigger" title="<%= _('Manage %s') % kind %>"><i class=<%= "icon-menu-#{kind.singularize}" %>></i><strong><%= ui_icon('ui-icon-triangle-1-s') + _('My %s') % kind %></strong></a> | ||
3 | + <ul class="simplemenu-submenu"> | ||
4 | + <% link.each do |link| %> | ||
5 | + <li class="simplemenu-item"><%= link %></li> | ||
6 | + <% end %> | ||
7 | + </ul> | ||
8 | +</div> |
@@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
1 | +<%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %> | ||
2 | +<div id='article'> | ||
3 | + <div class="comments" id="comments_list"> | ||
4 | + <ul class="article-comments-list"> | ||
5 | + <%= render :partial => 'comment/comment', :collection => @comment_spam %> | ||
6 | + </ul> | ||
7 | + </div> | ||
8 | +</div> | ||
9 | + | ||
10 | +<%= pagination_links @comment_spam, :param_name => :comments_page %> | ||
11 | + |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +<% render :layout => 'task', :locals => { :task => task } do %> | ||
2 | + <% content_for :extra_buttons do %> | ||
3 | + <%= button_to_function('down', _('Show details'), "toggleDetails(this, '#{_('Hide details')}', '#{_('Show details')}')" ) %> | ||
4 | + <% end %> | ||
5 | + | ||
6 | + <% content_for :extra_content do %> | ||
7 | + <ul class="suggest-article-details" style="display: none"> | ||
8 | + <li><strong><%=_('Sent by')%></strong>: <%=task.name%> </li> | ||
9 | + <li><strong><%=_('Email')%></strong>: <%=task.email%> </li> | ||
10 | + <li><strong><%=_('Source')%></strong>: <%=task.source_name%> </li> | ||
11 | + <li><strong><%=_('Source URL')%></strong>: <%=task.source%> </li> | ||
12 | + <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li> | ||
13 | + <li><strong><%=_('Lead')%></strong>: <%=task.article_abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_abstract%> </li> | ||
14 | + <li><strong><%=_('Body')%></strong>: | ||
15 | + <div class='suggest-article-body'> | ||
16 | + <%= task.article_body %> | ||
17 | + </div> | ||
18 | + </li> | ||
19 | + </ul> | ||
20 | + <% end %> | ||
21 | +<% end %> |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +<div class="task_box" id="task-<%= task.id %>"> | ||
2 | + <%= render :partial => 'tasks/task_icon', :locals => {:task => task} %> | ||
3 | + <%= render :partial => 'tasks/task_title', :locals => {:task => task} %> | ||
4 | + <div class="task-information"> | ||
5 | + <%= task_information(task) %> | ||
6 | + </div> | ||
7 | + | ||
8 | + <%= yield %> <% # ??? %> | ||
9 | + | ||
10 | + <% button_bar do %> | ||
11 | + <%= button_to_function('new', _('Mark as NOT SPAM'), 'removeTaskBox(this, %s, "%s", "")' % [url_for(:mark_task_as_ham => task.id).to_json, "task-#{task.id}"]) %> | ||
12 | + <%= yield :extra_buttons %> | ||
13 | + <%= button_to_function('delete', _('Remove'), 'removeTaskBox(this, %s, "%s", %s)' % [url_for(:profile => params[:profile], :remove_task => task.id).to_json, "task-#{task.id}", _('Are you sure you want to remove this article suggestion?').to_json]) %> | ||
14 | + | ||
15 | + <% end %> | ||
16 | + | ||
17 | + <%= yield :extra_content %> | ||
18 | +</div> |
app/views/spam/index.rhtml
1 | +<%= stylesheet('tasks') %> | ||
2 | + | ||
1 | <h1><%= _('Manage SPAM') %></h1> | 3 | <h1><%= _('Manage SPAM') %></h1> |
2 | 4 | ||
5 | +<% no_tabs = @comment_spam.blank? && @task_spam.blank? %> | ||
6 | + | ||
7 | +<%= _('There are no spams to review.') if no_tabs %> | ||
8 | + | ||
3 | <% button_bar do %> | 9 | <% button_bar do %> |
4 | <%= button :back, _('Back to control panel'), :controller => :profile_editor %> | 10 | <%= button :back, _('Back to control panel'), :controller => :profile_editor %> |
5 | <% end %> | 11 | <% end %> |
6 | 12 | ||
7 | <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %> | 13 | <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %> |
8 | -<div id='article'> | ||
9 | - <div class="comments" id="comments_list"> | ||
10 | - <ul class="article-comments-list"> | ||
11 | - <%= render :partial => 'comment/comment', :collection => @spam %> | ||
12 | - </ul> | ||
13 | - </div> | ||
14 | -</div> | ||
15 | 14 | ||
16 | -<%= pagination_links @spam %> | 15 | +<% tabs = [] %> |
16 | +<% tabs << {:title => _('Comment Spam'), :id => 'comment-spam', | ||
17 | + :content => (render :partial => 'comment_spam')} if @comment_spam.present? %> | ||
18 | +<% tabs << {:title => _('Task Spam'), :id => 'task-spam', | ||
19 | + :content => (render :partial => 'task_spam') } if @task_spam.present? %> | ||
20 | +<%= render_tabs(tabs) %> | ||
17 | 21 | ||
18 | -<% button_bar do %> | ||
19 | - <%= button :back, _('Back to control panel'), :controller => :profile_editor %> | 22 | +<% unless no_tabs %> |
23 | + <% button_bar do %> | ||
24 | + <%= button :back, _('Back to control panel'), :controller => :profile_editor %> | ||
25 | + <% end %> | ||
20 | <% end %> | 26 | <% end %> |
27 | + | ||
28 | +<%= javascript_include_tag 'spam' %> |
app/views/tasks/_task.rhtml
1 | <div class="task_box" id="task-<%= task.id %>"> | 1 | <div class="task_box" id="task-<%= task.id %>"> |
2 | 2 | ||
3 | - <div class="task_icon"> | ||
4 | - <% | ||
5 | - icon_info = task.icon | ||
6 | - if icon_info[:type] == :profile_image | ||
7 | - icon = profile_image(icon_info[:profile], :minor) | ||
8 | - elsif icon_info[:type] == :defined_image | ||
9 | - icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />" | ||
10 | - end | ||
11 | - %> | ||
12 | - <%= | ||
13 | - if icon_info[:url] | ||
14 | - link_to(icon, icon_info[:url]) | ||
15 | - else | ||
16 | - icon | ||
17 | - end | ||
18 | - %> | ||
19 | - | ||
20 | - </div> | 3 | + <%= render :partial => 'task_icon', :locals => {:task => task} %> |
21 | 4 | ||
22 | <div class="task_decisions"> | 5 | <div class="task_decisions"> |
23 | <%= | 6 | <%= |
@@ -39,9 +22,7 @@ | @@ -39,9 +22,7 @@ | ||
39 | %> | 22 | %> |
40 | </div><!-- class="task_decisions" --> | 23 | </div><!-- class="task_decisions" --> |
41 | 24 | ||
42 | - <strong class="task_title"> | ||
43 | - <%= task.title %> | ||
44 | - </strong> | 25 | + <%= render :partial => 'task_title', :locals => {:task => task} %> |
45 | 26 | ||
46 | <div class="task_information"> | 27 | <div class="task_information"> |
47 | <%= task_information(task) %> | 28 | <%= task_information(task) %> |
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +<% | ||
2 | + icon_info = task.icon | ||
3 | + if icon_info[:type] == :profile_image | ||
4 | + icon = profile_image(icon_info[:profile], :minor) | ||
5 | + elsif icon_info[:type] == :defined_image | ||
6 | + icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />" | ||
7 | + end | ||
8 | + | ||
9 | + if icon_info[:url] | ||
10 | + icon = link_to(icon, icon_info[:url]) | ||
11 | + end | ||
12 | +%> | ||
13 | + | ||
14 | +<div class="task_icon"> | ||
15 | + <%= icon %> | ||
16 | +</div> |
config/initializers/plugins.rb
@@ -4,4 +4,5 @@ require 'noosfero/plugin/manager' | @@ -4,4 +4,5 @@ require 'noosfero/plugin/manager' | ||
4 | require 'noosfero/plugin/active_record' | 4 | require 'noosfero/plugin/active_record' |
5 | require 'noosfero/plugin/mailer_base' | 5 | require 'noosfero/plugin/mailer_base' |
6 | require 'noosfero/plugin/settings' | 6 | require 'noosfero/plugin/settings' |
7 | +require 'noosfero/plugin/spammable' | ||
7 | Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS | 8 | Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS |
config/noosfero.yml.dist
@@ -8,6 +8,7 @@ development: | @@ -8,6 +8,7 @@ development: | ||
8 | gravatar: wavatar | 8 | gravatar: wavatar |
9 | googlemaps_initial_zoom: 4 | 9 | googlemaps_initial_zoom: 4 |
10 | exception_recipients: [admin@example.com] | 10 | exception_recipients: [admin@example.com] |
11 | + max_upload_size: 5MB | ||
11 | 12 | ||
12 | test: | 13 | test: |
13 | 14 |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +class AddSpamToTask < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + change_table :tasks do |t| | ||
4 | + t.boolean :spam, :default => false | ||
5 | + end | ||
6 | + Task.update_all ["spam = ?", false] | ||
7 | + add_index :tasks, [:spam] | ||
8 | + end | ||
9 | + | ||
10 | + def self.down | ||
11 | + remove_column :tasks, :spam | ||
12 | + end | ||
13 | +end |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +class AddImageToArticle < ActiveRecord::Migration | ||
2 | + | ||
3 | + def self.up | ||
4 | + add_column :articles, :image_id, :integer | ||
5 | + add_column :article_versions, :image_id, :integer | ||
6 | + end | ||
7 | + | ||
8 | + def self.down | ||
9 | + remove_column :articles, :image_id | ||
10 | + remove_column :article_versions, :image_id | ||
11 | + end | ||
12 | + | ||
13 | +end |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +class AddPositionToArticle < ActiveRecord::Migration | ||
2 | + | ||
3 | + def self.up | ||
4 | + add_column :articles, :position, :integer | ||
5 | + add_column :article_versions, :position, :integer | ||
6 | + end | ||
7 | + | ||
8 | + def self.down | ||
9 | + remove_column :articles, :position | ||
10 | + remove_column :article_versions, :position | ||
11 | + end | ||
12 | + | ||
13 | +end |
db/migrate/20131113151730_add_image_to_article.rb
@@ -1,13 +0,0 @@ | @@ -1,13 +0,0 @@ | ||
1 | -class AddImageToArticle < ActiveRecord::Migration | ||
2 | - | ||
3 | - def self.up | ||
4 | - add_column :articles, :image_id, :integer | ||
5 | - add_column :article_versions, :image_id, :integer | ||
6 | - end | ||
7 | - | ||
8 | - def self.down | ||
9 | - remove_column :articles, :image_id | ||
10 | - remove_column :article_versions, :image_id | ||
11 | - end | ||
12 | - | ||
13 | -end |
db/migrate/20131113151835_add_position_to_article.rb
@@ -1,13 +0,0 @@ | @@ -1,13 +0,0 @@ | ||
1 | -class AddPositionToArticle < ActiveRecord::Migration | ||
2 | - | ||
3 | - def self.up | ||
4 | - add_column :articles, :position, :integer | ||
5 | - add_column :article_versions, :position, :integer | ||
6 | - end | ||
7 | - | ||
8 | - def self.down | ||
9 | - remove_column :articles, :position | ||
10 | - remove_column :article_versions, :position | ||
11 | - end | ||
12 | - | ||
13 | -end |
db/migrate/20131116165327_enable_enterprises_list_on_user_menu.rb
0 → 100644
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +class EnableEnterprisesListOnUserMenu < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + # The enterprises were always listed on user menu. | ||
4 | + # As now it is configured by admin, the running environments should not need to enable it | ||
5 | + select_all("select id from environments").each do |environment| | ||
6 | + env = Environment.find(environment['id']) | ||
7 | + env.enable(:display_my_enterprises_on_user_menu) | ||
8 | + end | ||
9 | + end | ||
10 | + | ||
11 | + def self.down | ||
12 | + #nothing to be done | ||
13 | + end | ||
14 | +end |
db/schema.rb
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | # | 9 | # |
10 | # It's strongly recommended to check this file into your version control system. | 10 | # It's strongly recommended to check this file into your version control system. |
11 | 11 | ||
12 | -ActiveRecord::Schema.define(:version => 20130711213046) do | 12 | +ActiveRecord::Schema.define(:version => 20131116165327) do |
13 | 13 | ||
14 | create_table "abuse_reports", :force => true do |t| | 14 | create_table "abuse_reports", :force => true do |t| |
15 | t.integer "reporter_id" | 15 | t.integer "reporter_id" |
@@ -86,6 +86,8 @@ ActiveRecord::Schema.define(:version => 20130711213046) do | @@ -86,6 +86,8 @@ ActiveRecord::Schema.define(:version => 20130711213046) do | ||
86 | t.string "language" | 86 | t.string "language" |
87 | t.string "source_name" | 87 | t.string "source_name" |
88 | t.integer "license_id" | 88 | t.integer "license_id" |
89 | + t.integer "image_id" | ||
90 | + t.integer "position" | ||
89 | end | 91 | end |
90 | 92 | ||
91 | add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id" | 93 | add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id" |
@@ -129,6 +131,8 @@ ActiveRecord::Schema.define(:version => 20130711213046) do | @@ -129,6 +131,8 @@ ActiveRecord::Schema.define(:version => 20130711213046) do | ||
129 | t.string "language" | 131 | t.string "language" |
130 | t.string "source_name" | 132 | t.string "source_name" |
131 | t.integer "license_id" | 133 | t.integer "license_id" |
134 | + t.integer "image_id" | ||
135 | + t.integer "position" | ||
132 | end | 136 | end |
133 | 137 | ||
134 | add_index "articles", ["name"], :name => "index_articles_on_name" | 138 | add_index "articles", ["name"], :name => "index_articles_on_name" |
@@ -547,8 +551,11 @@ ActiveRecord::Schema.define(:version => 20130711213046) do | @@ -547,8 +551,11 @@ ActiveRecord::Schema.define(:version => 20130711213046) do | ||
547 | t.datetime "created_at" | 551 | t.datetime "created_at" |
548 | t.string "target_type" | 552 | t.string "target_type" |
549 | t.integer "image_id" | 553 | t.integer "image_id" |
554 | + t.boolean "spam", :default => false | ||
550 | end | 555 | end |
551 | 556 | ||
557 | + add_index "tasks", ["spam"], :name => "index_tasks_on_spam" | ||
558 | + | ||
552 | create_table "thumbnails", :force => true do |t| | 559 | create_table "thumbnails", :force => true do |t| |
553 | t.integer "size" | 560 | t.integer "size" |
554 | t.string "content_type" | 561 | t.string "content_type" |
features/browse_catalogs.feature
@@ -9,7 +9,7 @@ Feature: browse catalogs | @@ -9,7 +9,7 @@ Feature: browse catalogs | ||
9 | And the following enterprises | 9 | And the following enterprises |
10 | | identifier | owner | name | enabled | | 10 | | identifier | owner | name | enabled | |
11 | | artebonito | joaosilva | Associação de Artesanato de Bonito | true | | 11 | | artebonito | joaosilva | Associação de Artesanato de Bonito | true | |
12 | - And feature "disable_products_for_enterprises" is disabled on environment | 12 | + And feature "products_for_enterprises" is enabled on environment |
13 | And the following product_categories | 13 | And the following product_categories |
14 | | name | | 14 | | name | |
15 | | categ1 | | 15 | | categ1 | |
features/browse_enterprises.feature
@@ -6,7 +6,7 @@ Background: | @@ -6,7 +6,7 @@ Background: | ||
6 | Given the following enterprises | 6 | Given the following enterprises |
7 | | identifier | name | | 7 | | identifier | name | |
8 | | shop1 | Shoes Shop | | 8 | | shop1 | Shoes Shop | |
9 | - And feature "disable_products_for_enterprises" is disabled on environment | 9 | + And feature "products_for_enterprises" is enabled on environment |
10 | And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment | 10 | And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment |
11 | 11 | ||
12 | Scenario: show all enterprises | 12 | Scenario: show all enterprises |
features/enterprise_homepage.feature
@@ -21,6 +21,7 @@ Feature: enterprise homepage | @@ -21,6 +21,7 @@ Feature: enterprise homepage | ||
21 | And the following product | 21 | And the following product |
22 | | name | category | owner | | 22 | | name | category | owner | |
23 | | Natural Handmade | soap | mayhem | | 23 | | Natural Handmade | soap | mayhem | |
24 | + And feature "products_for_enterprises" is enabled on environment | ||
24 | 25 | ||
25 | 26 | ||
26 | Scenario: display profile info | 27 | Scenario: display profile info |
features/manage_inputs.feature
@@ -19,7 +19,7 @@ Feature: manage inputs | @@ -19,7 +19,7 @@ Feature: manage inputs | ||
19 | And the following product | 19 | And the following product |
20 | | owner | category | name | | 20 | | owner | category | name | |
21 | | redemoinho | rock | Abbey Road | | 21 | | redemoinho | rock | Abbey Road | |
22 | - And feature "disable_products_for_enterprises" is disabled on environment | 22 | + And feature "products_for_enterprises" is enabled on environment |
23 | And the following units | 23 | And the following units |
24 | | singular | plural | | 24 | | singular | plural | |
25 | | Meter | Meters | | 25 | | Meter | Meters | |
features/manage_product_price_details.feature
@@ -19,7 +19,7 @@ Feature: manage product price details | @@ -19,7 +19,7 @@ Feature: manage product price details | ||
19 | And the following product | 19 | And the following product |
20 | | owner | category | name | price | | 20 | | owner | category | name | price | |
21 | | redemoinho | rock | Abbey Road | 80.0 | | 21 | | redemoinho | rock | Abbey Road | 80.0 | |
22 | - And feature "disable_products_for_enterprises" is disabled on environment | 22 | + And feature "products_for_enterprises" is enabled on environment |
23 | And the following inputs | 23 | And the following inputs |
24 | | product | category | price_per_unit | amount_used | | 24 | | product | category | price_per_unit | amount_used | |
25 | | Abbey Road | Rock | 10.0 | 2 | | 25 | | Abbey Road | Rock | 10.0 | 2 | |
features/manage_products.feature
@@ -9,7 +9,7 @@ Feature: manage products | @@ -9,7 +9,7 @@ Feature: manage products | ||
9 | And the following enterprises | 9 | And the following enterprises |
10 | | identifier | owner | name | enabled | | 10 | | identifier | owner | name | enabled | |
11 | | redemoinho | joaosilva | Rede Moinho | true | | 11 | | redemoinho | joaosilva | Rede Moinho | true | |
12 | - And feature "disable_products_for_enterprises" is disabled on environment | 12 | + And feature "products_for_enterprises" is enabled on environment |
13 | 13 | ||
14 | Scenario: display "create new product" button | 14 | Scenario: display "create new product" button |
15 | Given I am logged in as "joaosilva" | 15 | Given I am logged in as "joaosilva" |
lib/feed_updater.rb
@@ -20,14 +20,20 @@ end | @@ -20,14 +20,20 @@ end | ||
20 | class FeedUpdater | 20 | class FeedUpdater |
21 | 21 | ||
22 | class ExceptionNotification < ActionMailer::Base | 22 | class ExceptionNotification < ActionMailer::Base |
23 | - def mail error | 23 | + def mail container, error |
24 | environment = Environment.default | 24 | environment = Environment.default |
25 | 25 | ||
26 | recipients NOOSFERO_CONF['exception_recipients'] | 26 | recipients NOOSFERO_CONF['exception_recipients'] |
27 | from environment.contact_email | 27 | from environment.contact_email |
28 | reply_to environment.contact_email | 28 | reply_to environment.contact_email |
29 | subject "[#{environment.name}] Feed-updater: #{error.message}" | 29 | subject "[#{environment.name}] Feed-updater: #{error.message}" |
30 | - body render(:text => error.backtrace.join("\n")) | 30 | + body render(:text => " |
31 | +Container: | ||
32 | +#{container.inspect} | ||
33 | + | ||
34 | +Backtrace: | ||
35 | +#{error.backtrace.join("\n")} | ||
36 | + ") | ||
31 | end | 37 | end |
32 | end | 38 | end |
33 | 39 | ||
@@ -88,11 +94,13 @@ class FeedUpdater | @@ -88,11 +94,13 @@ class FeedUpdater | ||
88 | if !running | 94 | if !running |
89 | break | 95 | break |
90 | end | 96 | end |
91 | - feed_handler.process(container) | 97 | + begin |
98 | + feed_handler.process(container) | ||
99 | + rescue Exception => e | ||
100 | + FeedUpdater::ExceptionNotification.deliver_mail container, e if NOOSFERO_CONF['exception_recipients'].present? | ||
101 | + end | ||
92 | end | 102 | end |
93 | end | 103 | end |
94 | - rescue Exception => e | ||
95 | - FeedUpdater::ExceptionNotification.deliver_mail e if NOOSFERO_CONF['exception_recipients'].present? | ||
96 | end | 104 | end |
97 | end | 105 | end |
98 | 106 |
@@ -0,0 +1,107 @@ | @@ -0,0 +1,107 @@ | ||
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 | + def id | ||
26 | + @file.id | ||
27 | + end | ||
28 | + | ||
29 | + def reload | ||
30 | + @file.reload | ||
31 | + self | ||
32 | + end | ||
33 | + | ||
34 | + # This method must be overridden in subclasses. | ||
35 | + # | ||
36 | + # If the class accepts the file, return a number that represents the | ||
37 | + # priority the class should be given to handle that file. Higher numbers | ||
38 | + # mean higher priority. | ||
39 | + # | ||
40 | + # If the class does not accept the file, return false. | ||
41 | + def self.accepts?(f) | ||
42 | + nil | ||
43 | + end | ||
44 | + | ||
45 | + def short_description | ||
46 | + _("File (%s)") % content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '') | ||
47 | + end | ||
48 | + | ||
49 | + # Define the css classes to style the page fragment with the file related | ||
50 | + # content. If you want other classes to identify this area to your | ||
51 | + # customized presenter, so do this: | ||
52 | + # def css_class_list | ||
53 | + # [super, 'myclass'].flatten | ||
54 | + # end | ||
55 | + def css_class_list | ||
56 | + [ @file.css_class_list, | ||
57 | + 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'), | ||
58 | + 'content-type_' + self.content_type.split('/')[0], | ||
59 | + 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-') | ||
60 | + ].flatten | ||
61 | + end | ||
62 | + | ||
63 | + # Enable file presenter to customize the css classes on view_page.rhtml | ||
64 | + # You may not overwrite this method on your customized presenter. | ||
65 | + def css_class_name | ||
66 | + [css_class_list].flatten.compact.join(' ') | ||
67 | + end | ||
68 | + | ||
69 | + # The generic icon class-name or the specific file path. | ||
70 | + # You may replace this method on your custom FilePresenter. | ||
71 | + # See the current used icons class-names in public/designs/icons/tango/style.css | ||
72 | + def icon_name | ||
73 | + if mime_type | ||
74 | + [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ] | ||
75 | + else | ||
76 | + 'upload-file' | ||
77 | + end | ||
78 | + end | ||
79 | + | ||
80 | + # Automatic render `file_presenter/<custom>.html.erb` to display your | ||
81 | + # custom presenter html content. | ||
82 | + # You may not overwrite this method on your customized presenter. | ||
83 | + # A variable with the same presenter name will be created to refer | ||
84 | + # to the file object. | ||
85 | + # Example: | ||
86 | + # The `FilePresenter::Image` render `file_presenter/image.html.erb` | ||
87 | + # inside the `file_presenter/image.html.erb` you can access the | ||
88 | + # required `FilePresenter::Image` instance in the `image` variable. | ||
89 | + def to_html(options = {}) | ||
90 | + file = self | ||
91 | + lambda do | ||
92 | + render :partial => file.class.to_s.underscore, | ||
93 | + :locals => { :options => options }, | ||
94 | + :object => file | ||
95 | + end | ||
96 | + end | ||
97 | + | ||
98 | + # That makes the presenter to works like any other `UploadedFile` instance. | ||
99 | + def method_missing(m, *args) | ||
100 | + @file.send(m, *args) | ||
101 | + end | ||
102 | +end | ||
103 | + | ||
104 | +# Preload FilePresenters to allow `FilePresenter.for()` to work | ||
105 | +Dir.glob(File.join('app', 'presenters', '*.rb')) do |file| | ||
106 | + load file | ||
107 | +end |
lib/noosfero/plugin.rb
@@ -155,6 +155,7 @@ class Noosfero::Plugin | @@ -155,6 +155,7 @@ class Noosfero::Plugin | ||
155 | 155 | ||
156 | # Here the developer may specify the events to which the plugins can | 156 | # Here the developer may specify the events to which the plugins can |
157 | # register and must return true or false. The default value must be false. | 157 | # register and must return true or false. The default value must be false. |
158 | + # Must also explicitly define its returning variables. | ||
158 | 159 | ||
159 | # -> If true, noosfero will include plugin_dir/public/style.css into | 160 | # -> If true, noosfero will include plugin_dir/public/style.css into |
160 | # application | 161 | # application |
@@ -162,10 +163,6 @@ class Noosfero::Plugin | @@ -162,10 +163,6 @@ class Noosfero::Plugin | ||
162 | false | 163 | false |
163 | end | 164 | end |
164 | 165 | ||
165 | - # Here the developer should specify the events to which the plugins can | ||
166 | - # register to. Must be explicitly defined its returning | ||
167 | - # variables. | ||
168 | - | ||
169 | # -> Adds buttons to the control panel | 166 | # -> Adds buttons to the control panel |
170 | # returns = { :title => title, :icon => icon, :url => url } | 167 | # returns = { :title => title, :icon => icon, :url => url } |
171 | # title = name that will be displayed. | 168 | # title = name that will be displayed. |
@@ -175,6 +172,13 @@ class Noosfero::Plugin | @@ -175,6 +172,13 @@ class Noosfero::Plugin | ||
175 | nil | 172 | nil |
176 | end | 173 | end |
177 | 174 | ||
175 | + # -> Customize profile block design and behavior | ||
176 | + # (overwrites profile_image_link function) | ||
177 | + # returns = lambda block that creates html code. | ||
178 | + def profile_image_link(profile, size, tag, extra_info) | ||
179 | + nil | ||
180 | + end | ||
181 | + | ||
178 | # -> Adds tabs to the profile | 182 | # -> Adds tabs to the profile |
179 | # returns = { :title => title, :id => id, :content => content, :start => start } | 183 | # returns = { :title => title, :id => id, :content => content, :start => start } |
180 | # title = name that will be displayed. | 184 | # title = name that will be displayed. |
@@ -304,45 +308,16 @@ class Noosfero::Plugin | @@ -304,45 +308,16 @@ class Noosfero::Plugin | ||
304 | scope | 308 | scope |
305 | end | 309 | end |
306 | 310 | ||
307 | - # This method is called by the CommentHandler background job before sending | ||
308 | - # the notification email. If the comment is marked as spam (i.e. by calling | ||
309 | - # <tt>comment.spam!</tt>), then the notification email will *not* be sent. | ||
310 | - # | ||
311 | - # example: | ||
312 | - # | ||
313 | - # def check_comment_for_spam(comment) | ||
314 | - # if anti_spam_service.is_spam?(comment) | ||
315 | - # comment.spam! | ||
316 | - # end | ||
317 | - # end | ||
318 | - # | ||
319 | - def check_comment_for_spam(comment) | 311 | + # -> Allows plugins to check weather object is a spam |
312 | + def check_for_spam(object) | ||
320 | end | 313 | end |
321 | 314 | ||
322 | - # This method is called when the user manually marks a comment as SPAM. A | ||
323 | - # plugin implementing this method should train its spam detection mechanism | ||
324 | - # by submitting this comment as a confirmed spam. | ||
325 | - # | ||
326 | - # example: | ||
327 | - # | ||
328 | - # def comment_marked_as_spam(comment) | ||
329 | - # anti_spam_service.train_with_spam(comment) | ||
330 | - # end | ||
331 | - # | ||
332 | - def comment_marked_as_spam(comment) | 315 | + # -> Allows plugins to know when an object is marked as a spam |
316 | + def marked_as_spam(object) | ||
333 | end | 317 | end |
334 | 318 | ||
335 | - # This method is called when the user manually marks a comment a NOT SPAM. A | ||
336 | - # plugin implementing this method should train its spam detection mechanism | ||
337 | - # by submitting this coimment as a confirmed ham. | ||
338 | - # | ||
339 | - # example: | ||
340 | - # | ||
341 | - # def comment_marked_as_ham(comment) | ||
342 | - # anti_spam_service.train_with_ham(comment) | ||
343 | - # end | ||
344 | - # | ||
345 | - def comment_marked_as_ham(comment) | 319 | + # -> Allows plugins to know when an object is marked as a ham |
320 | + def marked_as_ham(object) | ||
346 | end | 321 | end |
347 | 322 | ||
348 | # Adds extra actions for comments | 323 | # Adds extra actions for comments |
lib/noosfero/plugin/manager.rb
@@ -34,18 +34,20 @@ class Noosfero::Plugin::Manager | @@ -34,18 +34,20 @@ class Noosfero::Plugin::Manager | ||
34 | alias :dispatch_scopes :dispatch_without_flatten | 34 | alias :dispatch_scopes :dispatch_without_flatten |
35 | 35 | ||
36 | def dispatch_first(event, *args) | 36 | def dispatch_first(event, *args) |
37 | - result = nil | 37 | + default = Noosfero::Plugin.new.send(event, *args) |
38 | + result = default | ||
38 | each do |plugin| | 39 | each do |plugin| |
39 | result = plugin.send(event, *args) | 40 | result = plugin.send(event, *args) |
40 | - break if result.present? | 41 | + break if result != default |
41 | end | 42 | end |
42 | result | 43 | result |
43 | end | 44 | end |
44 | 45 | ||
45 | def fetch_first_plugin(event, *args) | 46 | def fetch_first_plugin(event, *args) |
47 | + default = Noosfero::Plugin.new.send(event, *args) | ||
46 | result = nil | 48 | result = nil |
47 | each do |plugin| | 49 | each do |plugin| |
48 | - if plugin.send(event, *args) | 50 | + if plugin.send(event, *args) != default |
49 | result = plugin.class | 51 | result = plugin.class |
50 | break | 52 | break |
51 | end | 53 | end |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +Spammable.module_eval do | ||
2 | + def marked_as_spam | ||
3 | + plugins.dispatch(:marked_as_spam, self) | ||
4 | + end | ||
5 | + | ||
6 | + def marked_as_ham | ||
7 | + plugins.dispatch(:marked_as_ham, self) | ||
8 | + end | ||
9 | + | ||
10 | + def check_for_spam | ||
11 | + plugins.dispatch(:check_for_spam, self) | ||
12 | + end | ||
13 | +end |
@@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
1 | +module Spammable | ||
2 | + def self.included(recipient) | ||
3 | + #TODO This line crashes the migration which includes the spam attribute to | ||
4 | + # Task... =P | ||
5 | + # No fail-safe until someone find out how to use this without crashing | ||
6 | + # the migration process | ||
7 | + #raise "This model (#{recipient.to_s}) should have a spam attribute!" if !recipient.new.respond_to?('spam=') | ||
8 | + recipient.extend(ClassMethods) | ||
9 | + end | ||
10 | + | ||
11 | + module ClassMethods | ||
12 | + def self.extended (base) | ||
13 | + if base.respond_to?(:named_scope) | ||
14 | + base.class_eval do | ||
15 | + named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false] | ||
16 | + named_scope :spam, :conditions => ['spam = ?', true] | ||
17 | + end | ||
18 | + end | ||
19 | + end | ||
20 | + end | ||
21 | + | ||
22 | + def spam? | ||
23 | + !spam.nil? && spam | ||
24 | + end | ||
25 | + | ||
26 | + def ham? | ||
27 | + !spam.nil? && !spam | ||
28 | + end | ||
29 | + | ||
30 | + def spam! | ||
31 | + before_spam! | ||
32 | + self.spam = true | ||
33 | + self.save! | ||
34 | + after_spam! | ||
35 | + self | ||
36 | + end | ||
37 | + | ||
38 | + def ham! | ||
39 | + before_ham! | ||
40 | + self.spam = false | ||
41 | + self.save! | ||
42 | + after_ham! | ||
43 | + self | ||
44 | + end | ||
45 | + | ||
46 | + def after_spam!; end | ||
47 | + def before_spam!; end | ||
48 | + | ||
49 | + def after_ham!; end | ||
50 | + def before_ham!; end | ||
51 | +end |
plugins/anti_spam/lib/anti_spam_plugin.rb
@@ -5,38 +5,37 @@ class AntiSpamPlugin < Noosfero::Plugin | @@ -5,38 +5,37 @@ class AntiSpamPlugin < Noosfero::Plugin | ||
5 | end | 5 | end |
6 | 6 | ||
7 | def self.plugin_description | 7 | def self.plugin_description |
8 | - _("Checks comments against a spam checking service compatible with the Akismet API") | 8 | + _("Tests comments and suggested articles against a spam checking service compatible with the Akismet API") |
9 | end | 9 | end |
10 | 10 | ||
11 | def self.host_default_setting | 11 | def self.host_default_setting |
12 | 'api.antispam.typepad.com' | 12 | 'api.antispam.typepad.com' |
13 | end | 13 | end |
14 | 14 | ||
15 | - def check_comment_for_spam(comment) | ||
16 | - if rakismet_call(comment, :spam?) | ||
17 | - comment.spam = true | ||
18 | - comment.save! | 15 | + def check_for_spam(object) |
16 | + if rakismet_call AntiSpamPlugin::Wrapper.wrap(object), object.environment, :spam? | ||
17 | + object.spam = true | ||
18 | + object.save! | ||
19 | end | 19 | end |
20 | end | 20 | end |
21 | 21 | ||
22 | - def comment_marked_as_spam(comment) | ||
23 | - rakismet_call(comment, :spam!) | 22 | + def marked_as_spam(object) |
23 | + rakismet_call AntiSpamPlugin::Wrapper.wrap(object), object.environment, :spam! | ||
24 | end | 24 | end |
25 | 25 | ||
26 | - def comment_marked_as_ham(comment) | ||
27 | - rakismet_call(comment, :ham!) | 26 | + def marked_as_ham(object) |
27 | + rakismet_call AntiSpamPlugin::Wrapper.wrap(object), object.environment, :ham! | ||
28 | end | 28 | end |
29 | 29 | ||
30 | protected | 30 | protected |
31 | 31 | ||
32 | - def rakismet_call(comment, op) | ||
33 | - settings = Noosfero::Plugin::Settings.new(comment.environment, self.class) | 32 | + def rakismet_call(submission, environment, op) |
33 | + settings = Noosfero::Plugin::Settings.new(environment, self.class) | ||
34 | 34 | ||
35 | Rakismet.host = settings.host | 35 | Rakismet.host = settings.host |
36 | Rakismet.key = settings.api_key | 36 | Rakismet.key = settings.api_key |
37 | - Rakismet.url = comment.environment.top_url | 37 | + Rakismet.url = environment.top_url |
38 | 38 | ||
39 | - submission = AntiSpamPlugin::CommentWrapper.new(comment) | ||
40 | submission.send(op) | 39 | submission.send(op) |
41 | end | 40 | end |
42 | 41 |
plugins/anti_spam/lib/anti_spam_plugin/comment_wrapper.rb
1 | -class AntiSpamPlugin::CommentWrapper < Struct.new(:comment) | ||
2 | - | ||
3 | - delegate :author_name, :author_email, :title, :body, :ip_address, :user_agent, :referrer, :to => :comment | ||
4 | - | ||
5 | - include Rakismet::Model | ||
6 | - | ||
7 | - alias :author :author_name | ||
8 | - alias :user_ip :ip_address | ||
9 | - alias :content :body | ||
10 | - | 1 | +class AntiSpamPlugin::CommentWrapper < AntiSpamPlugin::Wrapper |
2 | + alias_attribute :author, :author_name | ||
3 | + alias_attribute :user_ip, :ip_address | ||
4 | + alias_attribute :content, :body | ||
5 | + | ||
6 | + def self.wraps?(object) | ||
7 | + object.kind_of?(Comment) | ||
8 | + end | ||
11 | end | 9 | end |
plugins/anti_spam/lib/anti_spam_plugin/suggest_article_wrapper.rb
0 → 100644
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +class AntiSpamPlugin::SuggestArticleWrapper < AntiSpamPlugin::Wrapper | ||
2 | + alias_attribute :author, :name | ||
3 | + alias_attribute :author_email, :email | ||
4 | + alias_attribute :user_ip, :ip_address | ||
5 | + alias_attribute :content, :article_body | ||
6 | + | ||
7 | + def self.wraps?(object) | ||
8 | + object.kind_of?(SuggestArticle) | ||
9 | + end | ||
10 | +end |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +class AntiSpamPlugin::Wrapper < SimpleDelegator | ||
2 | + include Rakismet::Model | ||
3 | + | ||
4 | + @@wrappers = [] | ||
5 | + | ||
6 | + def self.wrap(object) | ||
7 | + wrapper = @@wrappers.find { |wrapper| wrapper.wraps?(object) } | ||
8 | + wrapper ? wrapper.new(object) : object | ||
9 | + end | ||
10 | + | ||
11 | + def self.wraps?(object) | ||
12 | + false | ||
13 | + end | ||
14 | + | ||
15 | + def self.inherited(child) | ||
16 | + @@wrappers << child | ||
17 | + end | ||
18 | +end |
plugins/anti_spam/test/unit/anti_spam_plugin/comment_wrapper_test.rb
1 | require 'test_helper' | 1 | require 'test_helper' |
2 | 2 | ||
3 | -class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase | 3 | +class AntiSpamPlugin::CommentWrapperTest < ActiveSupport::TestCase |
4 | 4 | ||
5 | def setup | 5 | def setup |
6 | @comment = Comment.new( | 6 | @comment = Comment.new( |
@@ -15,10 +15,6 @@ class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase | @@ -15,10 +15,6 @@ class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase | ||
15 | @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment) | 15 | @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment) |
16 | end | 16 | end |
17 | 17 | ||
18 | - should 'use Rakismet::Model' do | ||
19 | - assert_includes @wrapper.class.included_modules, Rakismet::Model | ||
20 | - end | ||
21 | - | ||
22 | should 'get contents' do | 18 | should 'get contents' do |
23 | assert_equal @comment.body, @wrapper.content | 19 | assert_equal @comment.body, @wrapper.content |
24 | end | 20 | end |
plugins/anti_spam/test/unit/anti_spam_plugin/suggest_article_wrapper_test.rb
0 → 100644
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
1 | +require 'test_helper' | ||
2 | + | ||
3 | +class AntiSpamPlugin::SuggestArticleWrapperTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + def setup | ||
6 | + @suggest_article = SuggestArticle.new( | ||
7 | + :article_body => 'comment body', | ||
8 | + :name => 'author', | ||
9 | + :email => 'foo@example.com', | ||
10 | + :ip_address => '1.2.3.4', | ||
11 | + :user_agent => 'Some Good Browser (I hope)', | ||
12 | + :referrer => 'http://noosfero.org/' | ||
13 | + ) | ||
14 | + @wrapper = AntiSpamPlugin::SuggestArticleWrapper.new(@suggest_article) | ||
15 | + end | ||
16 | + | ||
17 | + should 'get contents' do | ||
18 | + assert_equal @suggest_article.article_body, @wrapper.content | ||
19 | + end | ||
20 | + | ||
21 | + should 'get author name' do | ||
22 | + assert_equal @suggest_article.name, @wrapper.author | ||
23 | + end | ||
24 | + | ||
25 | + should 'get author email' do | ||
26 | + assert_equal @suggest_article.email, @wrapper.author_email | ||
27 | + end | ||
28 | + | ||
29 | + should 'get IP address' do | ||
30 | + assert_equal @suggest_article.ip_address, @wrapper.user_ip | ||
31 | + end | ||
32 | + | ||
33 | + should 'get User-Agent' do | ||
34 | + assert_equal @suggest_article.user_agent, @wrapper.user_agent | ||
35 | + end | ||
36 | + | ||
37 | + should 'get get Referrer' do | ||
38 | + assert_equal @suggest_article.referrer, @wrapper.referrer | ||
39 | + end | ||
40 | + | ||
41 | +end |
plugins/anti_spam/test/unit/anti_spam_plugin/wrapper_test.rb
0 → 100644
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +require 'test_helper' | ||
2 | +require 'anti_spam_plugin/wrapper' | ||
3 | + | ||
4 | +class AntiSpamPluginWrapperTest < ActiveSupport::TestCase | ||
5 | + should 'use Rakismet::Model' do | ||
6 | + wrapped = AntiSpamPlugin::Wrapper.new(mock) | ||
7 | + assert_includes wrapped.class.included_modules, Rakismet::Model | ||
8 | + end | ||
9 | + | ||
10 | + should 'wrap object according to wraps? method' do | ||
11 | + class EvenWrapper < AntiSpamPlugin::Wrapper | ||
12 | + def self.wraps?(object) | ||
13 | + object % 2 == 0 | ||
14 | + end | ||
15 | + end | ||
16 | + class OddWrapper < AntiSpamPlugin::Wrapper | ||
17 | + def self.wraps?(object) | ||
18 | + object % 2 != 0 | ||
19 | + end | ||
20 | + end | ||
21 | + | ||
22 | + assert AntiSpamPlugin::Wrapper.wrap(5).kind_of?(OddWrapper) | ||
23 | + assert AntiSpamPlugin::Wrapper.wrap(6).kind_of?(EvenWrapper) | ||
24 | + end | ||
25 | +end |
plugins/anti_spam/test/unit/anti_spam_plugin_test.rb
@@ -2,35 +2,36 @@ require 'test_helper' | @@ -2,35 +2,36 @@ require 'test_helper' | ||
2 | 2 | ||
3 | class AntiSpamPluginTest < ActiveSupport::TestCase | 3 | class AntiSpamPluginTest < ActiveSupport::TestCase |
4 | 4 | ||
5 | - def setup | ||
6 | - profile = fast_create(Profile) | ||
7 | - article = fast_create(TextileArticle, :profile_id => profile.id) | ||
8 | - @comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article') | 5 | + class SpammableContent |
6 | + attr_accessor :spam | ||
7 | + include Spammable | ||
9 | 8 | ||
10 | - @settings = Noosfero::Plugin::Settings.new(@comment.environment, AntiSpamPlugin) | ||
11 | - @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3' | ||
12 | - @settings.save! | 9 | + def save!; end |
10 | + def environment; Environment.default; end | ||
11 | + end | ||
13 | 12 | ||
13 | + def setup | ||
14 | + @spammable = SpammableContent.new | ||
14 | @plugin = AntiSpamPlugin.new | 15 | @plugin = AntiSpamPlugin.new |
15 | - @plugin.context = @comment | ||
16 | end | 16 | end |
17 | 17 | ||
18 | - should 'check for spam and mark comment as spam if server says it is spam' do | ||
19 | - AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam?).returns(true) | ||
20 | - @comment.expects(:save!) | 18 | + attr_accessor :spammable |
21 | 19 | ||
22 | - @plugin.check_comment_for_spam(@comment) | ||
23 | - assert @comment.spam | ||
24 | - end | 20 | + should 'check for spam and mark as spam if server says it is spam' do |
21 | + spammable.expects(:spam?).returns(true) | ||
22 | + spammable.expects(:save!) | ||
25 | 23 | ||
26 | - should 'report spam' do | ||
27 | - AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam!) | ||
28 | - @plugin.comment_marked_as_spam(@comment) | 24 | + @plugin.check_for_spam(spammable) |
25 | + assert spammable.spam | ||
29 | end | 26 | end |
30 | 27 | ||
31 | - should 'report ham' do | ||
32 | - AntiSpamPlugin::CommentWrapper.any_instance.expects(:ham!) | ||
33 | - @plugin.comment_marked_as_ham(@comment) | 28 | + should 'report comment spam' do |
29 | + spammable.expects(:spam!) | ||
30 | + @plugin.marked_as_spam(spammable) | ||
34 | end | 31 | end |
35 | 32 | ||
33 | + should 'report comment ham' do | ||
34 | + spammable.expects(:ham!) | ||
35 | + @plugin.marked_as_ham(spammable) | ||
36 | + end | ||
36 | end | 37 | end |
plugins/community_track/controllers/myprofile/community_track_plugin_myprofile_controller.rb
0 → 100644
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +class CommunityTrackPluginMyprofileController < MyProfileController | ||
2 | + append_view_path File.join(File.dirname(__FILE__) + '/../../views') | ||
3 | + | ||
4 | + before_filter :allow_edit_track, :only => :save_order | ||
5 | + | ||
6 | + def save_order | ||
7 | + track = profile.articles.find(params[:track]) | ||
8 | + track.reorder_steps(params[:step_ids]) | ||
9 | + redirect_to track.url | ||
10 | + end | ||
11 | + | ||
12 | + protected | ||
13 | + | ||
14 | + def allow_edit_track | ||
15 | + render_access_denied unless profile.articles.find(params[:track]).allow_edit?(user) | ||
16 | + end | ||
17 | + | ||
18 | +end |
plugins/community_track/controllers/public/community_track_plugin_public_controller.rb
0 → 100644
@@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
1 | +class CommunityTrackPluginPublicController < PublicController | ||
2 | + append_view_path File.join(File.dirname(__FILE__) + '/../../views') | ||
3 | + | ||
4 | + no_design_blocks | ||
5 | + | ||
6 | + before_filter :login_required, :only => :select_community | ||
7 | + | ||
8 | + def view_tracks | ||
9 | + block = Block.find(params[:id]) | ||
10 | + p = params[:page].to_i | ||
11 | + per_page = params[:per_page] | ||
12 | + per_page ||= block.limit | ||
13 | + per_page = per_page.to_i | ||
14 | + tracks = block.tracks(p, per_page) | ||
15 | + | ||
16 | + render :update do |page| | ||
17 | + page.insert_html :bottom, "track_list_#{block.id}", :partial => "blocks/#{block.track_partial}", :collection => tracks, :locals => {:block => block} | ||
18 | + | ||
19 | + if block.has_page?(p+1, per_page) | ||
20 | + page.replace_html "track_list_more_#{block.id}", :partial => 'blocks/track_list_more', :locals => {:block => block, :page => p+1, :force_same_page => params[:force_same_page], :per_page => per_page} | ||
21 | + else | ||
22 | + page.replace_html "track_list_more_#{block.id}", '' | ||
23 | + end | ||
24 | + end | ||
25 | + end | ||
26 | + | ||
27 | + def all_tracks | ||
28 | + @per_page = 5 #FIXME | ||
29 | + @block = Block.find(params[:id]) | ||
30 | + @tracks = @block.tracks(1, @per_page) | ||
31 | + @show_more = @block.has_page?(2, @per_page) | ||
32 | + end | ||
33 | + | ||
34 | + def select_community | ||
35 | + @communities = user.memberships.select{ |community| user.has_permission?('post_content', community) } | ||
36 | + @back_to = request.url | ||
37 | + if request.post? | ||
38 | + community_identifier = params[:community_identifier] | ||
39 | + if community_identifier.nil? | ||
40 | + @failed = [_('Select one community to proceed')] | ||
41 | + else | ||
42 | + redirect_to :controller => 'cms', :action => 'new', :type => "CommunityTrackPlugin::Track", :profile => community_identifier | ||
43 | + end | ||
44 | + end | ||
45 | + end | ||
46 | + | ||
47 | +end |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
1 | +class CommunityTrackPlugin < Noosfero::Plugin | ||
2 | + | ||
3 | + def self.plugin_name | ||
4 | + 'Community Track' | ||
5 | + end | ||
6 | + | ||
7 | + def self.plugin_description | ||
8 | + _("New kind of content for communities.") | ||
9 | + end | ||
10 | + | ||
11 | + def stylesheet? | ||
12 | + true | ||
13 | + end | ||
14 | + | ||
15 | + def content_types | ||
16 | + if context.respond_to?(:params) && context.params | ||
17 | + types = [] | ||
18 | + parent_id = context.params[:parent_id] | ||
19 | + types << CommunityTrackPlugin::Track if context.profile.community? && !parent_id | ||
20 | + parent = parent_id ? context.profile.articles.find(parent_id) : nil | ||
21 | + types << CommunityTrackPlugin::Step if parent.kind_of?(CommunityTrackPlugin::Track) | ||
22 | + types | ||
23 | + else | ||
24 | + [CommunityTrackPlugin::Track, CommunityTrackPlugin::Step] | ||
25 | + end | ||
26 | + end | ||
27 | + | ||
28 | + def self.extra_blocks | ||
29 | + { CommunityTrackPlugin::TrackListBlock => {:position => 1}, CommunityTrackPlugin::TrackCardListBlock => {} } | ||
30 | + end | ||
31 | + | ||
32 | + def content_remove_new(page) | ||
33 | + page.kind_of?(CommunityTrackPlugin::Track) | ||
34 | + end | ||
35 | + | ||
36 | +end |
plugins/community_track/lib/community_track_plugin/step.rb
0 → 100644
@@ -0,0 +1,105 @@ | @@ -0,0 +1,105 @@ | ||
1 | +class CommunityTrackPlugin::Step < Folder | ||
2 | + | ||
3 | + settings_items :hidden, :type => :boolean, :default => false | ||
4 | + | ||
5 | + alias :tools :children | ||
6 | + | ||
7 | + acts_as_list :scope => :parent | ||
8 | + | ||
9 | + def belong_to_track | ||
10 | + errors.add(:parent, "Step not allowed at this parent.") if !parent.kind_of?(CommunityTrackPlugin::Track) | ||
11 | + end | ||
12 | + | ||
13 | + validate :belong_to_track | ||
14 | + validates_presence_of :start_date, :end_date | ||
15 | + validate :end_date_equal_or_after_start_date | ||
16 | + | ||
17 | + after_save :schedule_activation | ||
18 | + | ||
19 | + before_create do |step| | ||
20 | + step.published = false | ||
21 | + true | ||
22 | + end | ||
23 | + | ||
24 | + before_create :set_hidden_position | ||
25 | + before_save :set_hidden_position | ||
26 | + | ||
27 | + def set_hidden_position | ||
28 | + if hidden | ||
29 | + decrement_positions_on_lower_items | ||
30 | + self[:position] = 0 | ||
31 | + elsif position == 0 | ||
32 | + add_to_list_bottom | ||
33 | + end | ||
34 | + end | ||
35 | + | ||
36 | + def end_date_equal_or_after_start_date | ||
37 | + if end_date && start_date | ||
38 | + errors.add(:end_date, _('must be equal or after start date.')) unless end_date >= start_date | ||
39 | + end | ||
40 | + end | ||
41 | + | ||
42 | + def self.short_description | ||
43 | + _("Step") | ||
44 | + end | ||
45 | + | ||
46 | + def self.description | ||
47 | + _('Defines a step.') | ||
48 | + end | ||
49 | + | ||
50 | + def accept_comments? | ||
51 | + false | ||
52 | + end | ||
53 | + | ||
54 | + def enabled_tools | ||
55 | + {TinyMceArticle => {:name => _('Article')}, Forum => {:name => _('Forum')}} | ||
56 | + end | ||
57 | + | ||
58 | + def to_html(options = {}) | ||
59 | + step = self | ||
60 | + lambda do | ||
61 | + render :file => 'content_viewer/step.rhtml', :locals => {:step => step} | ||
62 | + end | ||
63 | + end | ||
64 | + | ||
65 | + def active? | ||
66 | + (start_date..end_date).include?(Date.today) | ||
67 | + end | ||
68 | + | ||
69 | + def finished? | ||
70 | + Date.today > end_date | ||
71 | + end | ||
72 | + | ||
73 | + def waiting? | ||
74 | + Date.today < start_date | ||
75 | + end | ||
76 | + | ||
77 | + def schedule_activation | ||
78 | + return if !changes['start_date'] && !changes['end_date'] && !changes['published'] | ||
79 | + today = Date.today | ||
80 | + if today <= end_date || published | ||
81 | + schedule_date = !published ? start_date : end_date + 1.day | ||
82 | + CommunityTrackPlugin::ActivationJob.find(id).destroy_all | ||
83 | + Delayed::Job.enqueue(CommunityTrackPlugin::ActivationJob.new(self.id), 0, schedule_date) | ||
84 | + end | ||
85 | + end | ||
86 | + | ||
87 | + def publish | ||
88 | + self[:published] = active? && !hidden | ||
89 | + save! | ||
90 | + end | ||
91 | + | ||
92 | + class CommunityTrackPlugin::ActivationJob < Struct.new(:step_id) | ||
93 | + | ||
94 | + def self.find(step_id) | ||
95 | + Delayed::Job.where(:handler => "--- !ruby/struct:CommunityTrackPlugin::ActivationJob \nstep_id: #{step_id}\n") | ||
96 | + end | ||
97 | + | ||
98 | + def perform | ||
99 | + step = CommunityTrackPlugin::Step.find(step_id) | ||
100 | + step.publish | ||
101 | + end | ||
102 | + | ||
103 | + end | ||
104 | + | ||
105 | +end |
plugins/community_track/lib/community_track_plugin/step_helper.rb
0 → 100644
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
1 | +module CommunityTrackPlugin::StepHelper | ||
2 | + | ||
3 | + def self.status_descriptions | ||
4 | + [_('Finished'), _('In progress'), _('Waiting')] | ||
5 | + end | ||
6 | + | ||
7 | + def self.status_classes | ||
8 | + ['step_finished', 'step_active', 'step_waiting'] | ||
9 | + end | ||
10 | + | ||
11 | + def status_description(step) | ||
12 | + CommunityTrackPlugin::StepHelper.status_descriptions[status_index(step)] | ||
13 | + end | ||
14 | + | ||
15 | + def status_class(step) | ||
16 | + CommunityTrackPlugin::StepHelper.status_classes[status_index(step)] | ||
17 | + end | ||
18 | + | ||
19 | + def custom_options_for_article(article) | ||
20 | + #no options for step? | ||
21 | + nil | ||
22 | + end | ||
23 | + | ||
24 | + protected | ||
25 | + | ||
26 | + def status_index(step) | ||
27 | + [step.finished?, step.active?, step.waiting?].find_index(true) | ||
28 | + end | ||
29 | + | ||
30 | +end |
plugins/community_track/lib/community_track_plugin/track.rb
0 → 100644
@@ -0,0 +1,70 @@ | @@ -0,0 +1,70 @@ | ||
1 | +class CommunityTrackPlugin::Track < Folder | ||
2 | + | ||
3 | + settings_items :goals, :type => :string | ||
4 | + settings_items :expected_results, :type => :string | ||
5 | + | ||
6 | + def self.icon_name(article = nil) | ||
7 | + 'community-track' | ||
8 | + end | ||
9 | + | ||
10 | + def self.short_description | ||
11 | + _("Track") | ||
12 | + end | ||
13 | + | ||
14 | + def self.description | ||
15 | + _('Defines a track.') | ||
16 | + end | ||
17 | + | ||
18 | + def steps | ||
19 | + #XXX article default order is name (acts_as_filesystem) -> should use reorder (rails3) | ||
20 | + steps_unsorted.sort_by(&:position).select{|s| !s.hidden} | ||
21 | + end | ||
22 | + | ||
23 | + def hidden_steps | ||
24 | + steps_unsorted.select{|s| s.hidden} | ||
25 | + end | ||
26 | + | ||
27 | + def reorder_steps(step_ids) | ||
28 | + transaction do | ||
29 | + step_ids.each_with_index do |step_id, i| | ||
30 | + step = steps_unsorted.find(step_id) | ||
31 | + step.update_attribute(:position, step.position = i + 1) | ||
32 | + end | ||
33 | + end | ||
34 | + end | ||
35 | + | ||
36 | + def steps_unsorted | ||
37 | + children.where(:type => 'CommunityTrackPlugin::Step') | ||
38 | + end | ||
39 | + | ||
40 | + def accept_comments? | ||
41 | + false | ||
42 | + end | ||
43 | + | ||
44 | + def comments_count | ||
45 | + steps_unsorted.joins(:children).sum('childrens_articles.comments_count') | ||
46 | + end | ||
47 | + | ||
48 | + def css_class_name | ||
49 | + "community-track-plugin-track" | ||
50 | + end | ||
51 | + | ||
52 | + #FIXME make this test | ||
53 | + def first_paragraph | ||
54 | + paragraphs = Hpricot(body).search('p') | ||
55 | + paragraphs.empty? ? '' : paragraphs.first.to_html | ||
56 | + end | ||
57 | + | ||
58 | + def category_name | ||
59 | + category = categories.first | ||
60 | + category ? category.name : '' | ||
61 | + end | ||
62 | + | ||
63 | + def to_html(options = {}) | ||
64 | + track = self | ||
65 | + lambda do | ||
66 | + render :file => 'content_viewer/track.rhtml', :locals => {:track => track} | ||
67 | + end | ||
68 | + end | ||
69 | + | ||
70 | +end |
plugins/community_track/lib/community_track_plugin/track_card_list_block.rb
0 → 100644
@@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
1 | +class CommunityTrackPlugin::TrackCardListBlock < CommunityTrackPlugin::TrackListBlock | ||
2 | + | ||
3 | + def self.description | ||
4 | + _('Track Card List') | ||
5 | + end | ||
6 | + | ||
7 | + def help | ||
8 | + _('This block displays a list of most relevant tracks as cards.') | ||
9 | + end | ||
10 | + | ||
11 | + def track_partial | ||
12 | + 'track_card' | ||
13 | + end | ||
14 | + | ||
15 | +end |
plugins/community_track/lib/community_track_plugin/track_helper.rb
0 → 100644
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +module CommunityTrackPlugin::TrackHelper | ||
2 | + | ||
3 | + def category_class(track) | ||
4 | + 'category_' + (track.categories.empty? ? 'not_defined' : track.categories.first.name.to_slug) | ||
5 | + end | ||
6 | + | ||
7 | + def track_card_lead(track) | ||
8 | + lead_stripped = strip_tags(track.lead) | ||
9 | + excerpt(lead_stripped, lead_stripped.first(3), track.image ? 180 : 300) | ||
10 | + end | ||
11 | + | ||
12 | +end |
plugins/community_track/lib/community_track_plugin/track_list_block.rb
0 → 100644
@@ -0,0 +1,64 @@ | @@ -0,0 +1,64 @@ | ||
1 | +class CommunityTrackPlugin::TrackListBlock < Block | ||
2 | + | ||
3 | + include CommunityTrackPlugin::StepHelper | ||
4 | + | ||
5 | + settings_items :limit, :type => :integer, :default => 3 | ||
6 | + settings_items :more_another_page, :type => :boolean, :default => false | ||
7 | + settings_items :category_ids, :type => Array, :default => [] | ||
8 | + | ||
9 | + def self.description | ||
10 | + _('Track List') | ||
11 | + end | ||
12 | + | ||
13 | + def help | ||
14 | + _('This block displays a list of most relevant tracks.') | ||
15 | + end | ||
16 | + | ||
17 | + def track_partial | ||
18 | + 'track' | ||
19 | + end | ||
20 | + | ||
21 | + def tracks(page=1, per_page=limit) | ||
22 | + all_tracks.order('hits DESC').paginate(:per_page => per_page, :page => page) | ||
23 | + end | ||
24 | + | ||
25 | + def count_tracks | ||
26 | + all_tracks.count | ||
27 | + end | ||
28 | + | ||
29 | + def accept_category?(cat) | ||
30 | + true #accept all? | ||
31 | + end | ||
32 | + | ||
33 | + def category_ids=(ids) | ||
34 | + settings[:category_ids] = ids.uniq.map{|item| item.to_i unless item.to_i.zero?}.compact | ||
35 | + end | ||
36 | + | ||
37 | + def all_tracks | ||
38 | + tracks = owner.articles.where(:type => 'CommunityTrackPlugin::Track') | ||
39 | + if !category_ids.empty? | ||
40 | + tracks = tracks.joins(:article_categorizations).where(:articles_categories => {:category_id => category_ids}) | ||
41 | + end | ||
42 | + tracks | ||
43 | + end | ||
44 | + | ||
45 | + def content(args={}) | ||
46 | + block = self | ||
47 | + lambda do | ||
48 | + render :file => 'blocks/track_list.rhtml', :locals => {:block => block} | ||
49 | + end | ||
50 | + end | ||
51 | + | ||
52 | + def has_page?(page, per_page=limit) | ||
53 | + return (page-1) * per_page < count_tracks | ||
54 | + end | ||
55 | + | ||
56 | + def footer | ||
57 | + block = self | ||
58 | + return nil if !has_page?(2) | ||
59 | + lambda do | ||
60 | + render :partial => 'blocks/track_list_more', :locals => {:block => block, :page => 2, :per_page => block.limit} | ||
61 | + end | ||
62 | + end | ||
63 | + | ||
64 | +end |
1.49 KB
@@ -0,0 +1,195 @@ | @@ -0,0 +1,195 @@ | ||
1 | +.icon-newcommunity-track, | ||
2 | +.icon-community-track { | ||
3 | + background-image: url(/plugins/community_track/icons/community-track.png) | ||
4 | +} | ||
5 | + | ||
6 | +.step_active, #article .step_active a { | ||
7 | + background-color: #CCEBD6; | ||
8 | + color: #338533; | ||
9 | +} | ||
10 | + | ||
11 | +.step_waiting, #article .step_waiting a { | ||
12 | + background-color: #FFFFD1; | ||
13 | + color: #D17519; | ||
14 | +} | ||
15 | + | ||
16 | +.step_finished, #article .step_finished a { | ||
17 | + background-color: #D1FFFF; | ||
18 | + color: #00297A; | ||
19 | +} | ||
20 | + | ||
21 | +.step_status_description { | ||
22 | + float: right; | ||
23 | +} | ||
24 | + | ||
25 | +.step { | ||
26 | + font-weight: bold; | ||
27 | +} | ||
28 | + | ||
29 | +.track_list .item .step { | ||
30 | + padding: 8px 5px; | ||
31 | +} | ||
32 | + | ||
33 | +#article .step a { | ||
34 | + text-decoration: none; | ||
35 | +} | ||
36 | + | ||
37 | +.track_list .item .track_content .lead { | ||
38 | + float: left; | ||
39 | + width: 50%; | ||
40 | +} | ||
41 | + | ||
42 | +.track_list .item .track_content .steps { | ||
43 | + float: right; | ||
44 | + width: 50%; | ||
45 | +} | ||
46 | + | ||
47 | +.track_list .item { | ||
48 | + border-bottom: 1px solid #DDDDDD; | ||
49 | +} | ||
50 | + | ||
51 | +.track_stats, .track_content { | ||
52 | + clear: both; | ||
53 | +} | ||
54 | + | ||
55 | +.track_stats .comments { | ||
56 | + float: left; | ||
57 | +} | ||
58 | + | ||
59 | +.track_stats .hits { | ||
60 | + float: right; | ||
61 | +} | ||
62 | + | ||
63 | +.track_list .item_card { | ||
64 | + width: 155px; | ||
65 | + border: 1px solid #DDDDDD; | ||
66 | + float: left; | ||
67 | + padding: 0px 8px; | ||
68 | + margin-left: 3px; | ||
69 | + margin-right: 3px; | ||
70 | + margin-bottom: 8px; | ||
71 | +} | ||
72 | + | ||
73 | +.steps .step { | ||
74 | + margin-top: 3px; | ||
75 | + margin-bottom: 3px; | ||
76 | +} | ||
77 | + | ||
78 | +.track_list .item_card .track_stats { | ||
79 | + border-top: 1px solid #DDDDDD; | ||
80 | +} | ||
81 | + | ||
82 | +.track_list .item_card a, .track_list .item_card a:hover, .track_list .item_card a:visited { | ||
83 | + text-decoration: none; | ||
84 | + color: #444; | ||
85 | +} | ||
86 | + | ||
87 | +.track_list .item_card:hover { | ||
88 | + background: #EEE; | ||
89 | +} | ||
90 | + | ||
91 | +.track_list .title { | ||
92 | + font-size: 16px; | ||
93 | + font-weight: bold; | ||
94 | + border-bottom: 1px solid #DDDDDD; | ||
95 | + padding: 2px 0px; | ||
96 | + margin-bottom: 5px; | ||
97 | + min-height: 10px; | ||
98 | +} | ||
99 | + | ||
100 | +.track_list .image img { | ||
101 | + max-width: 100%; | ||
102 | + max-height: 100px; | ||
103 | + display: block; | ||
104 | + margin-left: auto; | ||
105 | + margin-right: auto; | ||
106 | +} | ||
107 | + | ||
108 | +.track_list .name { | ||
109 | + padding-top: 5px; | ||
110 | +} | ||
111 | + | ||
112 | +.track_list .item_card { | ||
113 | + height: 270px; | ||
114 | +} | ||
115 | + | ||
116 | +.track_list .track_content { | ||
117 | + height: 250px; | ||
118 | +} | ||
119 | + | ||
120 | +#track .step_list { | ||
121 | + list-style-type: none; | ||
122 | + margin: 0; | ||
123 | + padding: 0; | ||
124 | +} | ||
125 | + | ||
126 | +#track .position { | ||
127 | + font-size: 24px; | ||
128 | + font-weight: bold; | ||
129 | + float: left; | ||
130 | + margin: 0 10px; | ||
131 | +} | ||
132 | + | ||
133 | +#track .step .name, #track .step .name a { | ||
134 | + font-weight: bold; | ||
135 | + color: #333; | ||
136 | +} | ||
137 | + | ||
138 | +#track .step .name a:hover { | ||
139 | + color: #555; | ||
140 | +} | ||
141 | + | ||
142 | +#track .step .date { | ||
143 | + font-size: 12px; | ||
144 | + color: #AAA; | ||
145 | +} | ||
146 | + | ||
147 | +#track .step .lead { | ||
148 | + margin: 0px 10px; | ||
149 | + color: #555; | ||
150 | +} | ||
151 | + | ||
152 | +#track .content { | ||
153 | + margin: 6px 0px; | ||
154 | + border-bottom: 1px solid #DDDDDD; | ||
155 | +} | ||
156 | + | ||
157 | +#track .ui-state-default .content { | ||
158 | + border-bottom: 0px; | ||
159 | +} | ||
160 | + | ||
161 | +.track_list .item .step .position { | ||
162 | + float: left; | ||
163 | + padding-right: 5px; | ||
164 | +} | ||
165 | + | ||
166 | +#track .actions .save_button { | ||
167 | + display: none; | ||
168 | +} | ||
169 | + | ||
170 | +#track .actions, #step .actions { | ||
171 | + margin-bottom: 20px; | ||
172 | +} | ||
173 | + | ||
174 | +#edit-track-list-block .categorie_box a { | ||
175 | + float: left; | ||
176 | +} | ||
177 | + | ||
178 | +.all_tracks .more_button { | ||
179 | + text-align: center; | ||
180 | +} | ||
181 | + | ||
182 | +#step .tools .item .name a, #step .tools .item .name a:hover { | ||
183 | + border: none; | ||
184 | + background-color: transparent; | ||
185 | + color: #666; | ||
186 | + font-weight: bold; | ||
187 | +} | ||
188 | + | ||
189 | +#step .tools .item .name a:hover { | ||
190 | + color: #888; | ||
191 | +} | ||
192 | + | ||
193 | +.community-track textarea { | ||
194 | + width: 100%; | ||
195 | +} |
plugins/community_track/test/functional/community_track_plugin_cms_controller_test.rb
0 → 100644
@@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | + | ||
3 | +# Re-raise errors caught by the controller. | ||
4 | +class CmsController; def rescue_action(e) raise e end; end | ||
5 | + | ||
6 | +class CmsControllerTest < ActionController::TestCase | ||
7 | + | ||
8 | + def setup | ||
9 | + @profile = fast_create(Community) | ||
10 | + @track = CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => 'track', :profile => @profile) | ||
11 | + @step = CommunityTrackPlugin::Step.create!(:name => 'step1', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | ||
12 | + | ||
13 | + user = create_user('testinguser') | ||
14 | + @profile.add_admin(user.person) | ||
15 | + login_as(user.login) | ||
16 | + end | ||
17 | + | ||
18 | + should 'be able to edit track' do | ||
19 | + get :edit, :id => @track.id, :profile => @profile.identifier | ||
20 | + assert_tag :tag => 'input', :attributes => { :id => 'article_name' } | ||
21 | + end | ||
22 | + | ||
23 | + should 'be able to edit step' do | ||
24 | + get :edit, :id => @step.id, :profile => @profile.identifier | ||
25 | + assert_tag :tag => 'input', :attributes => { :id => 'article_name' } | ||
26 | + end | ||
27 | + | ||
28 | + should 'be able to save track' do | ||
29 | + get :edit, :id => @track.id, :profile => @profile.identifier | ||
30 | + post :edit, :id => @track.id, :profile => @profile.identifier, :article => {:name => 'changed'} | ||
31 | + @track.reload | ||
32 | + assert_equal 'changed', @track.name | ||
33 | + end | ||
34 | + | ||
35 | + should 'be able to save step' do | ||
36 | + get :edit, :id => @step.id, :profile => @profile.identifier | ||
37 | + post :edit, :id => @step.id, :profile => @profile.identifier, :article => {:name => 'changed'} | ||
38 | + @step.reload | ||
39 | + assert_equal 'changed', @step.name | ||
40 | + end | ||
41 | + | ||
42 | + should 'do not be able to edit visibility of step' do | ||
43 | + get :edit, :id => @step.id, :profile => @profile.identifier | ||
44 | + assert_no_tag :tag => 'input', :attributes => { :name => 'article[published]' } | ||
45 | + end | ||
46 | + | ||
47 | +end |
plugins/community_track/test/functional/community_track_plugin_content_viewer_controller_test.rb
0 → 100644
@@ -0,0 +1,147 @@ | @@ -0,0 +1,147 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | + | ||
3 | +class ContentViewerController | ||
4 | + append_view_path File.join(File.dirname(__FILE__) + '/../../views') | ||
5 | + def rescue_action(e) | ||
6 | + raise e | ||
7 | + end | ||
8 | +end | ||
9 | + | ||
10 | +class ContentViewerControllerTest < ActionController::TestCase | ||
11 | + | ||
12 | + def setup | ||
13 | + @profile = fast_create(Community) | ||
14 | + @track = CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => 'track', :profile => @profile) | ||
15 | + category = fast_create(Category, :name => "education") | ||
16 | + @track.add_category(category) | ||
17 | + | ||
18 | + @step = CommunityTrackPlugin::Step.create!(:name => 'step1', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | ||
19 | + | ||
20 | + user = create_user('testinguser') | ||
21 | + login_as(user.login) | ||
22 | + @profile.add_admin(user.person) | ||
23 | + end | ||
24 | + | ||
25 | + should 'show actions for tracks when user has permission for edit' do | ||
26 | + get :view_page, @track.url | ||
27 | + assert_tag :tag => 'div', :attributes => {:id => 'track' }, :descendant => { :tag => 'div', :attributes => { :class => 'actions' } } | ||
28 | + end | ||
29 | + | ||
30 | + should 'do not show actions for tracks when user has not permission for edit' do | ||
31 | + user = create_user('intruder') | ||
32 | + logout | ||
33 | + login_as(user.login) | ||
34 | + get :view_page, @track.url | ||
35 | + assert_no_tag :tag => 'div', :attributes => {:id => 'track' }, :descendant => { :tag => 'div', :attributes => { :class => 'actions' } } | ||
36 | + end | ||
37 | + | ||
38 | + should 'do not show new button at article toolbar for tracks' do | ||
39 | + user = create_user('intruder') | ||
40 | + logout | ||
41 | + login_as(user.login) | ||
42 | + get :view_page, @track.url | ||
43 | + assert_no_tag :tag => 'div', :attributes => {:id => 'article-actions'}, :descendant => { :tag => 'div', :attributes => { :id => 'icon-new' } } | ||
44 | + end | ||
45 | + | ||
46 | + should 'display steps for tracks' do | ||
47 | + get :view_page, @track.url | ||
48 | + assert_tag :tag => 'ul', :attributes => { :id => 'sortable' }, :descendant => {:tag => 'li', :attributes => { :class => 'step' } } | ||
49 | + end | ||
50 | + | ||
51 | + should 'display hidden field with step id' do | ||
52 | + get :view_page, @track.url | ||
53 | + assert_tag :tag => 'input', :attributes => { :name => 'step_ids[]' } | ||
54 | + end | ||
55 | + | ||
56 | + should 'show step' do | ||
57 | + get :view_page, @step.url | ||
58 | + assert_tag :tag => 'div', :attributes => { :id => 'step' } | ||
59 | + end | ||
60 | + | ||
61 | + should 'show tools for a step' do | ||
62 | + Article.create!(:profile => @profile, :name => 'article', :parent => @step) | ||
63 | + get :view_page, @step.url | ||
64 | + assert_tag :tag => 'div', :attributes => { :class => 'tools' }, :descendant => { :tag => 'div', :attributes => { :class => 'item' } } | ||
65 | + end | ||
66 | + | ||
67 | + should 'show actions for steps when user has permission for edit' do | ||
68 | + get :view_page, @step.url | ||
69 | + assert_tag :tag => 'div', :attributes => {:id => 'step' }, :descendant => { :tag => 'div', :attributes => { :class => 'actions' } } | ||
70 | + end | ||
71 | + | ||
72 | + should 'show actions for enabled tools in step' do | ||
73 | + get :view_page, @step.url | ||
74 | + assert_tag 'div', :attributes => {:class => 'actions' }, :descendant => { :tag => 'a', :attributes => { :class => 'button with-text icon-new icon-newforum' } } | ||
75 | + assert_tag 'div', :attributes => {:class => 'actions' }, :descendant => { :tag => 'a', :attributes => { :class => 'button with-text icon-new icon-newtext-html' } } | ||
76 | + end | ||
77 | + | ||
78 | + should 'do not show actions for steps when user has not permission for edit' do | ||
79 | + user = create_user('intruder') | ||
80 | + logout | ||
81 | + login_as(user.login) | ||
82 | + get :view_page, @step.url | ||
83 | + assert_no_tag :tag => 'div', :attributes => {:id => 'step' }, :descendant => { :tag => 'div', :attributes => { :class => 'actions' } } | ||
84 | + end | ||
85 | + | ||
86 | + should 'render a div with block id for track list block' do | ||
87 | + box = fast_create(Box, :owner_id => @profile.id, :owner_type => @profile.class.name) | ||
88 | + @block = CommunityTrackPlugin::TrackListBlock.create!(:box => box) | ||
89 | + @profile.boxes << box | ||
90 | + get :view_page, @step.url | ||
91 | + assert_tag :tag => 'div', :attributes => { :class => 'track_list', :id => "track_list_#{@block.id}" } | ||
92 | + end | ||
93 | + | ||
94 | + should 'render a div with block id for track card list block' do | ||
95 | + box = fast_create(Box, :owner_id => @profile.id, :owner_type => @profile.class.name) | ||
96 | + @block = CommunityTrackPlugin::TrackCardListBlock.create!(:box => box) | ||
97 | + @profile.boxes << box | ||
98 | + get :view_page, @step.url | ||
99 | + assert_tag :tag => 'div', :attributes => { :class => 'track_list', :id => "track_list_#{@block.id}" } | ||
100 | + end | ||
101 | + | ||
102 | + should 'render tracks in track list block' do | ||
103 | + box = fast_create(Box, :owner_id => @profile.id, :owner_type => @profile.class.name) | ||
104 | + @block = CommunityTrackPlugin::TrackListBlock.create!(:box => box) | ||
105 | + @profile.boxes << box | ||
106 | + get :view_page, @step.url | ||
107 | + assert_tag :tag => 'div', :attributes => { :class => 'item category_education' }, :descendant => { :tag => 'div', :attributes => { :class => 'steps' }, :descendant => { :tag => 'div', :attributes => { :class => "step #{@block.status_class(@step)}" } } } | ||
108 | + end | ||
109 | + | ||
110 | + should 'render tracks in track card list block' do | ||
111 | + box = fast_create(Box, :owner_id => @profile.id, :owner_type => @profile.class.name) | ||
112 | + @block = CommunityTrackPlugin::TrackCardListBlock.create!(:box => box) | ||
113 | + @profile.boxes << box | ||
114 | + get :view_page, @step.url | ||
115 | + assert_tag :tag => 'div', :attributes => { :class => 'item_card category_education' }, :descendant => { :tag => 'div', :attributes => { :class => 'track_content' } } | ||
116 | + assert_tag :tag => 'div', :attributes => { :class => 'item_card category_education' }, :descendant => { :tag => 'div', :attributes => { :class => 'track_stats' } } | ||
117 | + end | ||
118 | + | ||
119 | + should 'render link to display more tracks in track list block' do | ||
120 | + box = fast_create(Box, :owner_id => @profile.id, :owner_type => @profile.class.name) | ||
121 | + @block = CommunityTrackPlugin::TrackCardListBlock.create!(:box => box) | ||
122 | + @profile.boxes << box | ||
123 | + | ||
124 | + (@block.limit+1).times do |i| | ||
125 | + CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => "track#{i}", :profile => @profile) | ||
126 | + end | ||
127 | + | ||
128 | + get :view_page, @step.url | ||
129 | + assert_tag :tag => 'div', :attributes => { :id => "track_list_more_#{@block.id}" }, :descendant => { :tag => 'div', :attributes => { :class => 'more' } } | ||
130 | + end | ||
131 | + | ||
132 | + should 'render link to show all tracks in track list block' do | ||
133 | + box = fast_create(Box, :owner_id => @profile.id, :owner_type => @profile.class.name) | ||
134 | + @block = CommunityTrackPlugin::TrackCardListBlock.create!(:box => box) | ||
135 | + @profile.boxes << box | ||
136 | + @block.more_another_page = true | ||
137 | + @block.save! | ||
138 | + | ||
139 | + (@block.limit+1).times do |i| | ||
140 | + CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => "track#{i}", :profile => @profile) | ||
141 | + end | ||
142 | + | ||
143 | + get :view_page, @step.url | ||
144 | + assert_tag :tag => 'div', :attributes => { :id => "track_list_more_#{@block.id}" }, :descendant => { :tag => 'div', :attributes => { :class => 'view_all' } } | ||
145 | + end | ||
146 | + | ||
147 | +end |
plugins/community_track/test/functional/community_track_plugin_environment_design_controller_test.rb
0 → 100644
@@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | + | ||
3 | +# Re-raise errors caught by the controller. | ||
4 | +class EnvironmentDesignController; def rescue_action(e) raise e end; end | ||
5 | + | ||
6 | +class EnvironmentDesignControllerTest < ActionController::TestCase | ||
7 | + | ||
8 | + def setup | ||
9 | + Environment.delete_all | ||
10 | + @environment = Environment.new(:name => 'testenv', :is_default => true) | ||
11 | + @environment.enabled_plugins = ['CommunityTrackPlugin'] | ||
12 | + @environment.save! | ||
13 | + | ||
14 | + user = create_user('testinguser') | ||
15 | + @environment.add_admin(user.person) | ||
16 | + login_as(user.login) | ||
17 | + | ||
18 | + box = Box.create!(:owner => @environment) | ||
19 | + @block = CommunityTrackPlugin::TrackListBlock.create!(:box => box) | ||
20 | + @block_card = CommunityTrackPlugin::TrackCardListBlock.create!(:box => box) | ||
21 | + end | ||
22 | + | ||
23 | + should 'be able to edit TrackListBlock' do | ||
24 | + get :edit, :id => @block.id | ||
25 | + assert_tag :tag => 'input', :attributes => { :id => 'block_title' } | ||
26 | + end | ||
27 | + | ||
28 | + should 'be able to save TrackListBlock' do | ||
29 | + get :edit, :id => @block.id | ||
30 | + post :save, :id => @block.id, :block => {:title => 'Tracks' } | ||
31 | + @block.reload | ||
32 | + assert_equal 'Tracks', @block.title | ||
33 | + end | ||
34 | + | ||
35 | + should 'be able to edit TrackCardListBlock' do | ||
36 | + get :edit, :id => @block_card.id | ||
37 | + assert_tag :tag => 'input', :attributes => { :id => 'block_title' } | ||
38 | + end | ||
39 | + | ||
40 | + should 'be able to save TrackCardListBlock' do | ||
41 | + get :edit, :id => @block_card.id | ||
42 | + post :save, :id => @block_card.id, :block => {:title => 'Tracks' } | ||
43 | + @block_card.reload | ||
44 | + assert_equal 'Tracks', @block_card.title | ||
45 | + end | ||
46 | + | ||
47 | +end |
plugins/community_track/test/functional/community_track_plugin_myprofile_controller_test.rb
0 → 100644
@@ -0,0 +1,49 @@ | @@ -0,0 +1,49 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | +require File.dirname(__FILE__) + '/../../controllers/myprofile/community_track_plugin_myprofile_controller' | ||
3 | + | ||
4 | +# Re-raise errors caught by the controller. | ||
5 | +class CommunityTrackPluginMyprofileController; def rescue_action(e) raise e end; end | ||
6 | + | ||
7 | +class CommunityTrackPluginMyprofileControllerTest < ActionController::TestCase | ||
8 | + | ||
9 | + def setup | ||
10 | + @controller = CommunityTrackPluginMyprofileController.new | ||
11 | + @request = ActionController::TestRequest.new | ||
12 | + @response = ActionController::TestResponse.new | ||
13 | + | ||
14 | + @profile = fast_create(Community) | ||
15 | + @track = CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => 'track', :profile => @profile) | ||
16 | + | ||
17 | + @user = create_user('testinguser') | ||
18 | + login_as(@user.login) | ||
19 | + @profile.add_admin(@user.person) | ||
20 | + end | ||
21 | + | ||
22 | + should 'redirect to track on save order' do | ||
23 | + get :save_order, :profile => @profile.identifier, :track => @track.id, :step_ids => [] | ||
24 | + assert_redirected_to @track.url | ||
25 | + end | ||
26 | + | ||
27 | + should 'save new step positions on save order' do | ||
28 | + step1 = CommunityTrackPlugin::Step.create!(:name => 'step1', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | ||
29 | + step2 = CommunityTrackPlugin::Step.create!(:name => 'step2', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | ||
30 | + assert_equal [step1, step2], @track.steps | ||
31 | + get :save_order, :profile => @profile.identifier, :track => @track.id, :step_ids => [step2.id, step1.id] | ||
32 | + assert_equal [step2, step1], @track.steps | ||
33 | + end | ||
34 | + | ||
35 | + should 'do not allow a user without permission to save order' do | ||
36 | + logout | ||
37 | + user = create_user('intruder') | ||
38 | + login_as(user.login) | ||
39 | + get :save_order, :profile => @profile.identifier, :track => @track.id, :step_ids => [] | ||
40 | + assert_response 403 | ||
41 | + end | ||
42 | + | ||
43 | + should 'redirect to login page if there is no user logged in' do | ||
44 | + logout | ||
45 | + get :save_order, :profile => @profile.identifier, :track => @track.id, :step_ids => [] | ||
46 | + assert_response 302 | ||
47 | + end | ||
48 | + | ||
49 | +end |
plugins/community_track/test/functional/community_track_plugin_public_controller_test.rb
0 → 100644
@@ -0,0 +1,109 @@ | @@ -0,0 +1,109 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | +require File.dirname(__FILE__) + '/../../controllers/public/community_track_plugin_public_controller' | ||
3 | + | ||
4 | +# Re-raise errors caught by the controller. | ||
5 | +class CommunityTrackPluginPublicController; def rescue_action(e) raise e end; end | ||
6 | + | ||
7 | +class CommunityTrackPluginPublicControllerTest < ActionController::TestCase | ||
8 | + | ||
9 | + def setup | ||
10 | + @community = fast_create(Community) | ||
11 | + @track = CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => 'track', :profile => @community) | ||
12 | + | ||
13 | + box = fast_create(Box, :owner_id => @community.id, :owner_type => 'Community') | ||
14 | + @card_block = CommunityTrackPlugin::TrackCardListBlock.create!(:box => box) | ||
15 | + @block = CommunityTrackPlugin::TrackListBlock.create!(:box => box) | ||
16 | + end | ||
17 | + | ||
18 | + should 'display tracks for card block' do | ||
19 | + xhr :get, :view_tracks, :id => @card_block.id, :page => 1 | ||
20 | + assert_match /track_list_#{@card_block.id}/, @response.body | ||
21 | + end | ||
22 | + | ||
23 | + should 'display tracks for list block' do | ||
24 | + xhr :get, :view_tracks, :id => @block.id, :page => 1 | ||
25 | + assert_match /track_list_#{@block.id}/, @response.body | ||
26 | + end | ||
27 | + | ||
28 | + should 'display tracks with page size' do | ||
29 | + 20.times do |i| | ||
30 | + track = CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => "track#{i}", :profile => @community) | ||
31 | + end | ||
32 | + xhr :get, :view_tracks, :id => @block.id, :page => 1, :per_page => 10 | ||
33 | + assert_equal 10, @response.body.scan(/item/).size | ||
34 | + end | ||
35 | + | ||
36 | + should 'default page size is the block limit' do | ||
37 | + 20.times do |i| | ||
38 | + track = CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => "track#{i}", :profile => @community) | ||
39 | + end | ||
40 | + xhr :get, :view_tracks, :id => @block.id, :page => 1 | ||
41 | + assert_equal @block.limit, @response.body.scan(/item/).size | ||
42 | + end | ||
43 | + | ||
44 | + should 'display page for all tracks' do | ||
45 | + get :all_tracks, :id => @block.id | ||
46 | + assert_match /track_list_#{@block.id}/, @response.body | ||
47 | + end | ||
48 | + | ||
49 | + should 'show more link in all tracks if there is no more tracks to show' do | ||
50 | + 10.times do |i| | ||
51 | + CommunityTrackPlugin::Track.create!(:abstract => 'abstract', :body => 'body', :name => "track#{i}", :profile => @community) | ||
52 | + end | ||
53 | + get :all_tracks, :id => @block.id | ||
54 | + assert assigns['show_more'] | ||
55 | + assert_match /track_list_more_#{@block.id}/, @response.body | ||
56 | + end | ||
57 | + | ||
58 | + should 'do not show more link in all tracks if there is no more tracks to show' do | ||
59 | + CommunityTrackPlugin::Track.destroy_all | ||
60 | + get :all_tracks, :id => @block.id | ||
61 | + assert !assigns['show_more'] | ||
62 | + assert_no_match /track_list_more_#{@block.id}/, @response.body | ||
63 | + end | ||
64 | + | ||
65 | + should 'show select community page if user is logged in' do | ||
66 | + user = create_user('testinguser') | ||
67 | + login_as(user.login) | ||
68 | + get :select_community | ||
69 | + assert_template 'select_community' | ||
70 | + end | ||
71 | + | ||
72 | + should 'redirect to login page if user try to access community selection' do | ||
73 | + logout | ||
74 | + get :select_community | ||
75 | + assert_redirected_to :controller => 'account', :action => 'login' | ||
76 | + end | ||
77 | + | ||
78 | + should 'display for selection communities where user has permission to post content' do | ||
79 | + user = create_user('testinguser') | ||
80 | + login_as(user.login) | ||
81 | + @community.add_member(user.person) | ||
82 | + get :select_community | ||
83 | + assert_tag :tag => 'li', :attributes => {:class => 'search-profile-item'} | ||
84 | + assert_tag :tag => 'input', :attributes => {:id => "community_identifier_#{@community.identifier}"} | ||
85 | + end | ||
86 | + | ||
87 | + should 'do not display communities where user has not permission to post content' do | ||
88 | + user = create_user('testinguser') | ||
89 | + login_as(user.login) | ||
90 | + get :select_community | ||
91 | + assert_no_tag :tag => 'input', :attributes => {:id => "community_identifier_#{@community.identifier}"} | ||
92 | + end | ||
93 | + | ||
94 | + should 'redirect to new content with track content type' do | ||
95 | + user = create_user('testinguser') | ||
96 | + login_as(user.login) | ||
97 | + post :select_community, :profile => user.person.identifier, :community_identifier => @community.identifier | ||
98 | + assert_redirected_to :controller => 'cms', :action => 'new', :type => "CommunityTrackPlugin::Track", :profile => @community.identifier | ||
99 | + end | ||
100 | + | ||
101 | + should 'return error message if user do not select a community' do | ||
102 | + user = create_user('testinguser') | ||
103 | + login_as(user.login) | ||
104 | + post :select_community, :profile => user.person.identifier, :community_identifier => nil | ||
105 | + assert_equal 1, assigns(:failed).count | ||
106 | + assert_tag :tag => 'div', :attributes => {:id => 'errorExplanation'} | ||
107 | + end | ||
108 | + | ||
109 | +end |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../test/test_helper' |