Commit e77ff7f178fb7f21b2a9994a2df04fe2d1f9c9f7

Authored by Victor Costa
2 parents 419fe2e5 ba1e746f

Merge branch 'rails3' into rails3_stable

Showing 77 changed files with 2666 additions and 480 deletions   Show diff stats
app/controllers/admin/environment_design_controller.rb
@@ -11,7 +11,7 @@ class EnvironmentDesignController < BoxOrganizerController @@ -11,7 +11,7 @@ class EnvironmentDesignController < BoxOrganizerController
11 def available_blocks 11 def available_blocks
12 # TODO EnvironmentStatisticsBlock is DEPRECATED and will be removed from 12 # TODO EnvironmentStatisticsBlock is DEPRECATED and will be removed from
13 # the Noosfero core soon, see ActionItem3045 13 # the Noosfero core soon, see ActionItem3045
14 - @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ] 14 + @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ]
15 @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment) 15 @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment)
16 end 16 end
17 17
app/controllers/my_profile/cms_controller.rb
@@ -144,6 +144,7 @@ class CmsController < MyProfileController @@ -144,6 +144,7 @@ class CmsController < MyProfileController
144 144
145 @article.profile = profile 145 @article.profile = profile
146 @article.last_changed_by = user 146 @article.last_changed_by = user
  147 + @article.created_by = user
147 148
148 translations if @article.translatable? 149 translations if @article.translatable?
149 150
app/controllers/my_profile/profile_design_controller.rb
@@ -15,14 +15,8 @@ class ProfileDesignController < BoxOrganizerController @@ -15,14 +15,8 @@ class ProfileDesignController < BoxOrganizerController
15 15
16 blocks += plugins.dispatch(:extra_blocks) 16 blocks += plugins.dispatch(:extra_blocks)
17 17
18 - # blocks exclusive for organizations  
19 - if profile.has_members?  
20 - blocks << MembersBlock  
21 - end  
22 -  
23 # blocks exclusive to people 18 # blocks exclusive to people
24 if profile.person? 19 if profile.person?
25 - blocks << FriendsBlock  
26 blocks << FavoriteEnterprisesBlock 20 blocks << FavoriteEnterprisesBlock
27 blocks << CommunitiesBlock 21 blocks << CommunitiesBlock
28 blocks << EnterprisesBlock 22 blocks << EnterprisesBlock
app/helpers/sweeper_helper.rb
@@ -18,9 +18,7 @@ module SweeperHelper @@ -18,9 +18,7 @@ module SweeperHelper
18 expire_timeout_fragment(profile.manage_friends_cache_key(:npage => i.to_s)) 18 expire_timeout_fragment(profile.manage_friends_cache_key(:npage => i.to_s))
19 end 19 end
20 20
21 - # friends blocks  
22 - blocks = profile.blocks.select{|b| b.kind_of?(FriendsBlock)}  
23 - BlockSweeper.expire_blocks(blocks) 21 + expire_blocks_cache(profile, [:profile])
24 end 22 end
25 23
26 def expire_communities(profile) 24 def expire_communities(profile)
app/models/approve_article.rb
@@ -48,7 +48,7 @@ class ApproveArticle &lt; Task @@ -48,7 +48,7 @@ class ApproveArticle &lt; Task
48 end 48 end
49 49
50 def perform 50 def perform
51 - article.copy!(:name => name, :abstract => abstract, :body => body, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted, :source => article.source, :last_changed_by_id => article.author_id) 51 + article.copy!(:name => name, :abstract => abstract, :body => body, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted, :source => article.source, :last_changed_by_id => article.last_changed_by_id, :created_by_id => article.created_by_id)
52 end 52 end
53 53
54 def title 54 def title
app/models/article.rb
@@ -40,12 +40,6 @@ class Article &lt; ActiveRecord::Base @@ -40,12 +40,6 @@ class Article &lt; ActiveRecord::Base
40 # xss_terminate plugin can't sanitize array fields 40 # xss_terminate plugin can't sanitize array fields
41 before_save :sanitize_tag_list 41 before_save :sanitize_tag_list
42 42
43 - before_create do |article|  
44 - if article.last_changed_by_id  
45 - article.author_name = Person.find(article.last_changed_by_id).name  
46 - end  
47 - end  
48 -  
49 belongs_to :profile 43 belongs_to :profile
50 validates_presence_of :profile_id, :name 44 validates_presence_of :profile_id, :name
51 validates_presence_of :slug, :path, :if => lambda { |article| !article.name.blank? } 45 validates_presence_of :slug, :path, :if => lambda { |article| !article.name.blank? }
@@ -55,6 +49,7 @@ class Article &lt; ActiveRecord::Base @@ -55,6 +49,7 @@ class Article &lt; ActiveRecord::Base
55 validates_uniqueness_of :slug, :scope => ['profile_id', 'parent_id'], :message => N_('The title (article name) is already being used by another article, please use another title.'), :if => lambda { |article| !article.slug.blank? } 49 validates_uniqueness_of :slug, :scope => ['profile_id', 'parent_id'], :message => N_('The title (article name) is already being used by another article, please use another title.'), :if => lambda { |article| !article.slug.blank? }
56 50
57 belongs_to :last_changed_by, :class_name => 'Person', :foreign_key => 'last_changed_by_id' 51 belongs_to :last_changed_by, :class_name => 'Person', :foreign_key => 'last_changed_by_id'
  52 + belongs_to :created_by, :class_name => 'Person', :foreign_key => 'created_by_id'
58 53
59 has_many :comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc' 54 has_many :comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc'
60 55
@@ -89,6 +84,11 @@ class Article &lt; ActiveRecord::Base @@ -89,6 +84,11 @@ class Article &lt; ActiveRecord::Base
89 article.parent = article.profile.blog 84 article.parent = article.profile.blog
90 end 85 end
91 end 86 end
  87 +
  88 + if article.created_by
  89 + article.author_name = article.created_by.name
  90 + end
  91 +
92 end 92 end
93 93
94 after_destroy :destroy_activity 94 after_destroy :destroy_activity
@@ -215,6 +215,10 @@ class Article &lt; ActiveRecord::Base @@ -215,6 +215,10 @@ class Article &lt; ActiveRecord::Base
215 acts_as_versioned 215 acts_as_versioned
216 self.non_versioned_columns << 'setting' 216 self.non_versioned_columns << 'setting'
217 217
  218 + def version_condition_met?
  219 + (['name', 'body', 'abstract', 'filename', 'start_date', 'end_date', 'image_id', 'license_id'] & changed).length > 0
  220 + end
  221 +
218 def comment_data 222 def comment_data
219 comments.map {|item| [item.title, item.body].join(' ') }.join(' ') 223 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
220 end 224 end
@@ -640,17 +644,13 @@ class Article &lt; ActiveRecord::Base @@ -640,17 +644,13 @@ class Article &lt; ActiveRecord::Base
640 644
641 def author(version_number = nil) 645 def author(version_number = nil)
642 if version_number 646 if version_number
643 - version = versions.find_by_version(version_number) 647 + version = self.versions.find_by_version(version_number)
644 author_id = version.last_changed_by_id if version 648 author_id = version.last_changed_by_id if version
645 - Person.exists?(author_id) ? Person.find(author_id) : nil  
646 else 649 else
647 - if versions.empty?  
648 - last_changed_by  
649 - else  
650 - author_id = versions.first.last_changed_by_id  
651 - Person.exists?(author_id) ? Person.find(author_id) : nil  
652 - end 650 + author_id = self.created_by_id
653 end 651 end
  652 +
  653 + environment.people.find_by_id(author_id)
