Commit 991e20ab80826953daa60eec2378381169c1de64
Exists in
master
and in
29 other branches
Merge branches 'next_colivre' and 'ActionItem2892' into ActionItem2892
Showing
211 changed files
with
4328 additions
and
568 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 211 files displayed.
COPYRIGHT
... | ... | @@ -4,8 +4,9 @@ Copyright (c) 2007-2009, |
4 | 4 | Cáritas Brasileira <http://www.caritasbrasileira.org/> |
5 | 5 | Copyright (c) 2007-2009, |
6 | 6 | Ynternet.org Foundation <http://www.ynternet.org/> |
7 | -Copyright (c) 2008-2009, | |
7 | +Copyright (c) 2008-2013, | |
8 | 8 | Colivre <http://www.colivre.coop.br/> |
9 | +Copyright (c) the Noosfero contributors. See AUTHORS | |
9 | 10 | |
10 | 11 | This program is free software: you can redistribute it and/or modify |
11 | 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 | 52 | development mailing list so that we can include it in the official repository. |
53 | 53 | This way other people using the same OS will have to put less effort to develop |
54 | 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 | -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 | 6 | * SystemTimer - http://ph7spot.com/musings/system-timer |
7 | 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 | 11 | # apt-get install librestclient-ruby pidgin-data ruby1.8-dev |
12 | 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 | 267 | @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) |
268 | 268 | @task = SuggestArticle.new(params[:task]) |
269 | 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 | 274 | if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save |
272 | 275 | session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') |
273 | 276 | redirect_to @back_to | ... | ... |
app/controllers/my_profile/memberships_controller.rb
... | ... | @@ -9,9 +9,10 @@ class MembershipsController < MyProfileController |
9 | 9 | def new_community |
10 | 10 | @community = Community.new(params[:community]) |
11 | 11 | @community.environment = environment |
12 | + @back_to = params[:back_to] || url_for(:action => 'index') | |
12 | 13 | if request.post? && @community.valid? |
13 | 14 | @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) |
14 | - redirect_to :action => 'index' | |
15 | + redirect_to @back_to | |
15 | 16 | return |
16 | 17 | end |
17 | 18 | end | ... | ... |
app/controllers/my_profile/profile_editor_controller.rb
... | ... | @@ -4,7 +4,7 @@ class ProfileEditorController < MyProfileController |
4 | 4 | protect 'destroy_profile', :profile, :only => [:destroy_profile] |
5 | 5 | |
6 | 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 | 8 | end |
9 | 9 | |
10 | 10 | helper :profile | ... | ... |
app/controllers/my_profile/spam_controller.rb
... | ... | @@ -14,9 +14,15 @@ class SpamController < MyProfileController |
14 | 14 | if params[:remove_comment] |
15 | 15 | profile.comments_received.find(params[:remove_comment]).destroy |
16 | 16 | end |
17 | + if params[:remove_task] | |
18 | + Task.to(profile).find_by_id(params[:remove_task]).destroy | |
19 | + end | |
17 | 20 | if params[:mark_comment_as_ham] |
18 | 21 | profile.comments_received.find(params[:mark_comment_as_ham]).ham! |
19 | 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 | 26 | if request.xhr? |
21 | 27 | json_response(true) |
22 | 28 | else |
... | ... | @@ -28,7 +34,8 @@ class SpamController < MyProfileController |
28 | 34 | return |
29 | 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 | 39 | end |
33 | 40 | |
34 | 41 | protected | ... | ... |
app/controllers/my_profile/tasks_controller.rb
... | ... | @@ -4,12 +4,12 @@ class TasksController < MyProfileController |
4 | 4 | |
5 | 5 | def index |
6 | 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 | 8 | @failed = params ? params[:failed] : {} |
9 | 9 | end |
10 | 10 | |
11 | 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 | 13 | end |
14 | 14 | |
15 | 15 | VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] |
... | ... | @@ -57,7 +57,7 @@ class TasksController < MyProfileController |
57 | 57 | end |
58 | 58 | |
59 | 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 | 61 | end |
62 | 62 | |
63 | 63 | def ticket_details | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -53,7 +53,9 @@ class ContentViewerController < ApplicationController |
53 | 53 | # At this point the page will be showed |
54 | 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 | 59 | headers['Content-Type'] = @page.mime_type |
58 | 60 | data = @page.data |
59 | 61 | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -558,6 +558,9 @@ module ApplicationHelper |
558 | 558 | # displays a link to the profile homepage with its image (as generated by |
559 | 559 | # #profile_image) and its name below it. |
560 | 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 | 564 | name = profile.short_name |
562 | 565 | if profile.person? |
563 | 566 | url = url_for(profile.check_friendship_url) |
... | ... | @@ -574,16 +577,16 @@ module ApplicationHelper |
574 | 577 | extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) |
575 | 578 | links = links_for_balloon(profile) |
576 | 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 | 590 | end |
588 | 591 | |
589 | 592 | def gravatar_url_for(email, options = {}) |
... | ... | @@ -1113,15 +1116,34 @@ module ApplicationHelper |
1113 | 1116 | result |
1114 | 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 | 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 | 1133 | end |
1123 | 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 | 1147 | def usermenu_logged_in |
1126 | 1148 | pending_tasks_count = '' |
1127 | 1149 | count = user ? Task.to(user).pending.count : -1 |
... | ... | @@ -1133,6 +1155,7 @@ module ApplicationHelper |
1133 | 1155 | render_environment_features(:usermenu) + |
1134 | 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 | 1157 | manage_enterprises.to_s + |
1158 | + manage_communities.to_s + | |
1136 | 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 | 1160 | pending_tasks_count + |
1138 | 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 | 1424 | end |
1402 | 1425 | |
1403 | 1426 | def filter_html(html, source) |
1404 | - if @plugins | |
1427 | + if @plugins && source.has_macro? | |
1405 | 1428 | html = convert_macro(html, source) |
1406 | 1429 | #TODO This parse should be done through the macro infra, but since there |
1407 | 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 | 42 | |
43 | 43 | def display_post(article, format = 'full') |
44 | 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 | 47 | article_title(article, :no_comments => no_comments) + html |
48 | 48 | end | ... | ... |
app/helpers/boxes_helper.rb
... | ... | @@ -100,9 +100,7 @@ module BoxesHelper |
100 | 100 | options[:title] = _("This block is invisible. Your visitors will not see it.") |
101 | 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 | 105 | box_decorator.block_target(block.box, block) + |
108 | 106 | content_tag('div', | ... | ... |
app/helpers/categories_helper.rb
... | ... | @@ -3,10 +3,21 @@ module CategoriesHelper |
3 | 3 | |
4 | 4 | COLORS = [ |
5 | 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 | 23 | TYPES = [ | ... | ... |
app/helpers/cms_helper.rb
... | ... | @@ -33,7 +33,7 @@ module CmsHelper |
33 | 33 | link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article) |
34 | 34 | else |
35 | 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 | 37 | else |
38 | 38 | link_to article_name, article.url, :class => icon_for_article(article) |
39 | 39 | end | ... | ... |
app/helpers/content_viewer_helper.rb
... | ... | @@ -17,7 +17,7 @@ module ContentViewerHelper |
17 | 17 | title = article.display_title if article.kind_of?(UploadedFile) && article.image? |
18 | 18 | title = article.title if title.blank? |
19 | 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 | 21 | unless args[:no_link] |
22 | 22 | title = content_tag('h1', link_to(article.name, article.url), :class => 'title') |
23 | 23 | end | ... | ... |
app/helpers/folder_helper.rb
... | ... | @@ -21,6 +21,7 @@ module FolderHelper |
21 | 21 | end |
22 | 22 | |
23 | 23 | def display_article_in_listing(article, recursive = false, level = 0) |
24 | + article = FilePresenter.for article | |
24 | 25 | article_link = if article.image? |
25 | 26 | link_to(' ' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)) |
26 | 27 | else |
... | ... | @@ -40,12 +41,15 @@ module FolderHelper |
40 | 41 | end |
41 | 42 | |
42 | 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 | 48 | if (icon =~ /\//) |
45 | 49 | icon |
46 | 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 | 53 | klasses += ' icon-upload-file' |
50 | 54 | end |
51 | 55 | klasses | ... | ... |
app/helpers/sweeper_helper.rb
... | ... | @@ -44,4 +44,30 @@ module SweeperHelper |
44 | 44 | def expire_profile_index(profile) |
45 | 45 | expire_timeout_fragment(profile.relationships_cache_key) |
46 | 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 | 73 | end | ... | ... |
app/models/article.rb
... | ... | @@ -156,8 +156,12 @@ class Article < ActiveRecord::Base |
156 | 156 | end |
157 | 157 | end |
158 | 158 | |
159 | + def css_class_list | |
160 | + [self.class.name.underscore.dasherize] | |
161 | + end | |
162 | + | |
159 | 163 | def css_class_name |
160 | - self.class.name.underscore.dasherize | |
164 | + [css_class_list].flatten.compact.join(' ') | |
161 | 165 | end |
162 | 166 | |
163 | 167 | def pending_categorizations |
... | ... | @@ -312,6 +316,10 @@ class Article < ActiveRecord::Base |
312 | 316 | def belongs_to_blog? |
313 | 317 | self.parent and self.parent.blog? |
314 | 318 | end |
319 | + | |
320 | + def belongs_to_forum? | |
321 | + self.parent and self.parent.forum? | |
322 | + end | |
315 | 323 | |
316 | 324 | def info_from_last_update |
317 | 325 | last_comment = comments.last |
... | ... | @@ -327,7 +335,7 @@ class Article < ActiveRecord::Base |
327 | 335 | end |
328 | 336 | |
329 | 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 | 339 | end |
332 | 340 | |
333 | 341 | def comment_url_structure(comment, action = :edit) |
... | ... | @@ -675,6 +683,10 @@ class Article < ActiveRecord::Base |
675 | 683 | |
676 | 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 | 690 | private |
679 | 691 | |
680 | 692 | def sanitize_tag_list | ... | ... |
app/models/article_block.rb
... | ... | @@ -12,7 +12,7 @@ class ArticleBlock < Block |
12 | 12 | block = self |
13 | 13 | lambda do |
14 | 14 | block_title(block.title) + |
15 | - (block.article ? article_to_html(block.article, | |
15 | + (block.article ? article_to_html(FilePresenter.for(block.article), | |
16 | 16 | :gallery_view => false, |
17 | 17 | :inside_block => block, # For Blogs and folders |
18 | 18 | :format => block.visualization_format # For Articles and contents |
... | ... | @@ -23,7 +23,7 @@ class ArticleBlock < Block |
23 | 23 | def article_id |
24 | 24 | self.settings[:article_id] |
25 | 25 | end |
26 | - | |
26 | + | |
27 | 27 | def article_id= value |
28 | 28 | self.settings[:article_id] = value.blank? ? nil : value.to_i |
29 | 29 | end |
... | ... | @@ -63,4 +63,9 @@ class ArticleBlock < Block |
63 | 63 | end |
64 | 64 | |
65 | 65 | settings_items :visualization_format, :type => :string, :default => 'short' |
66 | + | |
67 | + def self.expire_on | |
68 | + { :profile => [:article], :environment => [:article] } | |
69 | + end | |
70 | + | |
66 | 71 | end | ... | ... |
app/models/block.rb
... | ... | @@ -138,4 +138,19 @@ class Block < ActiveRecord::Base |
138 | 138 | 4.hours |
139 | 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 | 156 | end | ... | ... |
app/models/blog_archives_block.rb
app/models/categories_block.rb
app/models/category.rb
... | ... | @@ -12,7 +12,7 @@ class Category < ActiveRecord::Base |
12 | 12 | validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n |
13 | 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 | 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 | 18 | # Finds all top level categories for a given environment. | ... | ... |
app/models/comment.rb
... | ... | @@ -16,9 +16,7 @@ class Comment < ActiveRecord::Base |
16 | 16 | has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy |
17 | 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 | 19 | named_scope :without_reply, :conditions => ['reply_of_id IS NULL'] |
21 | - named_scope :spam, :conditions => ['spam = ?', true] | |
22 | 20 | |
23 | 21 | # unauthenticated authors: |
24 | 22 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) |
... | ... | @@ -108,6 +106,17 @@ class Comment < ActiveRecord::Base |
108 | 106 | |
109 | 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 | 120 | def verify_and_notify |
112 | 121 | check_for_spam |
113 | 122 | unless spam? |
... | ... | @@ -115,10 +124,6 @@ class Comment < ActiveRecord::Base |
115 | 124 | end |
116 | 125 | end |
117 | 126 | |
118 | - def check_for_spam | |
119 | - plugins.dispatch(:check_comment_for_spam, self) | |
120 | - end | |
121 | - | |
122 | 127 | def notify_by_mail |
123 | 128 | if source.kind_of?(Article) && article.notify_comments? |
124 | 129 | if !notification_emails.empty? |
... | ... | @@ -205,37 +210,6 @@ class Comment < ActiveRecord::Base |
205 | 210 | @rejected = true |
206 | 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 | 213 | def need_moderation? |
240 | 214 | article.moderate_comments? && (author.nil? || article.author != author) |
241 | 215 | end | ... | ... |
app/models/environment.rb
... | ... | @@ -127,7 +127,9 @@ class Environment < ActiveRecord::Base |
127 | 127 | 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'), |
128 | 128 | 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'), |
129 | 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 | 134 | end |
133 | 135 | ... | ... |
app/models/link_list_block.rb
... | ... | @@ -33,6 +33,12 @@ class LinkListBlock < Block |
33 | 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 | 42 | settings_items :links, Array, :default => [] |
37 | 43 | |
38 | 44 | before_save do |block| |
... | ... | @@ -57,7 +63,7 @@ class LinkListBlock < Block |
57 | 63 | def link_html(link) |
58 | 64 | klass = 'icon-' + link[:icon] if link[:icon] |
59 | 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 | 68 | end |
63 | 69 | ... | ... |
app/models/raw_html_block.rb
app/models/recent_documents_block.rb
app/models/spammer_logger.rb
... | ... | @@ -6,6 +6,8 @@ class SpammerLogger < Logger |
6 | 6 | if object |
7 | 7 | if object.kind_of?(Comment) |
8 | 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 | 11 | end |
10 | 12 | else |
11 | 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 | 11 | settings_items :source, :type => String |
12 | 12 | settings_items :source_name, :type => String |
13 | 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 | 26 | def sender |
16 | 27 | "#{name} (#{email})" |
... | ... | @@ -61,4 +72,12 @@ class SuggestArticle < Task |
61 | 72 | _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } |
62 | 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 | 83 | end | ... | ... |
app/models/tags_block.rb
app/models/task.rb
... | ... | @@ -235,6 +235,8 @@ class Task < ActiveRecord::Base |
235 | 235 | end |
236 | 236 | end |
237 | 237 | |
238 | + include Spammable | |
239 | + | |
238 | 240 | protected |
239 | 241 | |
240 | 242 | # This method must be overrided in subclasses, and its implementation must do |
... | ... | @@ -275,6 +277,7 @@ class Task < ActiveRecord::Base |
275 | 277 | named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } |
276 | 278 | named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } |
277 | 279 | |
280 | + | |
278 | 281 | named_scope :to, lambda { |profile| |
279 | 282 | environment_condition = nil |
280 | 283 | if profile.person? | ... | ... |
app/models/uploaded_file.rb
... | ... | @@ -41,7 +41,25 @@ class UploadedFile < Article |
41 | 41 | end |
42 | 42 | |
43 | 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 | 63 | end |
46 | 64 | |
47 | 65 | # FIXME need to define min/max file size |
... | ... | @@ -52,20 +70,28 @@ class UploadedFile < Article |
52 | 70 | has_attachment :storage => :file_system, |
53 | 71 | :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' }, |
54 | 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 | 77 | delay_attachment_fu_thumbnails |
60 | 78 | |
61 | 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 | 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 | 93 | end |
94 | + 'upload-file' | |
69 | 95 | end |
70 | 96 | |
71 | 97 | def mime_type |
... | ... | @@ -91,40 +117,27 @@ class UploadedFile < Article |
91 | 117 | end |
92 | 118 | |
93 | 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 | 127 | article = self |
95 | 128 | if image? |
96 | 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 | 134 | end |
124 | 135 | else |
125 | 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 | 141 | end |
129 | 142 | end |
130 | 143 | end | ... | ... |
... | ... | @@ -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 | 16 | end |
17 | 17 | end |
18 | 18 | |
19 | + | |
19 | 20 | protected |
20 | 21 | |
21 | 22 | def expire_caches(article) |
23 | + expire_blocks_cache(article.profile, [:article]) | |
24 | + | |
22 | 25 | return if !article.environment |
26 | + | |
23 | 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 | 28 | env = article.profile.environment |
29 | 29 | if env && (env.portal_community == article.profile) |
30 | 30 | article.environment.locales.keys.each do |locale| | ... | ... |
app/sweepers/category_sweeper.rb
... | ... | @@ -3,7 +3,13 @@ class CategorySweeper < ActiveRecord::Observer |
3 | 3 | include SweeperHelper |
4 | 4 | |
5 | 5 | def after_save(category) |
6 | + expire_blocks_cache(category.environment, [:category]) | |
7 | + | |
8 | + # Needed for environments with application layout | |
6 | 9 | expire_fragment(category.environment.id.to_s + "_categories_menu") |
7 | 10 | end |
8 | 11 | |
12 | + def after_destroy(category) | |
13 | + expire_blocks_cache(category.environment, [:category]) | |
14 | + end | |
9 | 15 | end | ... | ... |
app/views/box_organizer/_link_list_block.rhtml
1 | 1 | <strong><%= _('Links') %></strong> |
2 | 2 | <div id='edit-link-list-block' style='width:450px'> |
3 | 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 | 5 | <% for link in @block.links do %> |
6 | 6 | <tr> |
7 | 7 | <td> |
... | ... | @@ -9,6 +9,9 @@ |
9 | 9 | </td> |
10 | 10 | <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td> |
11 | 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 | 15 | </tr> |
13 | 16 | <% end %> |
14 | 17 | </table> |
... | ... | @@ -18,7 +21,9 @@ |
18 | 21 | page.insert_html :bottom, 'links', content_tag('tr', |
19 | 22 | content_tag('td', icon_selector('ok')) + |
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 | 28 | javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight") |
24 | 29 | end %> | ... | ... |
app/views/cms/view.rhtml
... | ... | @@ -40,13 +40,16 @@ |
40 | 40 | </tr> |
41 | 41 | <% end %> |
42 | 42 | |
43 | - <% @articles.each do |article| %> | |
43 | + <% @articles.each do |article| article = FilePresenter.for article %> | |
44 | 44 | <tr title="<%= article.title%>" > |
45 | - <td> | |
45 | + <td class="article-name"> | |
46 | 46 | <%= link_to_article(article) %> |
47 | 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 | 53 | </td> |
51 | 54 | <td class="article-controls"> |
52 | 55 | <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %> | ... | ... |
... | ... | @@ -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 @@ |
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 | 46 | |
47 | 47 | <%= template_options(Community, 'community')%> |
48 | 48 | |
49 | + <%= hidden_field_tag('back_to', @back_to) %> | |
50 | + | |
49 | 51 | <% button_bar do %> |
50 | 52 | <%= submit_button(:save, _('Create')) %> |
51 | - <%= button(:cancel, _('Cancel'), :action => 'index') %> | |
53 | + <%= button(:cancel, _('Cancel'), @back_to ) %> | |
52 | 54 | <% end %> |
53 | 55 | |
54 | 56 | <% end %> | ... | ... |
app/views/shared/_manage_enterprises.rhtml
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 | 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 | 9 | <% button_bar do %> |
4 | 10 | <%= button :back, _('Back to control panel'), :controller => :profile_editor %> |
5 | 11 | <% end %> |
6 | 12 | |
7 | 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 | 26 | <% end %> |
27 | + | |
28 | +<%= javascript_include_tag 'spam' %> | ... | ... |
app/views/tasks/_task.rhtml
1 | 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 | 5 | <div class="task_decisions"> |
23 | 6 | <%= |
... | ... | @@ -39,9 +22,7 @@ |
39 | 22 | %> |
40 | 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 | 27 | <div class="task_information"> |
47 | 28 | <%= task_information(task) %> | ... | ... |
... | ... | @@ -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
config/noosfero.yml.dist
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 | 9 | # |
10 | 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 | 14 | create_table "abuse_reports", :force => true do |t| |
15 | 15 | t.integer "reporter_id" |
... | ... | @@ -86,6 +86,8 @@ ActiveRecord::Schema.define(:version => 20130711213046) do |
86 | 86 | t.string "language" |
87 | 87 | t.string "source_name" |
88 | 88 | t.integer "license_id" |
89 | + t.integer "image_id" | |
90 | + t.integer "position" | |
89 | 91 | end |
90 | 92 | |
91 | 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 | 131 | t.string "language" |
130 | 132 | t.string "source_name" |
131 | 133 | t.integer "license_id" |
134 | + t.integer "image_id" | |
135 | + t.integer "position" | |
132 | 136 | end |
133 | 137 | |
134 | 138 | add_index "articles", ["name"], :name => "index_articles_on_name" |
... | ... | @@ -547,8 +551,11 @@ ActiveRecord::Schema.define(:version => 20130711213046) do |
547 | 551 | t.datetime "created_at" |
548 | 552 | t.string "target_type" |
549 | 553 | t.integer "image_id" |
554 | + t.boolean "spam", :default => false | |
550 | 555 | end |
551 | 556 | |
557 | + add_index "tasks", ["spam"], :name => "index_tasks_on_spam" | |
558 | + | |
552 | 559 | create_table "thumbnails", :force => true do |t| |
553 | 560 | t.integer "size" |
554 | 561 | t.string "content_type" | ... | ... |
features/browse_catalogs.feature
... | ... | @@ -9,7 +9,7 @@ Feature: browse catalogs |
9 | 9 | And the following enterprises |
10 | 10 | | identifier | owner | name | enabled | |
11 | 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 | 13 | And the following product_categories |
14 | 14 | | name | |
15 | 15 | | categ1 | | ... | ... |
features/browse_enterprises.feature
... | ... | @@ -6,7 +6,7 @@ Background: |
6 | 6 | Given the following enterprises |
7 | 7 | | identifier | name | |
8 | 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 | 10 | And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment |
11 | 11 | |
12 | 12 | Scenario: show all enterprises | ... | ... |
features/enterprise_homepage.feature
features/manage_inputs.feature
... | ... | @@ -19,7 +19,7 @@ Feature: manage inputs |
19 | 19 | And the following product |
20 | 20 | | owner | category | name | |
21 | 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 | 23 | And the following units |
24 | 24 | | singular | plural | |
25 | 25 | | Meter | Meters | | ... | ... |
features/manage_product_price_details.feature
... | ... | @@ -19,7 +19,7 @@ Feature: manage product price details |
19 | 19 | And the following product |
20 | 20 | | owner | category | name | price | |
21 | 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 | 23 | And the following inputs |
24 | 24 | | product | category | price_per_unit | amount_used | |
25 | 25 | | Abbey Road | Rock | 10.0 | 2 | | ... | ... |
features/manage_products.feature
... | ... | @@ -9,7 +9,7 @@ Feature: manage products |
9 | 9 | And the following enterprises |
10 | 10 | | identifier | owner | name | enabled | |
11 | 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 | 14 | Scenario: display "create new product" button |
15 | 15 | Given I am logged in as "joaosilva" | ... | ... |
lib/feed_updater.rb
... | ... | @@ -20,14 +20,20 @@ end |
20 | 20 | class FeedUpdater |
21 | 21 | |
22 | 22 | class ExceptionNotification < ActionMailer::Base |
23 | - def mail error | |
23 | + def mail container, error | |
24 | 24 | environment = Environment.default |
25 | 25 | |
26 | 26 | recipients NOOSFERO_CONF['exception_recipients'] |
27 | 27 | from environment.contact_email |
28 | 28 | reply_to environment.contact_email |
29 | 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 | 37 | end |
32 | 38 | end |
33 | 39 | |
... | ... | @@ -88,11 +94,13 @@ class FeedUpdater |
88 | 94 | if !running |
89 | 95 | break |
90 | 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 | 102 | end |
93 | 103 | end |
94 | - rescue Exception => e | |
95 | - FeedUpdater::ExceptionNotification.deliver_mail e if NOOSFERO_CONF['exception_recipients'].present? | |
96 | 104 | end |
97 | 105 | end |
98 | 106 | ... | ... |
... | ... | @@ -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 | 155 | |
156 | 156 | # Here the developer may specify the events to which the plugins can |
157 | 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 | 160 | # -> If true, noosfero will include plugin_dir/public/style.css into |
160 | 161 | # application |
... | ... | @@ -162,10 +163,6 @@ class Noosfero::Plugin |
162 | 163 | false |
163 | 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 | 166 | # -> Adds buttons to the control panel |
170 | 167 | # returns = { :title => title, :icon => icon, :url => url } |
171 | 168 | # title = name that will be displayed. |
... | ... | @@ -175,6 +172,13 @@ class Noosfero::Plugin |
175 | 172 | nil |
176 | 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 | 182 | # -> Adds tabs to the profile |
179 | 183 | # returns = { :title => title, :id => id, :content => content, :start => start } |
180 | 184 | # title = name that will be displayed. |
... | ... | @@ -304,45 +308,16 @@ class Noosfero::Plugin |
304 | 308 | scope |
305 | 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 | 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 | 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 | 321 | end |
347 | 322 | |
348 | 323 | # Adds extra actions for comments | ... | ... |
lib/noosfero/plugin/manager.rb
... | ... | @@ -34,18 +34,20 @@ class Noosfero::Plugin::Manager |
34 | 34 | alias :dispatch_scopes :dispatch_without_flatten |
35 | 35 | |
36 | 36 | def dispatch_first(event, *args) |
37 | - result = nil | |
37 | + default = Noosfero::Plugin.new.send(event, *args) | |
38 | + result = default | |
38 | 39 | each do |plugin| |
39 | 40 | result = plugin.send(event, *args) |
40 | - break if result.present? | |
41 | + break if result != default | |
41 | 42 | end |
42 | 43 | result |
43 | 44 | end |
44 | 45 | |
45 | 46 | def fetch_first_plugin(event, *args) |
47 | + default = Noosfero::Plugin.new.send(event, *args) | |
46 | 48 | result = nil |
47 | 49 | each do |plugin| |
48 | - if plugin.send(event, *args) | |
50 | + if plugin.send(event, *args) != default | |
49 | 51 | result = plugin.class |
50 | 52 | break |
51 | 53 | end | ... | ... |
... | ... | @@ -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 @@ |
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 | 5 | end |
6 | 6 | |
7 | 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 | 9 | end |
10 | 10 | |
11 | 11 | def self.host_default_setting |
12 | 12 | 'api.antispam.typepad.com' |
13 | 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 | 19 | end |
20 | 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 | 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 | 28 | end |
29 | 29 | |
30 | 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 | 35 | Rakismet.host = settings.host |
36 | 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 | 39 | submission.send(op) |
41 | 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 | 9 | end | ... | ... |
plugins/anti_spam/lib/anti_spam_plugin/suggest_article_wrapper.rb
0 → 100644
... | ... | @@ -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 @@ |
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 | 1 | require 'test_helper' |
2 | 2 | |
3 | -class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase | |
3 | +class AntiSpamPlugin::CommentWrapperTest < ActiveSupport::TestCase | |
4 | 4 | |
5 | 5 | def setup |
6 | 6 | @comment = Comment.new( |
... | ... | @@ -15,10 +15,6 @@ class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase |
15 | 15 | @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment) |
16 | 16 | end |
17 | 17 | |
18 | - should 'use Rakismet::Model' do | |
19 | - assert_includes @wrapper.class.included_modules, Rakismet::Model | |
20 | - end | |
21 | - | |
22 | 18 | should 'get contents' do |
23 | 19 | assert_equal @comment.body, @wrapper.content |
24 | 20 | end | ... | ... |
plugins/anti_spam/test/unit/anti_spam_plugin/suggest_article_wrapper_test.rb
0 → 100644
... | ... | @@ -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 @@ |
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 | 2 | |
3 | 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 | 15 | @plugin = AntiSpamPlugin.new |
15 | - @plugin.context = @comment | |
16 | 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 | 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 | 31 | end |
35 | 32 | |
33 | + should 'report comment ham' do | |
34 | + spammable.expects(:ham!) | |
35 | + @plugin.marked_as_ham(spammable) | |
36 | + end | |
36 | 37 | end | ... | ... |
plugins/community_track/controllers/myprofile/community_track_plugin_myprofile_controller.rb
0 → 100644
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
1 | +require File.dirname(__FILE__) + '/../../../test/test_helper' | ... | ... |
plugins/community_track/test/unit/community_track_plugin/step_helper_test.rb
0 → 100644
... | ... | @@ -0,0 +1,38 @@ |
1 | +require File.dirname(__FILE__) + '/../../test_helper' | |
2 | + | |
3 | +class StepHelperTest < ActiveSupport::TestCase | |
4 | + | |
5 | + include CommunityTrackPlugin::StepHelper | |
6 | + | |
7 | + def setup | |
8 | + @step = CommunityTrackPlugin::Step.new | |
9 | + @step.stubs(:active?).returns(false) | |
10 | + @step.stubs(:finished?).returns(false) | |
11 | + @step.stubs(:waiting?).returns(false) | |
12 | + end | |
13 | + | |
14 | + should 'return active class when step is active' do | |
15 | + @step.stubs(:active?).returns(true) | |
16 | + assert_equal 'step_active', status_class(@step) | |
17 | + end | |
18 | + | |
19 | + should 'return finished class when step is finished' do | |
20 | + @step.stubs(:finished?).returns(true) | |
21 | + assert_equal 'step_finished', status_class(@step) | |
22 | + end | |
23 | + | |
24 | + should 'return waiting class when step is active' do | |
25 | + @step.stubs(:waiting?).returns(true) | |
26 | + assert_equal 'step_waiting', status_class(@step) | |
27 | + end | |
28 | + | |
29 | + should 'return a description for status' do | |
30 | + @step.stubs(:waiting?).returns(true) | |
31 | + assert_equal _('Waiting'), status_description(@step) | |
32 | + end | |
33 | + | |
34 | + should 'return nil at custom_options_for_article' do | |
35 | + assert !custom_options_for_article(fast_create(Article)) | |
36 | + end | |
37 | + | |
38 | +end | ... | ... |
plugins/community_track/test/unit/community_track_plugin/step_test.rb
0 → 100644
... | ... | @@ -0,0 +1,265 @@ |
1 | +require File.dirname(__FILE__) + '/../../test_helper' | |
2 | + | |
3 | +class StepTest < ActiveSupport::TestCase | |
4 | + | |
5 | + def setup | |
6 | + @profile = fast_create(Community) | |
7 | + @track = CommunityTrackPlugin::Track.create(:profile_id => @profile.id, :name => 'track') | |
8 | + @step = CommunityTrackPlugin::Step.new(:name => 'Step', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | |
9 | + Delayed::Job.destroy_all | |
10 | + end | |
11 | + | |
12 | + should 'describe yourself' do | |
13 | + assert CommunityTrackPlugin::Step.description | |
14 | + end | |
15 | + | |
16 | + should 'has a short description' do | |
17 | + assert CommunityTrackPlugin::Step.short_description | |
18 | + end | |
19 | + | |
20 | + should 'set published to false on create' do | |
21 | + today = Date.today | |
22 | + step = CommunityTrackPlugin::Step.create(:name => 'Step', :body => 'body', :profile => @profile, :parent => @track, :start_date => today, :end_date => today, :published => true) | |
23 | + assert !step.published | |
24 | + end | |
25 | + | |
26 | + should 'do not allow step creation with a parent that is not a track' do | |
27 | + today = Date.today | |
28 | + blog = fast_create(Blog) | |
29 | + step = CommunityTrackPlugin::Step.new(:name => 'Step', :body => 'body', :profile => @profile, :parent => blog, :start_date => today, :end_date => today, :published => true) | |
30 | + assert !step.save | |
31 | + end | |
32 | + | |
33 | + should 'do not allow step creation without a parent' do | |
34 | + today = Date.today | |
35 | + step = CommunityTrackPlugin::Step.new(:name => 'Step', :body => 'body', :profile => @profile, :parent => nil, :start_date => today, :end_date => today, :published => true) | |
36 | + assert !step.save | |
37 | + end | |
38 | + | |
39 | + should 'create step if end date is equal to start date' do | |
40 | + @step.start_date = Date.today | |
41 | + @step.end_date = Date.today | |
42 | + assert @step.save | |
43 | + end | |
44 | + | |
45 | + should 'create step if end date is after start date' do | |
46 | + @step.start_date = Date.today | |
47 | + @step.end_date = Date.today + 1.day | |
48 | + assert @step.save | |
49 | + end | |
50 | + | |
51 | + should 'do not create step if end date is before start date' do | |
52 | + @step.start_date = Date.today | |
53 | + @step.end_date = Date.today - 1.day | |
54 | + assert !@step.save | |
55 | + end | |
56 | + | |
57 | + should 'do not validate date period if start date is nil' do | |
58 | + @step.start_date = nil | |
59 | + @step.end_date_equal_or_after_start_date.inspect | |
60 | + assert [], @step.errors | |
61 | + end | |
62 | + | |
63 | + should 'do not validate date period if end date is nil' do | |
64 | + @step.end_date = nil | |
65 | + @step.end_date_equal_or_after_start_date.inspect | |
66 | + assert [], @step.errors | |
67 | + end | |
68 | + | |
69 | + should 'be active if today is between start and end dates' do | |
70 | + @step.start_date = Date.today | |
71 | + @step.end_date = Date.today + 1.day | |
72 | + assert @step.active? | |
73 | + end | |
74 | + | |
75 | + should 'be finished if today is after the end date' do | |
76 | + @step.start_date = Date.today - 2.day | |
77 | + @step.end_date = Date.today - 1.day | |
78 | + assert @step.finished? | |
79 | + end | |
80 | + | |
81 | + should 'be waiting if today is before the end date' do | |
82 | + @step.start_date = Date.today + 1.day | |
83 | + @step.end_date = Date.today + 2.day | |
84 | + assert @step.waiting? | |
85 | + end | |
86 | + | |
87 | + should 'return delayed job created with a specific step_id' do | |
88 | + step_id = 0 | |
89 | + CommunityTrackPlugin::ActivationJob.new(step_id) | |
90 | + assert CommunityTrackPlugin::ActivationJob.find(step_id) | |
91 | + end | |
92 | + | |
93 | + should 'create delayed job' do | |
94 | + @step.start_date = Date.today | |
95 | + @step.end_date = Date.today | |
96 | + @step.schedule_activation | |
97 | + assert_equal 1, Delayed::Job.count | |
98 | + assert_equal @step.start_date, Delayed::Job.first.run_at.to_date | |
99 | + end | |
100 | + | |
101 | + should 'do not duplicate delayed job' do | |
102 | + @step.start_date = Date.today | |
103 | + @step.end_date = Date.today | |
104 | + @step.schedule_activation | |
105 | + @step.schedule_activation | |
106 | + assert_equal 1, Delayed::Job.count | |
107 | + end | |
108 | + | |
109 | + should 'create delayed job when a step is saved' do | |
110 | + @step.start_date = Date.today | |
111 | + @step.end_date = Date.today | |
112 | + @step.save! | |
113 | + assert_equal @step.start_date, Delayed::Job.first.run_at.to_date | |
114 | + end | |
115 | + | |
116 | + should 'create delayed job even if start date has passed' do | |
117 | + @step.start_date = Date.today - 2.days | |
118 | + @step.end_date = Date.today | |
119 | + @step.schedule_activation | |
120 | + assert_equal @step.start_date, Delayed::Job.first.run_at.to_date | |
121 | + end | |
122 | + | |
123 | + should 'do not create delayed job if end date has passed and step is not published' do | |
124 | + @step.start_date = Date.today - 5.days | |
125 | + @step.end_date = Date.today - 2.days | |
126 | + @step.published = false | |
127 | + @step.schedule_activation | |
128 | + assert_equal 0, Delayed::Job.count | |
129 | + end | |
130 | + | |
131 | + should 'create delayed job if end date has passed and step is published' do | |
132 | + @step.start_date = Date.today - 5.days | |
133 | + @step.end_date = Date.today - 2.days | |
134 | + @step.published = true | |
135 | + @step.schedule_activation | |
136 | + assert_equal @step.end_date + 1.day, Delayed::Job.first.run_at.to_date | |
137 | + end | |
138 | + | |
139 | + should 'change publish to true on perform delayed job in a active step' do | |
140 | + @step.start_date = Date.today | |
141 | + @step.end_date = Date.today + 2.days | |
142 | + @step.published = false | |
143 | + @step.save! | |
144 | + CommunityTrackPlugin::ActivationJob.new(@step.id).perform | |
145 | + @step.reload | |
146 | + assert @step.published | |
147 | + end | |
148 | + | |
149 | + should 'reschedule delayed job after change publish to true' do | |
150 | + @step.start_date = Date.today | |
151 | + @step.end_date = Date.today + 2.days | |
152 | + @step.published = false | |
153 | + @step.save! | |
154 | + assert_equal @step.start_date, Delayed::Job.first.run_at.to_date | |
155 | + process_delayed_job_queue | |
156 | + assert_equal @step.end_date + 1.day, Delayed::Job.first.run_at.to_date | |
157 | + end | |
158 | + | |
159 | + should 'do not schedule delayed job if save but do not modify date fields and published status' do | |
160 | + @step.start_date = Date.today | |
161 | + @step.end_date = Date.today | |
162 | + @step.published = false | |
163 | + @step.save! | |
164 | + assert_equal 1, Delayed::Job.count | |
165 | + Delayed::Job.destroy_all | |
166 | + @step.name = 'changed name' | |
167 | + @step.save! | |
168 | + assert_equal 0, Delayed::Job.count | |
169 | + end | |
170 | + | |
171 | + should 'set position on save' do | |
172 | + assert !@step.position | |
173 | + @step.save! | |
174 | + assert_equal 1, @step.position | |
175 | + step2 = CommunityTrackPlugin::Step.new(:name => 'Step2', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | |
176 | + step2.save! | |
177 | + assert_equal 2, step2.position | |
178 | + end | |
179 | + | |
180 | + should 'publish step if it is active' do | |
181 | + @step.start_date = Date.today | |
182 | + @step.save! | |
183 | + assert !@step.published | |
184 | + @step.publish | |
185 | + @step.reload | |
186 | + assert @step.published | |
187 | + end | |
188 | + | |
189 | + should 'do not publish step if it is not active' do | |
190 | + @step.start_date = Date.today + 2.days | |
191 | + @step.end_date = Date.today + 3.days | |
192 | + @step.save! | |
193 | + assert !@step.published | |
194 | + @step.publish | |
195 | + @step.reload | |
196 | + assert !@step.published | |
197 | + end | |
198 | + | |
199 | + should 'unpublish step if it is not active anymore' do | |
200 | + @step.start_date = Date.today | |
201 | + @step.save! | |
202 | + @step.publish | |
203 | + @step.reload | |
204 | + assert @step.published | |
205 | + | |
206 | + @step.start_date = Date.today - 2.days | |
207 | + @step.end_date = Date.today - 1.day | |
208 | + @step.save! | |
209 | + @step.publish | |
210 | + @step.reload | |
211 | + assert !@step.published | |
212 | + end | |
213 | + | |
214 | + should 'set position to zero if step is hidden' do | |
215 | + @step.hidden = true | |
216 | + @step.save! | |
217 | + assert_equal 0, @step.position | |
218 | + end | |
219 | + | |
220 | + should 'change position to zero if step becomes hidden' do | |
221 | + @step.save! | |
222 | + assert_equal 1, @step.position | |
223 | + @step.hidden = true | |
224 | + @step.save! | |
225 | + assert_equal 0, @step.position | |
226 | + end | |
227 | + | |
228 | + should 'change position to botton if a hidden step becomes visible' do | |
229 | + step1 = CommunityTrackPlugin::Step.new(:name => 'Step1', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | |
230 | + step1.save! | |
231 | + @step.hidden = true | |
232 | + @step.save! | |
233 | + assert_equal 0, @step.position | |
234 | + @step.hidden = false | |
235 | + @step.save! | |
236 | + assert_equal 2, @step.position | |
237 | + end | |
238 | + | |
239 | + should 'decrement lower items positions if a step becomes hidden' do | |
240 | + @step.save! | |
241 | + step1 = CommunityTrackPlugin::Step.new(:name => 'Step1', :body => 'body', :profile => @profile, :parent => @track, :published => false, :end_date => Date.today, :start_date => Date.today) | |
242 | + step1.save! | |
243 | + assert_equal 2, step1.position | |
244 | + @step.hidden = true | |
245 | + @step.save! | |
246 | + step1.reload | |
247 | + assert_equal 1, step1.position | |
248 | + end | |
249 | + | |
250 | + should 'do not publish a hidden step' do | |
251 | + @step.start_date = Date.today | |
252 | + @step.hidden = true | |
253 | + @step.save! | |
254 | + assert !@step.published | |
255 | + @step.publish | |
256 | + @step.reload | |
257 | + assert !@step.published | |
258 | + end | |
259 | + | |
260 | + should 'return enabled tools for a step' do | |
261 | + assert_includes @step.enabled_tools, TinyMceArticle | |
262 | + assert_includes @step.enabled_tools, Forum | |
263 | + end | |
264 | + | |
265 | +end | ... | ... |