654 end 654 end
655 655
656 def author_name(version_number = nil) 656 def author_name(version_number = nil)
app/models/box.rb
@@ -34,14 +34,11 @@ class Box &lt; ActiveRecord::Base @@ -34,14 +34,11 @@ class Box &lt; ActiveRecord::Base
34 FansBlock, 34 FansBlock,
35 FavoriteEnterprisesBlock, 35 FavoriteEnterprisesBlock,
36 FeedReaderBlock, 36 FeedReaderBlock,
37 - FriendsBlock,  
38 HighlightsBlock, 37 HighlightsBlock,
39 LinkListBlock, 38 LinkListBlock,
40 LoginBlock, 39 LoginBlock,
41 MainBlock, 40 MainBlock,
42 - MembersBlock,  
43 MyNetworkBlock, 41 MyNetworkBlock,
44 - PeopleBlock,  
45 ProfileImageBlock, 42 ProfileImageBlock,
46 RawHTMLBlock, 43 RawHTMLBlock,
47 RecentDocumentsBlock, 44 RecentDocumentsBlock,
@@ -63,14 +60,11 @@ class Box &lt; ActiveRecord::Base @@ -63,14 +60,11 @@ class Box &lt; ActiveRecord::Base
63 FavoriteEnterprisesBlock, 60 FavoriteEnterprisesBlock,
64 FeaturedProductsBlock, 61 FeaturedProductsBlock,
65 FeedReaderBlock, 62 FeedReaderBlock,
66 - FriendsBlock,  
67 HighlightsBlock, 63 HighlightsBlock,
68 LinkListBlock, 64 LinkListBlock,
69 LocationBlock, 65 LocationBlock,
70 LoginBlock, 66 LoginBlock,
71 - MembersBlock,  
72 MyNetworkBlock, 67 MyNetworkBlock,
73 - PeopleBlock,  
74 ProductsBlock, 68 ProductsBlock,
75 ProductCategoriesBlock, 69 ProductCategoriesBlock,
76 ProfileImageBlock, 70 ProfileImageBlock,
app/models/community.rb
@@ -85,10 +85,6 @@ class Community &lt; Organization @@ -85,10 +85,6 @@ class Community &lt; Organization
85 recent_documents(limit, ["articles.type != ? AND articles.highlighted = ?", 'Folder', highlight]) 85 recent_documents(limit, ["articles.type != ? AND articles.highlighted = ?", 'Folder', highlight])
86 end 86 end
87 87
88 - def blocks_to_expire_cache  
89 - [MembersBlock]  
90 - end  
91 -  
92 def each_member(offset=0) 88 def each_member(offset=0)
93 while member = self.members.first(:order => :id, :offset => offset) 89 while member = self.members.first(:order => :id, :offset => offset)
94 yield member 90 yield member
app/models/environment.rb
@@ -182,7 +182,6 @@ class Environment &lt; ActiveRecord::Base @@ -182,7 +182,6 @@ class Environment &lt; ActiveRecord::Base
182 182
183 # "right" area 183 # "right" area
184 env.boxes[2].blocks << CommunitiesBlock.new(:limit => 6) 184 env.boxes[2].blocks << CommunitiesBlock.new(:limit => 6)
185 - env.boxes[2].blocks << PeopleBlock.new(:limit => 6)  
186 end 185 end
187 186
188 # One Environment can be reached by many domains 187 # One Environment can be reached by many domains
app/models/environment.rb.orig 0 → 100644
@@ -0,0 +1,938 @@ @@ -0,0 +1,938 @@
  1 +# A Environment is like a website to be hosted in the platform. It may
  2 +# contain multiple Profile's and can be identified by several different
  3 +# domains.
  4 +class Environment < ActiveRecord::Base
  5 +
  6 +<<<<<<< HEAD
  7 + attr_accessible :name, :is_default, :signup_welcome_text_subject, :signup_welcome_text_body, :terms_of_use, :message_for_disabled_enterprise, :news_amount_by_folder, :default_language, :languages, :description, :organization_approval_method, :enabled_plugins, :enabled_features, :enabled_blocks, :redirection_after_login, :redirection_after_signup, :contact_email, :theme, :reports_lower_bound, :noreply_email, :signup_welcome_screen_body
  8 +=======
  9 + attr_accessible :name, :is_default, :signup_welcome_text_subject, :signup_welcome_text_body, :terms_of_use, :message_for_disabled_enterprise, :news_amount_by_folder, :default_language, :languages, :description, :organization_approval_method, :enabled_plugins, :enabled_features, :disabled_blocks, :redirection_after_login, :redirection_after_signup, :contact_email, :theme, :reports_lower_bound
  10 +>>>>>>> rails3_AI3163-enable_disable_blocks
  11 +
  12 + has_many :users
  13 +
  14 + self.partial_updates = false
  15 +
  16 + has_many :tasks, :dependent => :destroy, :as => 'target'
  17 +
  18 + IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/
  19 +
  20 + def self.verify_filename(filename)
  21 + filename += '.txt' if File.extname(filename) =~ IDENTIFY_SCRIPTS
  22 + filename
  23 + end
  24 +
  25 + PERMISSIONS['Environment'] = {
  26 + 'view_environment_admin_panel' => N_('View environment admin panel'),
  27 + 'edit_environment_features' => N_('Edit environment features'),
  28 + 'edit_environment_design' => N_('Edit environment design'),
  29 + 'manage_environment_categories' => N_('Manage environment categories'),
  30 + 'manage_environment_roles' => N_('Manage environment roles'),
  31 + 'manage_environment_validators' => N_('Manage environment validators'),
  32 + 'manage_environment_users' => N_('Manage environment users'),
  33 + 'manage_environment_templates' => N_('Manage environment templates'),
  34 + 'manage_environment_licenses' => N_('Manage environment licenses'),
  35 + 'manage_environment_trusted_sites' => N_('Manage environment trusted sites'),
  36 + 'edit_appearance' => N_('Edit appearance'),
  37 + }
  38 +
  39 + module Roles
  40 + def self.admin(env_id)
  41 + Role.find_by_key_and_environment_id('environment_administrator', env_id)
  42 + end
  43 + end
  44 +
  45 + after_create :create_roles
  46 + def create_roles
  47 + Role.create!(
  48 + :key => 'environment_administrator',
  49 + :name => N_('Environment Administrator'),
  50 + :environment => self,
  51 + :permissions => PERMISSIONS[Environment.name].keys + PERMISSIONS[Profile.name].keys
  52 + )
  53 + Role.create!(
  54 + :key => 'profile_admin',
  55 + :name => N_('Profile Administrator'),
  56 + :environment => self,
  57 + :permissions => PERMISSIONS[Profile.name].keys
  58 + )
  59 + # members for enterprises, communities etc
  60 + Role.create!(
  61 + :key => "profile_member",
  62 + :name => N_('Member'),
  63 + :environment => self,
  64 + :permissions => [
  65 + 'invite_members',
  66 + ]
  67 + )
  68 + # moderators for enterprises, communities etc
  69 + Role.create!(
  70 + :key => 'profile_moderator',
  71 + :name => N_('Moderator'),
  72 + :environment => self,
  73 + :permissions => [
  74 + 'manage_memberships',
  75 + 'edit_profile_design',
  76 + 'manage_products',
  77 + 'manage_friends',
  78 + 'perform_task'
  79 + ]
  80 + )
  81 + end
  82 +
  83 + def add_admin(user)
  84 + self.affiliate(user, Environment::Roles.admin(self.id))
  85 + end
  86 +
  87 + def remove_admin(user)
  88 + self.disaffiliate(user, Environment::Roles.admin(self.id))
  89 + end
  90 +
  91 + def admins
  92 + Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', Environment::Roles.admin(self).id])
  93 + end
  94 +
  95 + # returns the available features for a Environment, in the form of a
  96 + # hash, with pairs in the form <tt>'feature_name' => 'Feature name'</tt>.
  97 + def self.available_features
  98 + {
  99 + 'disable_asset_articles' => _('Disable search for articles '),
  100 + 'disable_asset_enterprises' => _('Disable search for enterprises'),
  101 + 'disable_asset_people' => _('Disable search for people'),
  102 + 'disable_asset_communities' => _('Disable search for communities'),
  103 + 'disable_asset_products' => _('Disable search for products'),
  104 + 'disable_asset_events' => _('Disable search for events'),
  105 + 'disable_categories' => _('Disable categories'),
  106 + 'disable_header_and_footer' => _('Disable header/footer editing by users'),
  107 + 'disable_gender_icon' => _('Disable gender icon'),
  108 + 'disable_categories_menu' => _('Disable the categories menu'),
  109 + 'disable_select_city_for_contact' => _('Disable state/city select for contact form'),
  110 + 'disable_contact_person' => _('Disable contact for people'),
  111 + 'disable_contact_community' => _('Disable contact for groups/communities'),
  112 +
  113 + 'products_for_enterprises' => _('Enable products for enterprises'),
  114 + 'enterprise_registration' => _('Enterprise registration'),
  115 + 'enterprise_activation' => _('Enable activation of enterprises'),
  116 + 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'),
  117 + 'enterprises_are_validated_when_created' => _('Enterprises are validated when created'),
  118 +
  119 + 'media_panel' => _('Media panel in WYSIWYG editor'),
  120 + 'select_preferred_domain' => _('Select preferred domains per profile'),
  121 + 'use_portal_community' => _('Use the portal as news source for front page'),
  122 + 'user_themes' => _('Allow users to create their own themes'),
  123 + 'search_in_home' => _("Display search form in home page"),
  124 +
  125 + 'cant_change_homepage' => _("Don't allow users to change which article to use as homepage"),
  126 + 'display_header_footer_explanation' => _("Display explanation about header and footer"),
  127 + 'articles_dont_accept_comments_by_default' => _("Articles don't accept comments by default"),
  128 + 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"),
  129 + 'enable_organization_url_change' => _("Allow organizations to change their URL"),
  130 + 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"),
  131 + 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'),
  132 + 'xmpp_chat' => _('XMPP/Jabber based chat'),
  133 + 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images'),
  134 + 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'),
  135 + 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'),
  136 + 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'),
  137 + 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login'),
  138 + 'display_my_communities_on_user_menu' => _('Display on menu the list of communities the user can manage'),
  139 + 'display_my_enterprises_on_user_menu' => _('Display on menu the list of enterprises the user can manage')
  140 + }
  141 + end
  142 +
  143 + def self.login_redirection_options
  144 + {
  145 + 'keep_on_same_page' => _('Stays on the same page the user was before login.'),
  146 + 'site_homepage' => _('Redirects the user to the environment homepage.'),
  147 + 'user_profile_page' => _('Redirects the user to his profile page.'),
  148 + 'user_homepage' => _('Redirects the user to his homepage.'),
  149 + 'user_control_panel' => _('Redirects the user to his control panel.')
  150 + }
  151 + end
  152 + validates_inclusion_of :redirection_after_login, :in => Environment.login_redirection_options.keys, :allow_nil => true
  153 +
  154 + def self.signup_redirection_options
  155 + {
  156 + 'keep_on_same_page' => _('Stays on the same page the user was before signup.'),
  157 + 'site_homepage' => _('Redirects the user to the environment homepage.'),
  158 + 'user_profile_page' => _('Redirects the user to his profile page.'),
  159 + 'user_homepage' => _('Redirects the user to his homepage.'),
  160 + 'user_control_panel' => _('Redirects the user to his control panel.')
  161 + }
  162 + end
  163 + validates_inclusion_of :redirection_after_signup, :in => Environment.signup_redirection_options.keys, :allow_nil => true
  164 +
  165 +
  166 + # #################################################
  167 + # Relationships and applied behaviour
  168 + # #################################################
  169 +
  170 + acts_as_having_boxes
  171 +
  172 + after_create do |env|
  173 + 3.times do
  174 + env.boxes << Box.new
  175 + end
  176 +
  177 + # main area
  178 + env.boxes[0].blocks << MainBlock.new
  179 +
  180 + # "left" area
  181 + env.boxes[1].blocks << LoginBlock.new
  182 + # TODO EnvironmentStatisticsBlock is DEPRECATED and will be removed from
  183 + # the Noosfero core soon, see ActionItem3045
  184 + env.boxes[1].blocks << EnvironmentStatisticsBlock.new
  185 + env.boxes[1].blocks << RecentDocumentsBlock.new
  186 +
  187 + # "right" area
  188 + env.boxes[2].blocks << CommunitiesBlock.new(:limit => 6)
  189 + env.boxes[2].blocks << PeopleBlock.new(:limit => 6)
  190 + end
  191 +
  192 + # One Environment can be reached by many domains
  193 + has_many :domains, :as => :owner
  194 + has_many :profiles, :dependent => :destroy
  195 +
  196 + has_many :organizations
  197 + has_many :enterprises
  198 + has_many :products, :through => :enterprises
  199 + has_many :people
  200 + has_many :communities
  201 + has_many :licenses
  202 +
  203 + has_many :categories
  204 + has_many :display_categories, :class_name => 'Category', :conditions => 'display_color is not null and parent_id is null', :order => 'display_color'
  205 +
  206 + has_many :product_categories, :conditions => { :type => 'ProductCategory'}
  207 + has_many :regions
  208 + has_many :states
  209 + has_many :cities
  210 +
  211 + has_many :roles, :dependent => :destroy
  212 +
  213 + has_many :qualifiers
  214 + has_many :certifiers
  215 +
  216 + has_many :mailings, :class_name => 'EnvironmentMailing', :foreign_key => :source_id, :as => 'source'
  217 +
  218 + acts_as_accessible
  219 +
  220 + has_many :units, :order => 'position'
  221 + has_many :production_costs, :as => :owner
  222 +
  223 + def superior_intances
  224 + [self, nil]
  225 + end
  226 + # #################################################
  227 + # Attributes
  228 + # #################################################
  229 +
  230 + # store the Environment settings as YAML-serialized Hash.
  231 + acts_as_having_settings :field => :settings
  232 +
  233 + # the environment's terms of use: every user must accept them before registering.
  234 + settings_items :terms_of_use, :type => String
  235 +
  236 + # the environment's terms of enterprise use: every enterprise member must accept them before
  237 + # registering or activating enterprises.
  238 + settings_items :terms_of_enterprise_use, :type => String
  239 +
  240 + # returns the approval method used for this environment. Possible values are:
  241 + #
  242 + # Defaults to <tt>:admim</tt>.
  243 + settings_items :organization_approval_method, :type => Symbol, :default => :admin
  244 +
  245 + # Whether this environment should force having 'www.' in its domain name or
  246 + # not. Defauls to false.
  247 + #
  248 + # Sets the value of #force_www. <tt>value</tt> must be a boolean.
  249 + #
  250 + # See also #default_hostname
  251 + settings_items :force_www, :default => false
  252 +
  253 + settings_items :message_for_friend_invitation, :type => String
  254 + def message_for_friend_invitation
  255 + settings[:message_for_member_invitation] || InviteFriend.mail_template
  256 + end
  257 +
  258 + settings_items :message_for_member_invitation, :type => String
  259 + def message_for_member_invitation
  260 + settings[:message_for_member_invitation] || InviteMember.mail_template
  261 + end
  262 +
  263 + settings_items :min_signup_delay, :type => Integer, :default => 3 #seconds
  264 + settings_items :activation_blocked_text, :type => String
  265 + settings_items :message_for_disabled_enterprise, :type => String,
  266 + :default => _('This enterprise needs to be enabled.')
  267 + settings_items :location, :type => String
  268 + settings_items :layout_template, :type => String, :default => 'default'
  269 + settings_items :homepage, :type => String
  270 + settings_items :description, :type => String, :default => '<div style="text-align: center"><a href="http://noosfero.org/"><img src="/images/noosfero-network.png" alt="Noosfero"/></a></div>'
  271 + settings_items :local_docs, :type => Array, :default => []
  272 + settings_items :news_amount_by_folder, :type => Integer, :default => 4
  273 + settings_items :help_message_to_add_enterprise, :type => String, :default => ''
  274 + settings_items :tip_message_enterprise_activation_question, :type => String, :default => ''
  275 +
  276 + settings_items :currency_unit, :type => String, :default => '$'
  277 + settings_items :currency_separator, :type => String, :default => '.'
  278 + settings_items :currency_delimiter, :type => String, :default => ','
  279 +
  280 + settings_items :trusted_sites_for_iframe, :type => Array, :default => %w[
  281 + developer.myspace.com
  282 + itheora.org
  283 + maps.google.com
  284 + platform.twitter.com
  285 + player.vimeo.com
  286 + stream.softwarelivre.org
  287 + tv.softwarelivre.org
  288 + www.facebook.com
  289 + www.flickr.com
  290 + www.gmodules.com
  291 + www.youtube.com
  292 + ] + ('a' .. 'z').map{|i| "#{i}.yimg.com"}
  293 +
  294 + settings_items :enabled_plugins, :type => Array, :default => []
  295 +
  296 + settings_items :disabled_blocks, :type => Array, :default => []
  297 +
  298 + settings_items :search_hints, :type => Hash, :default => {}
  299 +
  300 + # Set to return http forbidden to host not on the allow origin list bellow
  301 + settings_items :restrict_to_access_control_origins, :default => false
  302 + # Set this according to http://www.w3.org/TR/cors/. Headers are set at every response
  303 + # For multiple domains acts as suggested in http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains
  304 + settings_items :access_control_allow_origin, :type => Array, :default => []
  305 + settings_items :access_control_allow_methods, :type => String
  306 +
  307 + settings_items :signup_welcome_screen_body, :type => String
  308 +
  309 + def has_custom_welcome_screen?
  310 + settings[:signup_welcome_screen_body].present?
  311 + end
  312 +
  313 + def news_amount_by_folder=(amount)
  314 + settings[:news_amount_by_folder] = amount.to_i
  315 + end
  316 +
  317 + # Enables a feature identified by its name
  318 + def enable(feature, must_save=true)
  319 + self.settings["#{feature}_enabled".to_sym] = true
  320 + self.save! if must_save
  321 + end
  322 +
  323 + def enable_plugin(plugin)
  324 + self.enabled_plugins += [plugin.to_s]
  325 + self.enabled_plugins.uniq!
  326 + self.save!
  327 + end
  328 +
  329 + # Disables a feature identified by its name
  330 + def disable(feature, must_save=true)
  331 + self.settings["#{feature}_enabled".to_sym] = false
  332 + self.save! if must_save
  333 + end
  334 +
  335 + def disable_plugin(plugin)
  336 + self.enabled_plugins.delete(plugin.to_s)
  337 + self.save!
  338 + end
  339 +
  340 + # Tells if a feature, identified by its name, is enabled
  341 + def enabled?(feature)
  342 + self.settings["#{feature}_enabled".to_sym] == true
  343 + end
  344 + def disabled?(feature)
  345 + !self.enabled?(feature)
  346 + end
  347 +
  348 + def plugin_enabled?(plugin)
  349 + enabled_plugins.include?(plugin.to_s)
  350 + end
  351 +
  352 + def block_disabled?(block)
  353 + disabled_blocks.include?(block.to_s)
  354 + end
  355 +
  356 + # enables the features identified by <tt>features</tt>, which is expected to
  357 + # be an Enumarable object containing the identifiers of the desired features.
  358 + # Passing <tt>nil</tt> is the same as passing an empty Array.
  359 + def enabled_features=(features)
  360 + features ||= []
  361 + self.class.available_features.keys.each do |feature|
  362 + if features.include? feature
  363 + self.enable(feature)
  364 + else
  365 + self.disable(feature)
  366 + end
  367 + end
  368 + end
  369 +
  370 + def enabled_features
  371 + features = self.class.available_features
  372 + features.delete_if{ |k, v| !self.enabled?(k) }
  373 + end
  374 +
  375 + DEFAULT_FEATURES = %w(
  376 + disable_asset_products
  377 + disable_gender_icon
  378 + products_for_enterprises
  379 + disable_select_city_for_contact
  380 + enterprise_registration
  381 + media_panel
  382 + organizations_are_moderated_by_default
  383 + show_balloon_with_profile_links_when_clicked
  384 + show_zoom_button_on_article_images
  385 + use_portal_community
  386 + )
  387 +
  388 + before_create :enable_default_features
  389 + def enable_default_features
  390 + DEFAULT_FEATURES.each do |feature|
  391 + enable(feature, false)
  392 + end
  393 + end
  394 +
  395 + # returns <tt>true</tt> if this Environment has terms of use to be
  396 + # accepted by users before registration.
  397 + def has_terms_of_use?
  398 + ! self.terms_of_use.blank?
  399 + end
  400 +
  401 + # returns <tt>true</tt> if this Environment has terms of enterprise use to be
  402 + # accepted by users before registration or activation of enterprises.
  403 + def has_terms_of_enterprise_use?
  404 + ! self.terms_of_enterprise_use.blank?
  405 + end
  406 +
  407 + # Sets the organization_approval_method. Only accepts the following values:
  408 + #
  409 + # * <tt>:admin</tt>: organization registration must be approved by the
  410 + # environment administrator.
  411 + # * <tt>:region</tt>: organization registering must be approved by some other
  412 + # organization asssigned as validator to the Region the new organization
  413 + # belongs to.
  414 + # * <tt>:none</tt>: organization registration is approved by default.
  415 + #
  416 + # Trying to set organization_approval_method to any other value will raise an
  417 + # ArgumentError.
  418 + #
  419 + # The value passed as argument is converted to a Symbol before being actually
  420 + # set to this setting.
  421 + def organization_approval_method=(value)
  422 + actual_value = value.to_sym
  423 +
  424 + accepted_values = %w[
  425 + admin
  426 + region
  427 + none
  428 + ].map(&:to_sym)
  429 + raise ArgumentError unless accepted_values.include?(actual_value)
  430 +
  431 + self.settings[:organization_approval_method] = actual_value
  432 + end
  433 +
  434 + def custom_person_fields
  435 + self.settings[:custom_person_fields].nil? ? {} : self.settings[:custom_person_fields]
  436 + end
  437 +
  438 + def custom_person_fields=(values)
  439 + if values['schooling'] && values['schooling']['active'] == 'true'
  440 + schooling_status = values['schooling']
  441 + end
  442 +
  443 + self.settings[:custom_person_fields] = values.delete_if { |key, value| ! Person.fields.include?(key)}
  444 + self.settings[:custom_person_fields].each_pair do |key, value|
  445 + if value['required'] == 'true'
  446 + self.settings[:custom_person_fields][key]['active'] = 'true'
  447 + self.settings[:custom_person_fields][key]['signup'] = 'true'
  448 + end
  449 + if value['signup'] == 'true'
  450 + self.settings[:custom_person_fields][key]['active'] = 'true'
  451 + end
  452 + end
  453 +
  454 + if schooling_status
  455 + self.settings[:custom_person_fields]['schooling_status'] = schooling_status
  456 + end
  457 + end
  458 +
  459 + def custom_person_field(field, status)
  460 + if (custom_person_fields[field] && custom_person_fields[field][status] == 'true')
  461 + return true
  462 + end
  463 + false
  464 + end
  465 +
  466 + def active_person_fields
  467 + (custom_person_fields.delete_if { |key, value| !custom_person_field(key, 'active')}).keys || []
  468 + end
  469 +
  470 + def required_person_fields
  471 + required_fields = []
  472 + active_person_fields.each do |field|
  473 + required_fields << field if custom_person_fields[field]['required'] == 'true'
  474 + end
  475 + required_fields
  476 + end
  477 +
  478 + def signup_person_fields
  479 + signup_fields = []
  480 + active_person_fields.each do |field|
  481 + signup_fields << field if custom_person_fields[field]['signup'] == 'true'
  482 + end
  483 + signup_fields
  484 + end
  485 +
  486 + def invitation_mail_template(profile)
  487 + if profile.person?
  488 + message_for_friend_invitation
  489 + else
  490 + message_for_member_invitation
  491 + end
  492 + end
  493 +
  494 + def custom_enterprise_fields
  495 + self.settings[:custom_enterprise_fields].nil? ? {} : self.settings[:custom_enterprise_fields]
  496 + end
  497 +
  498 + def custom_enterprise_fields=(values)
  499 + self.settings[:custom_enterprise_fields] = values.delete_if { |key, value| ! Enterprise.fields.include?(key)}
  500 + self.settings[:custom_enterprise_fields].each_pair do |key, value|
  501 + if value['required'] == 'true'
  502 + self.settings[:custom_enterprise_fields][key]['active'] = 'true'
  503 + self.settings[:custom_enterprise_fields][key]['signup'] = 'true'
  504 + end
  505 + if value['signup'] == 'true'
  506 + self.settings[:custom_enterprise_fields][key]['active'] = 'true'
  507 + end
  508 + end
  509 + end
  510 +
  511 + def custom_enterprise_field(field, status)
  512 + if (custom_enterprise_fields[field] && custom_enterprise_fields[field][status] == 'true')
  513 + return true
  514 + end
  515 + false
  516 + end
  517 +
  518 + def active_enterprise_fields
  519 + (custom_enterprise_fields.delete_if { |key, value| !custom_enterprise_field(key, 'active')}).keys || []
  520 + end
  521 +
  522 + def required_enterprise_fields
  523 + required_fields = []
  524 + active_enterprise_fields.each do |field|
  525 + required_fields << field if custom_enterprise_fields[field]['required'] == 'true'
  526 + end
  527 + required_fields
  528 + end
  529 +
  530 + def signup_enterprise_fields
  531 + signup_fields = []
  532 + active_enterprise_fields.each do |field|
  533 + signup_fields << field if custom_enterprise_fields[field]['signup'] == 'true'
  534 + end
  535 + signup_fields
  536 + end
  537 +
  538 + def custom_community_fields
  539 + self.settings[:custom_community_fields].nil? ? {} : self.settings[:custom_community_fields]
  540 + end
  541 + def custom_community_fields=(values)
  542 + self.settings[:custom_community_fields] = values.delete_if { |key, value| ! Community.fields.include?(key) }
  543 + self.settings[:custom_community_fields].each_pair do |key, value|
  544 + if value['required'] == 'true'
  545 + self.settings[:custom_community_fields][key]['active'] = 'true'
  546 + self.settings[:custom_community_fields][key]['signup'] = 'true'
  547 + end
  548 + if value['signup'] == 'true'
  549 + self.settings[:custom_community_fields][key]['active'] = 'true'
  550 + end
  551 + end
  552 + end
  553 +
  554 + def custom_community_field(field, status)
  555 + if (custom_community_fields[field] && custom_community_fields[field][status] == 'true')
  556 + return true
  557 + end
  558 + false
  559 + end
  560 +
  561 + def active_community_fields
  562 + (custom_community_fields.delete_if { |key, value| !custom_community_field(key, 'active')}).keys
  563 + end
  564 +
  565 + def required_community_fields
  566 + required_fields = []
  567 + active_community_fields.each do |field|
  568 + required_fields << field if custom_community_fields[field]['required'] == 'true'
  569 + end
  570 + required_fields
  571 + end
  572 +
  573 + def signup_community_fields
  574 + signup_fields = []
  575 + active_community_fields.each do |field|
  576 + signup_fields << field if custom_community_fields[field]['signup'] == 'true'
  577 + end
  578 + signup_fields
  579 + end
  580 +
  581 + serialize :signup_welcome_text, Hash
  582 + def signup_welcome_text
  583 + self[:signup_welcome_text] ||= {}
  584 + end
  585 +
  586 + def signup_welcome_text_subject
  587 + self.signup_welcome_text[:subject]
  588 + end
  589 +
  590 + def signup_welcome_text_subject=(subject)
  591 + self.signup_welcome_text[:subject] = subject
  592 + end
  593 +
  594 + def signup_welcome_text_body
  595 + self.signup_welcome_text[:body]
  596 + end
  597 +
  598 + def signup_welcome_text_body=(body)
  599 + self.signup_welcome_text[:body] = body
  600 + end
  601 +
  602 + def has_signup_welcome_text?
  603 + signup_welcome_text && !signup_welcome_text_body.blank?
  604 + end
  605 +
  606 + # #################################################
  607 + # Validations
  608 + # #################################################
  609 +
  610 + # <tt>name</tt> is mandatory
  611 + validates_presence_of :name
  612 +
  613 + # only one environment can be the default one
  614 + validates_uniqueness_of :is_default, :if => (lambda do |environment| environment.is_default? end), :message => N_('Only one Virtual Community can be the default one')
  615 +
  616 + validates_format_of :contact_email, :noreply_email, :with => Noosfero::Constants::EMAIL_FORMAT, :allow_blank => true
  617 +
  618 + xss_terminate :only => [ :message_for_disabled_enterprise ], :with => 'white_list', :on => 'validation'
  619 +
  620 + validates_presence_of :theme
  621 + validates_numericality_of :reports_lower_bound, :allow_nil => false, :only_integer => true, :greater_than_or_equal_to => 0
  622 +
  623 + include WhiteListFilter
  624 + filter_iframes :message_for_disabled_enterprise
  625 + def iframe_whitelist
  626 + trusted_sites_for_iframe
  627 + end
  628 +
  629 + # #################################################
  630 + # Business logic in general
  631 + # #################################################
  632 +
  633 + # the default Environment.
  634 + def self.default
  635 + self.find(:first, :conditions => [ 'is_default = ?', true ] )
  636 + end
  637 +
  638 + # returns an array with the top level categories for this environment.
  639 + def top_level_categories
  640 + Category.top_level_for(self)
  641 + end
  642 +
  643 + # Returns the hostname of the first domain associated to this environment.
  644 + #
  645 + # If #force_www is true, adds 'www.' at the beginning of the hostname. If the
  646 + # environment has not associated domains, returns 'localhost'.
  647 + def default_hostname(email_hostname = false)
  648 + domain = 'localhost'
  649 + unless self.domains(true).empty?
  650 + domain = (self.domains.find_by_is_default(true) || self.domains.find(:first, :order => 'id')).name
  651 + domain = email_hostname ? domain : (force_www ? ('www.' + domain) : domain)
  652 + end
  653 + domain
  654 + end
  655 +
  656 + def admin_url
  657 + { :controller => 'admin_panel', :action => 'index' }
  658 + end
  659 +
  660 + def top_url
  661 + url = 'http://'
  662 + url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname)
  663 + url << ':' << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port)
  664 + url
  665 + end
  666 +
  667 + def to_s
  668 + self.name || '?'
  669 + end
  670 +
  671 + has_many :articles, :through => :profiles
  672 + def recent_documents(limit = 10, options = {}, pagination = true)
  673 + self.articles.recent(limit, options, pagination)
  674 + end
  675 +
  676 + has_many :events, :through => :profiles, :source => :articles, :class_name => 'Event'
  677 +
  678 + has_many :tags, :through => :articles
  679 +
  680 + def tag_counts
  681 + articles.tag_counts.inject({}) do |memo,tag|
  682 + memo[tag.name] = tag.count
  683 + memo
  684 + end
  685 + end
  686 +
  687 + def themes
  688 + if settings[:themes]
  689 + Theme.system_themes.select { |theme| settings[:themes].include?(theme.id) }
  690 + else
  691 + []
  692 + end
  693 + end
  694 +
  695 + def themes=(values)
  696 + settings[:themes] = values
  697 + end
  698 +
  699 + def add_themes(values)
  700 + if settings[:themes].nil?
  701 + self.themes = values
  702 + else
  703 + settings[:themes] += values
  704 + end
  705 + end
  706 +
  707 + def update_theme(theme)
  708 + self.theme = theme
  709 + self.save!
  710 + end
  711 +
  712 + def update_layout_template(template)
  713 + self.layout_template = template
  714 + self.save!
  715 + end
  716 +
  717 + before_create do |env|
  718 + env.settings[:themes] ||= %w[
  719 + aluminium
  720 + butter
  721 + chameleon
  722 + chocolate
  723 + noosfero
  724 + orange
  725 + plum
  726 + scarletred
  727 + skyblue
  728 + ]
  729 + end
  730 +
  731 + def community_template
  732 + template = Community.find_by_id settings[:community_template_id]
  733 + template if template && template.is_template
  734 + end
  735 +
  736 + def community_template=(value)
  737 + settings[:community_template_id] = value.id
  738 + end
  739 +
  740 + def person_template
  741 + template = Person.find_by_id settings[:person_template_id]
  742 + template if template && template.is_template
  743 + end
  744 +
  745 + def person_template=(value)
  746 + settings[:person_template_id] = value.id
  747 + end
  748 +
  749 + def enterprise_template
  750 + template = Enterprise.find_by_id settings[:enterprise_template_id]
  751 + template if template && template.is_template
  752 + end
  753 +
  754 + def enterprise_template=(value)
  755 + settings[:enterprise_template_id] = value.id
  756 + end
  757 +
  758 + def inactive_enterprise_template
  759 + template = Enterprise.find_by_id settings[:inactive_enterprise_template_id]
  760 + template if template && template.is_template
  761 + end
  762 +
  763 + def inactive_enterprise_template=(value)
  764 + settings[:inactive_enterprise_template_id] = value.id
  765 + end
  766 +
  767 + def replace_enterprise_template_when_enable
  768 + settings[:replace_enterprise_template_when_enable] || false
  769 + end
  770 +
  771 + def replace_enterprise_template_when_enable=(value)
  772 + settings[:replace_enterprise_template_when_enable] = value
  773 + end
  774 +
  775 + def portal_community
  776 + Community[settings[:portal_community_identifier]]
  777 + end
  778 +
  779 + def portal_community=(value)
  780 + settings[:portal_community_identifier] = value.nil? ? nil : value.identifier
  781 + end
  782 +
  783 + def unset_portal_community!
  784 + self.portal_community=nil
  785 + self.portal_folders=nil
  786 + self.news_amount_by_folder=nil
  787 + self.disable('use_portal_community')
  788 + self.save
  789 + end
  790 +
  791 + def is_portal_community?(profile)
  792 + portal_community == profile
  793 + end
  794 +
  795 + def portal_folders
  796 + (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(:first, :conditions => { :id => fid }) }.compact
  797 + end
  798 +
  799 + def portal_folders=(folders)
  800 + settings[:portal_folders] = folders ? folders.map(&:id) : nil
  801 + end
  802 +
  803 + def portal_news_cache_key(language='en')
  804 + "home-page-news/#{cache_key}-#{language}"
  805 + end
  806 +
  807 + def notification_emails
  808 + [noreply_email.blank? ? nil : noreply_email].compact + admins.map(&:email)
  809 + end
  810 +
  811 + after_create :create_templates
  812 +
  813 + def create_templates
  814 + prefix = self.name.to_slug + '_'
  815 +
  816 + enterprise_template = Enterprise.new(
  817 + :name => 'Enterprise template',
  818 + :identifier => prefix + 'enterprise_template'
  819 + )
  820 +
  821 + inactive_enterprise_template = Enterprise.new(
  822 + :name => 'Inactive Enterprise template',
  823 + :identifier => prefix + 'inactive_enterprise_template'
  824 + )
  825 +
  826 + community_template = Community.new(
  827 + :name => 'Community template',
  828 + :identifier => prefix + 'community_template'
  829 + )
  830 +
  831 + [
  832 + enterprise_template,
  833 + inactive_enterprise_template,
  834 + community_template
  835 + ].each do |profile|
  836 + profile.is_template = true
  837 + profile.visible = false
  838 + profile.environment = self
  839 + profile.save!
  840 + end
  841 +
  842 + pass = Digest::MD5.hexdigest rand.to_s
  843 + user = User.new(:login => (prefix + 'person_template'), :email => (prefix + 'template@template.noo'), :password => pass, :password_confirmation => pass)
  844 + user.environment = self
  845 + user.save!
  846 +
  847 + person_template = user.person
  848 + person_template.name = "Person template"
  849 + person_template.is_template = true
  850 + person_template.visible = false
  851 + person_template.save!
  852 +
  853 + self.enterprise_template = enterprise_template
  854 + self.inactive_enterprise_template = inactive_enterprise_template
  855 + self.community_template = community_template
  856 + self.person_template = person_template
  857 + self.save!
  858 + end
  859 +
  860 + after_create :create_default_licenses
  861 + def create_default_licenses
  862 + [
  863 + { :name => 'CC (by)', :url => 'http://creativecommons.org/licenses/by/3.0/legalcode'},
  864 + { :name => 'CC (by-nd)', :url => 'http://creativecommons.org/licenses/by-nd/3.0/legalcode'},
  865 + { :name => 'CC (by-sa)', :url => 'http://creativecommons.org/licenses/by-sa/3.0/legalcode'},
  866 + { :name => 'CC (by-nc)', :url => 'http://creativecommons.org/licenses/by-nc/3.0/legalcode'},
  867 + { :name => 'CC (by-nc-nd)', :url => 'http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode'},
  868 + { :name => 'CC (by-nc-sa)', :url => 'http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode'},
  869 + { :name => 'Free Art', :url => 'http://artlibre.org/licence/lal/en'},
  870 + { :name => 'GNU FDL', :url => 'http://www.gnu.org/licenses/fdl-1.3.txt'},
  871 + ].each do |data|
  872 + license = License.new(data)
  873 + license.environment = self
  874 + license.save!
  875 + end
  876 + end
  877 +
  878 + def highlighted_products_with_image(options = {})
  879 + Product.find(:all, {:conditions => {:highlighted => true, :profile_id => self.enterprises.find(:all, :select => :id) }, :joins => :image}.merge(options))
  880 + end
  881 +
  882 + settings_items :home_cache_in_minutes, :type => :integer, :default => 5
  883 + settings_items :general_cache_in_minutes, :type => :integer, :default => 15
  884 + settings_items :profile_cache_in_minutes, :type => :integer, :default => 15
  885 +
  886 + def image_galleries
  887 + portal_community ? portal_community.image_galleries : []
  888 + end
  889 +
  890 + serialize :languages
  891 +
  892 + before_validation do |environment|
  893 + environment.default_language = nil if environment.default_language.blank?
  894 + end
  895 +
  896 + validate :default_language_available
  897 + validate :languages_available
  898 +
  899 + def locales
  900 + if languages.present?
  901 + languages.inject({}) {|r, l| r.merge({l => Noosfero.locales[l]})}
  902 + else
  903 + Noosfero.locales
  904 + end
  905 + end
  906 +
  907 + def default_locale
  908 + default_language || Noosfero.default_locale
  909 + end
  910 +
  911 + def available_locales
  912 + locales_list = locales.keys
  913 + # move English to the beginning
  914 + if locales_list.include?('en')
  915 + locales_list = ['en'] + (locales_list - ['en']).sort
  916 + end
  917 + locales_list
  918 + end
  919 +
  920 + private
  921 +
  922 + def default_language_available
  923 + if default_language.present? && !available_locales.include?(default_language)
  924 + errors.add(:default_language, _('is not available.'))
  925 + end
  926 + end
  927 +
  928 + def languages_available
  929 + if languages.present?
  930 + languages.each do |language|
  931 + if !Noosfero.available_locales.include?(language)
  932 + errors.add(:languages, _('have unsupported languages.'))
  933 + break
  934 + end
  935 + end
  936 + end
  937 + end
  938 +end
app/models/friends_block.rb
@@ -1,26 +0,0 @@ @@ -1,26 +0,0 @@
1 -class FriendsBlock < ProfileListBlock  
2 -  
3 - def self.description  
4 - _('Friends')  
5 - end  
6 -  
7 - def default_title  
8 - n_('{#} friend', '{#} friends', profile_count)  
9 - end  
10 -  
11 - def help  
12 - _('This block displays your friends.')  
13 - end  
14 -  
15 - def footer  
16 - owner_id = owner.identifier  
17 - proc do  
18 - link_to s_('friends|View all'), :profile => owner_id, :controller => 'profile', :action => 'friends'  
19 - end  
20 - end  
21 -  
22 - def profiles  
23 - owner.friends  
24 - end  
25 -  
26 -end  
app/models/members_block.rb
@@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
1 -class MembersBlock < ProfileListBlock  
2 - settings_items :show_join_leave_button, :type => :boolean, :default => false  
3 -  
4 - attr_accessible :show_join_leave_button  
5 -  
6 - def self.description  
7 - _('Members')  
8 - end  
9 -  
10 - def default_title  
11 - _('{#} members')  
12 - end  
13 -  
14 - def help  
15 - _('This block presents the members of a collective.')  
16 - end  
17 -  
18 - def footer  
19 - profile = self.owner  
20 - s = show_join_leave_button  
21 -  
22 - proc do  
23 - render :file => 'blocks/members', :locals => { :profile => profile, :show_join_leave_button => s}  
24 - end  
25 - end  
26 -  
27 - def profiles  
28 - owner.members  
29 - end  
30 -  
31 - def extra_option  
32 - data = {  
33 - :human_name => _("Show join leave button"),  
34 - :name => 'block[show_join_leave_button]',  
35 - :value => true,  
36 - :checked => show_join_leave_button,  
37 - :options => {}  
38 - }  
39 - end  
40 -  
41 - def cache_key(language='en', user=nil)  
42 - logged = ''  
43 - if user  
44 - logged += '-logged-in'  
45 - if user.is_member_of? self.owner  
46 - logged += '-member'  
47 - end  
48 - end  
49 - super + logged  
50 - end  
51 -  
52 -end  
app/models/organization.rb
@@ -123,7 +123,7 @@ class Organization &lt; Profile @@ -123,7 +123,7 @@ class Organization &lt; Profile
123 [ 123 [
124 [MainBlock.new], 124 [MainBlock.new],
125 [ProfileImageBlock.new, LinkListBlock.new(:links => links)], 125 [ProfileImageBlock.new, LinkListBlock.new(:links => links)],
126 - [MembersBlock.new, RecentDocumentsBlock.new] 126 + [RecentDocumentsBlock.new]
127 ] 127 ]
128 end 128 end
129 129
app/models/people_block.rb
@@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
1 -class PeopleBlock < ProfileListBlock  
2 -  
3 - def default_title  
4 - _('People')  
5 - end  
6 -  
7 - def help  
8 - _('Clicking a person takes you to his/her homepage')  
9 - end  
10 -  
11 - def self.description  
12 - _('Random people')  
13 - end  
14 -  
15 - def profiles  
16 - owner.people  
17 - end  
18 -  
19 - def footer  
20 - lambda do |context|  
21 - link_to _('View all'), :controller => 'search', :action => 'people'  
22 - end  
23 - end  
24 -  
25 -end  
app/models/person.rb
@@ -269,7 +269,7 @@ class Person &lt; Profile @@ -269,7 +269,7 @@ class Person &lt; Profile
269 [ 269 [
270 [MainBlock.new], 270 [MainBlock.new],
271 [ProfileImageBlock.new(:show_name => true), LinkListBlock.new(:links => links), RecentDocumentsBlock.new], 271 [ProfileImageBlock.new(:show_name => true), LinkListBlock.new(:links => links), RecentDocumentsBlock.new],
272 - [FriendsBlock.new, CommunitiesBlock.new] 272 + [CommunitiesBlock.new]
273 ] 273 ]
274 end 274 end
275 275
app/sweepers/friendship_sweeper.rb
@@ -34,8 +34,7 @@ protected @@ -34,8 +34,7 @@ protected
34 expire_timeout_fragment(profile.manage_friends_cache_key(:npage => i.to_s)) 34 expire_timeout_fragment(profile.manage_friends_cache_key(:npage => i.to_s))
35 end 35 end
36 36
37 - blocks = profile.blocks.select{|b| b.kind_of?(FriendsBlock)}  
38 - BlockSweeper.expire_blocks(blocks) 37 + expire_blocks_cache(profile, [:profile])
39 end 38 end
40 39
41 end 40 end
app/views/blocks/members.html.erb
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -<%= link_to _('View all'), :profile => profile.identifier, :controller => 'profile', :action => 'members' %>  
2 -  
3 -<% if show_join_leave_button %>  
4 - <%= render :partial => 'blocks/profile_info_actions/join_leave_community' %>  
5 -<% end %>  
app/views/layouts/application-ng.html.erb
@@ -83,5 +83,10 @@ @@ -83,5 +83,10 @@
83 <%= noosfero_layout_features %> 83 <%= noosfero_layout_features %>
84 <%= theme_javascript_ng %> 84 <%= theme_javascript_ng %>
85 <%= addthis_javascript %> 85 <%= addthis_javascript %>
  86 + <%=
  87 + @plugins.dispatch(:body_ending).map do |content|
  88 + if content.respond_to?(:call) then instance_exec(&content).html_safe else content.html_safe end
  89 + end.join("\n")
  90 + %>
86 </body> 91 </body>
87 </html> 92 </html>
app/views/profile_members/_members_list.html.erb
@@ -17,9 +17,9 @@ @@ -17,9 +17,9 @@
17 <%= button_without_text :edit, _('Edit'), :action => 'change_role', :id => m %> 17 <%= button_without_text :edit, _('Edit'), :action => 'change_role', :id => m %>
18 <%= button_to_remote_without_text(:remove, _('Remove'), 18 <%= button_to_remote_without_text(:remove, _('Remove'),
19 :update => 'members-list', 19 :update => 'members-list',
20 - :loading => j('$("members-list").addClassName("loading")'),  
21 - :success => j("$('tr-#{m.identifier}').show()"),  
22 - :complete => j('$("members-list").removeClassName("loading")'), 20 + :loading => "$('members-list').addClassName('loading')",
  21 + :success => "$('tr-#{m.identifier}').show()",
  22 + :complete => "$('members-list').removeClassName('loading')",
23 :url => { :id => m }.merge(remove_action)) if m != user %> 23 :url => { :id => m }.merge(remove_action)) if m != user %>
24 </div> 24 </div>
25 </td> 25 </td>
config/application.rb
@@ -20,7 +20,7 @@ module Noosfero @@ -20,7 +20,7 @@ module Noosfero
20 require 'noosfero/plugin' 20 require 'noosfero/plugin'
21 21
22 # Adds custom attributes to the Set of allowed html attributes for the #sanitize helper 22 # Adds custom attributes to the Set of allowed html attributes for the #sanitize helper
23 - config.action_view.sanitized_allowed_attributes = 'align', 'border', 'alt', 'vspace', 'hspace', 'width', 'heigth', 'value', 'type', 'data', 'style', 'target', 'codebase', 'archive', 'classid', 'code', 'flashvars', 'scrolling', 'frameborder', 'controls', 'autoplay' 23 + config.action_view.sanitized_allowed_attributes = 'align', 'border', 'alt', 'vspace', 'hspace', 'width', 'heigth', 'value', 'type', 'data', 'style', 'target', 'codebase', 'archive', 'classid', 'code', 'flashvars', 'scrolling', 'frameborder', 'controls', 'autoplay', 'colspan', 'rowspan'
24 24
25 # Adds custom tags to the Set of allowed html tags for the #sanitize helper 25 # Adds custom tags to the Set of allowed html tags for the #sanitize helper
26 config.action_view.sanitized_allowed_tags = 'object', 'embed', 'param', 'table', 'tr', 'th', 'td', 'applet', 'comment', 'iframe', 'audio', 'video', 'source' 26 config.action_view.sanitized_allowed_tags = 'object', 'embed', 'param', 'table', 'tr', 'th', 'td', 'applet', 'comment', 'iframe', 'audio', 'video', 'source'
config/plugins/people_block 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +../../plugins/people_block
0 \ No newline at end of file 2 \ No newline at end of file
db/migrate/20140415125414_add_created_by_to_article.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +class AddCreatedByToArticle < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :articles, :created_by_id, :integer
  4 + add_column :article_versions, :created_by_id, :integer
  5 +
  6 + execute("UPDATE article_versions SET created_by_id = last_changed_by_id")
  7 +
  8 + execute("UPDATE articles SET created_by_id = article_versions.created_by_id
  9 +FROM article_versions WHERE article_versions.article_id = articles.id AND
  10 +article_versions.version = 1")
  11 + end
  12 +
  13 + def self.down
  14 + remove_column :articles, :created_by_id
  15 + remove_column :article_versions, :created_by_id
  16 + end
  17 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20140408172149) do 14 +ActiveRecord::Schema.define(:version => 20140415125414) do
15 15
16 create_table "abuse_reports", :force => true do |t| 16 create_table "abuse_reports", :force => true do |t|
17 t.integer "reporter_id" 17 t.integer "reporter_id"
@@ -95,6 +95,7 @@ ActiveRecord::Schema.define(:version =&gt; 20140408172149) do @@ -95,6 +95,7 @@ ActiveRecord::Schema.define(:version =&gt; 20140408172149) do
95 t.integer "license_id" 95 t.integer "license_id"
96 t.integer "image_id" 96 t.integer "image_id"
97 t.integer "position" 97 t.integer "position"
  98 + t.integer "created_by_id"
98 end 99 end
99 100
100 add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id" 101 add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id"
@@ -140,6 +141,7 @@ ActiveRecord::Schema.define(:version =&gt; 20140408172149) do @@ -140,6 +141,7 @@ ActiveRecord::Schema.define(:version =&gt; 20140408172149) do
140 t.integer "license_id" 141 t.integer "license_id"
141 t.integer "image_id" 142 t.integer "image_id"
142 t.integer "position" 143 t.integer "position"
  144 + t.integer "created_by_id"
143 end 145 end
144 146
145 add_index "articles", ["comments_count"], :name => "index_articles_on_comments_count" 147 add_index "articles", ["comments_count"], :name => "index_articles_on_comments_count"
features/my_network_block.feature
@@ -9,9 +9,11 @@ Feature: my_network_block @@ -9,9 +9,11 @@ Feature: my_network_block
9 And the following blocks 9 And the following blocks
10 | owner | type | 10 | owner | type |
11 | joaosilva | MyNetworkBlock | 11 | joaosilva | MyNetworkBlock |
  12 + | joaosilva | FriendsBlock |
12 And the following communities 13 And the following communities
13 | identifier | name | public_profile | 14 | identifier | name | public_profile |
14 | public-community | Public Community | true | 15 | public-community | Public Community | true |
  16 + And plugin FriendsBlock is enabled on environment
15 17
16 @selenium 18 @selenium
17 Scenario: display how many public/private communities I am member 19 Scenario: display how many public/private communities I am member
@@ -46,6 +48,10 @@ Feature: my_network_block @@ -46,6 +48,10 @@ Feature: my_network_block
46 | login | name | public_profile | 48 | login | name | public_profile |
47 | mariasilva | Maria Silva | true | 49 | mariasilva | Maria Silva | true |
48 | josesilva | Jose Silva | false | 50 | josesilva | Jose Silva | false |
  51 + And the following blocks
  52 + | owner | type |
  53 + | mariasilva | FriendsBlock |
  54 + | josesilva | FriendsBlock |
49 And "joaosilva" is friend of "mariasilva" 55 And "joaosilva" is friend of "mariasilva"
50 And I am logged in as "joaosilva" 56 And I am logged in as "joaosilva"
51 And I am on joaosilva's homepage 57 And I am on joaosilva's homepage
@@ -59,6 +65,10 @@ Feature: my_network_block @@ -59,6 +65,10 @@ Feature: my_network_block
59 | login | name | 65 | login | name |
60 | mariasilva | Maria Silva | 66 | mariasilva | Maria Silva |
61 | josesilva | Jose Silva | 67 | josesilva | Jose Silva |
  68 + And the following blocks
  69 + | owner | type |
  70 + | mariasilva | FriendsBlock |
  71 + | josesilva | FriendsBlock |
62 And "josesilva" is invisible 72 And "josesilva" is invisible
63 And "joaosilva" is friend of "mariasilva" 73 And "joaosilva" is friend of "mariasilva"
64 And I am logged in as "joaosilva" 74 And I am logged in as "joaosilva"
lib/noosfero/plugin.rb
@@ -277,6 +277,12 @@ class Noosfero::Plugin @@ -277,6 +277,12 @@ class Noosfero::Plugin
277 nil 277 nil
278 end 278 end
279 279
  280 + # -> Adds content to the ending of the page
  281 + # returns = lambda block that creates html code or raw rhtml/html.erb
  282 + def body_ending
  283 + nil
  284 + end
  285 +
280 # -> Adds content to the ending of the page head 286 # -> Adds content to the ending of the page head
281 # returns = lambda block that creates html code or raw rhtml/html.erb 287 # returns = lambda block that creates html code or raw rhtml/html.erb
282 def head_ending 288 def head_ending
plugins/display_content/lib/display_content_block.rb
@@ -102,7 +102,7 @@ class DisplayContentBlock &lt; Block @@ -102,7 +102,7 @@ class DisplayContentBlock &lt; Block
102 end 102 end
103 103
104 def parent_nodes 104 def parent_nodes
105 - @parent_nodes ||= self.holder.articles.find(nodes).map { |article| get_parent(article) }.compact.flatten 105 + @parent_nodes ||= self.holder.articles.where(:id => nodes).map { |article| get_parent(article) }.compact.flatten
106 end 106 end
107 107
108 VALID_CONTENT = ['RawHTMLArticle', 'TextArticle', 'TextileArticle', 'TinyMceArticle', 'Folder', 'Blog', 'Forum'] 108 VALID_CONTENT = ['RawHTMLArticle', 'TextArticle', 'TextileArticle', 'TinyMceArticle', 'Folder', 'Blog', 'Forum']
plugins/display_content/test/unit/display_content_block_test.rb
@@ -549,7 +549,7 @@ class DisplayContentBlockTest &lt; ActiveSupport::TestCase @@ -549,7 +549,7 @@ class DisplayContentBlockTest &lt; ActiveSupport::TestCase
549 a1 = fast_create(PluginArticle, :name => 'test article 1', :profile_id => profile.id) 549 a1 = fast_create(PluginArticle, :name => 'test article 1', :profile_id => profile.id)
550 Noosfero::Plugin.stubs(:all).returns([Plugin1.name]) 550 Noosfero::Plugin.stubs(:all).returns([Plugin1.name])
551 env = fast_create(Environment) 551 env = fast_create(Environment)
552 - env.enable_plugin(Plugin1) 552 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([Plugin1.new])
553 553
554 block = DisplayContentBlock.new 554 block = DisplayContentBlock.new
555 box = mock() 555 box = mock()
@@ -632,4 +632,19 @@ class DisplayContentBlockTest &lt; ActiveSupport::TestCase @@ -632,4 +632,19 @@ class DisplayContentBlockTest &lt; ActiveSupport::TestCase
632 assert_equal [UploadedFile, Event, TinyMceArticle, TextileArticle, RawHTMLArticle, Folder, Blog, Forum, Gallery, RssFeed, SomePluginContent], block.available_content_types 632 assert_equal [UploadedFile, Event, TinyMceArticle, TextileArticle, RawHTMLArticle, Folder, Blog, Forum, Gallery, RssFeed, SomePluginContent], block.available_content_types
633 end 633 end
634 634
  635 + should 'do not fail if a selected article was removed' do
  636 + profile = create_user('testuser').person
  637 + Article.delete_all
  638 + f1 = fast_create(Folder, :name => 'test folder 1', :profile_id => profile.id)
  639 + a1 = fast_create(TextileArticle, :name => 'test article 1', :profile_id => profile.id, :parent_id => f1.id)
  640 +
  641 + checked_articles= {a1.id => true}
  642 +
  643 + block = DisplayContentBlock.new
  644 + block.stubs(:holder).returns(profile)
  645 + block.checked_nodes= checked_articles
  646 + a1.destroy
  647 + assert_equal [], block.parent_nodes
  648 + end
  649 +
635 end 650 end
plugins/people_block/before_disable.rb 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +raise "\nPeopleBlockPlugin shouldn't be enabled/disabled by hand, Noosfero" +
  2 + "\ninstallation already comes with it enabled by default!\n"
plugins/people_block/controllers/people_block_plugin_profile_controller.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +class PeopleBlockPluginProfileController < ProfileController
  2 +
  3 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  4 +
  5 + def members
  6 + if is_cache_expired?(profile.members_cache_key(params))
  7 + if(params[:role_key])
  8 + role = Role.find_by_key_and_environment_id(params[:role_key], profile.environment)
  9 + @members = profile.members.with_role(role.id).includes(relations_to_include).paginate(:per_page => members_per_page, :page => params[:npage])
  10 + @members_title = role.name
  11 + else
  12 + @members = profile.members.includes(relations_to_include).paginate(:per_page => members_per_page, :page => params[:npage])
  13 + @members_title = 'members'
  14 + end
  15 + end
  16 + render "profile/members"
  17 + end
  18 +
  19 +end
plugins/people_block/db/migrate/20140605222753_enable_people_block_plugin.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class EnablePeopleBlockPlugin < ActiveRecord::Migration
  2 + def up
  3 + Environment.all.each do |env|
  4 + env.enabled_plugins << 'PeopleBlockPlugin'
  5 + env.enabled_plugins.uniq!
  6 + env.save!
  7 + end
  8 + end
  9 +
  10 + def down
  11 + Environment.all.each do |env|
  12 + env.enabled_plugins.delete_if {|i| i== 'PeopleBlockPlugin'}
  13 + env.save!
  14 + end
  15 + end
  16 +end
plugins/people_block/lib/ext/person.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +require_dependency 'person'
  2 +
  3 +class Person
  4 +
  5 + scope :with_role, lambda { |role_id|
  6 + { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ["role_assignments.role_id = #{role_id}"] }
  7 + }
  8 +
  9 +end
plugins/people_block/lib/friends_block.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +class FriendsBlock < PeopleBlockBase
  2 +
  3 + def self.description
  4 + _('Friends')
  5 + end
  6 +
  7 + def help
  8 + _('Clicking a friend takes you to his/her homepage')
  9 + end
  10 +
  11 + def default_title
  12 + n_('{#} friend', '{#} friends', profile_count)
  13 + end
  14 +
  15 + def profiles
  16 + owner.friends
  17 + end
  18 +
  19 + def footer
  20 + profile = self.owner
  21 + proc do
  22 + render :file => 'blocks/friends', :locals => { :profile => profile }
  23 + end
  24 + end
  25 +
  26 + def self.expire_on
  27 + { :profile => [:profile] }
  28 + end
  29 +
  30 +end
plugins/people_block/lib/members_block.rb 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +class MembersBlock < PeopleBlockBase
  2 + settings_items :show_join_leave_button, :type => :boolean, :default => false
  3 + settings_items :visible_role, :type => :string, :default => nil
  4 + attr_accessible :show_join_leave_button, :visible_role
  5 +
  6 + def self.description
  7 + _('Members')
  8 + end
  9 +
  10 + def help
  11 + _('Clicking a member takes you to his/her homepage')
  12 + end
  13 +
  14 + def default_title
  15 + title = role ? role.name : n_('members')
  16 + _('{#} %s') % title
  17 + end
  18 +
  19 + def profiles
  20 + role ? owner.members.with_role(role.id) : owner.members
  21 + end
  22 +
  23 + def footer
  24 + profile = self.owner
  25 + role_key = visible_role
  26 + s = show_join_leave_button
  27 + proc do
  28 + render :file => 'blocks/members', :locals => { :profile => profile, :show_join_leave_button => s, :role_key => role_key}
  29 + end
  30 + end
  31 +
  32 + def role
  33 + visible_role && !visible_role.empty? ? Role.find_by_key_and_environment_id(visible_role, owner.environment) : nil
  34 + end
  35 +
  36 + def roles
  37 + Profile::Roles.organization_member_roles(owner.environment)
  38 + end
  39 +
  40 + def extra_option
  41 + data = {
  42 + :human_name => _("Show join leave button"),
  43 + :name => 'block[show_join_leave_button]',
  44 + :value => true,
  45 + :checked => show_join_leave_button,
  46 + :options => {}
  47 + }
  48 + end
  49 +
  50 +end
plugins/people_block/lib/people_block.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +class PeopleBlock < PeopleBlockBase
  2 +
  3 + def self.description
  4 + _('People')
  5 + end
  6 +
  7 + def help
  8 + _('Clicking a person takes you to his/her homepage')
  9 + end
  10 +
  11 + def default_title
  12 + _('{#} People')
  13 + end
  14 +
  15 + def profiles
  16 + owner.people
  17 + end
  18 +
  19 + def footer
  20 + proc do
  21 + render :file => 'blocks/people'
  22 + end
  23 + end
  24 +
  25 +end
plugins/people_block/lib/people_block_base.rb 0 → 100644
@@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
  1 +class PeopleBlockBase < Block
  2 + settings_items :prioritize_profiles_with_image, :type => :boolean, :default => true
  3 + settings_items :limit, :type => :integer, :default => 6
  4 + settings_items :name, :type => String, :default => ""
  5 + settings_items :address, :type => String, :default => ""
  6 + attr_accessible :name, :address, :prioritize_profiles_with_image
  7 +
  8 + def self.description
  9 + _('Random people')
  10 + end
  11 +
  12 + def help
  13 + _('Clicking on the people or groups will take you to their home page.')
  14 + end
  15 +
  16 + def default_title
  17 + _('{#} People')
  18 + end
  19 +
  20 + def view_title
  21 + title.gsub('{#}', profile_count.to_s)
  22 + end
  23 +
  24 + def profiles
  25 + owner.profiles
  26 + end
  27 +
  28 + def profile_list
  29 + result = nil
  30 + visible_profiles = profiles.visible.includes([:image,:domains,:preferred_domain,:environment])
  31 + if !prioritize_profiles_with_image
  32 + result = visible_profiles.all(:limit => limit, :order => 'updated_at DESC').sort_by{ rand }
  33 + elsif visible_profiles.with_image.count >= limit
  34 + result = visible_profiles.with_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
  35 + else
  36 + result = visible_profiles.with_image.sort_by{ rand } + visible_profiles.without_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
  37 + end
  38 + result.slice(0..limit-1)
  39 + end
  40 +
  41 + def profile_count
  42 + profiles.visible.count
  43 + end
  44 +
  45 + def content(args={})
  46 + profiles = self.profile_list
  47 + title = self.view_title
  48 +
  49 + if !self.name.blank? && !self.address.blank?
  50 + name = self.name
  51 + expanded_address = expand_address(self.address)
  52 + end
  53 +
  54 + proc do
  55 + count = 0
  56 + list = profiles.map {|item|
  57 + count += 1
  58 + send(:profile_image_link, item, :minor )
  59 + }.join("\n")
  60 + if list.empty?
  61 + list = content_tag 'div', _('None'), :class => 'common-profile-list-block-none'
  62 + else
  63 + if !name.blank? && !expanded_address.blank?
  64 + list << content_tag(
  65 + 'div',
  66 + content_tag(
  67 + 'li',
  68 + content_tag(
  69 + 'div',
  70 + link_to(
  71 + content_tag('span', name, :class => 'banner-span' ),
  72 + expanded_address,
  73 + :title => name
  74 + ),
  75 + :class => 'banner-div'
  76 + ),
  77 + :class => 'vcard'
  78 + ),
  79 + :class => 'common-profile-list-block'
  80 + )
  81 + end
  82 + list = content_tag 'ul', list
  83 + end
  84 + block_title(title) + content_tag('div', list + tag('br', :style => 'clear:both'))
  85 + end
  86 + end
  87 +
  88 + def expand_address(address)
  89 + if address !~ /^[a-z]+:\/\// && address !~ /^\//
  90 + 'http://' + address
  91 + else
  92 + address
  93 + end
  94 + end
  95 +
  96 + def extra_option
  97 + { }
  98 + end
  99 +
  100 +end
plugins/people_block/lib/people_block_plugin.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class PeopleBlockPlugin < Noosfero::Plugin
  2 +
  3 + def self.plugin_name
  4 + "People Block Plugin"
  5 + end
  6 +
  7 + def self.plugin_description
  8 + _("A plugin that adds a people block")
  9 + end
  10 +
  11 + def self.extra_blocks
  12 + {
  13 + PeopleBlock => {:type => Environment},
  14 + MembersBlock => {:type => Community},
  15 + FriendsBlock => {:type => Person}
  16 + }
  17 + end
  18 +
  19 + def self.has_admin_url?
  20 + false
  21 + end
  22 +
  23 + def stylesheet?
  24 + true
  25 + end
  26 +
  27 +end
plugins/people_block/public/style.css 0 → 100644
@@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
  1 +/*******************************************************************
  2 + * COMMON *
  3 + *******************************************************************/
  4 +.people-block .block-footer-content a {
  5 + position: absolute;
  6 + top: 2px;
  7 + right: 0px;
  8 + font-size: 11px;
  9 + color: #000;
  10 + text-decoration: none;
  11 + padding-right: 15px;
  12 +}
  13 +
  14 +.people-block .banner-span {
  15 + color: #000;
  16 + font-size: 14pt;
  17 + font-weight: bold;
  18 + background-color: #EEE;
  19 +}
  20 +
  21 +
  22 +/*******************************************************************
  23 + * MAIN BOX - 1 *
  24 + *******************************************************************/
  25 +.box-1 .people-block .banner-div {
  26 + line-height: 112px;
  27 +}
  28 +
  29 +.box-1 .people-block .banner-span {
  30 + width: 204px;
  31 +}
  32 +
  33 +
  34 +/*******************************************************************
  35 + * LEFT/RIGHT BOXES *
  36 + *******************************************************************/
  37 +.box-2 .people-block .banner-div,
  38 +.box-3 .people-block .banner-div {
  39 + line-height: 78px;
  40 +}
  41 +
  42 +.box-2 .people-block .banner-div a,
  43 +.box-3 .people-block .banner-div a {
  44 + height: 78px;
  45 +}
  46 +
  47 +.box-2 .people-block .banner-span,
  48 +.box-3 .people-block .banner-span {
  49 + width: 116px;
  50 +}
  51 +
  52 +.box-2 .people-block ul,
  53 +.box-3 .people-block ul {
  54 + min-width: 196px;
  55 + width: 192px;
  56 + margin: 0px 0px 0px -3px;
  57 + padding: 0px;
  58 +}
  59 +
  60 +/*******************************************************************
  61 + * BLOCKs *
  62 + *******************************************************************/
  63 +#content .friends-block ul,
  64 +#content .members-block ul {
  65 + min-width: 196px;
  66 + width: 192px;
  67 + margin: 0px 0px 0px -3px;
  68 + padding: 0px;
  69 +}
  70 +#content .box-1 .people-block ul,
  71 +#content .box-1 .friends-block ul,
  72 +#content .box-1 .members-block ul {
  73 + width: auto;
  74 + display: block;
  75 +}
  76 +#content .people-block .block-footer-content a,
  77 +#content .friends-block .block-footer-content a,
  78 +#content .members-block .block-footer-content a {
  79 + position: absolute;
  80 + top: 2px;
  81 + right: 0px;
  82 + font-size: 11px;
  83 + color: #000;
  84 + text-decoration: none;
  85 + padding-right: 15px;
  86 +}
  87 +#content .members-block .block-footer-content .join-leave-button a {
  88 + position: relative;
  89 + background-color: #EEE;
  90 + border: 1px solid #CCC;
  91 + color: #555;
  92 + padding-right: inherit;
  93 +}
  94 +#content .members-block .block-footer-content .join-leave-button a:hover {
  95 + color: #FFF;
  96 + background-color: #555;
  97 + border: 1px solid #2e3436;
  98 + text-decoration: none;
  99 +}
  100 +#content .people-block .block-footer-content a.view-all,
  101 +#content .friends-block .block-footer-content a.view-all,
  102 +#content .members-block .block-footer-content a.view-all {
  103 + background: url(/designs/themes/base/imgs/arrow-right-p.png) 100% 50% no-repeat;
  104 +}
plugins/people_block/test/functional/people_block_plugin_environment_design_controller_test.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  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 + @controller = EnvironmentDesignController.new
  10 + @request = ActionController::TestRequest.new
  11 + @response = ActionController::TestResponse.new
  12 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([PeopleBlockPlugin.new])
  13 + end
  14 +
  15 + should 'be able to edit PeopleBlock' do
  16 + login_as(create_admin_user(Environment.default))
  17 + b = PeopleBlock.create!
  18 + e = Environment.default
  19 + e.boxes.create!
  20 + e.boxes.first.blocks << b
  21 + get :edit, :id => b.id
  22 + assert_tag :tag => 'input', :attributes => { :id => 'block_limit' }
  23 + end
  24 +
  25 +end
plugins/people_block/test/functional/people_block_plugin_profile_controller_test.rb 0 → 100644
@@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/people_block_plugin_profile_controller'
  3 +
  4 +
  5 +# Re-raise errors caught by the controller.
  6 +class PeopleBlockPluginProfileController; def rescue_action(e) raise e end; end
  7 +
  8 +class PeopleBlockPluginProfileControllerTest < ActionController::TestCase
  9 +
  10 + def setup
  11 + @controller = PeopleBlockPluginProfileController.new
  12 + @request = ActionController::TestRequest.new
  13 + @response = ActionController::TestResponse.new
  14 +
  15 + @profile = fast_create(Community)
  16 +
  17 + @environment = @profile.environment
  18 + @environment.enabled_plugins = ['PeopleBlockPlugin']
  19 + @environment.save!
  20 +
  21 + MembersBlock.delete_all
  22 + @block = MembersBlock.new
  23 + @block.box = @profile.boxes.first
  24 + @block.save!
  25 +
  26 + @admin = create_user('adminprofile').person
  27 + @member = create_user('memberprofile').person
  28 + @moderator = create_user('moderatorprofile').person
  29 + @profile.add_moderator(@moderator)
  30 + @profile.add_member(@member)
  31 + @profile.add_admin(@admin)
  32 + end
  33 +
  34 + attr_accessor :profile, :block, :admin, :member, :moderator
  35 +
  36 + should 'list members without role_key' do
  37 + get :members, :profile => profile.identifier
  38 + assert_response :success
  39 + assert_template 'members'
  40 + assert_equivalent [@admin, @member, @moderator], assigns(:members)
  41 + assert_match /adminprofile/, @response.body
  42 + assert_match /memberprofile/, @response.body
  43 + assert_match /moderatorprofile/, @response.body
  44 + end
  45 +
  46 + should 'list members with role_key=nil' do
  47 + get :members, :profile => profile.identifier, :role_key => nil
  48 + assert_response :success
  49 + assert_template 'members'
  50 + assert_equivalent [@admin, @member, @moderator], assigns(:members)
  51 + assert_match /adminprofile/, @response.body
  52 + assert_match /memberprofile/, @response.body
  53 + assert_match /moderatorprofile/, @response.body
  54 + end
  55 +
  56 + should 'list members only' do
  57 + get :members, :profile => profile.identifier, :role_key => Profile::Roles.member(profile.environment.id).key
  58 + assert_response :success
  59 + assert_template 'members'
  60 + assert_equal [@member], assigns(:members)
  61 + assert_no_match /adminprofile/, @response.body
  62 + assert_match /memberprofile/, @response.body
  63 + assert_no_match /moderatorprofile/, @response.body
  64 + end
  65 +
  66 + should 'list moderators only' do
  67 + get :members, :profile => profile.identifier, :role_key => Profile::Roles.moderator(profile.environment.id).key
  68 + assert_response :success
  69 + assert_template 'members'
  70 + assert_equal [@moderator], assigns(:members)
  71 + assert_no_match /adminprofile/, @response.body
  72 + assert_no_match /memberprofile/, @response.body
  73 + assert_match /moderatorprofile/, @response.body
  74 + end
  75 +
  76 +end
plugins/people_block/test/functional/people_block_plugin_profile_design_controller_test.rb 0 → 100644
@@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +# Re-raise errors caught by the controller.
  4 +class ProfileDesignController; def rescue_action(e) raise e end; end
  5 +
  6 +class ProfileDesignControllerTest < ActionController::TestCase
  7 +
  8 + def setup
  9 + @controller = ProfileDesignController.new
  10 + @request = ActionController::TestRequest.new
  11 + @response = ActionController::TestResponse.new
  12 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([PeopleBlockPlugin.new])
  13 + end
  14 +
  15 + should 'display *block people-block* class at design blocks page' do
  16 + user = create_user('testinguser')
  17 + login_as(user.login)
  18 +
  19 + @profile = user.person
  20 + @environment = @profile.environment
  21 + @environment.save!
  22 +
  23 + FriendsBlock.delete_all
  24 + @box1 = Box.create!(:owner => @profile)
  25 + @profile.boxes = [@box1]
  26 +
  27 + @block = FriendsBlock.new
  28 + @block.box = @box1
  29 + @block.save!
  30 +
  31 + @profile.blocks<<@block
  32 + @profile.save!
  33 +
  34 + get :index, :profile => @profile.identifier
  35 + assert_tag :div, :attributes => {:class => 'block friends-block'}
  36 + end
  37 +
  38 + should 'the people block is available for person profile' do
  39 + profile = mock
  40 + profile.stubs(:has_members?).returns(false)
  41 + profile.stubs(:person?).returns(true)
  42 + profile.stubs(:community?).returns(false)
  43 + profile.stubs(:enterprise?).returns(false)
  44 + profile.stubs(:has_blog?).returns(false)
  45 + profile.stubs(:is_admin?).with(anything).returns(false)
  46 + environment = mock
  47 + profile.stubs(:environment).returns(environment)
  48 + environment.stubs(:enabled?).returns(false)
  49 + @controller.stubs(:profile).returns(profile)
  50 + @controller.stubs(:user).returns(profile)
  51 + assert_includes @controller.available_blocks, FriendsBlock
  52 + end
  53 +
  54 + should 'the people block is available for community profile' do
  55 + profile = mock
  56 + profile.stubs(:has_members?).returns(true)
  57 + profile.stubs(:person?).returns(false)
  58 + profile.stubs(:community?).returns(true)
  59 + profile.stubs(:enterprise?).returns(false)
  60 + profile.stubs(:has_blog?).returns(false)
  61 + profile.stubs(:is_admin?).with(anything).returns(false)
  62 + environment = mock
  63 + profile.stubs(:environment).returns(environment)
  64 + environment.stubs(:enabled?).returns(false)
  65 + @controller.stubs(:profile).returns(profile)
  66 + @controller.stubs(:user).returns(profile)
  67 + assert_includes @controller.available_blocks, MembersBlock
  68 + end
  69 +
  70 +end
plugins/people_block/test/test_helper.rb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
plugins/people_block/test/unit/friends_block_test.rb 0 → 100644
@@ -0,0 +1,156 @@ @@ -0,0 +1,156 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class FriendsBlockTest < ActiveSupport::TestCase
  4 +
  5 + should 'inherit from Block' do
  6 + assert_kind_of Block, FriendsBlock.new
  7 + end
  8 +
  9 +
  10 + should 'declare its default title' do
  11 + assert_not_equal Block.new.default_title, FriendsBlock.new.default_title
  12 + end
  13 +
  14 +
  15 + should 'describe itself' do
  16 + assert_not_equal Block.description, FriendsBlock.description
  17 + end
  18 +
  19 +
  20 + should 'is editable' do
  21 + block = FriendsBlock.new
  22 + assert block.editable?
  23 + end
  24 +
  25 +
  26 + should 'have field limit' do
  27 + block = FriendsBlock.new
  28 + assert_respond_to block, :limit
  29 + end
  30 +
  31 +
  32 + should 'default value of limit' do
  33 + block = FriendsBlock.new
  34 + assert_equal 6, block.limit
  35 + end
  36 +
  37 +
  38 + should 'have field name' do
  39 + block = FriendsBlock.new
  40 + assert_respond_to block, :name
  41 + end
  42 +
  43 +
  44 + should 'default value of name' do
  45 + block = FriendsBlock.new
  46 + assert_equal "", block.name
  47 + end
  48 +
  49 +
  50 + should 'have field address' do
  51 + block = FriendsBlock.new
  52 + assert_respond_to block, :address
  53 + end
  54 +
  55 +
  56 + should 'default value of address' do
  57 + block = FriendsBlock.new
  58 + assert_equal "", block.address
  59 + end
  60 +
  61 +
  62 + should 'prioritize profiles with image by default' do
  63 + assert FriendsBlock.new.prioritize_people_with_image
  64 + end
  65 +
  66 +
  67 + should 'accept a limit of people to be displayed' do
  68 + block = FriendsBlock.new
  69 + block.limit = 20
  70 + assert_equal 20, block.limit
  71 + end
  72 +
  73 +
  74 + should 'list friends from person' do
  75 + owner = fast_create(Person)
  76 + friend1 = fast_create(Person)
  77 + friend2 = fast_create(Person)
  78 + owner.add_friend(friend1)
  79 + owner.add_friend(friend2)
  80 +
  81 + block = FriendsBlock.new
  82 +
  83 + block.expects(:owner).returns(owner).at_least_once
  84 + expects(:profile_image_link).with(friend1, :minor).returns(friend1.name)
  85 + expects(:profile_image_link).with(friend2, :minor).returns(friend2.name)
  86 + expects(:block_title).with(anything).returns('')
  87 +
  88 + content = instance_eval(&block.content)
  89 +
  90 + assert_match(/#{friend1.name}/, content)
  91 + assert_match(/#{friend2.name}/, content)
  92 + end
  93 +
  94 +
  95 + should 'link to "all friends"' do
  96 + person1 = create_user('mytestperson').person
  97 +
  98 + block = FriendsBlock.new
  99 + block.expects(:owner).returns(person1).at_least_once
  100 +
  101 + expects(:_).with('View all').returns('View all')
  102 + expects(:link_to).with('View all', :profile => 'mytestperson', :controller => 'profile', :action => 'friends').returns('link-to-friends')
  103 +
  104 + assert_equal 'link-to-friends', instance_eval(&block.footer)
  105 + end
  106 +
  107 +
  108 + should 'count number of owner friends' do
  109 + owner = fast_create(Person)
  110 + friend1 = fast_create(Person)
  111 + friend2 = fast_create(Person)
  112 + friend3 = fast_create(Person)
  113 + owner.add_friend(friend1)
  114 + owner.add_friend(friend2)
  115 + owner.add_friend(friend3)
  116 +
  117 + block = FriendsBlock.new
  118 + block.expects(:owner).returns(owner).at_least_once
  119 +
  120 + assert_equal 3, block.profile_count
  121 + end
  122 +
  123 +
  124 + should 'count number of public and private friends' do
  125 + owner = fast_create(Person)
  126 + private_p = fast_create(Person, {:public_profile => false})
  127 + public_p = fast_create(Person, {:public_profile => true})
  128 +
  129 + owner.add_friend(private_p)
  130 + owner.add_friend(public_p)
  131 +
  132 + block = FriendsBlock.new
  133 + block.expects(:owner).returns(owner).at_least_once
  134 +
  135 + assert_equal 2, block.profile_count
  136 + end
  137 +
  138 +
  139 + should 'not count number of invisible friends' do
  140 + owner = fast_create(Person)
  141 + private_p = fast_create(Person, {:visible => false})
  142 + public_p = fast_create(Person, {:visible => true})
  143 +
  144 + owner.add_friend(private_p)
  145 + owner.add_friend(public_p)
  146 +
  147 + block = FriendsBlock.new
  148 + block.expects(:owner).returns(owner).at_least_once
  149 +
  150 + assert_equal 1, block.profile_count
  151 + end
  152 +
  153 + protected
  154 + include NoosferoTestHelper
  155 +
  156 +end
plugins/people_block/test/unit/members_block_test.rb 0 → 100644
@@ -0,0 +1,252 @@ @@ -0,0 +1,252 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class MembersBlockTest < ActiveSupport::TestCase
  4 +
  5 + should 'inherit from Block' do
  6 + assert_kind_of Block, MembersBlock.new
  7 + end
  8 +
  9 +
  10 + should 'declare its default title' do
  11 + assert_not_equal Block.new.default_title, MembersBlock.new.default_title
  12 + end
  13 +
  14 +
  15 + should 'describe itself' do
  16 + assert_not_equal Block.description, MembersBlock.description
  17 + end
  18 +
  19 +
  20 + should 'is editable' do
  21 + block = MembersBlock.new
  22 + assert block.editable?
  23 + end
  24 +
  25 +
  26 + should 'have field limit' do
  27 + block = MembersBlock.new
  28 + assert_respond_to block, :limit
  29 + end
  30 +
  31 +
  32 + should 'default value of limit' do
  33 + block = MembersBlock.new
  34 + assert_equal 6, block.limit
  35 + end
  36 +
  37 +
  38 + should 'have field name' do
  39 + block = MembersBlock.new
  40 + assert_respond_to block, :name
  41 + end
  42 +
  43 +
  44 + should 'default value of name' do
  45 + block = MembersBlock.new
  46 + assert_equal "", block.name
  47 + end
  48 +
  49 +
  50 + should 'have field address' do
  51 + block = MembersBlock.new
  52 + assert_respond_to block, :address
  53 + end
  54 +
  55 +
  56 + should 'default value of address' do
  57 + block = MembersBlock.new
  58 + assert_equal "", block.address
  59 + end
  60 +
  61 +
  62 + should 'prioritize profiles with image by default' do
  63 + assert MembersBlock.new.prioritize_people_with_image
  64 + end
  65 +
  66 +
  67 + should 'respect limit when listing members' do
  68 + community = fast_create(Community)
  69 + p1 = fast_create(Person)
  70 + p2 = fast_create(Person)
  71 + p3 = fast_create(Person)
  72 + p4 = fast_create(Person)
  73 +
  74 + community.add_member(p1)
  75 + community.add_member(p2)
  76 + community.add_member(p3)
  77 + community.add_member(p4)
  78 +
  79 + block = MembersBlock.new(:limit => 3)
  80 + block.stubs(:owner).returns(community)
  81 +
  82 + assert_equal 3, block.profile_list.size
  83 + end
  84 +
  85 +
  86 + should 'accept a limit of members to be displayed' do
  87 + block = MembersBlock.new
  88 + block.limit = 20
  89 + assert_equal 20, block.limit
  90 + end
  91 +
  92 +
  93 + should 'list members from community' do
  94 + owner = fast_create(Community)
  95 + person1 = fast_create(Person)
  96 + person2 = fast_create(Person)
  97 + owner.add_member(person1)
  98 + owner.add_member(person2)
  99 +
  100 + block = MembersBlock.new
  101 +
  102 + block.expects(:owner).returns(owner).at_least_once
  103 + expects(:profile_image_link).with(person1, :minor).returns(person1.name)
  104 + expects(:profile_image_link).with(person2, :minor).returns(person2.name)
  105 + expects(:block_title).with(anything).returns('')
  106 +
  107 + content = instance_eval(&block.content)
  108 +
  109 + assert_match(/#{person1.name}/, content)
  110 + assert_match(/#{person2.name}/, content)
  111 + end
  112 +
  113 + should 'count number of public and private members' do
  114 + owner = fast_create(Community)
  115 + private_p = fast_create(Person, {:public_profile => false})
  116 + public_p = fast_create(Person, {:public_profile => true})
  117 +
  118 + owner.add_member(private_p)
  119 + owner.add_member(public_p)
  120 +
  121 + block = MembersBlock.new
  122 + block.expects(:owner).returns(owner).at_least_once
  123 +
  124 + assert_equal 2, block.profile_count
  125 + end
  126 +
  127 +
  128 + should 'not count number of invisible members' do
  129 + owner = fast_create(Community)
  130 + private_p = fast_create(Person, {:visible => false})
  131 + public_p = fast_create(Person, {:visible => true})
  132 +
  133 + owner.add_member(private_p)
  134 + owner.add_member(public_p)
  135 +
  136 + block = MembersBlock.new
  137 + block.expects(:owner).returns(owner).at_least_once
  138 +
  139 + assert_equal 1, block.profile_count
  140 + end
  141 +
  142 + should 'provide link to members page without a visible_role selected' do
  143 + profile = create_user('mytestuser').person
  144 + block = MembersBlock.new
  145 + block.box = profile.boxes.first
  146 + block.save!
  147 +
  148 + expects(:_).with('View all').returns('View all')
  149 + expects(:link_to).with('View all' , :profile => 'mytestuser', :controller => 'people_block_plugin_profile', :action => 'members', :role_key => block.visible_role).returns('link-to-members')
  150 +
  151 + assert_equal 'link-to-members', instance_eval(&block.footer)
  152 + end
  153 +
  154 + should 'provide link to members page with a selected role' do
  155 + profile = create_user('mytestuser').person
  156 + block = MembersBlock.new
  157 + block.box = profile.boxes.first
  158 + block.visible_role = 'profile_member'
  159 + block.save!
  160 +
  161 + expects(:_).with('View all').returns('View all')
  162 + expects(:link_to).with('View all' , :profile => 'mytestuser', :controller => 'people_block_plugin_profile', :action => 'members', :role_key => block.visible_role).returns('link-to-members')
  163 +
  164 + assert_equal 'link-to-members', instance_eval(&block.footer)
  165 + end
  166 +
  167 + should 'provide a role to be displayed (and default to nil)' do
  168 + env = fast_create(Environment)
  169 + env.boxes << Box.new
  170 + block = MembersBlock.new
  171 + assert_equal nil, block.visible_role
  172 + env.boxes.first.blocks << block
  173 + block.visible_role = 'profile_member'
  174 + block.save!
  175 + assert_equal 'profile_member', block.visible_role
  176 + end
  177 +
  178 + should 'list all members' do
  179 + env = fast_create(Environment)
  180 + env.boxes << Box.new
  181 + profile1 = fast_create(Person, :environment_id => env.id)
  182 + profile2 = fast_create(Person, :environment_id => env.id)
  183 +
  184 + block = MembersBlock.new
  185 + owner = fast_create(Community)
  186 + block.stubs(:owner).returns(owner)
  187 + env.boxes.first.blocks << block
  188 + block.save!
  189 +
  190 + owner.add_member profile1
  191 + owner.add_member profile2
  192 + profiles = block.profiles
  193 +
  194 + assert_includes profiles, profile1
  195 + assert_includes profiles, profile2
  196 + end
  197 +
  198 + should 'list only profiles with moderator role' do
  199 + env = fast_create(Environment)
  200 + env.boxes << Box.new
  201 + profile1 = fast_create(Person, :environment_id => env.id)
  202 + profile2 = fast_create(Person, :environment_id => env.id)
  203 +
  204 + block = MembersBlock.new
  205 + owner = fast_create(Community)
  206 + block.visible_role = Profile::Roles.moderator(owner.environment.id).key
  207 + block.stubs(:owner).returns(owner)
  208 + env.boxes.first.blocks << block
  209 + block.save!
  210 +
  211 + owner.add_member profile2
  212 + owner.add_moderator profile1
  213 + profiles = block.profiles
  214 +
  215 + assert_includes profiles, profile1
  216 + assert_not_includes profiles, profile2
  217 + end
  218 +
  219 + should 'list only profiles with member role' do
  220 + env = fast_create(Environment)
  221 + env.boxes << Box.new
  222 + profile1 = fast_create(Person, :environment_id => env.id)
  223 + profile2 = fast_create(Person, :environment_id => env.id)
  224 +
  225 + block = MembersBlock.new
  226 + owner = fast_create(Community)
  227 + block.visible_role = Profile::Roles.member(owner.environment.id).key
  228 + block.stubs(:owner).returns(owner)
  229 + env.boxes.first.blocks << block
  230 + block.save!
  231 +
  232 + owner.add_member profile2
  233 + owner.add_moderator profile1
  234 + profiles = block.profiles
  235 +
  236 + assert_not_includes profiles, profile1
  237 + assert_includes profiles, profile2
  238 + end
  239 +
  240 + should 'list available roles' do
  241 + block = MembersBlock.new
  242 + owner = fast_create(Community)
  243 + block.stubs(:owner).returns(owner)
  244 + assert_includes block.roles, Profile::Roles.member(owner.environment.id)
  245 + assert_includes block.roles, Profile::Roles.admin(owner.environment.id)
  246 + assert_includes block.roles, Profile::Roles.moderator(owner.environment.id)
  247 + end
  248 +
  249 + protected
  250 + include NoosferoTestHelper
  251 +
  252 +end
plugins/people_block/test/unit/people_block_plugin_test.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class PeopleBlockPluginTest < ActiveSupport::TestCase
  4 +
  5 + should "return PeopleBlock in extra_blocks class method" do
  6 + assert PeopleBlockPlugin.extra_blocks.keys.include?(PeopleBlock)
  7 + end
  8 +
  9 + should "return MembersBlock in extra_blocks class method" do
  10 + assert PeopleBlockPlugin.extra_blocks.keys.include?(MembersBlock)
  11 + end
  12 +
  13 + should "return FriendsBlock in extra_blocks class method" do
  14 + assert PeopleBlockPlugin.extra_blocks.keys.include?(FriendsBlock)
  15 + end
  16 +
  17 + should "return false for class method has_admin_url?" do
  18 + assert !PeopleBlockPlugin.has_admin_url?
  19 + end
  20 +
  21 + should "return false for class method stylesheet?" do
  22 + assert PeopleBlockPlugin.new.stylesheet?
  23 + end
  24 +
  25 +end
plugins/people_block/test/unit/people_block_test.rb 0 → 100644
@@ -0,0 +1,146 @@ @@ -0,0 +1,146 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class PeopleBlockTest < ActiveSupport::TestCase
  4 +
  5 + should 'inherit from Block' do
  6 + assert_kind_of Block, PeopleBlock.new
  7 + end
  8 +
  9 +
  10 + should 'declare its default title' do
  11 + assert_not_equal Block.new.default_title, PeopleBlock.new.default_title
  12 + end
  13 +
  14 +
  15 + should 'describe itself' do
  16 + assert_not_equal Block.description, PeopleBlock.description
  17 + end
  18 +
  19 +
  20 + should 'is editable' do
  21 + block = PeopleBlock.new
  22 + assert block.editable?
  23 + end
  24 +
  25 +
  26 + should 'have field limit' do
  27 + block = PeopleBlock.new
  28 + assert_respond_to block, :limit
  29 + end
  30 +
  31 +
  32 + should 'default value of limit' do
  33 + block = PeopleBlock.new
  34 + assert_equal 6, block.limit
  35 + end
  36 +
  37 +
  38 + should 'have field name' do
  39 + block = PeopleBlock.new
  40 + assert_respond_to block, :name
  41 + end
  42 +
  43 +
  44 + should 'default value of name' do
  45 + block = PeopleBlock.new
  46 + assert_equal "", block.name
  47 + end
  48 +
  49 +
  50 + should 'have field address' do
  51 + block = PeopleBlock.new
  52 + assert_respond_to block, :address
  53 + end
  54 +
  55 +
  56 + should 'default value of address' do
  57 + block = PeopleBlock.new
  58 + assert_equal "", block.address
  59 + end
  60 +
  61 +
  62 + should 'prioritize profiles with image by default' do
  63 + assert PeopleBlock.new.prioritize_profiles_with_image
  64 + end
  65 +
  66 +
  67 + should 'respect limit when listing people' do
  68 + env = fast_create(Environment)
  69 + p1 = fast_create(Person, :environment_id => env.id)
  70 + p2 = fast_create(Person, :environment_id => env.id)
  71 + p3 = fast_create(Person, :environment_id => env.id)
  72 + p4 = fast_create(Person, :environment_id => env.id)
  73 +
  74 + block = PeopleBlock.new(:limit => 3)
  75 + block.stubs(:owner).returns(env)
  76 +
  77 + assert_equal 3, block.profile_list.size
  78 + end
  79 +
  80 +
  81 + should 'accept a limit of people to be displayed' do
  82 + block = PeopleBlock.new
  83 + block.limit = 20
  84 + assert_equal 20, block.limit
  85 + end
  86 +
  87 +
  88 + should 'list people from environment' do
  89 + owner = fast_create(Environment)
  90 + person1 = fast_create(Person, :environment_id => owner.id)
  91 + person2 = fast_create(Person, :environment_id => owner.id)
  92 +
  93 + block = PeopleBlock.new
  94 +
  95 + block.expects(:owner).returns(owner).at_least_once
  96 + expects(:profile_image_link).with(person1, :minor).returns(person1.name)
  97 + expects(:profile_image_link).with(person2, :minor).returns(person2.name)
  98 + expects(:block_title).with(anything).returns('')
  99 +
  100 + content = instance_exec(&block.content)
  101 +
  102 + assert_match(/#{person1.name}/, content)
  103 + assert_match(/#{person2.name}/, content)
  104 + end
  105 +
  106 +
  107 + should 'link to "all people"' do
  108 + env = fast_create(Environment)
  109 +
  110 + block = PeopleBlock.new
  111 +
  112 + stubs(:_).with('View all').returns('View all')
  113 + stubs(:link_to).returns('link-to-people')
  114 + stubs(:url_for).returns(' ')
  115 +
  116 + assert_equal 'link-to-people', instance_exec(&block.footer)
  117 + end
  118 +
  119 +
  120 + should 'count number of public and private people' do
  121 + owner = fast_create(Environment)
  122 + private_p = fast_create(Person, :public_profile => false, :environment_id => owner.id)
  123 + public_p = fast_create(Person, :public_profile => true, :environment_id => owner.id)
  124 +
  125 + block = PeopleBlock.new
  126 + block.expects(:owner).returns(owner).at_least_once
  127 +
  128 + assert_equal 2, block.profile_count
  129 + end
  130 +
  131 +
  132 + should 'not count number of invisible people' do
  133 + owner = fast_create(Environment)
  134 + private_p = fast_create(Person, :visible => false, :environment_id => owner.id)
  135 + public_p = fast_create(Person, :visible => true, :environment_id => owner.id)
  136 +
  137 + block = PeopleBlock.new
  138 + block.expects(:owner).returns(owner).at_least_once
  139 +
  140 + assert_equal 1, block.profile_count
  141 + end
  142 +
  143 + protected
  144 + include NoosferoTestHelper
  145 +
  146 +end
plugins/people_block/views/blocks/friends.html.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +<%= link_to s_('friends|View all'), {:profile => profile.identifier, :controller => 'profile', :action => 'friends'}, :class => 'view-all' %>
plugins/people_block/views/blocks/members.html.erb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<%= link_to _('View all'), {:profile => profile.identifier, :controller => 'people_block_plugin_profile', :action => 'members', :role_key => role_key}, :class => 'view-all' %>
  2 +
  3 +<% if show_join_leave_button %>
  4 + <%= render :partial => 'blocks/profile_info_actions/join_leave_community' %>
  5 +<% end %>
plugins/people_block/views/blocks/people.html.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +<%= link_to _('View all'), {:controller => 'search', :action => 'people'}, :class => 'view-all' %>
plugins/people_block/views/box_organizer/_people_block_base.html.erb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +<%= labelled_form_field _('Name:'), text_field(:block, :name) %>
  2 +
  3 +<%= labelled_form_field _('Address:'), text_field(:block, :address) %>
  4 +
  5 +<% if @block.kind_of?(MembersBlock) %>
  6 + <%= labelled_form_field _('Filter by role:'), '' %>
  7 + <%= select_tag 'block[visible_role]', options_for_select(@block.roles.map{|r| [r.name, r.key]}.insert(0,''), @block.visible_role) %>
  8 +<% end %>
  9 +
  10 +<%= render :partial => 'profile_list_block' %>
plugins/people_block/views/environment_design 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +box_organizer
0 \ No newline at end of file 2 \ No newline at end of file
plugins/people_block/views/profile_design 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +box_organizer
0 \ No newline at end of file 2 \ No newline at end of file
plugins/piwik/controllers/piwik_plugin_admin_controller.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class PiwikPluginAdminController < AdminController
  2 +
  3 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  4 +
  5 + def index
  6 + if request.post?
  7 + if @environment.update_attributes(params[:environment])
  8 + session[:notice] = _('Piwik plugin settings updated successfully.')
  9 + else
  10 + session[:notice] = _('Piwik plugin settings could not be saved.')
  11 + end
  12 + redirect_to :controller => 'plugins', :action => 'index'
  13 + end
  14 + end
  15 +
  16 +end
plugins/piwik/lib/ext/environment.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +require_dependency 'environment'
  2 +
  3 +class Environment
  4 + settings_items :piwik_domain
  5 + settings_items :piwik_site_id
  6 + attr_accessible :piwik_domain, :piwik_site_id
  7 +end
plugins/piwik/lib/piwik_plugin.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +class PiwikPlugin < Noosfero::Plugin
  2 +
  3 + include ActionView::Helpers::JavaScriptHelper
  4 + include ActionView::Helpers::FormHelper
  5 + include ActionView::Helpers::UrlHelper
  6 + include ActionView::Helpers::TagHelper
  7 +
  8 + def self.plugin_name
  9 + "Piwik"
  10 + end
  11 +
  12 + def self.plugin_description
  13 + _("Tracking and web analytics to your Noosfero's environment")
  14 + end
  15 +
  16 + def body_ending
  17 + domain = context.environment.piwik_domain
  18 + site_id = context.environment.piwik_site_id
  19 + unless domain.blank? || site_id.blank?
  20 + expanded_template('tracking-code.rhtml',{:domain => domain, :site_id => site_id})
  21 + end
  22 + end
  23 +
  24 +end
plugins/piwik/test/functional/piwik_plugin_test.rb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/piwik_plugin_admin_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class PiwikPluginAdminController; def rescue_action(e) raise e end; end
  6 +
  7 +class PiwikPluginAdminControllerTest < ActionController::TestCase
  8 +
  9 + def setup
  10 + @environment = Environment.default
  11 + user_login = create_admin_user(@environment)
  12 + login_as(user_login)
  13 + @environment.enabled_plugins = ['PiwikPlugin']
  14 + @environment.save!
  15 + end
  16 +
  17 + should 'access index action' do
  18 + get :index
  19 + assert_template 'index'
  20 + assert_response :success
  21 + end
  22 +
  23 + should 'update piwik plugin settings' do
  24 + assert_nil @environment.reload.piwik_domain
  25 + assert_nil @environment.reload.piwik_site_id
  26 + post :index, :environment => { :piwik_domain => 'http://something', :piwik_site_id => 10 }
  27 + assert_not_nil @environment.reload.piwik_domain
  28 + assert_not_nil @environment.reload.piwik_site_id
  29 + end
  30 +
  31 +end
plugins/piwik/test/unit/piwik_plugin_test.rb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +
  3 +class PiwikPluginTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @plugin = PiwikPlugin.new
  7 + @context = mock()
  8 + @plugin.context = @context
  9 + @environment = Environment.new
  10 + @context.stubs(:environment).returns(@environment)
  11 + end
  12 +
  13 + should 'add content at the body ending unless domain and site_id are blank' do
  14 + @environment.piwik_domain = 'piwik.domain.example.com'
  15 + @environment.piwik_site_id = 5
  16 + @plugin.stubs(:expanded_template).returns('content')
  17 + assert_equal 'content', @plugin.body_ending
  18 + end
  19 +
  20 + should 'not add any content at the body ending if domain is blank' do
  21 + @environment.piwik_domain = nil
  22 + @environment.piwik_site_id = 5
  23 + @plugin.stubs(:expanded_template).returns('content')
  24 + assert_equal nil, @plugin.body_ending
  25 + end
  26 +
  27 + should 'not add any content at the body ending if site_id is blank' do
  28 + @environment.piwik_domain = 'piwik.domain.example.com'
  29 + @environment.piwik_site_id = nil
  30 + @plugin.stubs(:expanded_template).returns('content')
  31 + assert_equal nil, @plugin.body_ending
  32 + end
  33 +
  34 + should 'extends Environment with attr piwik_domain' do
  35 + assert_respond_to Environment.new, :piwik_domain
  36 + end
  37 +
  38 + should 'extends Environment with attr piwik_site_id' do
  39 + assert_respond_to Environment.new, :piwik_site_id
  40 + end
  41 +
  42 +end
plugins/piwik/views/piwik_plugin_admin/index.html.erb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +<h1><%= _("Piwik plugin settings") %></h1>
  2 +
  3 +<%= form_for(:environment) do |f| %>
  4 +
  5 + <%= labelled_form_field _('Piwik domain'), f.text_field(:piwik_domain) %>
  6 +
  7 + <%= labelled_form_field _('Piwik site id'), f.text_field(:piwik_site_id) %>
  8 +
  9 + <% button_bar do %>
  10 + <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %>
  11 + <% end %>
  12 +
  13 +<% end %>
plugins/piwik/views/tracking-code.rhtml 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +<!-- Piwik -->
  2 +<script type="text/javascript">
  3 + var _paq = _paq || [];
  4 + _paq.push(['trackPageView']);
  5 + _paq.push(['enableLinkTracking']);
  6 + (function() {
  7 + var u=(("https:" == document.location.protocol) ? "https" : "http") + "://<%= escape_javascript locals[:domain] %>/piwik/";
  8 + _paq.push(['setTrackerUrl', u+'piwik.php']);
  9 + _paq.push(['setSiteId', <%= escape_javascript locals[:site_id] %>]);
  10 + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript';
  11 + g.defer=true; g.async=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
  12 + })();
  13 +</script>
  14 +<noscript><p><img src="http://<%= escape_javascript locals[:domain] %>/piwik/piwik.php?idsite=<%= escape_javascript locals[:site_id] %>" style="border:0;" alt="" /></p></noscript>
  15 +<!-- End Piwik Code -->
plugins/video/lib/video_block.rb
@@ -6,8 +6,10 @@ class VideoBlock &lt; Block @@ -6,8 +6,10 @@ class VideoBlock &lt; Block
6 settings_items :width, :type => :integer, :default => 400 6 settings_items :width, :type => :integer, :default => 400
7 settings_items :height, :type => :integer, :default => 315 7 settings_items :height, :type => :integer, :default => 315
8 8
  9 + YOUTUBE_ID_FORMAT = '\w-'
  10 +
9 def is_youtube? 11 def is_youtube?
10 - url.match(/.*(youtube.com.*v=[[:alnum:]]+|youtu.be\/[[:alnum:]]+).*/) ? true : false 12 + url.match(/.*(youtube.com.*v=[#{YOUTUBE_ID_FORMAT}]+|youtu.be\/[#{YOUTUBE_ID_FORMAT}]+).*/) ? true : false
11 end 13 end
12 14
13 def is_vimeo? 15 def is_vimeo?
@@ -46,8 +48,8 @@ class VideoBlock &lt; Block @@ -46,8 +48,8 @@ class VideoBlock &lt; Block
46 48
47 def extract_youtube_id 49 def extract_youtube_id
48 return nil unless is_youtube? 50 return nil unless is_youtube?
49 - youtube_match = url.match('v=([[:alnum:]]*)')  
50 - youtube_match ||= url.match('youtu.be\/([[:alnum:]]*)') 51 + youtube_match = url.match("v=([#{YOUTUBE_ID_FORMAT}]*)")
  52 + youtube_match ||= url.match("youtu.be\/([#{YOUTUBE_ID_FORMAT}]*)")
51 youtube_match[1] unless youtube_match.nil? 53 youtube_match[1] unless youtube_match.nil?
52 end 54 end
53 55
plugins/video/test/unit/video_block_test.rb
@@ -70,6 +70,13 @@ class VideoBlockTest &lt; ActiveSupport::TestCase @@ -70,6 +70,13 @@ class VideoBlockTest &lt; ActiveSupport::TestCase
70 assert_equal id, block.send('extract_youtube_id') 70 assert_equal id, block.send('extract_youtube_id')
71 end 71 end
72 72
  73 + should "extract youtube id from youtube video url's if it has underline and hyphen" do
  74 + block = VideoBlock.new
  75 + id = 'oi43_re-d2'
  76 + block.url = "youtube.com/?v=#{id}"
  77 + assert_equal id, block.send('extract_youtube_id')
  78 + end
  79 +
73 should "extract youtube id from youtube video url's if it's a valid youtube short url" do 80 should "extract youtube id from youtube video url's if it's a valid youtube short url" do
74 block = VideoBlock.new 81 block = VideoBlock.new
75 id = 'oi43jre2d2' 82 id = 'oi43jre2d2'
public/designs/themes/base/style.css
@@ -478,12 +478,9 @@ div#notice { @@ -478,12 +478,9 @@ div#notice {
478 478
479 /************************** Profile List *****************************/ 479 /************************** Profile List *****************************/
480 480
481 -#content .people-block ul,  
482 #content .profile-list-block ul, 481 #content .profile-list-block ul,
483 #content .enterprises-block ul, 482 #content .enterprises-block ul,
484 -#content .members-block ul,  
485 #content .communities-block ul, 483 #content .communities-block ul,
486 -#content .friends-block ul,  
487 #content .fans-block ul { 484 #content .fans-block ul {
488 min-width: 196px; 485 min-width: 196px;
489 width: 192px; 486 width: 192px;
@@ -491,24 +488,18 @@ div#notice { @@ -491,24 +488,18 @@ div#notice {
491 padding: 0px; 488 padding: 0px;
492 } 489 }
493 490
494 -#content .box-1 .people-block ul,  
495 #content .box-1 .profile-list-block ul, 491 #content .box-1 .profile-list-block ul,
496 #content .box-1 .enterprises-block ul, 492 #content .box-1 .enterprises-block ul,
497 -#content .box-1 .members-block ul,  
498 #content .box-1 .communities-block ul, 493 #content .box-1 .communities-block ul,
499 -#content .box-1 .friends-block ul,  
500 #content .box-1 .fans-block ul { 494 #content .box-1 .fans-block ul {
501 width: auto; 495 width: auto;
502 display: block; 496 display: block;
503 } 497 }
504 498
505 #content .tags-block .block-footer-content a, 499 #content .tags-block .block-footer-content a,
506 -#content .people-block .block-footer-content a,  
507 #content .profile-list-block .block-footer-content a, 500 #content .profile-list-block .block-footer-content a,
508 #content .enterprises-block .block-footer-content a, 501 #content .enterprises-block .block-footer-content a,
509 -#content .members-block .block-footer-content a,  
510 -#content .communities-block .block-footer-content a,  
511 -#content .friends-block .block-footer-content a { 502 +#content .communities-block .block-footer-content a {
512 position: absolute; 503 position: absolute;
513 top: 2px; 504 top: 2px;
514 right: 0px; 505 right: 0px;
@@ -518,29 +509,12 @@ div#notice { @@ -518,29 +509,12 @@ div#notice {
518 padding-right: 15px; 509 padding-right: 15px;
519 } 510 }
520 #content .tags-block .block-footer-content a, 511 #content .tags-block .block-footer-content a,
521 -#content .people-block .block-footer-content a,  
522 #content .profile-list-block .block-footer-content a, 512 #content .profile-list-block .block-footer-content a,
523 #content .enterprises-block .block-footer-content a, 513 #content .enterprises-block .block-footer-content a,
524 -#content .communities-block .block-footer-content a,  
525 -#content .friends-block .block-footer-content a { 514 +#content .communities-block .block-footer-content a {
526 background: url(imgs/arrow-right-p.png) 100% 50% no-repeat; 515 background: url(imgs/arrow-right-p.png) 100% 50% no-repeat;
527 } 516 }
528 517
529 -#content .members-block .block-footer-content .join-leave-button a {  
530 - position: relative;  
531 - background-color: #EEE;  
532 - border: 1px solid #CCC;  
533 - color: #555;  
534 - padding-right: inherit;  
535 -}  
536 -  
537 -#content .members-block .block-footer-content .join-leave-button a:hover {  
538 - color: #FFF;  
539 - background-color: #555;  
540 - border: 1px solid #2e3436;  
541 - text-decoration: none;  
542 -}  
543 -  
544 #content .profile-list-block .block-title { 518 #content .profile-list-block .block-title {
545 text-align: left; 519 text-align: left;
546 } 520 }
public/plugins/people_block 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +../../plugins/people_block/public
0 \ No newline at end of file 2 \ No newline at end of file
script/noosfero-plugins
@@ -76,7 +76,8 @@ run(){ @@ -76,7 +76,8 @@ run(){
76 76
77 _enable(){ 77 _enable(){
78 plugin="$1" 78 plugin="$1"
79 - source="$available_plugins_dir/$plugin" 79 + cd $enabled_plugins_dir
  80 + source="../../plugins/$plugin"
80 target="$enabled_plugins_dir/$plugin" 81 target="$enabled_plugins_dir/$plugin"
81 run "$source/before_enable.rb" 82 run "$source/before_enable.rb"
82 if [ -h "$target" ]; then 83 if [ -h "$target" ]; then
@@ -98,11 +99,13 @@ _enable(){ @@ -98,11 +99,13 @@ _enable(){
98 fi 99 fi
99 fi 100 fi
100 if [ "$installation_ok" = true ] && [ "$dependencies_ok" = true ]; then 101 if [ "$installation_ok" = true ] && [ "$dependencies_ok" = true ]; then
101 - ln -s "$source" "$target" 102 + ln -s "$source" "$plugin"
102 plugins_public_dir="$NOOSFERO_DIR/public/plugins" 103 plugins_public_dir="$NOOSFERO_DIR/public/plugins"
103 plugins_features_dir="$NOOSFERO_DIR/features/plugins" 104 plugins_features_dir="$NOOSFERO_DIR/features/plugins"
104 - test -d "$target/public/" && ln -s "$target/public" "$plugins_public_dir/$plugin"  
105 - test -d "$NOOSFERO_DIR/features" && test -d "$target/features" && ln -s "$target/features" "$plugins_features_dir/$plugin" 105 + cd $plugins_public_dir
  106 + test -d "$source/public" && ln -s "$source/public" "$plugin"
  107 + cd $plugins_features_dir
  108 + test -d "$NOOSFERO_DIR/features" && test -d "$source/features" && ln -s "$source/features" "$plugin"
106 _say "$plugin enabled" 109 _say "$plugin enabled"
107 run "$source/after_enable.rb" 110 run "$source/after_enable.rb"
108 needs_migrate=true 111 needs_migrate=true
@@ -122,15 +125,19 @@ _disable(){ @@ -122,15 +125,19 @@ _disable(){
122 target="$enabled_plugins_dir/$plugin" 125 target="$enabled_plugins_dir/$plugin"
123 plugins_public_dir="$NOOSFERO_DIR/public/plugins" 126 plugins_public_dir="$NOOSFERO_DIR/public/plugins"
124 plugins_features_dir="$NOOSFERO_DIR/features/plugins" 127 plugins_features_dir="$NOOSFERO_DIR/features/plugins"
125 - run "$source/before_disable.rb"  
126 - if [ -h "$target" ]; then  
127 - rm "$target"  
128 - test -h "$plugins_public_dir/$plugin" && rm "$plugins_public_dir/$plugin"  
129 - test -h "$plugins_features_dir/$plugin" && rm "$plugins_features_dir/$plugin"  
130 - _say "$plugin disabled"  
131 - run "$source/after_disable.rb" 128 + if ! run "$source/before_disable.rb"; then
  129 + echo "W: failed to disabling $plugin"
  130 + echo
132 else 131 else
133 - _say "$plugin already disabled" 132 + if [ -h "$target" ]; then
  133 + rm "$target"
  134 + test -h "$plugins_public_dir/$plugin" && rm "$plugins_public_dir/$plugin"
  135 + test -h "$plugins_features_dir/$plugin" && rm "$plugins_features_dir/$plugin"
  136 + _say "$plugin disabled"
  137 + run "$source/after_disable.rb"
  138 + else
  139 + _say "$plugin already disabled"
  140 + fi
134 fi 141 fi
135 } 142 }
136 143
test/functional/cms_controller_test.rb
@@ -1228,7 +1228,7 @@ class CmsControllerTest &lt; ActionController::TestCase @@ -1228,7 +1228,7 @@ class CmsControllerTest &lt; ActionController::TestCase
1228 should 'allow user edit article if he is owner and has publish permission' do 1228 should 'allow user edit article if he is owner and has publish permission' do
1229 c = Community.create!(:name => 'test_comm', :identifier => 'test_comm') 1229 c = Community.create!(:name => 'test_comm', :identifier => 'test_comm')
1230 u = create_user_with_permission('test_user', 'publish_content', c) 1230 u = create_user_with_permission('test_user', 'publish_content', c)
1231 - a = create(Article, :profile => c, :name => 'test_article', :last_changed_by => u) 1231 + a = create(Article, :profile => c, :name => 'test_article', :created_by => u)
1232 login_as :test_user 1232 login_as :test_user
1233 @controller.stubs(:user).returns(u) 1233 @controller.stubs(:user).returns(u)
1234 1234
@@ -1774,6 +1774,31 @@ class CmsControllerTest &lt; ActionController::TestCase @@ -1774,6 +1774,31 @@ class CmsControllerTest &lt; ActionController::TestCase
1774 assert_equal 'first version', Article.find(article.id).name 1774 assert_equal 'first version', Article.find(article.id).name
1775 end 1775 end
1776 1776
  1777 + should 'set created_by when creating article' do
  1778 + login_as(profile.identifier)
  1779 +
  1780 + post :new, :type => 'TinyMceArticle', :profile => profile.identifier, :article => { :name => 'changed by me', :body => 'content ...' }
  1781 +
  1782 + a = profile.articles.find_by_path('changed-by-me')
  1783 + assert_not_nil a
  1784 + assert_equal profile, a.created_by
  1785 + end
  1786 +
  1787 + should 'not change created_by when updating article' do
  1788 + other_person = create_user('otherperson').person
  1789 +
  1790 + a = profile.articles.build(:name => 'my article')
  1791 + a.created_by = other_person
  1792 + a.save!
  1793 +
  1794 + login_as(profile.identifier)
  1795 + post :edit, :profile => profile.identifier, :id => a.id, :article => { :body => 'new content for this article' }
  1796 +
  1797 + a.reload
  1798 +
  1799 + assert_equal other_person, a.created_by
  1800 + end
  1801 +
1777 protected 1802 protected
1778 1803
1779 # FIXME this is to avoid adding an extra dependency for a proper JSON parser. 1804 # FIXME this is to avoid adding an extra dependency for a proper JSON parser.
test/functional/content_viewer_controller_test.rb
@@ -743,7 +743,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -743,7 +743,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
743 c = Community.create!(:name => 'test_com') 743 c = Community.create!(:name => 'test_com')
744 u = create_user_with_permission('test_user', 'publish_content', c) 744 u = create_user_with_permission('test_user', 'publish_content', c)
745 login_as u.identifier 745 login_as u.identifier
746 - a = create(Article, :profile => c, :name => 'test-article', :last_changed_by => u, :published => false) 746 + a = create(Article, :profile => c, :name => 'test-article', :created_by => u, :published => false)
747 747
748 get :view_page, :profile => c.identifier, :page => a.path 748 get :view_page, :profile => c.identifier, :page => a.path
749 749
test/functional/environment_design_controller_test.rb
@@ -8,7 +8,7 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase @@ -8,7 +8,7 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase
8 8
9 # TODO EnvironmentStatisticsBlock is DEPRECATED and will be removed from 9 # TODO EnvironmentStatisticsBlock is DEPRECATED and will be removed from
10 # the Noosfero core soon, see ActionItem3045 10 # the Noosfero core soon, see ActionItem3045
11 - ALL_BLOCKS = [ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ] 11 + ALL_BLOCKS = [ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ]
12 12
13 def setup 13 def setup
14 @controller = EnvironmentDesignController.new 14 @controller = EnvironmentDesignController.new
@@ -107,16 +107,6 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase @@ -107,16 +107,6 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase
107 assert_tag :tag => 'input', :attributes => { :id => 'block_limit' } 107 assert_tag :tag => 'input', :attributes => { :id => 'block_limit' }
108 end 108 end
109 109
110 - should 'be able to edit PeopleBlock' do  
111 - login_as(create_admin_user(Environment.default))  
112 - b = PeopleBlock.create!  
113 - e = Environment.default  
114 - e.boxes.create!  
115 - e.boxes.first.blocks << b  
116 - get :edit, :id => b.id  
117 - assert_tag :tag => 'input', :attributes => { :id => 'block_limit' }  
118 - end  
119 -  
120 should 'be able to edit SlideshowBlock' do 110 should 'be able to edit SlideshowBlock' do
121 login_as(create_admin_user(Environment.default)) 111 login_as(create_admin_user(Environment.default))
122 b = SlideshowBlock.create! 112 b = SlideshowBlock.create!
test/functional/profile_design_controller_test.rb
@@ -6,8 +6,7 @@ class ProfileDesignController; def rescue_action(e) raise e end; end @@ -6,8 +6,7 @@ class ProfileDesignController; def rescue_action(e) raise e end; end
6 class ProfileDesignControllerTest < ActionController::TestCase 6 class ProfileDesignControllerTest < ActionController::TestCase
7 7
8 COMMOM_BLOCKS = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock ] 8 COMMOM_BLOCKS = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock ]
9 - PERSON_BLOCKS = COMMOM_BLOCKS + [FriendsBlock, FavoriteEnterprisesBlock, CommunitiesBlock, EnterprisesBlock ]  
10 - PERSON_BLOCKS_WITH_MEMBERS = PERSON_BLOCKS + [MembersBlock] 9 + PERSON_BLOCKS = COMMOM_BLOCKS + [ FavoriteEnterprisesBlock, CommunitiesBlock, EnterprisesBlock ]
11 PERSON_BLOCKS_WITH_BLOG = PERSON_BLOCKS + [BlogArchivesBlock] 10 PERSON_BLOCKS_WITH_BLOG = PERSON_BLOCKS + [BlogArchivesBlock]
12 11
13 ENTERPRISE_BLOCKS = COMMOM_BLOCKS + [DisabledEnterpriseMessageBlock, FeaturedProductsBlock, FansBlock, ProductCategoriesBlock] 12 ENTERPRISE_BLOCKS = COMMOM_BLOCKS + [DisabledEnterpriseMessageBlock, FeaturedProductsBlock, FansBlock, ProductCategoriesBlock]
@@ -525,23 +524,6 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase @@ -525,23 +524,6 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
525 assert_equal PERSON_BLOCKS, @controller.available_blocks 524 assert_equal PERSON_BLOCKS, @controller.available_blocks
526 end 525 end
527 526
528 - should 'the person with members blocks are all available' do  
529 - profile = mock  
530 - profile.stubs(:has_members?).returns(true)  
531 - profile.stubs(:person?).returns(true)  
532 - profile.stubs(:community?).returns(true)  
533 - profile.stubs(:enterprise?).returns(false)  
534 - profile.stubs(:has_blog?).returns(false)  
535 - profile.stubs(:is_admin?).with(anything).returns(false)  
536 - environment = mock  
537 - profile.stubs(:environment).returns(environment)  
538 - environment.stubs(:enabled?).returns(false)  
539 - @controller.stubs(:profile).returns(profile)  
540 - @controller.stubs(:user).returns(profile)  
541 - Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([])  
542 - assert_equal [], @controller.available_blocks - PERSON_BLOCKS_WITH_MEMBERS  
543 - end  
544 -  
545 should 'the person with blog blocks are all available' do 527 should 'the person with blog blocks are all available' do
546 profile = mock 528 profile = mock
547 profile.stubs(:has_members?).returns(false) 529 profile.stubs(:has_members?).returns(false)
test/unit/article_test.rb
@@ -154,7 +154,7 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -154,7 +154,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
154 assert a4.errors[:slug.to_s].present? 154 assert a4.errors[:slug.to_s].present?
155 end 155 end
156 156
157 - should 'record who did the last change' do 157 + should 'last_changed_by be a person' do
158 a = profile.articles.build(:name => 'test') 158 a = profile.articles.build(:name => 'test')
159 159
160 # must be a person 160 # must be a person
@@ -167,6 +167,19 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -167,6 +167,19 @@ class ArticleTest &lt; ActiveSupport::TestCase
167 end 167 end
168 end 168 end
169 169
  170 + should 'created_by be a person' do
  171 + a = profile.articles.build(:name => 'test')
  172 +
  173 + # must be a person
  174 + assert_raise ActiveRecord::AssociationTypeMismatch do
  175 + a.created_by = Profile.new
  176 + end
  177 + assert_nothing_raised do
  178 + a.created_by = Person.new
  179 + a.save!
  180 + end
  181 + end
  182 +
170 should 'not show private documents as recent' do 183 should 'not show private documents as recent' do
171 p = create_user('usr1').person 184 p = create_user('usr1').person
172 Article.destroy_all 185 Article.destroy_all
@@ -802,7 +815,7 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -802,7 +815,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
802 should 'allow author to edit if is publisher' do 815 should 'allow author to edit if is publisher' do
803 c = fast_create(Community) 816 c = fast_create(Community)
804 p = create_user_with_permission('test_user', 'publish_content', c) 817 p = create_user_with_permission('test_user', 'publish_content', c)
805 - a = create(Article, :name => 'a test article', :last_changed_by => p, :profile_id => c.id) 818 + a = create(Article, :name => 'a test article', :created_by => p, :profile_id => c.id)
806 819
807 assert a.allow_post_content?(p) 820 assert a.allow_post_content?(p)
808 end 821 end
@@ -1380,17 +1393,17 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -1380,17 +1393,17 @@ class ArticleTest &lt; ActiveSupport::TestCase
1380 1393
1381 should "the author_name returns the name of the article's author" do 1394 should "the author_name returns the name of the article's author" do
1382 author = fast_create(Person) 1395 author = fast_create(Person)
1383 - a = create(Article, :name => 'a test article', :last_changed_by => author, :profile_id => profile.id) 1396 + a = create(Article, :name => 'a test article', :created_by => author, :profile_id => profile.id)
1384 assert_equal author.name, a.author_name 1397 assert_equal author.name, a.author_name
1385 author.destroy 1398 author.destroy
1386 - a.reload 1399 + a = Article.find(a.id)
1387 a.author_name = 'some name' 1400 a.author_name = 'some name'
1388 assert_equal 'some name', a.author_name 1401 assert_equal 'some name', a.author_name
1389 end 1402 end
1390 1403
1391 should 'retrieve latest info from topic when has no comments' do 1404 should 'retrieve latest info from topic when has no comments' do
1392 forum = fast_create(Forum, :name => 'Forum test', :profile_id => profile.id) 1405 forum = fast_create(Forum, :name => 'Forum test', :profile_id => profile.id)
1393 - post = fast_create(TextileArticle, :name => 'First post', :profile_id => profile.id, :parent_id => forum.id, :updated_at => Time.now, :last_changed_by_id => profile.id) 1406 + post = fast_create(TextileArticle, :name => 'First post', :profile_id => profile.id, :parent_id => forum.id, :updated_at => Time.now, :last_changed_by_id => profile.id, :created_by_id => profile.id)
1394 assert_equal post.updated_at, post.info_from_last_update[:date] 1407 assert_equal post.updated_at, post.info_from_last_update[:date]
1395 assert_equal profile.name, post.info_from_last_update[:author_name] 1408 assert_equal profile.name, post.info_from_last_update[:author_name]
1396 assert_equal profile.url, post.info_from_last_update[:author_url] 1409 assert_equal profile.url, post.info_from_last_update[:author_url]
@@ -1744,30 +1757,30 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -1744,30 +1757,30 @@ class ArticleTest &lt; ActiveSupport::TestCase
1744 1757
1745 should 'set author_name before creating article if there is an author' do 1758 should 'set author_name before creating article if there is an author' do
1746 author = fast_create(Person) 1759 author = fast_create(Person)
1747 - article = create(Article, :name => 'Test', :profile => profile, :last_changed_by => author) 1760 + article = create(Article, :name => 'Test', :profile => profile, :created_by => author)
1748 assert_equal author.name, article.author_name 1761 assert_equal author.name, article.author_name
1749 1762
1750 author_name = author.name 1763 author_name = author.name
1751 author.destroy 1764 author.destroy
1752 - article.reload 1765 + article = Article.find(article.id)
1753 assert_equal author_name, article.author_name 1766 assert_equal author_name, article.author_name
1754 end 1767 end
1755 1768
1756 should "author_id return the author id of the article's author" do 1769 should "author_id return the author id of the article's author" do
1757 author = fast_create(Person) 1770 author = fast_create(Person)
1758 - article = create(Article, :name => 'Test', :profile => profile, :last_changed_by => author) 1771 + article = create(Article, :name => 'Test', :profile => profile, :created_by => author)
1759 assert_equal author.id, article.author_id 1772 assert_equal author.id, article.author_id
1760 end 1773 end
1761 1774
1762 should "author_id return nil if there is no article's author" do 1775 should "author_id return nil if there is no article's author" do
1763 - article = create(Article, :name => 'Test', :profile => profile, :last_changed_by => nil) 1776 + article = create(Article, :name => 'Test', :profile => profile, :created_by => nil)
1764 assert_nil article.author_id 1777 assert_nil article.author_id
1765 end 1778 end
1766 1779
1767 should "return the author of a specific version" do 1780 should "return the author of a specific version" do
1768 author1 = fast_create(Person) 1781 author1 = fast_create(Person)
1769 author2 = fast_create(Person) 1782 author2 = fast_create(Person)
1770 - article = create(Article, :name => 'first version', :profile => profile, :last_changed_by => author1) 1783 + article = create(Article, :name => 'first version', :profile => profile, :created_by => author1, :last_changed_by => author1)
1771 article.name = 'second version' 1784 article.name = 'second version'
1772 article.last_changed_by = author2 1785 article.last_changed_by = author2
1773 article.save 1786 article.save
@@ -1778,7 +1791,7 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -1778,7 +1791,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1778 should "return the author_name of a specific version" do 1791 should "return the author_name of a specific version" do
1779 author1 = fast_create(Person) 1792 author1 = fast_create(Person)
1780 author2 = fast_create(Person) 1793 author2 = fast_create(Person)
1781 - article = create(Article, :name => 'first version', :profile => profile, :last_changed_by => author1) 1794 + article = create(Article, :name => 'first version', :profile => profile, :created_by => author1)
1782 article.name = 'second version' 1795 article.name = 'second version'
1783 article.last_changed_by = author2 1796 article.last_changed_by = author2
1784 article.save 1797 article.save
@@ -1828,4 +1841,12 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -1828,4 +1841,12 @@ class ArticleTest &lt; ActiveSupport::TestCase
1828 assert_equivalent [c3], Article.with_types(['Event']) 1841 assert_equivalent [c3], Article.with_types(['Event'])
1829 end 1842 end
1830 1843
  1844 + should 'not create version when receive a comment' do
  1845 + a = Article.new(:name => 'my article', :body => 'my text')
  1846 + a.profile = profile
  1847 + a.save!
  1848 + Comment.create!(:title => 'test', :body => 'asdsad', :author => profile, :source => a)
  1849 + assert_equal 1, a.versions.count
  1850 + end
  1851 +
1831 end 1852 end
test/unit/box_test.rb
@@ -39,14 +39,11 @@ class BoxTest &lt; ActiveSupport::TestCase @@ -39,14 +39,11 @@ class BoxTest &lt; ActiveSupport::TestCase
39 assert blocks.include?('fans-block') 39 assert blocks.include?('fans-block')
40 assert blocks.include?('favorite-enterprises-block') 40 assert blocks.include?('favorite-enterprises-block')
41 assert blocks.include?('feed-reader-block') 41 assert blocks.include?('feed-reader-block')
42 - assert blocks.include?('friends-block')  
43 assert blocks.include?('highlights-block') 42 assert blocks.include?('highlights-block')
44 assert blocks.include?('link-list-block') 43 assert blocks.include?('link-list-block')
45 assert blocks.include?('login-block') 44 assert blocks.include?('login-block')
46 assert blocks.include?('main-block') 45 assert blocks.include?('main-block')
47 - assert blocks.include?('members-block')  
48 assert blocks.include?('my-network-block') 46 assert blocks.include?('my-network-block')
49 - assert blocks.include?('people-block')  
50 assert blocks.include?('profile-image-block') 47 assert blocks.include?('profile-image-block')
51 assert blocks.include?('raw-html-block') 48 assert blocks.include?('raw-html-block')
52 assert blocks.include?('recent-documents-block') 49 assert blocks.include?('recent-documents-block')
@@ -74,14 +71,11 @@ class BoxTest &lt; ActiveSupport::TestCase @@ -74,14 +71,11 @@ class BoxTest &lt; ActiveSupport::TestCase
74 assert blocks.include?('favorite-enterprises-block') 71 assert blocks.include?('favorite-enterprises-block')
75 assert blocks.include?('featured-products-block') 72 assert blocks.include?('featured-products-block')
76 assert blocks.include?('feed-reader-block') 73 assert blocks.include?('feed-reader-block')
77 - assert blocks.include?('friends-block')  
78 assert blocks.include?('highlights-block') 74 assert blocks.include?('highlights-block')
79 assert blocks.include?('link-list-block') 75 assert blocks.include?('link-list-block')
80 assert blocks.include?('location-block') 76 assert blocks.include?('location-block')
81 assert blocks.include?('login-block') 77 assert blocks.include?('login-block')
82 - assert blocks.include?('members-block')  
83 assert blocks.include?('my-network-block') 78 assert blocks.include?('my-network-block')
84 - assert blocks.include?('people-block')  
85 assert blocks.include?('products-block') 79 assert blocks.include?('products-block')
86 assert blocks.include?('profile-image-block') 80 assert blocks.include?('profile-image-block')
87 assert blocks.include?('profile-info-block') 81 assert blocks.include?('profile-info-block')
test/unit/forum_helper_test.rb
@@ -38,7 +38,7 @@ class ForumHelperTest &lt; ActiveSupport::TestCase @@ -38,7 +38,7 @@ class ForumHelperTest &lt; ActiveSupport::TestCase
38 38
39 should 'return post update if it has no comments' do 39 should 'return post update if it has no comments' do
40 author = create_user('forum test author').person 40 author = create_user('forum test author').person
41 - some_post = create(TextileArticle, :name => 'First post', :profile => profile, :parent => forum, :published => true, :last_changed_by => author) 41 + some_post = create(TextileArticle, :name => 'First post', :profile => profile, :parent => forum, :published => true, :created_by => author)
42 assert some_post.comments.empty? 42 assert some_post.comments.empty?
43 out = last_topic_update(some_post) 43 out = last_topic_update(some_post)
44 assert_match some_post.updated_at.to_s, out 44 assert_match some_post.updated_at.to_s, out
test/unit/friends_block_test.rb
@@ -1,86 +0,0 @@ @@ -1,86 +0,0 @@
1 -require File.dirname(__FILE__) + '/../test_helper'  
2 -  
3 -class FriendsBlockTest < ActiveSupport::TestCase  
4 -  
5 - should 'describe itself' do  
6 - assert_not_equal ProfileListBlock.description, FriendsBlock.description  
7 - end  
8 -  
9 - should 'declare its default title' do  
10 - FriendsBlock.any_instance.stubs(:profile_count).returns(0)  
11 - assert_not_equal ProfileListBlock.new.default_title, FriendsBlock.new.default_title  
12 - end  
13 -  
14 - should 'list owner friends' do  
15 - p1 = create_user('testuser1').person  
16 - p2 = create_user('testuser2').person  
17 - p3 = create_user('testuser3').person  
18 - p4 = create_user('testuser4').person  
19 -  
20 - p1.add_friend(p2)  
21 - p1.add_friend(p3)  
22 - p1.add_friend(p4)  
23 - p1.friends.reload  
24 -  
25 - block = FriendsBlock.new  
26 - block.expects(:owner).returns(p1)  
27 -  
28 - assert_equivalent [p2, p3, p4], block.profiles  
29 - end  
30 -  
31 - should 'point to list with all friends' do  
32 - block = FriendsBlock.new  
33 - user = mock  
34 - user.expects(:identifier).returns('theuser')  
35 - block.expects(:owner).returns(user)  
36 -  
37 - expects(:link_to).with('View all', :profile => 'theuser', :controller => 'profile', :action => 'friends')  
38 -  
39 - instance_eval(&block.footer)  
40 - end  
41 -  
42 - should 'count number of owner friends' do  
43 - p1 = create_user('testuser1').person  
44 - p2 = create_user('testuser2').person  
45 - p3 = create_user('testuser3').person  
46 - p4 = create_user('testuser4').person  
47 -  
48 - p1.add_friend(p2)  
49 - p1.add_friend(p3)  
50 - p1.add_friend(p4)  
51 -  
52 - block = FriendsBlock.new  
53 - block.expects(:owner).returns(p1)  
54 -  
55 - assert_equal 3, block.profile_count  
56 - end  
57 -  
58 - should 'count number of public and private people' do  
59 - owner = create_user('testuser1').person  
60 - private_p = fast_create(Person, {:public_profile => false})  
61 - public_p = fast_create(Person, {:public_profile => true})  
62 -  
63 - owner.add_friend(private_p)  
64 - owner.add_friend(public_p)  
65 -  
66 - block = FriendsBlock.new  
67 - block.expects(:owner).returns(owner)  
68 -  
69 - assert_equal 2, block.profile_count  
70 - end  
71 -  
72 - should 'not count number of invisible people' do  
73 - owner = create_user('testuser1').person  
74 - private_p = fast_create(Person, {:visible => false})  
75 - public_p = fast_create(Person, {:visible => true})  
76 -  
77 - owner.add_friend(private_p)  
78 - owner.add_friend(public_p)  
79 -  
80 - block = FriendsBlock.new  
81 - block.expects(:owner).returns(owner)  
82 -  
83 - assert_equal 1, block.profile_count  
84 - end  
85 -  
86 -end  
test/unit/members_block_test.rb
@@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
1 -require File.dirname(__FILE__) + '/../test_helper'  
2 -  
3 -class MembersBlockTest < ActiveSupport::TestCase  
4 -  
5 - should 'inherit from ProfileListBlock' do  
6 - assert_kind_of ProfileListBlock, MembersBlock.new  
7 - end  
8 -  
9 - should 'describe itself' do  
10 - assert_not_equal ProfileListBlock.description, MembersBlock.description  
11 - end  
12 -  
13 - should 'provide a default title' do  
14 - assert_not_equal ProfileListBlock.new.default_title, MembersBlock.new.default_title  
15 - end  
16 -  
17 - should 'display members file' do  
18 - community = fast_create(Community)  
19 - block = MembersBlock.create  
20 - block.expects(:owner).returns(community)  
21 -  
22 - self.expects(:render).with(:file => 'blocks/members', :locals => { :profile => community, :show_join_leave_button => false}).returns('file-with-members-list')  
23 - assert_equal 'file-with-members-list', instance_eval(&block.footer)  
24 - end  
25 -  
26 - should 'pick random members' do  
27 - block = MembersBlock.new  
28 -  
29 - owner = mock  
30 - block.expects(:owner).returns(owner)  
31 -  
32 - list = []  
33 - owner.expects(:members).returns(list)  
34 -  
35 - assert_same list, block.profiles  
36 - end  
37 -  
38 - should 'use logged-in to compose cache key' do  
39 - person = fast_create(Person)  
40 - community = fast_create(Community)  
41 - block = MembersBlock.create  
42 - block.expects(:owner).returns(community)  
43 -  
44 - assert_match(/-logged-in/,block.cache_key('en', person))  
45 - end  
46 -  
47 - should 'use logged-in and member to compose cache key for members' do  
48 - person = fast_create(Person)  
49 - community = fast_create(Community)  
50 - community.add_member person  
51 - block = MembersBlock.create  
52 - block.expects(:owner).returns(community)  
53 -  
54 - assert_match(/-logged-in-member/,block.cache_key('en', person))  
55 - end  
56 -  
57 - should 'not change block cache key if user is nil' do  
58 - block = MembersBlock.new  
59 - assert_equal block.cache_key('en'), block.cache_key('en', nil)  
60 - end  
61 -  
62 -end  
test/unit/people_block_test.rb
@@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
1 -require File.dirname(__FILE__) + '/../test_helper'  
2 -  
3 -class PeopleBlockTest < ActiveSupport::TestCase  
4 -  
5 - should 'inherit from ProfileListBlock' do  
6 - assert_kind_of ProfileListBlock, PeopleBlock.new  
7 - end  
8 -  
9 - should 'declare its default title' do  
10 - assert_not_equal ProfileListBlock.new.default_title, PeopleBlock.new.default_title  
11 - end  
12 -  
13 - should 'describe itself' do  
14 - assert_not_equal ProfileListBlock.description, PeopleBlock.description  
15 - end  
16 -  
17 - should 'give help' do  
18 - assert_not_equal ProfileListBlock.new.help, PeopleBlock.new.help  
19 - end  
20 -  
21 - should 'list people' do  
22 - owner = fast_create(Environment)  
23 - block = PeopleBlock.new  
24 - block.expects(:owner).returns(owner).at_least_once  
25 - person1 = fast_create(Person, :environment_id => owner.id)  
26 - person2 = fast_create(Person, :environment_id => owner.id)  
27 -  
28 - expects(:profile_image_link).with(person1, :minor).returns(person1.name)  
29 - expects(:profile_image_link).with(person2, :minor).returns(person2.name)  
30 - expects(:block_title).with(anything).returns('')  
31 -  
32 - content = instance_eval(&block.content)  
33 -  
34 - assert_match(/#{person1.name}/, content)  
35 - assert_match(/#{person2.name}/, content)  
36 - end  
37 -  
38 - should 'link to browse people' do  
39 - block = PeopleBlock.new  
40 - block.stubs(:owner).returns(Environment.default)  
41 -  
42 - expects(:_).with('View all').returns('View all people')  
43 - expects(:link_to).with('View all people', :controller => 'search', :action => 'people')  
44 - instance_eval(&block.footer)  
45 - end  
46 -  
47 - protected  
48 - include NoosferoTestHelper  
49 -  
50 -end  
test/unit/tiny_mce_article_test.rb
@@ -8,7 +8,7 @@ class TinyMceArticleTest &lt; ActiveSupport::TestCase @@ -8,7 +8,7 @@ class TinyMceArticleTest &lt; ActiveSupport::TestCase
8 @profile = create_user('zezinho').person 8 @profile = create_user('zezinho').person
9 end 9 end
10 attr_reader :profile 10 attr_reader :profile
11 - 11 +
12 # this test can be removed when we get real tests for TinyMceArticle 12 # this test can be removed when we get real tests for TinyMceArticle
13 should 'be an article' do 13 should 'be an article' do
14 assert_subclass TextArticle, TinyMceArticle 14 assert_subclass TextArticle, TinyMceArticle
@@ -210,7 +210,7 @@ end @@ -210,7 +210,7 @@ end
210 assert_equal true, a.notifiable? 210 assert_equal true, a.notifiable?
211 assert_equal true, a.advertise? 211 assert_equal true, a.advertise?
212 assert_equal true, a.is_trackable? 212 assert_equal true, a.is_trackable?
213 - 213 +
214 a.published=false 214 a.published=false
215 assert_equal false, a.published? 215 assert_equal false, a.published?
216 assert_equal false, a.is_trackable? 216 assert_equal false, a.is_trackable?
@@ -237,4 +237,13 @@ end @@ -237,4 +237,13 @@ end
237 assert_tag_in_string article.body, :tag => 'source', :attributes => {:src => 'http://example.ogv', :type => 'video/ogg'} 237 assert_tag_in_string article.body, :tag => 'source', :attributes => {:src => 'http://example.ogv', :type => 'video/ogg'}
238 end 238 end
239 239
  240 + should 'not sanitize colspan and rowspan attributes' do
  241 + article = TinyMceArticle.create!(:name => 'table with colspan and rowspan',
  242 + :body => "<table colspan='2' rowspan='3'><tr></tr></table>",
  243 + :profile => profile
  244 + )
  245 + assert_tag_in_string article.body, :tag => 'table',
  246 + :attributes => { :colspan => 2, :rowspan => 3 }
  247 + end
  248 +
240 end 249 end
vendor/plugins/acts_as_list/Rakefile 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +require 'rake'
  2 +require 'rake/testtask'
  3 +
  4 +desc 'Default: run acts_as_list unit tests.'
  5 +task :default => :test
  6 +
  7 +desc 'Test the acts_as_ordered_tree plugin.'
  8 +Rake::TestTask.new(:test) do |t|
  9 + t.libs << 'lib'
  10 + t.pattern = 'test/**/*_test.rb'
  11 + t.verbose = true
  12 +end
vendor/plugins/acts_as_list/lib/active_record/acts/list.rb
@@ -26,8 +26,8 @@ module ActiveRecord @@ -26,8 +26,8 @@ module ActiveRecord
26 # Configuration options are: 26 # Configuration options are:
27 # 27 #
28 # * +column+ - specifies the column name to use for keeping the position integer (default: +position+) 28 # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
29 - # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>  
30 - # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible 29 + # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
  30 + # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
31 # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key. 31 # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
32 # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt> 32 # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
33 def acts_as_list(options = {}) 33 def acts_as_list(options = {})
@@ -39,11 +39,16 @@ module ActiveRecord @@ -39,11 +39,16 @@ module ActiveRecord
39 if configuration[:scope].is_a?(Symbol) 39 if configuration[:scope].is_a?(Symbol)
40 scope_condition_method = %( 40 scope_condition_method = %(
41 def scope_condition 41 def scope_condition
42 - if #{configuration[:scope].to_s}.nil?  
43 - "#{configuration[:scope].to_s} IS NULL"  
44 - else  
45 - "#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}" 42 + self.class.send(:sanitize_sql_hash_for_conditions, { :#{configuration[:scope].to_s} => send(:#{configuration[:scope].to_s}) })
  43 + end
  44 + )
  45 + elsif configuration[:scope].is_a?(Array)
  46 + scope_condition_method = %(
  47 + def scope_condition
  48 + attrs = %w(#{configuration[:scope].join(" ")}).inject({}) do |memo,column|
  49 + memo[column.intern] = send(column.intern); memo
46 end 50 end
  51 + self.class.send(:sanitize_sql_hash_for_conditions, attrs)
47 end 52 end
48 ) 53 )
49 else 54 else
@@ -63,7 +68,7 @@ module ActiveRecord @@ -63,7 +68,7 @@ module ActiveRecord
63 68
64 #{scope_condition_method} 69 #{scope_condition_method}
65 70
66 - before_destroy :remove_from_list 71 + before_destroy :decrement_positions_on_lower_items
67 before_create :add_to_list_bottom 72 before_create :add_to_list_bottom
68 EOV 73 EOV
69 end 74 end
@@ -250,7 +255,7 @@ module ActiveRecord @@ -250,7 +255,7 @@ module ActiveRecord
250 increment_positions_on_lower_items(position) 255 increment_positions_on_lower_items(position)
251 self.update_attribute(position_column, position) 256 self.update_attribute(position_column, position)
252 end 257 end
253 - end 258 + end
254 end 259 end
255 end 260 end
256 end 261 end
vendor/plugins/acts_as_list/test/list_test.rb
@@ -6,14 +6,15 @@ require &#39;active_record&#39; @@ -6,14 +6,15 @@ require &#39;active_record&#39;
6 6
7 require "#{File.dirname(__FILE__)}/../init" 7 require "#{File.dirname(__FILE__)}/../init"
8 8
9 -ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:") 9 +ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
10 10
11 def setup_db 11 def setup_db
12 ActiveRecord::Schema.define(:version => 1) do 12 ActiveRecord::Schema.define(:version => 1) do
13 create_table :mixins do |t| 13 create_table :mixins do |t|
14 t.column :pos, :integer 14 t.column :pos, :integer
15 t.column :parent_id, :integer 15 t.column :parent_id, :integer
16 - t.column :created_at, :datetime 16 + t.column :parent_type, :string
  17 + t.column :created_at, :datetime
17 t.column :updated_at, :datetime 18 t.column :updated_at, :datetime
18 end 19 end
19 end 20 end
@@ -46,6 +47,11 @@ class ListWithStringScopeMixin &lt; ActiveRecord::Base @@ -46,6 +47,11 @@ class ListWithStringScopeMixin &lt; ActiveRecord::Base
46 def self.table_name() "mixins" end 47 def self.table_name() "mixins" end
47 end 48 end
48 49
  50 +class ArrayScopeListMixin < Mixin
  51 + acts_as_list :column => "pos", :scope => [:parent_id, :parent_type]
  52 +
  53 + def self.table_name() "mixins" end
  54 +end
49 55
50 class ListTest < Test::Unit::TestCase 56 class ListTest < Test::Unit::TestCase
51 57
@@ -95,7 +101,7 @@ class ListTest &lt; Test::Unit::TestCase @@ -95,7 +101,7 @@ class ListTest &lt; Test::Unit::TestCase
95 101
96 def test_injection 102 def test_injection
97 item = ListMixin.new(:parent_id => 1) 103 item = ListMixin.new(:parent_id => 1)
98 - assert_equal "parent_id = 1", item.scope_condition 104 + assert_equal '"mixins"."parent_id" = 1', item.scope_condition
99 assert_equal "pos", item.position_column 105 assert_equal "pos", item.position_column
100 end 106 end
101 107
@@ -187,40 +193,60 @@ class ListTest &lt; Test::Unit::TestCase @@ -187,40 +193,60 @@ class ListTest &lt; Test::Unit::TestCase
187 new2.move_higher 193 new2.move_higher
188 assert_equal [new2, new1, new3], ListMixin.find(:all, :conditions => 'parent_id IS NULL', :order => 'pos') 194 assert_equal [new2, new1, new3], ListMixin.find(:all, :conditions => 'parent_id IS NULL', :order => 'pos')
189 end 195 end
190 -  
191 -  
192 - def test_remove_from_list_should_then_fail_in_list? 196 +
  197 + def test_remove_from_list_should_then_fail_in_list?
193 assert_equal true, ListMixin.find(1).in_list? 198 assert_equal true, ListMixin.find(1).in_list?
194 ListMixin.find(1).remove_from_list 199 ListMixin.find(1).remove_from_list
195 assert_equal false, ListMixin.find(1).in_list? 200 assert_equal false, ListMixin.find(1).in_list?
196 - end  
197 -  
198 - def test_remove_from_list_should_set_position_to_nil 201 + end
  202 +
  203 + def test_remove_from_list_should_set_position_to_nil
199 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id) 204 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
200 -  
201 - ListMixin.find(2).remove_from_list  
202 - 205 +
  206 + ListMixin.find(2).remove_from_list
  207 +
203 assert_equal [2, 1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id) 208 assert_equal [2, 1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
204 - 209 +
205 assert_equal 1, ListMixin.find(1).pos 210 assert_equal 1, ListMixin.find(1).pos
206 assert_equal nil, ListMixin.find(2).pos 211 assert_equal nil, ListMixin.find(2).pos
207 assert_equal 2, ListMixin.find(3).pos 212 assert_equal 2, ListMixin.find(3).pos
208 assert_equal 3, ListMixin.find(4).pos 213 assert_equal 3, ListMixin.find(4).pos
209 - end  
210 -  
211 - def test_remove_before_destroy_does_not_shift_lower_items_twice 214 + end
  215 +
  216 + def test_remove_before_destroy_does_not_shift_lower_items_twice
212 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id) 217 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
213 -  
214 - ListMixin.find(2).remove_from_list  
215 - ListMixin.find(2).destroy  
216 - 218 +
  219 + ListMixin.find(2).remove_from_list
  220 + ListMixin.find(2).destroy
  221 +
217 assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id) 222 assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
218 - 223 +
  224 + assert_equal 1, ListMixin.find(1).pos
  225 + assert_equal 2, ListMixin.find(3).pos
  226 + assert_equal 3, ListMixin.find(4).pos
  227 + end
  228 +
  229 + def test_before_destroy_callbacks_do_not_update_position_to_nil_before_deleting_the_record
  230 + assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
  231 +
  232 + # We need to trigger all the before_destroy callbacks without actually
  233 + # destroying the record so we can see the affect the callbacks have on
  234 + # the record.
  235 + list = ListMixin.find(2)
  236 + if list.respond_to?(:run_callbacks)
  237 + list.run_callbacks(:destroy)
  238 + else
  239 + list.send(:callback, :before_destroy)
  240 + end
  241 +
  242 + assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
  243 +
219 assert_equal 1, ListMixin.find(1).pos 244 assert_equal 1, ListMixin.find(1).pos
  245 + assert_equal 2, ListMixin.find(2).pos
220 assert_equal 2, ListMixin.find(3).pos 246 assert_equal 2, ListMixin.find(3).pos
221 assert_equal 3, ListMixin.find(4).pos 247 assert_equal 3, ListMixin.find(4).pos
222 - end  
223 - 248 + end
  249 +
224 end 250 end
225 251
226 class ListSubTest < Test::Unit::TestCase 252 class ListSubTest < Test::Unit::TestCase
@@ -271,7 +297,7 @@ class ListSubTest &lt; Test::Unit::TestCase @@ -271,7 +297,7 @@ class ListSubTest &lt; Test::Unit::TestCase
271 297
272 def test_injection 298 def test_injection
273 item = ListMixin.new("parent_id"=>1) 299 item = ListMixin.new("parent_id"=>1)
274 - assert_equal "parent_id = 1", item.scope_condition 300 + assert_equal '"mixins"."parent_id" = 1', item.scope_condition
275 assert_equal "pos", item.position_column 301 assert_equal "pos", item.position_column
276 end 302 end
277 303
@@ -330,3 +356,165 @@ class ListSubTest &lt; Test::Unit::TestCase @@ -330,3 +356,165 @@ class ListSubTest &lt; Test::Unit::TestCase
330 end 356 end
331 357
332 end 358 end
  359 +
  360 +class ArrayScopeListTest < Test::Unit::TestCase
  361 +
  362 + def setup
  363 + setup_db
  364 + (1..4).each { |counter| ArrayScopeListMixin.create! :pos => counter, :parent_id => 5, :parent_type => 'ParentClass' }
  365 + end
  366 +
  367 + def teardown
  368 + teardown_db
  369 + end
  370 +
  371 + def test_reordering
  372 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  373 +
  374 + ArrayScopeListMixin.find(2).move_lower
  375 + assert_equal [1, 3, 2, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  376 +
  377 + ArrayScopeListMixin.find(2).move_higher
  378 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  379 +
  380 + ArrayScopeListMixin.find(1).move_to_bottom
  381 + assert_equal [2, 3, 4, 1], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  382 +
  383 + ArrayScopeListMixin.find(1).move_to_top
  384 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  385 +
  386 + ArrayScopeListMixin.find(2).move_to_bottom
  387 + assert_equal [1, 3, 4, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  388 +
  389 + ArrayScopeListMixin.find(4).move_to_top
  390 + assert_equal [4, 1, 3, 2], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  391 + end
  392 +
  393 + def test_move_to_bottom_with_next_to_last_item
  394 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  395 + ArrayScopeListMixin.find(3).move_to_bottom
  396 + assert_equal [1, 2, 4, 3], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  397 + end
  398 +
  399 + def test_next_prev
  400 + assert_equal ArrayScopeListMixin.find(2), ArrayScopeListMixin.find(1).lower_item
  401 + assert_nil ArrayScopeListMixin.find(1).higher_item
  402 + assert_equal ArrayScopeListMixin.find(3), ArrayScopeListMixin.find(4).higher_item
  403 + assert_nil ArrayScopeListMixin.find(4).lower_item
  404 + end
  405 +
  406 + def test_injection
  407 + item = ArrayScopeListMixin.new(:parent_id => 1, :parent_type => 'ParentClass')
  408 + assert_equal '"mixins"."parent_id" = 1 AND "mixins"."parent_type" = \'ParentClass\'', item.scope_condition
  409 + assert_equal "pos", item.position_column
  410 + end
  411 +
  412 + def test_insert
  413 + new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  414 + assert_equal 1, new.pos
  415 + assert new.first?
  416 + assert new.last?
  417 +
  418 + new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  419 + assert_equal 2, new.pos
  420 + assert !new.first?
  421 + assert new.last?
  422 +
  423 + new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  424 + assert_equal 3, new.pos
  425 + assert !new.first?
  426 + assert new.last?
  427 +
  428 + new = ArrayScopeListMixin.create(:parent_id => 0, :parent_type => 'ParentClass')
  429 + assert_equal 1, new.pos
  430 + assert new.first?
  431 + assert new.last?
  432 + end
  433 +
  434 + def test_insert_at
  435 + new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  436 + assert_equal 1, new.pos
  437 +
  438 + new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  439 + assert_equal 2, new.pos
  440 +
  441 + new = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  442 + assert_equal 3, new.pos
  443 +
  444 + new4 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  445 + assert_equal 4, new4.pos
  446 +
  447 + new4.insert_at(3)
  448 + assert_equal 3, new4.pos
  449 +
  450 + new.reload
  451 + assert_equal 4, new.pos
  452 +
  453 + new.insert_at(2)
  454 + assert_equal 2, new.pos
  455 +
  456 + new4.reload
  457 + assert_equal 4, new4.pos
  458 +
  459 + new5 = ArrayScopeListMixin.create(:parent_id => 20, :parent_type => 'ParentClass')
  460 + assert_equal 5, new5.pos
  461 +
  462 + new5.insert_at(1)
  463 + assert_equal 1, new5.pos
  464 +
  465 + new4.reload
  466 + assert_equal 5, new4.pos
  467 + end
  468 +
  469 + def test_delete_middle
  470 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  471 +
  472 + ArrayScopeListMixin.find(2).destroy
  473 +
  474 + assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  475 +
  476 + assert_equal 1, ArrayScopeListMixin.find(1).pos
  477 + assert_equal 2, ArrayScopeListMixin.find(3).pos
  478 + assert_equal 3, ArrayScopeListMixin.find(4).pos
  479 +
  480 + ArrayScopeListMixin.find(1).destroy
  481 +
  482 + assert_equal [3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  483 +
  484 + assert_equal 1, ArrayScopeListMixin.find(3).pos
  485 + assert_equal 2, ArrayScopeListMixin.find(4).pos
  486 + end
  487 +
  488 + def test_remove_from_list_should_then_fail_in_list?
  489 + assert_equal true, ArrayScopeListMixin.find(1).in_list?
  490 + ArrayScopeListMixin.find(1).remove_from_list
  491 + assert_equal false, ArrayScopeListMixin.find(1).in_list?
  492 + end
  493 +
  494 + def test_remove_from_list_should_set_position_to_nil
  495 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  496 +
  497 + ArrayScopeListMixin.find(2).remove_from_list
  498 +
  499 + assert_equal [2, 1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  500 +
  501 + assert_equal 1, ArrayScopeListMixin.find(1).pos
  502 + assert_equal nil, ArrayScopeListMixin.find(2).pos
  503 + assert_equal 2, ArrayScopeListMixin.find(3).pos
  504 + assert_equal 3, ArrayScopeListMixin.find(4).pos
  505 + end
  506 +
  507 + def test_remove_before_destroy_does_not_shift_lower_items_twice
  508 + assert_equal [1, 2, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  509 +
  510 + ArrayScopeListMixin.find(2).remove_from_list
  511 + ArrayScopeListMixin.find(2).destroy
  512 +
  513 + assert_equal [1, 3, 4], ArrayScopeListMixin.find(:all, :conditions => "parent_id = 5 AND parent_type = 'ParentClass'", :order => 'pos').map(&:id)
  514 +
  515 + assert_equal 1, ArrayScopeListMixin.find(1).pos
  516 + assert_equal 2, ArrayScopeListMixin.find(3).pos
  517 + assert_equal 3, ArrayScopeListMixin.find(4).pos
  518 + end
  519 +
  520 +end