Commit cc81e07501b41177911bd389ce1bba503e1bd206

Authored by Leandro Santos
2 parents 97e6b3c1 6e1949cf

merging with master

Showing 136 changed files with 2164 additions and 423 deletions   Show diff stats
AUTHORS.md
... ... @@ -92,6 +92,7 @@ Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>
92 92 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
93 93 Daniel Bucher <daniel.bucher88@gmail.com>
94 94 Daniel Cunha <daniel@colivre.coop.br>
  95 +daniel <dtygel@eita.org.br>
95 96 David Carlos <ddavidcarlos1392@gmail.com>
96 97 diegoamc <diegoamc90@gmail.com>
97 98 Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com>
... ... @@ -120,6 +121,7 @@ Diego Araujo + Rodrigo Souto + Rafael Manzo &lt;rr.manzo@gmail.com&gt;
120 121 Diego + Jefferson <diegoamc90@gmail.com>
121 122 Diego Martinez <diegoamc90@gmail.com>
122 123 Diego + Renan <renanteruoc@gmail.com>
  124 +dtygel <dtygel@gmail.com>
123 125 DylanGuedes <djmgguedes@gmail.com>
124 126 Eduardo Passos <eduardo@risa.localdomain.localhost>
125 127 Eduardo Passos <eduardosteps@gmail.com>
... ... @@ -144,6 +146,7 @@ Italo Valcy &lt;italo@dcc.ufba.br&gt;
144 146 Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com>
145 147 Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com>
146 148 Jefferson Fernandes + Joao M. M. Silva <jeffs.fernandes@gmail.com>
  149 +Jérôme Jutteau <j.jutteau@gmail.com>
147 150 João da Silva + Eduardo Morais + Rafael Manzo <rr.manzo@gmail.com>
148 151 João da Silva <jaodsilv@linux.ime.usp.br>
149 152 João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br>
... ... @@ -247,8 +250,10 @@ Tallys Martins &lt;tallysmartins@gmail.com&gt;
247 250 Tallys Martins <tallysmartins@yahoo.com.br>
248 251 tallys <tallys@tallys>
249 252 tallys <tallys@tallys.(none)>
  253 +Thiago Casotti <thiago.casotti@uol.com.br>
250 254 Thiago Zoroastro <thiago.zoroastro@bol.com.br>
251 255 Tuux <tuxa@galaxie.eu.org>
  256 +TWS <tablettws@gmail.com>
252 257 Valessio Brito <contato@valessiobrito.com.br>
253 258 Valessio Brito <contato@valessiobrito.info>
254 259 Valessio Brito <valessio@gmail.com>
... ...
Gemfile
... ... @@ -20,18 +20,17 @@ gem &#39;locale&#39;, &#39;~&gt; 2.0.5&#39;
20 20 gem 'whenever', :require => false
21 21 gem 'eita-jrails', '>= 0.9.5', :require => 'jrails'
22 22  
23   -group :assets do
24   - gem 'uglifier', '>= 1.0.3'
25   - gem 'sass-rails'
26   -end
  23 +# asset pipeline
  24 +gem 'uglifier', '>= 1.0.3'
  25 +gem 'sass-rails'
27 26  
28 27 group :production do
29 28 gem 'dalli', '~> 2.7.0'
30 29 end
31 30  
32 31 group :test do
33   - gem 'rspec', '~> 2.10.0'
34   - gem 'rspec-rails', '~> 2.10.1'
  32 + gem 'rspec', '~> 2.14.0'
  33 + gem 'rspec-rails', '~> 2.14.1'
35 34 gem 'mocha', '~> 1.1.0', :require => false
36 35 end
37 36  
... ...
app/controllers/admin/role_controller.rb
... ... @@ -2,7 +2,7 @@ class RoleController &lt; AdminController
2 2 protect 'manage_environment_roles', :environment
3 3  
4 4 def index
5   - @roles = environment.roles.find(:all)
  5 + @roles = environment.roles.find(:all, :conditions => {:profile_id => nil})
6 6 end
7 7  
8 8 def new
... ...
app/controllers/application_controller.rb
... ... @@ -9,6 +9,7 @@ class ApplicationController &lt; ActionController::Base
9 9 before_filter :allow_cross_domain_access
10 10 before_filter :login_required, :if => :private_environment?
11 11 before_filter :verify_members_whitelist, :if => [:private_environment?, :user]
  12 + before_filter :redirect_to_current_user
12 13  
13 14 def verify_members_whitelist
14 15 render_access_denied unless user.is_admin? || environment.in_whitelist?(user)
... ... @@ -192,4 +193,15 @@ class ApplicationController &lt; ActionController::Base
192 193 def private_environment?
193 194 @environment.enabled?(:restrict_to_members)
194 195 end
  196 +
  197 + def redirect_to_current_user
  198 + if params[:profile] == '~'
  199 + if logged_in?
  200 + redirect_to params.merge(:profile => user.identifier)
  201 + else
  202 + render_not_found
  203 + end
  204 + end
  205 + end
  206 +
195 207 end
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -357,7 +357,8 @@ class CmsController &lt; MyProfileController
357 357 @task.ip_address = request.remote_ip
358 358 @task.user_agent = request.user_agent
359 359 @task.referrer = request.referrer
360   - if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
  360 + @task.requestor = current_person if logged_in?
  361 + if (logged_in? || verify_recaptcha(:model => @task, :message => _('Please type the words correctly'))) && @task.save
361 362 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
362 363 redirect_to @back_to
363 364 end
... ...
app/controllers/my_profile/profile_design_controller.rb
... ... @@ -4,11 +4,19 @@ class ProfileDesignController &lt; BoxOrganizerController
4 4  
5 5 protect 'edit_profile_design', :profile
6 6  
7   - before_filter :protect_fixed_block, :only => [:save, :move_block]
  7 + before_filter :protect_uneditable_block, :only => [:save]
  8 + before_filter :protect_fixed_block, :only => [:move_block]
  9 +
  10 + def protect_uneditable_block
  11 + block = boxes_holder.blocks.find(params[:id].gsub(/^block-/, ''))
  12 + if !current_person.is_admin? && !block.editable?
  13 + render_access_denied
  14 + end
  15 + end
8 16  
9 17 def protect_fixed_block
10 18 block = boxes_holder.blocks.find(params[:id].gsub(/^block-/, ''))
11   - if block.fixed && !current_person.is_admin?
  19 + if !current_person.is_admin? && !block.movable?
12 20 render_access_denied
13 21 end
14 22 end
... ...
app/controllers/my_profile/profile_members_controller.rb
... ... @@ -58,6 +58,7 @@ class ProfileMembersController &lt; MyProfileController
58 58  
59 59 def change_role
60 60 @roles = Profile::Roles.organization_member_roles(environment.id)
  61 + @custom_roles = profile.custom_roles
61 62 begin
62 63 @member = profile.members.find(params[:id])
63 64 rescue ActiveRecord::RecordNotFound
... ...
app/controllers/my_profile/profile_roles_controller.rb 0 → 100644
... ... @@ -0,0 +1,116 @@
  1 +class ProfileRolesController < MyProfileController
  2 +
  3 + protect 'manage_custom_roles', :profile
  4 +
  5 + def index
  6 + @roles = profile.custom_roles
  7 + end
  8 +
  9 + def new
  10 + @role = Role.new
  11 + end
  12 +
  13 + def create
  14 + @role = Role.new({:name => params[:role][:name], :permissions => params[:role][:permissions], :environment => environment }, :without_protection => true)
  15 + if @role.save
  16 + profile.custom_roles << @role
  17 + redirect_to :action => 'show', :id => @role
  18 + else
  19 + session[:notice] = _('Failed to create role')
  20 + render :action => 'new'
  21 + end
  22 + end
  23 +
  24 + def show
  25 + @role = environment.roles.find(params[:id])
  26 + end
  27 +
  28 + def edit
  29 + @role = environment.roles.find(params[:id])
  30 + end
  31 +
  32 + def assign_role_by_members
  33 + return redirect_to "/" if params[:q].nil? or !request.xhr?
  34 + arg = params[:q].downcase
  35 + result = find_by_contents(:people, environment, profile.members, params[:q])[:results]
  36 + render :text => prepare_to_token_input(result).to_json
  37 + end
  38 +
  39 + def destroy
  40 + @role = environment.roles.find(params[:id])
  41 + @members = profile.members_by_role(@role)
  42 + @roles_list = all_roles(environment, profile)
  43 + @roles_list.delete(@role)
  44 + end
  45 +
  46 + def remove
  47 + @role = environment.roles.find(params[:id])
  48 + @members = profile.members_by_role(@role)
  49 + member_roles = params[:roles] ? environment.roles.find(params[:roles].select{|r|!r.to_i.zero?}) : []
  50 + append_roles(@members, member_roles, profile)
  51 + if @role.destroy
  52 + session[:notice] = _('Role successfuly removed!')
  53 + else
  54 + session[:notice] = _('Failed to remove role!')
  55 + end
  56 + redirect_to :action => 'index'
  57 + end
  58 +
  59 + def update
  60 + @role = environment.roles.find(params[:id])
  61 + if @role.update_attributes(params[:role])
  62 + redirect_to :action => 'show', :id => @role
  63 + else
  64 + session[:notice] = _('Failed to edit role')
  65 + render :action => 'edit'
  66 + end
  67 + end
  68 +
  69 + def assign
  70 + @role = environment.roles.find(params[:id])
  71 + @roles_list = all_roles(environment, profile)
  72 + @roles_list.delete(@role)
  73 + end
  74 +
  75 + def define
  76 + @role = environment.roles.find(params[:id])
  77 + selected_role = params[:selected_role] ? environment.roles.find(params[:selected_role].to_i) : nil
  78 + if params[:assign_role_by].eql? "members"
  79 + members_list = params[:person_id].split(',').collect {|id| environment.profiles.find(id.to_i)}
  80 + members_list.collect{|person| person.add_role(@role, profile)}
  81 + elsif params[:assign_role_by].eql? "roles"
  82 + members = profile.members_by_role(selected_role)
  83 + replace_role(members, selected_role, @role, profile)
  84 + else
  85 + session[:notice] = _("Error")
  86 + end
  87 + redirect_to :action => 'index'
  88 + end
  89 +
  90 + protected
  91 +
  92 + def append_roles(members, roles, profile)
  93 + members.each do |person|
  94 + all_roles = person.find_roles(profile).map(&:role) + roles
  95 + person.define_roles(all_roles, profile)
  96 + end
  97 + end
  98 +
  99 + def all_roles(environment, profile)
  100 + Profile::Roles.organization_member_roles(environment.id) + profile.custom_roles
  101 + end
  102 +
  103 + def replace_roles(members, roles, profile)
  104 + members.each do |person|
  105 + person.define_roles(roles, profile)
  106 + end
  107 + end
  108 +
  109 + def replace_role(members, role, new_role, profile)
  110 + members.each do |person|
  111 + person.remove_role(role, profile)
  112 + person.add_role(new_role, profile)
  113 + end
  114 + end
  115 +
  116 +end
... ...
app/helpers/application_helper.rb
... ... @@ -707,6 +707,24 @@ module ApplicationHelper
707 707 javascript_include_tag script if script
708 708 end
709 709  
  710 + def template_path
  711 + if profile.nil?
  712 + "/designs/templates/#{environment.layout_template}"
  713 + else
  714 + "/designs/templates/#{profile.layout_template}"
  715 + end
  716 + end
  717 +
  718 + def template_javascript_src
  719 + script = File.join template_path, '/javascripts/template.js'
  720 + script if File.exists? File.join(Rails.root, 'public', script)
  721 + end
  722 +
  723 + def templete_javascript_ng
  724 + script = template_javascript_src
  725 + javascript_include_tag script if script
  726 + end
  727 +
710 728 def file_field_or_thumbnail(label, image, i)
711 729 display_form_field label, (
712 730 render :partial => (image && image.valid? ? 'shared/show_thumbnail' : 'shared/change_image'),
... ...
app/helpers/blog_helper.rb
... ... @@ -17,28 +17,28 @@ module BlogHelper
17 17 _('Configure blog')
18 18 end
19 19  
20   - def list_posts(articles, format = 'full', paginate = true)
  20 + def list_posts(articles, conf = { format: 'full', paginate: true })
21 21 pagination = will_paginate(articles, {
22 22 :param_name => 'npage',
23 23 :previous_label => _('&laquo; Newer posts'),
24 24 :next_label => _('Older posts &raquo;'),
25 25 :params => {:action=>"view_page", :page=>articles.first.parent.path.split('/'), :controller=>"content_viewer"}
26   - }) if articles.present? && paginate
  26 + }) if articles.present? && conf[:paginate]
27 27 content = []
28 28 artic_len = articles.length
29 29 articles.each_with_index{ |art,i|
30   - css_add = [ 'position-'+(i+1).to_s() ]
  30 + css_add = [ 'blog-post', 'position-'+(i+1).to_s() ]
31 31 position = (i%2 == 0) ? 'odd-post' : 'even-post'
32 32 css_add << 'first' if i == 0
33 33 css_add << 'last' if i == (artic_len-1)
34 34 css_add << 'not-published' if !art.published?
35   - css_add << position + '-inner'
36   - content << content_tag('div',
37   - content_tag('div',
38   - display_post(art, format).html_safe + '<br style="clear:both"/>'.html_safe,
39   - :class => 'blog-post ' + css_add.join(' '),
40   - :id => "post-#{art.id}"), :class => position
41   - )
  35 + css_add << position
  36 + content << (content_tag 'div', id: "post-#{art.id}", class: css_add do
  37 + content_tag 'div', class: position + '-inner blog-post-inner' do
  38 + display_post(art, conf[:format]).html_safe +
  39 + '<br style="clear:both"/>'.html_safe
  40 + end
  41 + end)
42 42 }
43 43 content.join("\n<hr class='sep-posts'/>\n") + (pagination or '')
44 44 end
... ... @@ -46,7 +46,16 @@ module BlogHelper
46 46 def display_post(article, format = 'full')
47 47 no_comments = (format == 'full') ? false : true
48 48 title = article_title(article, :no_comments => no_comments)
49   - html = send("display_#{format}_format", FilePresenter.for(article)).html_safe
  49 + method = "display_#{format.split('+')[0]}_format"
  50 + html = send(method, FilePresenter.for(article)).html_safe
  51 + if format.split('+')[1] == 'pic'
  52 + img = article.first_image
  53 + if img.blank?
  54 + '<div class="post-pic empty"></div>'
  55 + else
  56 + '<div class="post-pic" style="background-image:url('+img+')"></div>'
  57 + end
  58 + end.to_s +
50 59 title + html
51 60 end
52 61  
... ...
app/helpers/boxes_helper.rb
... ... @@ -190,7 +190,7 @@ module BoxesHelper
190 190 else
191 191 "before-block-#{block.id}"
192 192 end
193   - if block.nil? or modifiable?(block)
  193 + if block.nil? || movable?(block)
194 194 content_tag('div', '&nbsp;', :id => id, :class => 'block-target' ) + drop_receiving_element(id, :url => { :action => 'move_block', :target => id }, :accept => box.acceptable_blocks, :hoverclass => 'block-target-hover')
195 195 else
196 196 ""
... ... @@ -199,14 +199,14 @@ module BoxesHelper
199 199  
200 200 # makes the given block draggable so it can be moved away.
201 201 def block_handle(block)
202   - modifiable?(block) ? draggable_element("block-#{block.id}", :revert => true) : ""
  202 + movable?(block) ? draggable_element("block-#{block.id}", :revert => true) : ""
203 203 end
204 204  
205 205 def block_edit_buttons(block)
206 206 buttons = []
207 207 nowhere = 'javascript: return false;'
208 208  
209   - if modifiable?(block)
  209 + if movable?(block)
210 210 if block.first?
211 211 buttons << icon_button('up-disabled', _("Can't move up anymore."), nowhere)
212 212 else
... ... @@ -229,15 +229,15 @@ module BoxesHelper
229 229 buttons << icon_button('left', _('Move to the opposite side'), { :action => 'move_block', :target => 'end-of-box-' + holder.boxes[1].id.to_s, :id => block.id }, :method => 'post' )
230 230 end
231 231 end
  232 + end
232 233  
233   - if block.editable?
234   - buttons << modal_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id })
235   - end
  234 + if editable?(block)
  235 + buttons << modal_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id })
  236 + end
236 237  
237   - if !block.main?
238   - buttons << icon_button(:delete, _('Remove block'), { :action => 'remove', :id => block.id }, { :method => 'post', :confirm => _('Are you sure you want to remove this block?')})
239   - buttons << icon_button(:clone, _('Clone'), { :action => 'clone_block', :id => block.id }, { :method => 'post' })
240   - end
  238 + if movable?(block) && !block.main?
  239 + buttons << icon_button(:delete, _('Remove block'), { :action => 'remove', :id => block.id }, { :method => 'post', :confirm => _('Are you sure you want to remove this block?')})
  240 + buttons << icon_button(:clone, _('Clone'), { :action => 'clone_block', :id => block.id }, { :method => 'post' })
241 241 end
242 242  
243 243 if block.respond_to?(:help)
... ... @@ -273,7 +273,11 @@ module BoxesHelper
273 273 classes
274 274 end
275 275  
276   - def modifiable?(block)
277   - return !block.fixed || environment.admins.include?(user)
  276 + def movable?(block)
  277 + return block.movable? || user.is_admin?
  278 + end
  279 +
  280 + def editable?(block)
  281 + return block.editable? || user.is_admin?
278 282 end
279 283 end
... ...
app/helpers/layout_helper.rb
... ... @@ -28,7 +28,7 @@ module LayoutHelper
28 28 end
29 29  
30 30 def noosfero_javascript
31   - plugins_javascripts = @plugins.flat_map{ |plugin| plugin.js_files.map{ |js| plugin.class.public_path(js, true) } }.flatten
  31 + plugins_javascripts = @plugins.flat_map{ |plugin| Array.wrap(plugin.js_files).map{ |js| plugin.class.public_path(js, true) } }.flatten
32 32  
33 33 output = ''
34 34 output += render 'layouts/javascript'
... ... @@ -38,6 +38,8 @@ module LayoutHelper
38 38 output += theme_javascript_ng.to_s
39 39 output += javascript_tag 'render_all_jquery_ui_widgets()'
40 40  
  41 + output += templete_javascript_ng.to_s
  42 +
41 43 output
42 44 end
43 45  
... ... @@ -70,11 +72,7 @@ module LayoutHelper
70 72 end
71 73  
72 74 def template_stylesheet_path
73   - if profile.nil?
74   - "/designs/templates/#{environment.layout_template}/stylesheets/style.css"
75   - else
76   - "/designs/templates/#{profile.layout_template}/stylesheets/style.css"
77   - end
  75 + File.join template_path, "/stylesheets/style.css"
78 76 end
79 77  
80 78  
... ...
app/helpers/tinymce_helper.rb
... ... @@ -17,6 +17,7 @@ module TinymceHelper
17 17 searchreplace wordcount visualblocks visualchars code fullscreen
18 18 insertdatetime media nonbreaking save table contextmenu directionality
19 19 emoticons template paste textcolor colorpicker textpattern],
  20 + :image_advtab => true,
20 21 :language => tinymce_language
21 22  
22 23 options[:toolbar1] = "insertfile undo redo | copy paste | bold italic underline | styleselect fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
... ...
app/models/article.rb
... ... @@ -5,7 +5,7 @@ class Article &lt; ActiveRecord::Base
5 5 :allow_members_to_edit, :translation_of_id, :language,
6 6 :license_id, :parent_id, :display_posts_in_current_language,
7 7 :category_ids, :posts_per_page, :moderate_comments,
8   - :accept_comments, :feed, :published, :source,
  8 + :accept_comments, :feed, :published, :source, :source_name,
9 9 :highlighted, :notify_comments, :display_hits, :slug,
10 10 :external_feed_builder, :display_versions, :external_link,
11 11 :image_builder, :show_to_followers
... ... @@ -769,7 +769,9 @@ class Article &lt; ActiveRecord::Base
769 769 end
770 770  
771 771 def first_image
772   - img = Nokogiri::HTML.fragment(self.lead.to_s).css('img[src]').first || Nokogiri::HTML.fragment(self.body.to_s).search('img').first
  772 + img = ( image.present? && { 'src' => image.public_filename } ) ||
  773 + Nokogiri::HTML.fragment(self.lead.to_s).css('img[src]').first ||
  774 + Nokogiri::HTML.fragment(self.body.to_s).search('img').first
773 775 img.nil? ? '' : img['src']
774 776 end
775 777  
... ...
app/models/block.rb
1 1 class Block < ActiveRecord::Base
2 2  
3   - attr_accessible :title, :display, :limit, :box_id, :posts_per_page, :visualization_format, :language, :display_user, :box, :fixed
  3 + attr_accessible :title, :display, :limit, :box_id, :posts_per_page,
  4 + :visualization_format, :language, :display_user,
  5 + :box, :edit_modes, :move_modes
4 6  
5 7 # to be able to generate HTML
6 8 include ActionView::Helpers::UrlHelper
... ... @@ -23,7 +25,7 @@ class Block &lt; ActiveRecord::Base
23 25 end
24 26  
25 27 def get_limit
26   - [0,limit].max
  28 + [0,limit.to_i].max
27 29 end
28 30  
29 31 def embed_code
... ... @@ -110,8 +112,13 @@ class Block &lt; ActiveRecord::Base
110 112 # * <tt>'all'</tt>: the block is always displayed
111 113 settings_items :language, :type => :string, :default => 'all'
112 114  
113   - # The block can be configured to be fixed. Only can be edited by environment admins
114   - settings_items :fixed, :type => :boolean, :default => false
  115 + # The block can be configured to define the edition modes options. Only can be edited by environment admins
  116 + # It can assume the following values:
  117 + #
  118 + # * <tt>'all'</tt>: the block owner has all edit options for this block
  119 + # * <tt>'none'</tt>: the block owner can't do anything with the block
  120 + settings_items :edit_modes, :type => :string, :default => 'all'
  121 + settings_items :move_modes, :type => :string, :default => 'all'
115 122  
116 123 # returns the description of the block, used when the user sees a list of
117 124 # blocks to choose one to include in the design.
... ... @@ -148,7 +155,11 @@ class Block &lt; ActiveRecord::Base
148 155  
149 156 # Is this block editable? (Default to <tt>false</tt>)
150 157 def editable?
151   - true
  158 + self.edit_modes == "all"
  159 + end
  160 +
  161 + def movable?
  162 + self.move_modes == "all"
152 163 end
153 164  
154 165 # must always return false, except on MainBlock clas.
... ... @@ -228,6 +239,21 @@ class Block &lt; ActiveRecord::Base
228 239 }
229 240 end
230 241  
  242 + def edit_block_options
  243 + @edit_options ||= {
  244 + 'all' => _('Can be modified'),
  245 + 'none' => _('Cannot be modified')
  246 + }
  247 + end
  248 +
  249 + def move_block_options
  250 + @move_options ||= {
  251 + 'all' => _('Can be moved'),
  252 + 'none' => _('Cannot be moved')
  253 + }
  254 + end
  255 +
  256 +
231 257 def duplicate
232 258 duplicated_block = self.dup
233 259 duplicated_block.display = 'never'
... ...
app/models/blog.rb
... ... @@ -76,7 +76,7 @@ class Blog &lt; Folder
76 76 end
77 77  
78 78 settings_items :visualization_format, :type => :string, :default => 'full'
79   - validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format
  79 + validates_inclusion_of :visualization_format, :in => [ 'full', 'short', 'short+pic' ], :if => :visualization_format
80 80  
81 81 settings_items :display_posts_in_current_language, :type => :boolean, :default => false
82 82  
... ...
app/models/environment.rb
... ... @@ -19,6 +19,8 @@ class Environment &lt; ActiveRecord::Base
19 19 filename
20 20 end
21 21  
  22 + NUMBER_OF_BOXES = 4
  23 +
22 24 PERMISSIONS['Environment'] = {
23 25 'view_environment_admin_panel' => N_('View environment admin panel'),
24 26 'edit_environment_features' => N_('Edit environment features'),
... ... @@ -172,7 +174,7 @@ class Environment &lt; ActiveRecord::Base
172 174 acts_as_having_boxes
173 175  
174 176 after_create do |env|
175   - 3.times do
  177 + NUMBER_OF_BOXES.times do
176 178 env.boxes << Box.new
177 179 end
178 180  
... ... @@ -737,8 +739,8 @@ class Environment &lt; ActiveRecord::Base
737 739 end
738 740  
739 741 def is_default_template?(template)
740   - is_default = template == community_default_template
741   - is_default = is_default || template == person_default_template
  742 + is_default = template == community_default_template
  743 + is_default = is_default || template == person_default_template
742 744 is_default = is_default || template == enterprise_default_template
743 745 is_default
744 746 end
... ...
app/models/highlights_block.rb
... ... @@ -26,8 +26,16 @@ class HighlightsBlock &lt; Block
26 26 end
27 27  
28 28 def featured_images
29   - block_images = images.select{|i| !i[:image_src].nil? }.sort { |x, y| x[:position] <=> y[:position] }
30   - shuffle ? block_images.shuffle : block_images
  29 + images = get_images
  30 + shuffle ? images.shuffle : images
  31 + end
  32 +
  33 + def get_images
  34 + images.select do |i|
  35 + !i[:image_src].nil?
  36 + end.sort do |x, y|
  37 + x[:position] <=> y[:position]
  38 + end
31 39 end
32 40  
33 41 def content(args={})
... ...
app/models/organization.rb
... ... @@ -29,6 +29,8 @@ class Organization &lt; Profile
29 29  
30 30 has_many :mailings, :class_name => 'OrganizationMailing', :foreign_key => :source_id, :as => 'source'
31 31  
  32 + has_many :custom_roles, :class_name => 'Role', :foreign_key => :profile_id
  33 +
32 34 scope :more_popular, :order => 'members_count DESC'
33 35  
34 36 validate :presence_of_required_fieds, :unless => :is_template
... ...
app/models/profile.rb
... ... @@ -22,6 +22,8 @@ class Profile &lt; ActiveRecord::Base
22 22 :display => %w[compact]
23 23 }
24 24  
  25 + NUMBER_OF_BOXES = 4
  26 +
25 27 def self.default_search_display
26 28 'compact'
27 29 end
... ... @@ -43,7 +45,7 @@ class Profile &lt; ActiveRecord::Base
43 45 find_role('editor', env_id)
44 46 end
45 47 def self.organization_member_roles(env_id)
46   - all_roles(env_id).select{ |r| r.key.match(/^profile_/) unless r.key.blank? }
  48 + all_roles(env_id).select{ |r| r.key.match(/^profile_/) unless r.key.blank? || !r.profile_id.nil?}
47 49 end
48 50 def self.all_roles(env_id)
49 51 Role.all :conditions => { :environment_id => env_id }
... ... @@ -75,6 +77,7 @@ class Profile &lt; ActiveRecord::Base
75 77 'publish_content' => N_('Publish content'),
76 78 'invite_members' => N_('Invite members'),
77 79 'send_mail_to_members' => N_('Send e-Mail to members'),
  80 + 'manage_custom_roles' => N_('Manage custom roles'),
78 81 }
79 82  
80 83 acts_as_accessible
... ... @@ -361,7 +364,7 @@ class Profile &lt; ActiveRecord::Base
361 364 if template
362 365 apply_template(template, :copy_articles => false)
363 366 else
364   - 3.times do
  367 + NUMBER_OF_BOXES.times do
365 368 self.boxes << Box.new
366 369 end
367 370  
... ... @@ -404,6 +407,7 @@ class Profile &lt; ActiveRecord::Base
404 407 alias_method_chain :template, :default
405 408  
406 409 def apply_template(template, options = {:copy_articles => true})
  410 + self.template = template
407 411 copy_blocks_from(template)
408 412 copy_articles_from(template) if options[:copy_articles]
409 413 self.apply_type_specific_template(template)
... ...
app/models/suggest_article.rb
1 1 class SuggestArticle < Task
2 2  
3   - validates_presence_of :target_id, :article_name, :email, :name, :article_body
  3 + validates_presence_of :target_id
  4 + validates_presence_of :email, :name, :if => Proc.new { |task| task.requestor.blank? }
  5 + validates_associated :article_object
4 6  
5 7 settings_items :email, :type => String
6 8 settings_items :name, :type => String
7   - settings_items :article_name, :type => String
8   - settings_items :article_body, :type => String
9   - settings_items :article_abstract, :type => String
10   - settings_items :article_parent_id, :type => String
11   - settings_items :source, :type => String
12   - settings_items :source_name, :type => String
13   - settings_items :highlighted, :type => :boolean, :default => false
14 9 settings_items :ip_address, :type => String
15 10 settings_items :user_agent, :type => String
16 11 settings_items :referrer, :type => String
  12 + settings_items :article, :type => Hash, :default => {}
17 13  
18 14 after_create :schedule_spam_checking
19 15  
... ... @@ -24,34 +20,49 @@ class SuggestArticle &lt; Task
24 20 include Noosfero::Plugin::HotSpot
25 21  
26 22 def sender
27   - "#{name} (#{email})"
  23 + requestor ? "#{requestor.name}" : "#{name} (#{email})"
  24 + end
  25 +
  26 + def article_object
  27 + if @article_object.nil?
  28 + @article_object = article_type.new(article.merge({:profile => target}).except(:type))
  29 + if requestor.present?
  30 + @article_object.author = requestor
  31 + else
  32 + @article_object.author_name = name
  33 + end
  34 + end
  35 + @article_object
  36 + end
  37 +
  38 + def article_type
  39 + if article[:type].present?
  40 + type = article[:type].constantize
  41 + return type if type < Article
  42 + end
  43 + TinyMceArticle
28 44 end
29 45  
30 46 def perform
31   - task = TinyMceArticle.new
32   - task.profile = target
33   - task.name = article_name
34   - task.author_name = name
35   - task.body = article_body
36   - task.abstract = article_abstract
37   - task.parent_id = article_parent_id
38   - task.source = source
39   - task.source_name = source_name
40   - task.highlighted = highlighted
41   - task.save!
  47 + article_object.save!
42 48 end
43 49  
44 50 def title
45 51 _("Article suggestion")
46 52 end
47 53  
  54 + def article_name
  55 + article[:name]
  56 + end
  57 +
48 58 def subject
49 59 article_name
50 60 end
51 61  
52 62 def information
53   - { :message => _('%{sender} suggested the publication of the article: %{subject}.'),
54   - :variables => {:sender => sender} }
  63 + variables = requestor.blank? ? {:requestor => sender} : {}
  64 + { :message => _('%{requestor} suggested the publication of the article: %{subject}.'),
  65 + :variables => variables }
55 66 end
56 67  
57 68 def accept_details
... ... @@ -63,8 +74,8 @@ class SuggestArticle &lt; Task
63 74 end
64 75  
65 76 def target_notification_description
66   - _('%{sender} suggested the publication of the article: %{article}.') %
67   - {:sender => sender, :article => article_name}
  77 + _('%{requestor} suggested the publication of the article: %{article}.') %
  78 + {:requestor => sender, :article => article_name}
68 79 end
69 80  
70 81 def target_notification_message
... ...
app/models/user.rb
... ... @@ -45,7 +45,7 @@ class User &lt; ActiveRecord::Base
45 45 p = Person.new
46 46  
47 47 p.attributes = user.person_data
48   - p.identifier = user.login
  48 + p.identifier = user.login if p.identifier.blank?
49 49 p.user = user
50 50 p.environment = user.environment
51 51 p.name ||= user.name || user.login
... ...
app/views/blocks/profile_info_actions/_join_leave_community.html.erb
1   -<div class='join-leave-button'>
  1 +<div class='join-leave-button require-login-popup'>
2 2 <% if logged_in? %>
3 3 <% if profile.members.include?(user) %>
4 4 <%= button(:delete, content_tag('span', _('Leave community')), profile.leave_url,
... ...
app/views/box_organizer/edit.html.erb
... ... @@ -5,12 +5,6 @@
5 5  
6 6 <%= labelled_form_field(_('Custom title for this block: '), text_field(:block, :title, :maxlength => 20)) %>
7 7  
8   - <% if environment.admins.include?(user) %>
9   - <div class="fixed_block">
10   - <%= labelled_check_box(_("Fixed"), "block[fixed]", value = "1", checked = @block.fixed) %>
11   - </div>
12   - <% end %>
13   -
14 8 <%= render :partial => partial_for_class(@block.class) %>
15 9  
16 10 <div class="display">
... ... @@ -25,6 +19,15 @@
25 19  
26 20 <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + environment.locales.map {|key, value| [value, key]} )) %>
27 21  
  22 + <% if user.is_admin? %>
  23 + <div class="edit-modes">
  24 + <%= labelled_form_field _('Edit options:'), select_tag('block[edit_modes]', options_from_collection_for_select(@block.edit_block_options, :first, :last, @block.edit_modes)) %>
  25 + </div>
  26 + <div class="move-modes">
  27 + <%= labelled_form_field _('Move options:'), select_tag('block[move_modes]', options_from_collection_for_select(@block.move_block_options, :first, :last, @block.move_modes)) %>
  28 + </div>
  29 + <% end %>
  30 +
28 31 <% button_bar do %>
29 32 <%= submit_button(:save, _('Save')) %>
30 33 <%= modal_close_button(_('Cancel')) %>
... ...
app/views/cms/_blog.html.erb
... ... @@ -64,7 +64,11 @@
64 64 <%= labelled_check_box(_('Remove cover image'),'remove_image',true,false)%>
65 65 <% end %>
66 66  
67   -<%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %>
  67 +<%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [
  68 + [ _('Full post'), 'full'],
  69 + [ _('First paragraph'), 'short'],
  70 + [ _('First paragraph, with post picture'), 'short+pic']
  71 +])) %>
68 72  
69 73 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %>
70 74  
... ...
app/views/cms/suggest_an_article.html.erb
... ... @@ -6,21 +6,22 @@
6 6  
7 7 <%= labelled_form_for 'task' do |f| %>
8 8  
9   - <%= required labelled_form_field(_('Title'), text_field(:task, 'article_name', :size => 50)) %>
  9 + <%= required labelled_form_field(_('Title'), text_field('task[article]', 'name', :size => 50)) %>
10 10  
11   - <%= labelled_form_field(_('Source'), text_field(:task, 'source_name')) %>
  11 + <%= labelled_form_field(_('Source'), text_field('task[article]', 'source_name')) %>
12 12  
13   - <%= labelled_form_field(_('Source URL'), text_field(:task, 'source')) %>
  13 + <%= labelled_form_field(_('Source URL'), text_field('task[article]', 'source')) %>
14 14  
15   - <%= required labelled_form_field(_('Your name'), text_field(:task, 'name')) %>
16   -
17   - <%= required labelled_form_field(_('Email'), text_field(:task, 'email')) %>
  15 + <% unless logged_in? %>
  16 + <%= required labelled_form_field(_('Your name'), text_field(:task, 'name')) %>
  17 + <%= required labelled_form_field(_('Email'), text_field(:task, 'email')) %>
  18 + <% end %>
18 19  
19   - <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :object => :task, :abstract_method => 'article_abstract', :body_method => 'article_body'} %>
  20 + <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :object => 'task[article]'} %>
20 21  
21 22 <%= hidden_field_tag('back_to', @back_to) %>
22 23  
23   - <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
  24 + <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) unless logged_in? %>
24 25  
25 26 <% button_bar do %>
26 27 <%= submit_button :save, _('Save') %>
... ...
app/views/content_viewer/blog_page.html.erb
... ... @@ -18,6 +18,9 @@
18 18 format = inside_block.visualization_format
19 19 paginate = false
20 20 end
21   - (blog.empty? ? content_tag('em', _('(no posts)')) : list_posts(posts, format, paginate))
  21 + (blog.empty? ?
  22 + content_tag('em', _('(no posts)')) :
  23 + list_posts(posts, format: format, paginate: paginate)
  24 + )
22 25 %>
23 26 </div>
... ...
app/views/features/manage_fields.html.erb
1 1 <h1><%= _('Manage fields displayed for profiles') %></h1>
2 2  
  3 +<%= javascript_include_tag "manage-fields.js" %>
  4 +
3 5 <% tabs = [] %>
4 6 <% tabs << {:title => _("Person's fields"), :id => 'person-fields',
5 7 :content => (render :partial => 'manage_person_fields')} %>
... ... @@ -11,5 +13,3 @@
11 13 <% end %>
12 14  
13 15 <%= render_tabs(tabs) %>
14   -
15   -<%= javascript_include_tag "manage-fields.js" %>
... ...
app/views/profile/report_abuse.html.erb
... ... @@ -24,7 +24,7 @@
24 24 $('#report-abuse-submit-button').css('cursor', 'progress');
25 25 $.ajax({
26 26 type: 'POST',
27   - url: <%= url_for({:controller => 'profile', :action => 'register_report', :profile => profile.identifier}) %>,
  27 + url: <%= url_for({:controller => 'profile', :action => 'register_report', :profile => profile.identifier}).to_json %>,
28 28 data: $(form).serialize(),
29 29 dataType: 'json',
30 30 success: function(data, status, ajax){
... ...
app/views/profile_editor/edit.html.erb
1 1 <h1><%= _('Profile settings for %s') % profile.name %></h1>
2 2  
3   -<%= javascript_include_tag 'deactivate_profile' %>
4 3 <%= error_messages_for :profile_data %>
5 4  
6 5 <%= labelled_form_for :profile_data, :html => { :id => 'profile-data', :multipart => true } do |f| %>
... ...
app/views/profile_editor/index.html.erb
... ... @@ -28,6 +28,8 @@
28 28  
29 29 <%= control_panel_button(_('Manage Content'), 'cms', :controller => 'cms') %>
30 30  
  31 + <%= control_panel_button(_('Manage Roles'), 'roles', :controller => 'profile_roles') %>
  32 +
31 33 <% unless profile.enterprise? %>
32 34 <%= case profile.blogs.count
33 35 when 0
... ...
app/views/profile_members/change_role.html.erb
1 1 <h3> <%= _('Changing role of %s') % @member.name %> </h3>
2 2  
3 3 <%= labelled_form_for :member, :url => {:action => 'update_roles'} do |f| %>
4   -
5   - <%= _('Roles:') %> <br>
  4 +
  5 + <h4><%= _('Roles:') %></h4>
6 6 <% @roles.each do |r| %>
7 7 <%= labelled_check_box(r.name, 'roles[]', r.id, @associations.map(&:role).include?(r) ) %><br/>
8 8 <ul class="role-permissions">
... ... @@ -11,6 +11,17 @@
11 11 <% end %>
12 12 </ul>
13 13 <% end %>
  14 + <% unless @custom_roles.empty? %>
  15 + <h4><%= _('Custom Roles:') %></h4>
  16 + <% @custom_roles.each do |r| %>
  17 + <%= labelled_check_box(r.name, 'roles[]', r.id, @associations.map(&:role).include?(r) ) %><br/>
  18 + <ul class="role-permissions">
  19 + <% r.permissions.each do |p| %>
  20 + <li> <%= permission_name(p) %> </li>
  21 + <% end %>
  22 + </ul>
  23 + <% end %>
  24 + <% end %>
14 25 <%= hidden_field_tag 'person', @member.id %>
15 26  
16 27 <% button_bar do %>
... ...
app/views/profile_roles/_form.html.erb 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +<%= error_messages_for :role %>
  2 +
  3 +<%= labelled_form_for :role, :url => (mode == :edit) ? {:action => 'update', :id => role} : {:action => 'create'} do |f| %>
  4 +
  5 + <%= required_fields_message %>
  6 +
  7 + <%= required f.text_field(:name) %>
  8 +
  9 + <% permissions.each do |key| %>
  10 + <div class="permissions <%= key.downcase %>">
  11 + <h4><%= _('%s Permissions:' % key) %></h4>
  12 + <% ActiveRecord::Base::PERMISSIONS[key].keys.each do |p| %>
  13 + <%= check_box_tag("role[permissions][]", p, role.has_permission?(p), { :id => p }) %>
  14 + <%= content_tag(:label, permission_name(p), { :for => p }) %><br/>
  15 + <% end %>
  16 + </div>
  17 + <% end %>
  18 +
  19 + <% button_bar do %>
  20 + <%= submit_button('save', (mode == :edit) ? _('Save changes') : _('Create role'), :cancel => {:action => 'index'} ) %>
  21 + <% end %>
  22 +<% end %>
... ...
app/views/profile_roles/assign.html.erb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +<%= javascript_include_tag('assign_role.js') %>
  2 +
  3 +<h1> <%= _("Assign #{@role.name}") %> </h1>
  4 +
  5 +
  6 +<%= labelled_form_for :role, :url => { :action => 'define', :id => @role.id } do |f| %>
  7 +
  8 + <h2>
  9 + <%= _("Assign role by:") %>
  10 + </h2>
  11 + <p>
  12 + <%= labelled_radio_button _("Members"), :assign_role_by, "members", true, :id => "assign_role_by_members", :class => "assign_role_by" %>
  13 + &nbsp;
  14 + <%= labelled_radio_button _("Roles"), :assign_role_by, "roles", false, :id => "assign_role_by_roles", :class => "assign_role_by" %>
  15 + </p>
  16 + <div class="assign_by_members">
  17 + <%=token_input_field_tag(:person_id, 'search-profile-members', {:action => 'assign_role_by_members'},
  18 + {:focus => false, :hint_text => _('Select members to assign the role')}) %>
  19 +
  20 + <% button_bar do %>
  21 + <%= submit_button(:forward, _("Confirm")) %>
  22 + <% end %>
  23 + </div>
  24 + <div class="assign_by_roles" style="display: none;">
  25 + <h6>
  26 + <%= _("Replace role: ") %>
  27 + </h6>
  28 + <% @roles_list.each do |role| %>
  29 + <%= labelled_radio_button role.name , :selected_role, role.id , false, :class => "selected_role" %> <br>
  30 + <% end %>
  31 + <% button_bar do %>
  32 + <%= submit_button('save',_('Confirm'), :cancel => {:action => 'index'} ) %>
  33 + <% end %>
  34 + </div>
  35 +<% end %>
... ...
app/views/profile_roles/destroy.html.erb 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +<h1> <%= _("Deleting #{@role.name}") %> </h1>
  2 +
  3 +<% if @members.nil? || @members.empty? %>
  4 + <p><%= _('This role is not being currently used.')%></p>
  5 + <p><%= _('Are you sure you want to delete this role?') %></p>
  6 +
  7 + <% button_bar do %>
  8 + <%= button(:remove, _('Yes, I am sure'), {:action => 'remove', :id => @role.id}, :method => :post) %>
  9 + <%= button(:cancel, _('No, I gave up'), {:action => 'index'}) %>
  10 + <% end %>
  11 +<% else %>
  12 + <p><%= _('There are members currently using this role.')%></p>
  13 + <p><%= _('To which role do you want to change them?') %></p>
  14 + <%= labelled_form_for :role, :url => { :action => 'remove', :id => @role.id } do |f| %>
  15 + <% @roles_list.each do |role| %>
  16 + <%= check_box_tag("roles[]", role.id, false ,{:id => role.key}) %>
  17 + <%= content_tag(:label, role.name, { :for => role.key }) %><br/>
  18 + <% end %>
  19 + <% button_bar do %>
  20 + <%= submit_button('save',_('Delete role'), :cancel => {:action => 'index'} ) %>
  21 + <% end %>
  22 + <% end %>
  23 +<% end %>
... ...
app/views/profile_roles/edit.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h1> <%= _("Editing #{@role.name}") %> </h1>
  2 +
  3 +<%= render :partial => 'form', :locals => { :mode => :edit, :role => @role, :permissions => [@role.kind] } %>
... ...
app/views/profile_roles/index.html.erb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +<h1><%= _('Manage user roles') %></h1>
  2 +
  3 +<table>
  4 + <tr>
  5 + <th><%= _('Role') %></th>
  6 + <th><%= _('Actions') %></th>
  7 + </tr>
  8 + <% @roles.each do |role| %>
  9 + <tr>
  10 + <td>
  11 + <%= link_to role.name, :action => 'show', :id => role %>
  12 + </td>
  13 + <td>
  14 + <div style="text-align: center;">
  15 + <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => role %>
  16 + <%= button_without_text :delete, _('Delete'), :action => 'destroy', :id => role %>
  17 + <%= button_without_text 'vertical-toggle', _('Assign'), :action => 'assign', :id => role %>
  18 + </div>
  19 + </td>
  20 + </tr>
  21 + <% end %>
  22 +</table>
  23 +
  24 +<% button_bar do %>
  25 + <%= button :add, _('Create a new role'), :action => 'new' %>
  26 + <%= button :back, _('Back to control panel'), :controller => 'profile_editor' %>
  27 +<% end %>
... ...
app/views/profile_roles/new.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h1> <%= _("Create a new role") %> </h1>
  2 +
  3 +<%= render :partial => 'form', :locals => { :mode => :create, :role => @role, :permissions => ['Profile'] } %>
... ...
app/views/profile_roles/show.html.erb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<h1> <%= _(@role.name) %></h1>
  2 +
  3 +<h3> <%= _('Permissions') %> </h3>
  4 +<ul>
  5 + <% @role.permissions.each do |p| %>
  6 + <li> <%= permission_name(p) %> </li>
  7 + <% end %>
  8 +</ul>
  9 +
  10 +<% button_bar do %>
  11 + <%= button :edit, _('Edit'), :action => 'edit', :id => @role %>
  12 + <%= button :back, _('Back to roles management'), :action => 'index' %>
  13 +<% end %>
... ...
app/views/shared/_profile_search_form.html.erb
  1 +<% search_label = _("Find in %s's content") % profile.short_name %>
1 2 <%= form_tag( { :controller => 'profile_search', :profile => profile.identifier}, :method => 'get', :class => 'search_form' ) do %>
2 3 <div class="search-field">
3 4 <span class="formfield">
4   - <%= search_input_with_suggestions 'q', :articles, @q, :title => _("Find in %s's content") % profile.short_name %>
  5 + <%= search_input_with_suggestions 'q', :articles, @q, :title => search_label, :placeholder => search_label %>
5 6 </span>
6 7 <%= submit_button(:search, _('Search')) %>
7 8 <div>
... ...
app/views/spam/_suggest_article.html.erb
... ... @@ -7,13 +7,13 @@
7 7 <ul class="suggest-article-details" style="display: none">
8 8 <li><strong><%=_('Sent by')%></strong>: <%=task.name%> </li>
9 9 <li><strong><%=_('Email')%></strong>: <%=task.email%> </li>
10   - <li><strong><%=_('Source')%></strong>: <%=task.source_name%> </li>
11   - <li><strong><%=_('Source URL')%></strong>: <%=task.source%> </li>
12   - <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li>
13   - <li><strong><%=_('Lead')%></strong>: <%=task.article_abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_abstract%> </li>
  10 + <li><strong><%=_('Source')%></strong>: <%=task.article_object.source_name%> </li>
  11 + <li><strong><%=_('Source URL')%></strong>: <%=task.article_object.source%> </li>
  12 + <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_object.parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li>
  13 + <li><strong><%=_('Lead')%></strong>: <%=task.article_object.abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_object.abstract%> </li>
14 14 <li><strong><%=_('Body')%></strong>:
15 15 <div class='suggest-article-body'>
16   - <%= task.article_body %>
  16 + <%= task.article_object.body %>
17 17 </div>
18 18 </li>
19 19 </ul>
... ...
app/views/tasks/_add_member_accept_details.html.erb
1 1 <%= content = _("Roles:")+"<br />"
2   -roles = Profile::Roles.organization_member_roles(task.target.environment.id)
  2 +roles = Profile::Roles.organization_member_roles(task.target.environment.id) + profile.custom_roles
3 3 roles.each do |role|
4 4 content += labelled_check_box(role.name, "tasks[#{task.id}][task][roles][]", role.id, false)+"<br />"
5 5 end
6 6 content_tag('p', content, :class => 'member-classify-suggestion')
7 7 %>
8   -
... ...
app/views/tasks/_suggest_article_accept_details.html.erb
1 1 <%= render :file => 'shared/tiny_mce' %>
2 2  
3   -<%= labelled_form_field(_("Sent by: "), f.text_field(:name)) %>
4   -<p><%= label_tag(_("Email: %s") % task.email) %> </p>
5   -<%= required labelled_form_field(_('Title'), f.text_field(:article_name, :size => 50)) %>
6   -<%= labelled_form_field(_('Source'), f.text_field(:source_name)) %>
7   -<%= labelled_form_field(_("Source URL"), f.text_field(:source)) %>
  3 +<% unless task.requestor %>
  4 + <%= labelled_form_field(_("Sent by: "), f.text_field(:name)) %>
  5 + <p><%= label_tag(_("Email: %s") % task.email) %> </p>
  6 +<% end %>
8 7  
9   -<%= select_profile_folder(_('Select the folder where the article must be published'), "tasks[#{task.id}][task][article_parent_id]", task.target) %>
10   -<%= labelled_form_field(_('Highlight this article'), f.check_box(:highlighted)) %>
  8 +<%= f.fields_for 'article', OpenStruct.new(task.article) do |a| %>
  9 + <%= required labelled_form_field(_('Title'), a.text_field(:name, :size => 50)) %>
  10 + <%= labelled_form_field(_('Source'), a.text_field(:source_name)) %>
  11 + <%= labelled_form_field(_("Source URL"), a.text_field(:source)) %>
11 12  
12   -<%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => f, :abstract_method => 'article_abstract', :body_method => 'article_body', :lead_id => task.id} %>
  13 + <%= select_profile_folder(_('Select the folder where the article must be published'), "tasks[#{task.id}][task][article][parent_id]", task.target, task.article[:parent_id]) %>
  14 + <%= labelled_form_field(_('Highlight this article'), a.check_box(:highlighted)) %>
  15 +
  16 + <%= a.hidden_field(:type) %>
  17 +
  18 + <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => a, :lead_id => task.id} %>
  19 +<% end %>
... ...
app/views/user_mailer/profiles_suggestions_email.html.erb
... ... @@ -10,8 +10,8 @@
10 10 <% @people_suggestions.each do |person| %>
11 11 <li><a href="<%= url_for(person.public_profile_url) %>"><%= person.name %></a></li>
12 12 <% end %>
13   - <ul>
14   - <%= _("To see the full list of friends suggestions, follow the link: %s") % @people_suggestions_url %>
  13 + </ul>
  14 + <%= _("To see the full list of friends suggestions, follow the link: %s") % url_for(@people_suggestions_url) %>
15 15 <% end %>
16 16  
17 17 <% unless @communities_suggestions.empty? %>
... ... @@ -21,13 +21,15 @@
21 21 <% @communities_suggestions.each do |community| %>
22 22 <li><a href="<%= url_for(community.public_profile_url) %>"><%= community.name %></a></li>
23 23 <% end %>
24   - <ul>
25   - <%= _("To see the full list of communities suggestions, follow the link: %s") % @communities_suggestions_url %>
  24 + </ul>
  25 + <%= _("To see the full list of communities suggestions, follow the link: %s") % url_for(@communities_suggestions_url) %>
26 26 <% end %>
27 27  
28 28  
29 29 <%= _("Greetings,") %>
30 30  
31   ---
32   -<%= _('%s team.') % @environment %>
  31 +<p><%= _("Greetings,") %></p>
  32 +
  33 +--<br/>
  34 +<%= _('%s team.') % @environment %><br/>
33 35 <%= @url %>
... ...
config/environments/development.rb
... ... @@ -35,4 +35,8 @@ Noosfero::Application.configure do
35 35 config.assets.debug = true
36 36  
37 37 config.consider_all_requests_local = true
  38 +
  39 + # send emails to /tmp/mails
  40 + config.action_mailer.delivery_method = :file
  41 +
38 42 end
... ...
config/routes.rb
... ... @@ -56,37 +56,37 @@ Noosfero::Application.routes.draw do
56 56 match 'search(/:action(/*category_path))', :controller => 'search'
57 57  
58 58 # events
59   - match 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/
60   - match 'profile/:profile/events_by_month', :controller => 'events', :action => 'events_by_month', :profile => /#{Noosfero.identifier_format}/
61   - match 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/
62   - match 'profile/:profile/events/:year/:month', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :profile => /#{Noosfero.identifier_format}/
63   - match 'profile/:profile/events', :controller => 'events', :action => 'events', :profile => /#{Noosfero.identifier_format}/
  59 + match 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format_in_url}/
  60 + match 'profile/:profile/events_by_month', :controller => 'events', :action => 'events_by_month', :profile => /#{Noosfero.identifier_format_in_url}/
  61 + match 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format_in_url}/
  62 + match 'profile/:profile/events/:year/:month', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :profile => /#{Noosfero.identifier_format_in_url}/
  63 + match 'profile/:profile/events', :controller => 'events', :action => 'events', :profile => /#{Noosfero.identifier_format_in_url}/
64 64  
65 65 # catalog
66   - match 'catalog/:profile', :controller => 'catalog', :action => 'index', :profile => /#{Noosfero.identifier_format}/, :as => :catalog
  66 + match 'catalog/:profile', :controller => 'catalog', :action => 'index', :profile => /#{Noosfero.identifier_format_in_url}/, :as => :catalog
67 67  
68 68 # invite
69   - match 'profile/:profile/invite/friends', :controller => 'invite', :action => 'invite_friends', :profile => /#{Noosfero.identifier_format}/
70   - match 'profile/:profile/invite/:action', :controller => 'invite', :profile => /#{Noosfero.identifier_format}/
  69 + match 'profile/:profile/invite/friends', :controller => 'invite', :action => 'invite_friends', :profile => /#{Noosfero.identifier_format_in_url}/
  70 + match 'profile/:profile/invite/:action', :controller => 'invite', :profile => /#{Noosfero.identifier_format_in_url}/
71 71  
72 72 # feeds per tag
73   - match 'profile/:profile/tags/:id/feed', :controller => 'profile', :action =>'tag_feed', :id => /.+/, :profile => /#{Noosfero.identifier_format}/, :as => :tag_feed
  73 + match 'profile/:profile/tags/:id/feed', :controller => 'profile', :action =>'tag_feed', :id => /.+/, :profile => /#{Noosfero.identifier_format_in_url}/, :as => :tag_feed
74 74  
75 75 # profile tags
76   - match 'profile/:profile/tags/:id', :controller => 'profile', :action => 'content_tagged', :id => /.+/, :profile => /#{Noosfero.identifier_format}/
77   - match 'profile/:profile/tags(/:id)', :controller => 'profile', :action => 'tags', :profile => /#{Noosfero.identifier_format}/
  76 + match 'profile/:profile/tags/:id', :controller => 'profile', :action => 'content_tagged', :id => /.+/, :profile => /#{Noosfero.identifier_format_in_url}/
  77 + match 'profile/:profile/tags(/:id)', :controller => 'profile', :action => 'tags', :profile => /#{Noosfero.identifier_format_in_url}/
78 78  
79 79 # profile search
80   - match 'profile/:profile/search', :controller => 'profile_search', :action => 'index', :profile => /#{Noosfero.identifier_format}/
  80 + match 'profile/:profile/search', :controller => 'profile_search', :action => 'index', :profile => /#{Noosfero.identifier_format_in_url}/
81 81  
82 82 # comments
83   - match 'profile/:profile/comment/:action/:id', :controller => 'comment', :profile => /#{Noosfero.identifier_format}/
  83 + match 'profile/:profile/comment/:action/:id', :controller => 'comment', :profile => /#{Noosfero.identifier_format_in_url}/
84 84  
85 85 # public profile information
86   - match 'profile/:profile(/:action(/:id))', :controller => 'profile', :action => 'index', :id => /[^\/]*/, :profile => /#{Noosfero.identifier_format}/, :as => :profile
  86 + match 'profile/:profile(/:action(/:id))', :controller => 'profile', :action => 'index', :id => /[^\/]*/, :profile => /#{Noosfero.identifier_format_in_url}/, :as => :profile
87 87  
88 88 # contact
89   - match 'contact/:profile/:action(/:id)', :controller => 'contact', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/
  89 + match 'contact/:profile/:action(/:id)', :controller => 'contact', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format_in_url}/
90 90  
91 91 # map balloon
92 92 match 'map_balloon/:action/:id', :controller => 'map_balloon', :id => /.*/
... ... @@ -98,8 +98,8 @@ Noosfero::Application.routes.draw do
98 98 ## Controllers that are profile-specific (for profile admins )
99 99 ######################################################
100 100 # profile customization - "My profile"
101   - match 'myprofile/:profile', :controller => 'profile_editor', :action => 'index', :profile => /#{Noosfero.identifier_format}/
102   - match 'myprofile/:profile/:controller(/:action(/:id))', :controller => Noosfero.pattern_for_controllers_in_directory('my_profile'), :profile => /#{Noosfero.identifier_format}/, :as => :myprofile
  101 + match 'myprofile/:profile', :controller => 'profile_editor', :action => 'index', :profile => /#{Noosfero.identifier_format_in_url}/
  102 + match 'myprofile/:profile/:controller(/:action(/:id))', :controller => Noosfero.pattern_for_controllers_in_directory('my_profile'), :profile => /#{Noosfero.identifier_format_in_url}/, :as => :myprofile
103 103  
104 104  
105 105 ######################################################
... ... @@ -127,14 +127,14 @@ Noosfero::Application.routes.draw do
127 127 # cache stuff - hack
128 128 match 'public/:action/:id', :controller => 'public'
129 129  
130   - match ':profile/*page/versions', :controller => 'content_viewer', :action => 'article_versions', :profile => /#{Noosfero.identifier_format}/, :constraints => EnvironmentDomainConstraint.new
  130 + match ':profile/*page/versions', :controller => 'content_viewer', :action => 'article_versions', :profile => /#{Noosfero.identifier_format_in_url}/, :constraints => EnvironmentDomainConstraint.new
131 131 match '*page/versions', :controller => 'content_viewer', :action => 'article_versions'
132 132  
133   - match ':profile/*page/versions_diff', :controller => 'content_viewer', :action => 'versions_diff', :profile => /#{Noosfero.identifier_format}/, :constraints => EnvironmentDomainConstraint.new
  133 + match ':profile/*page/versions_diff', :controller => 'content_viewer', :action => 'versions_diff', :profile => /#{Noosfero.identifier_format_in_url}/, :constraints => EnvironmentDomainConstraint.new
134 134 match '*page/versions_diff', :controller => 'content_viewer', :action => 'versions_diff'
135 135  
136 136 # match requests for profiles that don't have a custom domain
137   - match ':profile(/*page)', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :constraints => EnvironmentDomainConstraint.new
  137 + match ':profile(/*page)', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format_in_url}/, :constraints => EnvironmentDomainConstraint.new
138 138  
139 139 # match requests for content in domains hosted for profiles
140 140 match '/(*page)', :controller => 'content_viewer', :action => 'view_page'
... ...
db/migrate/20150203143051_add_reference_to_role.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +class AddReferenceToRole < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :roles, :profile_id, :integer
  4 + end
  5 + def self.down
  6 + remove_column :roles , :profile_id
  7 + end
  8 +end
... ...
db/migrate/20150210143723_add_custom_roles_permission_to_admin_roles.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +class AddCustomRolesPermissionToAdminRoles < ActiveRecord::Migration
  2 + def self.up
  3 + environment_admin = Role.find_by_key("environment_administrator")
  4 + profile_admin = Role.find_by_key("profile_admin")
  5 + environment_admin.permissions.append("manage_custom_roles")
  6 + profile_admin.permissions.append("manage_custom_roles")
  7 + environment_admin.save!
  8 + profile_admin.save!
  9 + end
  10 + def self.down
  11 + environment_admin = Role.find_by_key("environment_administrator")
  12 + profile_admin = Role.find_by_key("profile_admin")
  13 + environment_admin.permissions.delete("manage_custom_roles")
  14 + profile_admin.permissions.delete("manage_custom_roles")
  15 + environment_admin.save!
  16 + profile_admin.save!
  17 + end
  18 +end
... ...
db/migrate/20150423144533_add_new_box_to_every_environment_and_profile.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class AddNewBoxToEveryEnvironmentAndProfile < ActiveRecord::Migration
  2 + def up
  3 + Environment.find_each do |env|
  4 + env.boxes << Box.new if env.boxes.count < 4
  5 + end
  6 +
  7 + Profile.find_each do |profile|
  8 + profile.boxes << Box.new if profile.boxes.count < 4
  9 + end
  10 + end
  11 +
  12 + def down
  13 + say "this migration can't be reverted"
  14 + end
  15 +end
... ...
db/schema.rb
... ... @@ -593,6 +593,7 @@ ActiveRecord::Schema.define(:version =&gt; 20150408231524) do
593 593 t.boolean "system", :default => false
594 594 t.text "permissions"
595 595 t.integer "environment_id"
  596 + t.integer "profile_id"
596 597 end
597 598  
598 599 create_table "scraps", :force => true do |t|
... ...
debian/changelog
  1 +noosfero (1.2~0) UNRELEASED; urgency=medium
  2 +
  3 + * Temporary version in heavy development
  4 +
  5 + -- Antonio Terceiro <terceiro@debian.org> Fri, 08 May 2015 16:08:18 -0300
  6 +
  7 +noosfero (1.1) wheezy; urgency=low
  8 +
  9 + * Noosfero 1.1 final release
  10 +
  11 + -- Antonio Terceiro <terceiro@colivre.coop.br> Mon, 04 May 2015 18:47:35 -0300
  12 +
1 13 noosfero (1.1~rc4) wheezy; urgency=medium
2 14  
3 15 * Fourth release candidate for Noosfero 1.1
... ...
debian/control
... ... @@ -14,8 +14,8 @@ Build-Depends: cucumber,
14 14 ruby-database-cleaner,
15 15 ruby-gettext,
16 16 ruby-mocha,
17   - ruby-rspec,
18   - ruby-rspec-rails,
  17 + ruby-rspec (>= 2.14),
  18 + ruby-rspec-rails (>= 2.14),
19 19 ruby-selenium-webdriver,
20 20 ruby-sqlite3,
21 21 ruby-tidy,
... ... @@ -59,7 +59,9 @@ Depends: adduser,
59 59 ruby-redcloth,
60 60 ruby-rest-client,
61 61 ruby-rmagick,
  62 + ruby-sass-rails,
62 63 ruby-tzinfo (>= 1.1.0-2~),
  64 + ruby-uglifier,
63 65 ruby-whenever,
64 66 ruby-will-paginate (>= 2.3.12-1~),
65 67 tango-icon-theme,
... ...
debian/noosfero.install
... ... @@ -25,6 +25,7 @@ etc/init.d/noosfero etc/init.d
25 25 etc/logrotate.d/noosfero etc/logrotate.d
26 26 etc/noosfero/varnish-accept-language.vcl etc/noosfero
27 27 etc/noosfero/varnish-noosfero.vcl etc/noosfero
  28 +etc/awstats-noosfero.conf etc/noosfero
28 29 lib usr/share/noosfero
29 30 locale usr/share/noosfero
30 31 plugins usr/share/noosfero
... ...
features/change_appearance.feature 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +Feature: Change appearance
  2 + As a user
  3 + I want to change the appearance of my profile page
  4 +
  5 + Background:
  6 + Given the following users
  7 + | login | name |
  8 + | joaosilva | Joao Silva |
  9 +
  10 + Scenario: Change appearance from default(3 boxes) to Left Top and Right(4 boxes)
  11 + Given I am logged in as "joaosilva"
  12 + And I go to joaosilva's control panel
  13 + And I follow "Edit sideboxes"
  14 + And I should not see an element ".box-4"
  15 + And I go to joaosilva's control panel
  16 + And I follow "Edit Appearance"
  17 + And I follow "Left Top and Right"
  18 + And I go to joaosilva's control panel
  19 + And I follow "Edit sideboxes"
  20 + And I should see an element ".box-4"
... ...
features/signup.feature
... ... @@ -267,32 +267,6 @@ Feature: signup
267 267 Then I should be on the homepage
268 268  
269 269 @selenium
270   - Scenario: join community on signup
271   - Given the following users
272   - | login | name |
273   - | mariasilva | Maria Silva |
274   - And the following communities
275   - | name | identifier | owner |
276   - | Free Software | freesoftware | mariasilva |
277   - And feature "skip_new_user_email_confirmation" is disabled on environment
278   - And I am on /freesoftware
279   - When I follow "Join"
280   - And I follow "New user"
281   - And I fill in the following within ".no-boxes":
282   - | e-Mail | josesilva@example.com |
283   - | Username | josesilva |
284   - | Password | secret |
285   - | Password confirmation | secret |
286   - | Full name | José da Silva |
287   - And wait for the captcha signup time
288   - And I press "Create my account"
289   - And I go to josesilva's confirmation URL
290   - And I fill in "Username" with "josesilva"
291   - And I fill in "Password" with "secret"
292   - And I press "Log in"
293   - Then "José da Silva" should be a member of "Free Software"
294   -
295   - @selenium
296 270 Scenario: user registration is moderated by admin
297 271 Given feature "admin_must_approve_new_users" is enabled on environment
298 272 And feature "skip_new_user_email_confirmation" is disabled on environment
... ...
features/step_definitions/noosfero_steps.rb
... ... @@ -94,8 +94,8 @@ Given /^the following blocks$/ do |table|
94 94 owner.boxes<< Box.new
95 95 owner.boxes.first.blocks << MainBlock.new
96 96 end
97   - box_id = owner.boxes.last.id
98   - klass.constantize.create!(item.merge(:box_id => box_id))
  97 + box = owner.boxes.where(:position => 3).first
  98 + klass.constantize.create!(item.merge(:box => box))
99 99 end
100 100 end
101 101  
... ...
lib/authenticated_system.rb
... ... @@ -60,7 +60,11 @@ module AuthenticatedSystem
60 60 if logged_in? && authorized?
61 61 true
62 62 else
63   - access_denied
  63 + if params[:require_login_popup]
  64 + render :json => { :require_login_popup => true }
  65 + else
  66 + access_denied
  67 + end
64 68 end
65 69 end
66 70  
... ...
lib/noosfero.rb
... ... @@ -57,6 +57,12 @@ module Noosfero
57 57 '[a-z0-9][a-z0-9~.]*([_\-][a-z0-9~.]+)*'
58 58 end
59 59  
  60 + # All valid identifiers, plus ~ meaning "the current user". See
  61 + # ApplicationController#redirect_to_current_user
  62 + def self.identifier_format_in_url
  63 + "(#{identifier_format}|~)"
  64 + end
  65 +
60 66 def self.default_hostname
61 67 Environment.table_exists? && Environment.default ? Environment.default.default_hostname : 'localhost'
62 68 end
... ...
lib/noosfero/plugin.rb
... ... @@ -8,6 +8,10 @@ class Noosfero::Plugin
8 8 self.context = context
9 9 end
10 10  
  11 + def environment
  12 + context.environment if self.context
  13 + end
  14 +
11 15 class << self
12 16  
13 17 attr_writer :should_load
... ... @@ -35,6 +39,7 @@ class Noosfero::Plugin
35 39 # filters must be loaded after all extensions
36 40 klasses.each do |plugin|
37 41 load_plugin_filters plugin
  42 + load_plugin_hotspots plugin
38 43 end
39 44 end
40 45  
... ... @@ -108,6 +113,23 @@ class Noosfero::Plugin
108 113 end
109 114 end
110 115  
  116 + # This is a generic method to extend the hotspots list with plugins
  117 + # hotspots. This allows plugins to extend other plugins.
  118 + # To use this, the plugin must define its hotspots inside a module Hotspots.
  119 + # Its also needed to include Noosfero::Plugin::HotSpot module
  120 + # in order to dispatch plugins methods.
  121 + #
  122 + # Checkout FooPlugin for usage example.
  123 + def load_plugin_hotspots(plugin)
  124 + ActionDispatch::Reloader.to_prepare do
  125 + begin
  126 + module_name = "#{plugin.name}::Hotspots"
  127 + Noosfero::Plugin.send(:include, module_name.constantize)
  128 + rescue NameError
  129 + end
  130 + end
  131 + end
  132 +
111 133 def add_controller_filters(controller_class, plugin, filters)
112 134 unless filters.is_a?(Array)
113 135 filters = [filters]
... ...
lib/noosfero/version.rb
1 1 module Noosfero
2 2 PROJECT = 'noosfero'
3   - VERSION = '1.1~rc4'
  3 + VERSION = '1.2~0'
4 4 end
5 5  
6 6 root = File.expand_path(File.dirname(__FILE__) + '/../..')
... ...
lib/tasks/backup.rake
1   -desc "Creates a backup of the user files stored in public/"
2   -task :backup do
3   - dirs = Dir.glob('public/images/[0-9][0-9][0-9][0-9]') + ['public/articles', 'public/thumbnails', 'public/user_themes'].select { |d| File.exists?(d) }
4   - tarball = 'backups/files-' + Time.now.strftime('%Y-%m-%d-%R') + '.tar'
  1 +task :load_backup_config do
  2 + $config = YAML.load_file('config/database.yml')
  3 +end
  4 +
  5 +task :check_backup_support => :load_backup_config do
  6 + if $config['production']['adapter'] != 'postgresql'
  7 + fail("Only PostgreSQL is supported for backups at the moment")
  8 + end
  9 +end
  10 +
  11 +backup_dirs = [
  12 + 'public/image_uploads',
  13 + 'public/articles',
  14 + 'public/thumbnails',
  15 + 'public/user_themes',
  16 +]
  17 +
  18 +desc "Creates a backup of the database and uploaded files"
  19 +task :backup => :check_backup_support do
  20 + dirs = backup_dirs.select { |d| File.exists?(d) }
  21 +
  22 + backup_name = Time.now.strftime('%Y-%m-%d-%R')
  23 + backup_file = File.join('tmp/backup', backup_name) + '.tar.gz'
  24 + mkdir_p 'tmp/backup'
  25 + dump = File.join('tmp/backup', backup_name) + '.sql'
  26 +
  27 + database = $config['production']['database']
  28 + host = $config['production']['host']
  29 + sh "pg_dump -h #{host} #{database} > #{dump}"
  30 +
  31 + sh 'tar', 'chaf', backup_file, dump, *dirs
  32 + rm_f dump
  33 +
  34 + puts "****************************************************"
  35 + puts "Backup in #{backup_file} !"
  36 + puts
  37 + puts "To restore, use:"
  38 + puts "$ rake restore BACKUP=#{backup_file}"
  39 + puts "****************************************************"
  40 +end
  41 +
  42 +def invalid_backup!(message, items=[])
  43 + puts "E: #{message}"
  44 + items.each do |i|
  45 + puts "E: - #{i}"
  46 + end
  47 + puts "E: Is this a backup archive created by Noosfero with \`rake backup\`?"
  48 + exit 1
  49 +end
  50 +
  51 +desc "Restores a backup created previousy with \`rake backup\`"
  52 +task :restore => :check_backup_support do
  53 + backup = ENV["BACKUP"]
  54 + unless backup
  55 + puts "usage: rake restore BACKUP=/path/to/backup"
  56 + exit 1
  57 + end
  58 +
  59 + files = `tar taf #{backup}`.split
  60 +
  61 + # validate files in the backup
  62 + invalid_files = []
  63 + files.each do |f|
  64 + if f !~ /tmp\/backup\// && (backup_dirs.none? { |d| f =~ /^#{d}\// })
  65 + invalid_files << f
  66 + end
  67 + end
  68 + if invalid_files.size > 0
  69 + invalid_backup!("Invalid files found in the backup archive", invalid_files)
  70 + end
  71 +
  72 + # find database dump in the archive
  73 + dumps = files.select do |f|
  74 + File.dirname(f) == 'tmp/backup' && f =~ /\.sql$/
  75 + end
  76 + if dumps.size == 0
  77 + invalid_backup!("Could not find a database dump in the archive.")
  78 + elsif dumps.size > 1
  79 + invalid_backup!("Multiple database dumps found in the archive:", dumps)
  80 + end
  81 + dump = dumps.first
  82 +
  83 + database = $config['production']['database']
  84 + username = $config['production']['username']
  85 + host = $config['production']['host']
  86 +
  87 + puts "WARNING: backups should be restored to an empty database, otherwise"
  88 + puts "data from the backup may not be loaded properly."
  89 + puts
  90 + puts 'You can remove the existing database and create a new one with:'
  91 + puts
  92 + puts "$ sudo -u postgres dropdb -h #{host} #{database}"
  93 + puts "$ sudo -u postgres createdb -h #{host} #{database} --owner #{username}"
  94 + puts
  95 + print "Are you sure you want to continue (y/N)? "
  96 + response = $stdin.gets.strip
  97 + unless ['y', 'yes'].include?(response.downcase)
  98 + puts "*** ABORTED."
  99 + exit 1
  100 + end
  101 +
  102 + sh 'tar', 'xaf', backup
  103 + sh "rails dbconsole production < #{dump}"
  104 + rm_f dump
5 105  
6   - mkdir_p(File.dirname(tarball))
7   - sh('tar', 'cf', tarball, *dirs)
  106 + puts "****************************************************"
  107 + puts "Backup restored!"
  108 + puts "****************************************************"
8 109 end
... ...
lib/tasks/doc.rake
... ... @@ -12,9 +12,8 @@ namespace :noosfero do
12 12 end
13 13 end
14 14 task :unlink_plugins_textiles do
15   - root = Pathname.new(File.dirname(__FILE__) + '/../..').expand_path
16   - rm_f Dir.glob(root.join('doc/noosfero/plugins/*.textile')) -
17   - [root.join('doc/noosfero/plugins/index.textile')]
  15 + rm_f Dir.glob('doc/noosfero/plugins/*.textile') -
  16 + ['doc/noosfero/plugins/index.textile']
18 17 end
19 18 input = Dir.glob('doc/noosfero/**/*.textile') + plugins_textiles.map{|i| "doc/noosfero/plugins/#{File.basename(i)}"}
20 19 topics_xhtml = input.map { |item| item.sub('.textile', '.en.xhtml') }.uniq
... ...
lib/tasks/error_messages.rake
... ... @@ -4,7 +4,7 @@ targets = []
4 4 templates.each do |template|
5 5 target = template.gsub(/.erb$/, '')
6 6 targets << target
7   - file target => [:makemo, template] do
  7 + file target => [:makemo, template, :environment] do
8 8 require 'erb'
9 9 erb = ERB.new(File.read(template))
10 10 File.open(target, 'w') do |file|
... ...
plugins/event/po/pt/event.po
... ... @@ -5,8 +5,8 @@
5 5 #
6 6 msgid ""
7 7 msgstr ""
8   -"Project-Id-Version: 1.0-690-gcb6e853\n"
9   -"POT-Creation-Date: 2015-03-05 12:09-0300\n"
  8 +"Project-Id-Version: 1.1~rc4\n"
  9 +"POT-Creation-Date: 2015-04-20 19:44-0300\n"
10 10 "PO-Revision-Date: 2015-01-30 00:18-0000\n"
11 11 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 12 "Language-Team: LANGUAGE <LL@li.org>\n"
... ... @@ -16,61 +16,53 @@ msgstr &quot;&quot;
16 16 "Content-Transfer-Encoding: 8bit\n"
17 17 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18 18  
  19 +#: plugins/event/lib/event_plugin.rb:4
  20 +msgid "Event Extras"
  21 +msgstr "Extras para Eventos"
  22 +
  23 +#: plugins/event/lib/event_plugin.rb:8
  24 +msgid ""
  25 +"Include a new block to show the environment's or profiles' events information"
  26 +msgstr ""
  27 +"Adiciona um novo bloco para apresentar as informações de eventos do ambiente "
  28 +"ou dos perfis"
  29 +
19 30 #: plugins/event/lib/event_plugin/event_block.rb:12
20 31 msgid "Events"
21 32 msgstr "Eventos"
22 33  
23 34 #: plugins/event/lib/event_plugin/event_block.rb:16
24 35 msgid "Show the profile events or all environment events."
25   -msgstr "Mostrar todos os eventos."
  36 +msgstr "Mostra todos os eventos de um perfil ou do ambiente."
26 37  
27 38 #: plugins/event/lib/event_plugin/event_block.rb:65
28   -#, fuzzy
29 39 msgid "One month ago"
30 40 msgid_plural "%d months ago"
31   -msgstr[0] "Iniciou a um mês atrás."
32   -msgstr[1] "Iniciou a %d meses atrás."
  41 +msgstr[0] "Um mês atrás"
  42 +msgstr[1] "%d meses atrás."
33 43  
34 44 #: plugins/event/lib/event_plugin/event_block.rb:67
35 45 msgid "Yesterday"
36 46 msgid_plural "%d days ago"
37   -msgstr[0] ""
38   -msgstr[1] ""
  47 +msgstr[0] "Ontem"
  48 +msgstr[1] "%d dias atrás"
39 49  
40 50 #: plugins/event/lib/event_plugin/event_block.rb:69
41 51 msgid "Today"
42   -msgstr ""
  52 +msgstr "Hoje"
43 53  
44 54 #: plugins/event/lib/event_plugin/event_block.rb:71
45 55 msgid "Tomorrow"
46 56 msgid_plural "%d days left to start"
47   -msgstr[0] ""
48   -msgstr[1] ""
  57 +msgstr[0] "Amanhã"
  58 +msgstr[1] "%d dias para começar"
49 59  
50 60 #: plugins/event/lib/event_plugin/event_block.rb:73
51   -#, fuzzy
52 61 msgid "One month left to start"
53 62 msgid_plural "%d months left to start"
54 63 msgstr[0] "Um mês para iniciar"
55 64 msgstr[1] "% meses para iniciar"
56 65  
57   -#: plugins/event/lib/event_plugin.rb:4
58   -msgid "Event Extras"
59   -msgstr "Eventos"
60   -
61   -#: plugins/event/lib/event_plugin.rb:8
62   -msgid ""
63   -"Include a new block to show the environment's or profiles' events information"
64   -msgstr ""
65   -"Adiciona um novo bloco para apresentar as informações de eventos do ambiente "
66   -"ou de perfis"
67   -
68   -#: plugins/event/views/event_plugin/event_block_item.html.erb:6
69   -msgid "Duration: 1 day"
70   -msgid_plural "Duration: %s days"
71   -msgstr[0] ""
72   -msgstr[1] ""
73   -
74 66 #: plugins/event/views/profile_design/event_plugin/_event_block.html.erb:1
75 67 msgid "Limit of items"
76 68 msgstr "Limite de itens"
... ... @@ -94,12 +86,17 @@ msgstr &quot;Mostrar apenas eventos futuros&quot;
94 86  
95 87 #: plugins/event/views/profile_design/event_plugin/_event_block.html.erb:22
96 88 msgid "Limit of days to display"
97   -msgstr "Limite de dias de distância para mostrar eventos"
  89 +msgstr "Limite de dias para mostrar"
98 90  
99 91 #: plugins/event/views/profile_design/event_plugin/_event_block.html.erb:24
100   -#, fuzzy
101 92 msgid "Only show events in this interval of days."
102   -msgstr "Mostar somente eventos que acontecem dentro do limite de dias"
  93 +msgstr "Mostar somente os eventos nesse intervalo de dias"
  94 +
  95 +#: plugins/event/views/event_plugin/event_block_item.html.erb:6
  96 +msgid "Duration: 1 day"
  97 +msgid_plural "Duration: %s days"
  98 +msgstr[0] "Duração: 1 dia"
  99 +msgstr[1] "Duração: %s dias"
103 100  
104 101 #~ msgid "Started one day ago."
105 102 #~ msgid_plural "Started %d days ago."
... ...
plugins/foo/lib/foo_plugin.rb
1 1 class FooPlugin < Noosfero::Plugin
  2 + include Noosfero::Plugin::HotSpot
2 3  
3 4 def self.plugin_name
4 5 "Foo"
... ... @@ -8,12 +9,29 @@ class FooPlugin &lt; Noosfero::Plugin
8 9 _("A sample plugin to test autoload craziness.")
9 10 end
10 11  
  12 + module Hotspots
  13 + # -> Custom foo plugin hotspot
  14 + # do something to extend the FooPlugin behaviour
  15 + # receive params a, b and c
  16 + # returns = boolean or something else
  17 + def foo_plugin_my_hotspot(a, b, c)
  18 + end
  19 +
  20 + # -> Custom title for foo profiles tab
  21 + # returns = a string with a custom title
  22 + def foo_plugin_tab_title
  23 + end
  24 + end
  25 +
11 26 def control_panel_buttons
12 27 {:title => 'Foo plugin button', :icon => '', :url => ''}
13 28 end
14 29  
15 30 def profile_tabs
16   - {:title => 'Foo plugin tab', :id => 'foo_plugin', :content => lambda {'Foo plugin random content'} }
  31 + title = plugins.dispatch_first(:foo_plugin_tab_title)
  32 + title = 'Foo plugin tab' unless title
  33 +
  34 + {:title => title, :id => 'foo_plugin', :content => lambda {'Foo plugin random content'} }
17 35 end
18 36  
19 37 end
... ...
plugins/foo/test/unit/foo_plugin_test.rb
... ... @@ -4,7 +4,25 @@ class FooPluginTest &lt; ActiveSupport::TestCase
4 4 def test_foo
5 5 FooPlugin::Bar.create!
6 6 end
  7 +
7 8 def test_monkey_patch
8 9 Profile.new.bar
9 10 end
  11 +
  12 + should "respond to new hotspots" do
  13 + plugin = FooPlugin.new
  14 +
  15 + assert plugin.respond_to?(:foo_plugin_my_hotspot)
  16 + assert plugin.respond_to?(:foo_plugin_tab_title)
  17 + end
  18 +
  19 + should "other plugin respond to new hotspots" do
  20 + class TestPlugin < Noosfero::Plugin
  21 + end
  22 +
  23 + plugin = TestPlugin.new
  24 +
  25 + assert plugin.respond_to?(:foo_plugin_my_hotspot)
  26 + assert plugin.respond_to?(:foo_plugin_tab_title)
  27 + end
10 28 end
... ...
plugins/google_analytics/lib/ext/profile.rb
... ... @@ -2,4 +2,9 @@ require_dependency &#39;profile&#39;
2 2  
3 3 class Profile
4 4 settings_items :google_analytics_profile_id
  5 + attr_accessible :google_analytics_profile_id
  6 +
  7 + descendants.each do |descendant|
  8 + descendant.attr_accessible :google_analytics_profile_id
  9 + end
5 10 end
... ...
plugins/google_analytics/lib/google_analytics_plugin.rb
... ... @@ -19,12 +19,15 @@ class GoogleAnalyticsPlugin &lt; Noosfero::Plugin
19 19  
20 20 def head_ending
21 21 unless profile_id.blank?
22   - expanded_template('tracking-code.rhtml',{:profile_id => profile_id})
  22 + expanded_template('tracking-code.html.erb',{:profile_id => profile_id})
23 23 end
24 24 end
25 25  
26 26 def profile_editor_extras
27   - expanded_template('profile-editor-extras.rhtml',{:profile_id => profile_id})
  27 + analytics_id = profile_id
  28 + lambda {
  29 + render :file => 'profile-editor-extras', :locals => { :profile_id => analytics_id }
  30 + }
28 31 end
29 32  
30 33 end
... ...
plugins/google_analytics/test/functional/profile_editor_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +require 'test_helper'
  2 +require 'profile_editor_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class ProfileEditorController; def rescue_action(e) raise e end; end
  6 +
  7 +class ProfileEditorControllerTest < ActionController::TestCase
  8 +
  9 + def setup
  10 + @controller = ProfileEditorController.new
  11 + @request = ActionController::TestRequest.new
  12 + @response = ActionController::TestResponse.new
  13 + @profile = create_user('default_user').person
  14 + login_as(@profile.identifier)
  15 + Environment.default.enable_plugin(GoogleAnalyticsPlugin.name)
  16 + end
  17 +
  18 + attr_accessor :profile
  19 +
  20 + should 'add extra fields to profile editor info and settings' do
  21 + get :edit, :profile => profile.identifier
  22 + assert_tag_in_string @response.body, :tag => 'label', :content => /Google Analytics/, :attributes => { :for => 'profile_data_google_analytics_profile_id' }
  23 + assert_tag_in_string @response.body, :tag => 'input', :attributes => { :id => 'profile_data_google_analytics_profile_id' }
  24 + end
  25 +
  26 + should 'save code filled in on field' do
  27 + post :edit, :profile => profile.identifier, :profile_data => {:google_analytics_profile_id => 12345678}
  28 + assert_equal '12345678', Person.find(profile.id).google_analytics_profile_id
  29 + end
  30 +
  31 +end
... ...
plugins/google_analytics/test/unit/google_analytics_plugin_test.rb
... ... @@ -27,11 +27,6 @@ class GoogleAnalyticsPluginTest &lt; ActiveSupport::TestCase
27 27 assert_equal 'content', @plugin.head_ending
28 28 end
29 29  
30   - should 'add extra fields to profile editor info and settings' do
31   - assert_tag_in_string @plugin.profile_editor_extras,
32   - :tag => 'input', :attributes => {:id => 'profile_data_google_analytics_profile_id', :value => 10}
33   - end
34   -
35 30 should 'extends Profile with attr google_analytics_profile_id' do
36 31 assert_respond_to Profile.new, :google_analytics_profile_id
37 32 end
... ...
plugins/google_analytics/views/profile-editor-extras.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h2><%= c_('Statistics') %></h2>
  2 +<%= labelled_form_field(_('Google Analytics Profile ID'), text_field(:profile_data, :google_analytics_profile_id, :value => profile_id)) %>
  3 +<%= link_to(_('See how to configure statistics for your profile'), '/doc/plugins/google_analytics', :target => '_blank') %>
... ...
plugins/google_analytics/views/profile-editor-extras.rhtml
... ... @@ -1,5 +0,0 @@
1   -<% extend ApplicationHelper %>
2   -
3   -<h2><%= c_('Statistics') %></h2>
4   -<%= labelled_form_field(_('Google Analytics Profile ID'), text_field(:profile_data, :google_analytics_profile_id, :value => profile_id)) %>
5   -<%= link_to(_('See how to configure statistics for your profile'), '/doc/plugins/google_analytics', :target => '_blank') %>
plugins/google_analytics/views/tracking-code.html.erb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<script>
  2 + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  3 + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  4 + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  5 + })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  6 +
  7 + ga('create', '<%= escape_javascript locals[:profile_id] %>', 'auto');
  8 + ga('send', 'pageview');
  9 +</script>
... ...
plugins/google_analytics/views/tracking-code.rhtml
... ... @@ -1,10 +0,0 @@
1   -<script type="text/javascript">
2   - var _gaq = _gaq || [];
3   - _gaq.push(['_setAccount', '<%= escape_javascript locals[:profile_id] %>']);
4   - _gaq.push(['_trackPageview']);
5   - (function() {
6   - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
7   - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
8   - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
9   - })();
10   -</script>
plugins/ldap/lib/ldap_authentication.rb
... ... @@ -77,18 +77,20 @@ class LdapAuthentication
77 77 end
78 78  
79 79 def get_user_attributes_from_ldap_entry(entry)
80   - {
81   - :dn => entry.dn,
82   - :fullname => LdapAuthentication.get_attr(entry, self.attr_fullname),
83   - :mail => LdapAuthentication.get_attr(entry, self.attr_mail),
84   - }
  80 + attributes = entry.instance_values["myhash"]
  81 +
  82 + attributes[:dn] = entry.dn
  83 + attributes[:fullname] = LdapAuthentication.get_attr(entry, self.attr_fullname)
  84 + attributes[:mail] = LdapAuthentication.get_attr(entry, self.attr_mail)
  85 +
  86 + attributes
85 87 end
86 88  
87 89 # Return the attributes needed for the LDAP search. It will only
88 90 # include the user attributes if on-the-fly registration is enabled
89 91 def search_attributes
90 92 if onthefly_register?
91   - ['dn', self.attr_fullname, self.attr_mail]
  93 + nil
92 94 else
93 95 ['dn']
94 96 end
... ... @@ -111,6 +113,7 @@ class LdapAuthentication
111 113 end
112 114 login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
113 115 object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
  116 +
114 117 attrs = {}
115 118  
116 119 search_filter = object_filter & login_filter
... ...
plugins/ldap/lib/ldap_plugin.rb
1 1 require File.dirname(__FILE__) + '/ldap_authentication.rb'
2 2  
3 3 class LdapPlugin < Noosfero::Plugin
  4 + include Noosfero::Plugin::HotSpot
4 5  
5 6 def self.plugin_name
6 7 "LdapPlugin"
... ... @@ -10,6 +11,26 @@ class LdapPlugin &lt; Noosfero::Plugin
10 11 _("A plugin that add ldap support.")
11 12 end
12 13  
  14 + module Hotspots
  15 + # -> Custom ldap plugin hotspot to set profile data before user creation
  16 + # receive the followings params:
  17 + # - attrs with ldap received data
  18 + # - login received by ldap
  19 + # - params from current context
  20 + # returns = updated person_data hash
  21 + def ldap_plugin_set_profile_data(attrs, params)
  22 + [attrs, params]
  23 + end
  24 +
  25 + # -> Custom ldap plugin hotspot to update user object
  26 + # receive the followings params:
  27 + # - user: user object
  28 + # - attrs with ldap received data
  29 + # returns = none
  30 + def ldap_plugin_update_user(user, attrs)
  31 + end
  32 + end
  33 +
13 34 def allow_user_registration
14 35 false
15 36 end
... ... @@ -35,17 +56,22 @@ class LdapPlugin &lt; Noosfero::Plugin
35 56  
36 57 if attrs
37 58 user.login = login
38   - user.email = attrs[:mail]
  59 + user.email = get_email(attrs, login)
39 60 user.name = attrs[:fullname]
40 61 user.password = password
41 62 user.password_confirmation = password
42   - user.person_data = context.params[:profile_data]
  63 + user.person_data = plugins.pipeline(:ldap_plugin_set_profile_data, attrs, context.params).last[:profile_data]
43 64 user.activated_at = Time.now.utc
44 65 user.activation_code = nil
45 66  
46 67 ldap = LdapAuthentication.new(context.environment.ldap_plugin_attributes)
47 68 begin
48   - user = nil unless user.save
  69 + if user.save
  70 + user.activate
  71 + plugins.dispatch(:ldap_plugin_update_user, user, attrs)
  72 + else
  73 + user = nil
  74 + end
49 75 rescue
50 76 #User not saved
51 77 end
... ... @@ -54,7 +80,6 @@ class LdapPlugin &lt; Noosfero::Plugin
54 80 end
55 81  
56 82 else
57   -
58 83 return nil if !user.activated?
59 84  
60 85 begin
... ... @@ -69,6 +94,16 @@ class LdapPlugin &lt; Noosfero::Plugin
69 94 user
70 95 end
71 96  
  97 + def get_email(attrs, login)
  98 + return attrs[:mail] unless attrs[:mail].blank?
  99 +
  100 + if attrs[:fullname]
  101 + return attrs[:fullname].to_slug + "@ldap.user"
  102 + else
  103 + return login.to_slug + "@ldap.user"
  104 + end
  105 + end
  106 +
72 107 def login_extra_contents
73 108 proc do
74 109 @person = Person.new(:environment => @environment)
... ...
plugins/require_auth_to_comment/controllers/require_auth_to_comment_plugin_admin_controller.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class RequireAuthToCommentPluginAdminController < AdminController
  2 +
  3 + def index
  4 + settings = params[:settings]
  5 + settings ||= {}
  6 + @settings = Noosfero::Plugin::Settings.new(environment, RequireAuthToCommentPlugin, settings)
  7 + if request.post?
  8 + @settings.save!
  9 + session[:notice] = 'Settings succefully saved.'
  10 + redirect_to :action => 'index'
  11 + end
  12 + end
  13 +
  14 +end
... ...
plugins/require_auth_to_comment/lib/require_auth_to_comment_plugin.rb
... ... @@ -21,11 +21,20 @@ class RequireAuthToCommentPlugin &lt; Noosfero::Plugin
21 21 end
22 22  
23 23 def stylesheet?
24   - true
  24 + !display_login_popup?
  25 + end
  26 +
  27 + def display_login_popup?
  28 + settings = Noosfero::Plugin::Settings.new(context.environment, self.class)
  29 + settings.require_type == 'display_login_popup'
  30 + end
  31 +
  32 + def self.require_type_default_setting
  33 + 'hide_button'
25 34 end
26 35  
27 36 def js_files
28   - ['hide_comment_form.js', 'jquery.livequery.min.js']
  37 + ['hide_comment_form.js', 'jquery.livequery.min.js'] + (display_login_popup? ? ['comment_require_login.js'] : [])
29 38 end
30 39  
31 40 def body_beginning
... ...
plugins/require_auth_to_comment/public/comment_require_login.js 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +(function($) {
  2 + $(window).bind('userDataLoaded', function(event, data) {
  3 + if (!data.login && $('meta[name="profile.allow_unauthenticated_comments"]').length <= 0) {
  4 + $('.display-comment-form').unbind();
  5 + $('.display-comment-form').addClass('require-login-popup');
  6 + }
  7 + });
  8 +})(jQuery);
... ...
plugins/require_auth_to_comment/test/unit/require_auth_to_comment_plugin_test.rb
... ... @@ -5,9 +5,10 @@ class RequireAuthToCommentPluginTest &lt; ActiveSupport::TestCase
5 5 def setup
6 6 @plugin = RequireAuthToCommentPlugin.new
7 7 @comment = Comment.new
  8 + @environment = fast_create(Environment)
8 9 end
9 10  
10   - attr_reader :plugin, :comment
  11 + attr_reader :plugin, :comment, :environment
11 12  
12 13 should 'reject comments for unauthenticated users' do
13 14 plugin.context = logged_in(false)
... ... @@ -29,6 +30,35 @@ class RequireAuthToCommentPluginTest &lt; ActiveSupport::TestCase
29 30 assert !comment.rejected?
30 31 end
31 32  
  33 + should 'the default require type setting be hide_button' do
  34 + assert_equal 'hide_button', plugin.class.require_type_default_setting
  35 + end
  36 +
  37 + should 'display_login_popup? be false by default' do
  38 + context = mock();
  39 + context.expects(:environment).returns(environment)
  40 + plugin.expects(:context).returns(context)
  41 + assert !plugin.display_login_popup?
  42 + end
  43 +
  44 + should 'display_login_popup? be true if require_type is defined as display_login_popup' do
  45 + context = mock();
  46 + context.expects(:environment).returns(environment)
  47 + environment[:settings] = {:require_auth_to_comment_plugin => {:require_type => "display_login_popup"}}
  48 + plugin.expects(:context).returns(context)
  49 + assert plugin.display_login_popup?
  50 + end
  51 +
  52 + should 'not display stylesheet if login popup is active' do
  53 + plugin.expects(:display_login_popup?).returns(true)
  54 + assert !plugin.stylesheet?
  55 + end
  56 +
  57 + should 'display stylesheet if login popup is inactive' do
  58 + plugin.expects(:display_login_popup?).returns(false)
  59 + assert plugin.stylesheet?
  60 + end
  61 +
32 62 protected
33 63  
34 64 def logged_in(boolean)
... ...
plugins/require_auth_to_comment/views/require_auth_to_comment_plugin_admin/index.html.erb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +<h1><%= _('Require auth to comment Settings')%></h1>
  2 +
  3 +<%= form_for(:settings) do |f| %>
  4 +
  5 + <div class="require-type">
  6 + <strong>
  7 + <div class="hide-button">
  8 + <%= radio_button(:settings, :require_type, 'hide_button') %> <%= _('Hide button') %>
  9 + </div>
  10 + <div class="display-login-popup">
  11 + <%= radio_button(:settings, :require_type, 'display_login_popup') %> <%= _('Display login popup') %>
  12 + </div>
  13 + </strong>
  14 + </div>
  15 +
  16 + <% button_bar do %>
  17 + <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %>
  18 + <% end %>
  19 +
  20 +<% end %>
... ...
plugins/send_email/controllers/send_email_plugin_base_controller.rb
... ... @@ -11,7 +11,8 @@ module SendEmailPluginBaseController
11 11 )
12 12 @mail.subject = params[:subject] unless params[:subject].blank?
13 13 if @mail.valid?
14   - SendEmailPlugin::Sender.send_message(request.referer, @context_url, @mail).deliver
  14 + @referer = request.referer
  15 + SendEmailPlugin::Sender.send_message(@referer, @context_url, @mail).deliver
15 16 if request.xhr?
16 17 render :text => _('Message sent')
17 18 else
... ...
plugins/send_email/lib/send_email_plugin.rb
... ... @@ -16,9 +16,9 @@ class SendEmailPlugin &lt; Noosfero::Plugin
16 16  
17 17 def parse_content(html, source)
18 18 if context.profile
19   - html.gsub!(/\{sendemail\}/, "/profile/#{context.profile.identifier}/plugin/send_email/deliver")
  19 + html.gsub!(/({|%7[Bb])sendemail(}|%7[Dd])/, "/profile/#{context.profile.identifier}/plugin/send_email/deliver")
20 20 else
21   - html.gsub!(/\{sendemail\}/, '/plugin/send_email/deliver')
  21 + html.gsub!(/({|%7[Bb])sendemail(}|%7[Dd])/, '/plugin/send_email/deliver')
22 22 end
23 23 [html, source]
24 24 end
... ...
plugins/send_email/lib/send_email_plugin/mail.rb
... ... @@ -10,12 +10,11 @@ class SendEmailPlugin::Mail
10 10 validate :recipients_format
11 11  
12 12 def initialize(attributes = {:subject => 'New mail'})
13   - @environment = attributes[:environment]
14   - @from = attributes[:from]
15   - @to = attributes[:to]
16   - @subject = attributes[:subject]
17   - @message = attributes[:message]
18   - @params = attributes[:params]
  13 + if attributes
  14 + attributes.each do |attr,value|
  15 + self.send("#{attr}=", value)
  16 + end
  17 + end
19 18 end
20 19  
21 20 def recipients_format
... ... @@ -36,7 +35,7 @@ class SendEmailPlugin::Mail
36 35 end
37 36  
38 37 def params=(value = {})
39   - [:action, :controller, :to, :message, :subject, :from].each{|k| value.delete(k)}
  38 + [:profile, :action, :controller, :to, :message, :subject, :from, :commit].each{|k| value.delete(k)}
40 39 @params = value
41 40 end
42 41  
... ...
plugins/send_email/lib/send_email_plugin/sender.rb
... ... @@ -7,9 +7,9 @@ class SendEmailPlugin::Sender &lt; Noosfero::Plugin::MailerBase
7 7 @params = mail.params
8 8  
9 9 mail(
  10 + content_type: 'text/plain',
10 11 to: mail.to,
11 12 from: mail.from,
12   - body: mail.params,
13 13 subject: "[#{mail.environment.name}] #{mail.subject}"
14 14 )
15 15 end
... ...
plugins/send_email/test/functional/send_email_plugin_base_controller_test.rb
... ... @@ -54,6 +54,13 @@ def run_common_tests
54 54 post :deliver, @extra_args.merge(:to => 'john@example.com', :message => 'Hi john', :subject => 'Hello john')
55 55 assert_equal '[Colivre.net] Hello john', ActionMailer::Base.deliveries.first.subject
56 56 end
  57 +
  58 + should 'deliver mail with message from view' do
  59 + Environment.any_instance.stubs(:send_email_plugin_allow_to).returns('john@example.com')
  60 + post :deliver, @extra_args.merge(:to => 'john@example.com', :message => 'Hi john', :subject => 'Hello john')
  61 + assert_match /Contact from/, ActionMailer::Base.deliveries.first.body.to_s
  62 + end
  63 +
57 64 end
58 65  
59 66 class SendEmailPluginProfileControllerTest < ActionController::TestCase
... ...
plugins/send_email/test/unit/send_email_plugin_sender_test.rb
... ... @@ -15,12 +15,14 @@ class SendEmailPluginSenderTest &lt; ActiveSupport::TestCase
15 15 end
16 16  
17 17 should 'be able to deliver mail' do
  18 + @mail.expects(:params).returns({})
18 19 response = SendEmailPlugin::Sender.send_message("http://localhost/contact", 'http//profile', @mail)
19 20 assert_equal 'noreply@localhost', response.from.join
20 21 assert_equal "[Noosfero] #{@mail.subject}", response.subject
21 22 end
22 23  
23 24 should 'deliver mail to john@example.com' do
  25 + @mail.expects(:params).returns({})
24 26 response = SendEmailPlugin::Sender.send_message("http://localhost/contact", 'http//profile', @mail)
25 27 assert_equal ['john@example.com'], response.to
26 28 end
... ...
plugins/send_email/test/unit/send_email_plugin_test.rb
... ... @@ -26,4 +26,12 @@ class SendEmailPluginTest &lt; ActiveSupport::TestCase
26 26 assert_match /profile\/#{@plugin.context.profile.identifier}\/plugin\/send_email\/deliver/, @plugin.parse_content("expand this macro {sendemail}", nil).first
27 27 end
28 28  
  29 + should 'expand macro used on form on profile context' do
  30 + profile = fast_create(Community)
  31 + @plugin.context.stubs(:profile).returns(profile)
  32 + article = RawHTMLArticle.create!(:name => 'Raw HTML', :body => "<form action='{sendemail}'></form>", :profile => profile)
  33 +
  34 + assert_match /profile\/#{profile.identifier}\/plugin\/send_email\/deliver/, @plugin.parse_content(article.to_html, nil).first
  35 + end
  36 +
29 37 end
... ...
plugins/send_email/views/send_email_plugin/sender/message.html.erb
... ... @@ -1,8 +0,0 @@
1   -<%= _('Contact from %s') % @referer %>
2   -
3   -<%= word_wrap(@message || @mail.message) %>
4   -<% (@params || @mail.params).each_pair do |key, value| %>
5   -<%= key %>: <%= word_wrap(value) %>
6   -<% end %>
7   ----
8   -<%= url_for @context_url %>
plugins/send_email/views/send_email_plugin/sender/send_message.html.erb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<%= _('Contact from %s') % @referer %>
  2 +
  3 +<%= word_wrap(@message || @mail.message) %>
  4 +
  5 +<% (@params || @mail.params).each_pair do |key, value| %>
  6 +<%= key %>: <%= word_wrap(value) %>
  7 +<% end %>
  8 +---
  9 +<%= url_for @context_url %>
... ...
plugins/send_email/views/send_email_plugin/success.html.erb
... ... @@ -2,7 +2,7 @@
2 2  
3 3 <table class='sendemail-plugin-message-sent'>
4 4 <tr><td class='label'><strong><%= c_('Subject') %>:</strong></td><td class='value'><em><%=h @mail.subject %></em></td></tr>
5   - <tr><td class='label'><strong><%= c_('Message') %>:</strong></td><td class='value'><pre><%=h render :file => 'send_email_plugin/sender/message' %></pre></td></tr>
  5 + <tr><td class='label'><strong><%= c_('Message') %>:</strong></td><td class='value'><pre><%=h render :file => 'send_email_plugin/sender/send_message' %></pre></td></tr>
6 6 </table>
7 7  
8 8 <p><%= button :back, c_('Back'), :back %></p>
... ...
plugins/sub_organizations/db/migrate/20150508153119_add_timestamp_to_relation.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +class AddTimestampToRelation < ActiveRecord::Migration
  2 + def change
  3 + add_column :sub_organizations_plugin_relations, :created_at, :datetime
  4 + add_column :sub_organizations_plugin_relations, :updated_at, :datetime
  5 + end
  6 +end
... ...
plugins/sub_organizations/lib/sub_organizations_plugin/relation.rb
1 1 class SubOrganizationsPlugin::Relation < Noosfero::Plugin::ActiveRecord
2   - record_timestamps = false
3   -
4 2 belongs_to :parent, :polymorphic => true
5 3 belongs_to :child, :polymorphic => true
6 4  
... ...
plugins/vote/public/style.css
1 1 .vote-actions {
2   - position: absolute;
3 2 top: 40px;
4 3 right: 0px;
5 4 }
... ...
plugins/vote/views/vote/_vote.html.erb
... ... @@ -5,7 +5,7 @@ reload_url = url_for(:controller =&gt; &#39;vote_plugin_profile&#39;, :profile =&gt; profile.i
5 5  
6 6 <span id="vote_<%= model %>_<%= target.id %>_<%= vote %>" data-reload_url=<%= reload_url %> class="vote-action action <%= action %>-action">
7 7  
8   - <%= link_to_remote content_tag(:span, count, :class=>'like-action-counter') + content_tag(:span, '', :class=>"action-icon #{action}"), :url => url, :html => {:class => "#{active ? 'like-action-active':''} #{user ? '':'disabled'}"} %>
  8 + <%= link_to content_tag(:span, count, :class=>'like-action-counter') + content_tag(:span, '', :class=>"action-icon #{action}"), url, :class => "#{active ? 'like-action-active':''} #{user ? '':'disabled'} require-login-popup" %>
9 9  
10 10 <% if !voters.blank? %>
11 11 <span class="vote-detail">
... ...
public/designs/templates/lefttopright/config.yml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +name: "Top and Side Bars"
  2 +title: "1 max colum at left, 1 min colum at right, 1 top line"
  3 +description: "A template with 1 colum in left, 1 colum in right and 1 line in top"
  4 +number_of_boxes: 4
... ...
public/designs/templates/lefttopright/javascripts/template.js 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +$(document).ready(function() {
  2 + var box_4_height = $(".box-4").height();
  3 +
  4 + // Make box-2(the most left one) stay align with box-4
  5 + $(".box-2").css("margin-top", "-"+box_4_height+"px");
  6 +});
... ...
public/designs/templates/lefttopright/stylesheets/style.css 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +#boxes {
  2 + display: table;
  3 + width: 100%;
  4 +}
  5 +
  6 +.box-1 {
  7 + width: 58%;
  8 + float: left;
  9 + margin: 1% 1% 0% 1%;
  10 +}
  11 +
  12 +
  13 +.box-2 {
  14 + position: relative;
  15 + float: left;
  16 + width: 20%;
  17 +}
  18 +
  19 +.box-3 {
  20 + position: relative;
  21 + float: right;
  22 + width: 20%;
  23 + margin-top: 1%;
  24 +}
  25 +
  26 +.box-4 {
  27 + float: left;
  28 + width: 79%;
  29 + margin-left: 21%;
  30 +}
  31 +
  32 +#profile-activity ul,
  33 +#profile-network ul,
  34 +#profile-wall ul {
  35 + width: 460px;
  36 +}
  37 +#profile-activity ul.comment-replies,
  38 +#profile-network ul.comment-replies,
  39 +#profile-wall ul.comment-replies {
  40 + width: auto;
  41 +}
  42 +
... ...
public/designs/templates/lefttopright/thumbnail.png 0 → 100644

3.02 KB

public/designs/templates/lefttopright/thumbnail.svg 0 → 100644
... ... @@ -0,0 +1,434 @@
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +
  4 +<svg
  5 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  6 + xmlns:cc="http://creativecommons.org/ns#"
  7 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8 + xmlns:svg="http://www.w3.org/2000/svg"
  9 + xmlns="http://www.w3.org/2000/svg"
  10 + xmlns:xlink="http://www.w3.org/1999/xlink"
  11 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  12 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  13 + width="64px"
  14 + height="64px"
  15 + id="svg2383"
  16 + sodipodi:version="0.32"
  17 + inkscape:version="0.48.3.1 r9886"
  18 + sodipodi:docname="thumbnail.svg"
  19 + inkscape:output_extension="org.inkscape.output.svg.inkscape"
  20 + inkscape:export-filename="thumbnail.png"
  21 + inkscape:export-xdpi="90"
  22 + inkscape:export-ydpi="90"
  23 + version="1.1">
  24 + <defs
  25 + id="defs2385">
  26 + <linearGradient
  27 + id="linearGradient3263">
  28 + <stop
  29 + id="stop3265"
  30 + offset="0"
  31 + style="stop-color:#204a87;stop-opacity:1" />
  32 + <stop
  33 + id="stop3267"
  34 + offset="1"
  35 + style="stop-color:#729fcf;stop-opacity:1" />
  36 + </linearGradient>
  37 + <linearGradient
  38 + id="linearGradient3257"
  39 + inkscape:collect="always">
  40 + <stop
  41 + id="stop3259"
  42 + offset="0"
  43 + style="stop-color:#204a87;stop-opacity:1" />
  44 + <stop
  45 + id="stop3261"
  46 + offset="1"
  47 + style="stop-color:#729fcf;stop-opacity:1" />
  48 + </linearGradient>
  49 + <linearGradient
  50 + inkscape:collect="always"
  51 + id="linearGradient3245">
  52 + <stop
  53 + style="stop-color:#204a87;stop-opacity:1"
  54 + offset="0"
  55 + id="stop3247" />
  56 + <stop
  57 + style="stop-color:#729fcf;stop-opacity:1"
  58 + offset="1"
  59 + id="stop3249" />
  60 + </linearGradient>
  61 + <inkscape:perspective
  62 + sodipodi:type="inkscape:persp3d"
  63 + inkscape:vp_x="0 : 32 : 1"
  64 + inkscape:vp_y="0 : 1000 : 0"
  65 + inkscape:vp_z="64 : 32 : 1"
  66 + inkscape:persp3d-origin="32 : 21.333333 : 1"
  67 + id="perspective2391" />
  68 + <filter
  69 + inkscape:collect="always"
  70 + id="filter3241">
  71 + <feGaussianBlur
  72 + inkscape:collect="always"
  73 + stdDeviation="0.9075"
  74 + id="feGaussianBlur3243" />
  75 + </filter>
  76 + <linearGradient
  77 + inkscape:collect="always"
  78 + xlink:href="#linearGradient3245"
  79 + id="linearGradient3251"
  80 + x1="11.5"
  81 + y1="60.5"
  82 + x2="3.5"
  83 + y2="3.5"
  84 + gradientUnits="userSpaceOnUse"
  85 + gradientTransform="translate(2,0)" />
  86 + <linearGradient
  87 + inkscape:collect="always"
  88 + xlink:href="#linearGradient3263"
  89 + id="linearGradient3253"
  90 + x1="49.5"
  91 + y1="60.5"
  92 + x2="23.5"
  93 + y2="3.5"
  94 + gradientUnits="userSpaceOnUse"
  95 + gradientTransform="translate(2,0)" />
  96 + <linearGradient
  97 + inkscape:collect="always"
  98 + xlink:href="#linearGradient3257"
  99 + id="linearGradient3255"
  100 + x1="60.5"
  101 + y1="60.5"
  102 + x2="51.5"
  103 + y2="3.5"
  104 + gradientUnits="userSpaceOnUse" />
  105 + <filter
  106 + color-interpolation-filters="sRGB"
  107 + inkscape:collect="always"
  108 + id="filter3241-2">
  109 + <feGaussianBlur
  110 + inkscape:collect="always"
  111 + stdDeviation="0.9075"
  112 + id="feGaussianBlur3243-4" />
  113 + </filter>
  114 + <linearGradient
  115 + inkscape:collect="always"
  116 + xlink:href="#linearGradient3245-1"
  117 + id="linearGradient3251-0"
  118 + x1="11.5"
  119 + y1="60.5"
  120 + x2="3.5"
  121 + y2="3.5"
  122 + gradientUnits="userSpaceOnUse"
  123 + gradientTransform="translate(249.71429,298.93361)" />
  124 + <linearGradient
  125 + inkscape:collect="always"
  126 + id="linearGradient3245-1">
  127 + <stop
  128 + style="stop-color:#204a87;stop-opacity:1"
  129 + offset="0"
  130 + id="stop3247-6" />
  131 + <stop
  132 + style="stop-color:#729fcf;stop-opacity:1"
  133 + offset="1"
  134 + id="stop3249-5" />
  135 + </linearGradient>
  136 + <filter
  137 + color-interpolation-filters="sRGB"
  138 + inkscape:collect="always"
  139 + id="filter3241-4-8">
  140 + <feGaussianBlur
  141 + inkscape:collect="always"
  142 + stdDeviation="0.9075"
  143 + id="feGaussianBlur3243-0-7" />
  144 + </filter>
  145 + <linearGradient
  146 + inkscape:collect="always"
  147 + xlink:href="#linearGradient3263-5-8"
  148 + id="linearGradient5185"
  149 + gradientUnits="userSpaceOnUse"
  150 + gradientTransform="matrix(1.4160224,0,0,0.76166072,243.22589,313.33388)"
  151 + x1="42.5"
  152 + y1="60.5"
  153 + x2="19.5"
  154 + y2="3.5" />
  155 + <linearGradient
  156 + id="linearGradient3263-5-8">
  157 + <stop
  158 + id="stop3265-4-3"
  159 + offset="0"
  160 + style="stop-color:#204a87;stop-opacity:1" />
  161 + <stop
  162 + id="stop3267-2-4"
  163 + offset="1"
  164 + style="stop-color:#729fcf;stop-opacity:1" />
  165 + </linearGradient>
  166 + <filter
  167 + color-interpolation-filters="sRGB"
  168 + inkscape:collect="always"
  169 + id="filter3241-0">
  170 + <feGaussianBlur
  171 + inkscape:collect="always"
  172 + stdDeviation="0.9075"
  173 + id="feGaussianBlur3243-3" />
  174 + </filter>
  175 + <linearGradient
  176 + inkscape:collect="always"
  177 + xlink:href="#linearGradient3245-7"
  178 + id="linearGradient5168"
  179 + gradientUnits="userSpaceOnUse"
  180 + gradientTransform="matrix(1.0025263,0,0,0.76423683,344.68831,300.13218)"
  181 + x1="11.5"
  182 + y1="60.5"
  183 + x2="3.5"
  184 + y2="3.5" />
  185 + <linearGradient
  186 + inkscape:collect="always"
  187 + id="linearGradient3245-7">
  188 + <stop
  189 + style="stop-color:#204a87;stop-opacity:1"
  190 + offset="0"
  191 + id="stop3247-0" />
  192 + <stop
  193 + style="stop-color:#729fcf;stop-opacity:1"
  194 + offset="1"
  195 + id="stop3249-7" />
  196 + </linearGradient>
  197 + <linearGradient
  198 + inkscape:collect="always"
  199 + xlink:href="#linearGradient3245-70"
  200 + id="linearGradient3251-55"
  201 + x1="11.5"
  202 + y1="60.5"
  203 + x2="3.5"
  204 + y2="3.5"
  205 + gradientUnits="userSpaceOnUse"
  206 + gradientTransform="matrix(1.0025263,0,0,0.76423683,344.68831,300.13218)" />
  207 + <linearGradient
  208 + inkscape:collect="always"
  209 + id="linearGradient3245-70">
  210 + <stop
  211 + style="stop-color:#204a87;stop-opacity:1"
  212 + offset="0"
  213 + id="stop3247-2" />
  214 + <stop
  215 + style="stop-color:#729fcf;stop-opacity:1"
  216 + offset="1"
  217 + id="stop3249-87" />
  218 + </linearGradient>
  219 + <linearGradient
  220 + inkscape:collect="always"
  221 + xlink:href="#linearGradient3245-1"
  222 + id="linearGradient5832"
  223 + gradientUnits="userSpaceOnUse"
  224 + gradientTransform="translate(249.71429,298.93361)"
  225 + x1="11.5"
  226 + y1="60.5"
  227 + x2="3.5"
  228 + y2="3.5" />
  229 + <linearGradient
  230 + inkscape:collect="always"
  231 + xlink:href="#linearGradient3263-5-8"
  232 + id="linearGradient5834"
  233 + gradientUnits="userSpaceOnUse"
  234 + gradientTransform="matrix(1.4160224,0,0,0.76166072,243.22589,313.33388)"
  235 + x1="42.5"
  236 + y1="60.5"
  237 + x2="19.5"
  238 + y2="3.5" />
  239 + <linearGradient
  240 + inkscape:collect="always"
  241 + xlink:href="#linearGradient3245-7"
  242 + id="linearGradient5836"
  243 + gradientUnits="userSpaceOnUse"
  244 + gradientTransform="matrix(1.0025263,0,0,0.76423683,344.68831,300.13218)"
  245 + x1="11.5"
  246 + y1="60.5"
  247 + x2="3.5"
  248 + y2="3.5" />
  249 + <linearGradient
  250 + inkscape:collect="always"
  251 + xlink:href="#linearGradient3245-70"
  252 + id="linearGradient5838"
  253 + gradientUnits="userSpaceOnUse"
  254 + gradientTransform="matrix(1.0025263,0,0,0.76423683,344.68831,300.13218)"
  255 + x1="11.5"
  256 + y1="60.5"
  257 + x2="3.5"
  258 + y2="3.5" />
  259 + </defs>
  260 + <sodipodi:namedview
  261 + id="base"
  262 + pagecolor="#ffffff"
  263 + bordercolor="#666666"
  264 + borderopacity="1.0"
  265 + inkscape:pageopacity="0.0"
  266 + inkscape:pageshadow="2"
  267 + inkscape:zoom="2.5351563"
  268 + inkscape:cx="-16.066434"
  269 + inkscape:cy="0.12147739"
  270 + inkscape:current-layer="layer1"
  271 + showgrid="true"
  272 + inkscape:document-units="px"
  273 + inkscape:grid-bbox="true"
  274 + inkscape:window-width="1364"
  275 + inkscape:window-height="678"
  276 + inkscape:window-x="0"
  277 + inkscape:window-y="27"
  278 + objecttolerance="10"
  279 + gridtolerance="10"
  280 + guidetolerance="10"
  281 + inkscape:window-maximized="0">
  282 + <inkscape:grid
  283 + type="xygrid"
  284 + id="grid2382"
  285 + visible="true"
  286 + enabled="true"
  287 + originx="0.5px"
  288 + originy="0.5px"
  289 + empcolor="#0000ff"
  290 + empopacity="0.1254902"
  291 + dotted="true" />
  292 + </sodipodi:namedview>
  293 + <metadata
  294 + id="metadata2388">
  295 + <rdf:RDF>
  296 + <cc:Work
  297 + rdf:about="">
  298 + <dc:format>image/svg+xml</dc:format>
  299 + <dc:type
  300 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  301 + </cc:Work>
  302 + </rdf:RDF>
  303 + </metadata>
  304 + <g
  305 + id="layer1"
  306 + inkscape:label="Layer 1"
  307 + inkscape:groupmode="layer">
  308 + <g
  309 + transform="translate(-247.62879,-298.93361)"
  310 + id="g5187">
  311 + <g
  312 + id="g4846">
  313 + <g
  314 + id="g3190-2"
  315 + style="opacity:0.4;filter:url(#filter3241-2)"
  316 + transform="translate(250.71429,299.93361)">
  317 + <rect
  318 + y="1.5"
  319 + x="1.5"
  320 + height="59"
  321 + width="11"
  322 + id="rect3184-1"
  323 + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  324 + ry="2"
  325 + rx="2" />
  326 + </g>
  327 + <rect
  328 + y="300.43362"
  329 + x="251.21428"
  330 + height="59"
  331 + width="11"
  332 + id="rect2395-8"
  333 + style="fill:url(#linearGradient5832);fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  334 + ry="2"
  335 + rx="2" />
  336 + <rect
  337 + ry="1"
  338 + style="opacity:0.6;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  339 + id="rect3158-7"
  340 + width="9"
  341 + height="57"
  342 + x="252.21428"
  343 + y="301.43362"
  344 + rx="1" />
  345 + </g>
  346 + <g
  347 + transform="matrix(0.69260722,0,0,1,81.51265,0)"
  348 + id="g5180">
  349 + <rect
  350 + rx="2"
  351 + ry="2"
  352 + style="opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3241-4-8);enable-background:accumulate"
  353 + id="rect3188-8"
  354 + width="31"
  355 + height="59"
  356 + x="16.5"
  357 + y="2.5"
  358 + transform="matrix(1.4160224,0,0,0.76166071,243.80989,314.09132)" />
  359 + <rect
  360 + y="314.47638"
  361 + x="265.17426"
  362 + height="44.937984"
  363 + width="43.896694"
  364 + id="rect3156-3"
  365 + style="fill:url(#linearGradient5834);fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:1.03852236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  366 + ry="1.5233214" />
  367 + <rect
  368 + y="315.23804"
  369 + x="266.59027"
  370 + height="43.414661"
  371 + width="41.064651"
  372 + id="rect3162-1"
  373 + style="opacity:0.6;fill:none;stroke:#ffffff;stroke-width:1.03852236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  374 + ry="0.7616607"
  375 + inkscape:export-xdpi="90"
  376 + inkscape:export-ydpi="90" />
  377 + </g>
  378 + <g
  379 + transform="matrix(0.9761091,0,0,1,6.3333026,0)"
  380 + id="g5108">
  381 + <rect
  382 + rx="2"
  383 + ry="2"
  384 + style="opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3241-0);enable-background:accumulate"
  385 + id="rect3184-5"
  386 + width="11"
  387 + height="59"
  388 + x="2.5"
  389 + y="2.5"
  390 + transform="matrix(0,1.0025263,-0.76423683,0,312.09322,298.92289)" />
  391 + <g
  392 + transform="matrix(0,1,-1,0,611.46116,-45.820829)"
  393 + id="g4877-9">
  394 + <rect
  395 + ry="1.5284736"
  396 + style="fill:url(#linearGradient5836);fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:0.87531;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  397 + id="rect2395-95"
  398 + width="11.02779"
  399 + height="45.089973"
  400 + x="346.19211"
  401 + y="301.27853" />
  402 + <rect
  403 + y="302.04276"
  404 + x="347.19461"
  405 + height="43.561501"
  406 + width="9.0227375"
  407 + id="rect3158-2"
  408 + style="opacity:0.6;fill:none;stroke:#ffffff;stroke-width:0.87531;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  409 + ry="0.76423681" />
  410 + </g>
  411 + </g>
  412 + <g
  413 + id="g4877-8"
  414 + transform="translate(-47.956003,13.002947)">
  415 + <rect
  416 + y="301.27853"
  417 + x="346.19211"
  418 + height="45.089973"
  419 + width="11.02779"
  420 + id="rect2395-1"
  421 + style="fill:url(#linearGradient5838);fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:0.87531;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  422 + ry="1.5284736" />
  423 + <rect
  424 + ry="0.76423681"
  425 + style="opacity:0.6;fill:none;stroke:#ffffff;stroke-width:0.87531;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  426 + id="rect3158-71"
  427 + width="9.0227375"
  428 + height="43.561501"
  429 + x="347.19461"
  430 + y="302.04276" />
  431 + </g>
  432 + </g>
  433 + </g>
  434 +</svg>
... ...
public/designs/templates/topleft/stylesheets/style.css
... ... @@ -20,7 +20,7 @@
20 20  
21 21 .box-3 {
22 22 position: relative;
23   - display: table-footer-group;
  23 + display: table-header-group;
24 24 width: 100%;
25 25 }
26 26  
... ...
public/images/blocks/4.png 0 → 100644

1.14 KB

public/images/blocks/4.svg 0 → 100644
... ... @@ -0,0 +1,83 @@
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +
  4 +<svg
  5 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  6 + xmlns:cc="http://creativecommons.org/ns#"
  7 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8 + xmlns:svg="http://www.w3.org/2000/svg"
  9 + xmlns="http://www.w3.org/2000/svg"
  10 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  11 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  12 + width="64"
  13 + height="64"
  14 + id="svg2"
  15 + sodipodi:version="0.32"
  16 + inkscape:version="0.48.3.1 r9886"
  17 + version="1.0"
  18 + sodipodi:docname="4.svg"
  19 + inkscape:output_extension="org.inkscape.output.svg.inkscape"
  20 + inkscape:export-filename="/home/noosfero/sites/noosfero/public/images/blocks/4.png"
  21 + inkscape:export-xdpi="87.968124"
  22 + inkscape:export-ydpi="87.968124">
  23 + <defs
  24 + id="defs4" />
  25 + <sodipodi:namedview
  26 + id="base"
  27 + pagecolor="#ffffff"
  28 + bordercolor="#666666"
  29 + borderopacity="1.0"
  30 + inkscape:pageopacity="0.0"
  31 + inkscape:pageshadow="2"
  32 + inkscape:zoom="5.921875"
  33 + inkscape:cx="32"
  34 + inkscape:cy="32"
  35 + inkscape:document-units="px"
  36 + inkscape:current-layer="layer1"
  37 + width="64px"
  38 + height="64px"
  39 + inkscape:window-width="1366"
  40 + inkscape:window-height="681"
  41 + inkscape:window-x="0"
  42 + inkscape:window-y="27"
  43 + showgrid="false"
  44 + inkscape:window-maximized="1" />
  45 + <metadata
  46 + id="metadata7">
  47 + <rdf:RDF>
  48 + <cc:Work
  49 + rdf:about="">
  50 + <dc:format>image/svg+xml</dc:format>
  51 + <dc:type
  52 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  53 + <dc:title></dc:title>
  54 + </cc:Work>
  55 + </rdf:RDF>
  56 + </metadata>
  57 + <g
  58 + inkscape:label="Camada 1"
  59 + inkscape:groupmode="layer"
  60 + id="layer1">
  61 + <path
  62 + sodipodi:type="arc"
  63 + style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:4.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.19607843"
  64 + id="path2162"
  65 + sodipodi:cx="24.12665"
  66 + sodipodi:cy="24.063324"
  67 + sodipodi:rx="21.593668"
  68 + sodipodi:ry="21.277044"
  69 + d="M 45.720318 24.063324 A 21.593668 21.277044 0 1 1 2.5329819,24.063324 A 21.593668 21.277044 0 1 1 45.720318 24.063324 z"
  70 + transform="translate(7.8733501,7.936676)" />
  71 + <text
  72 + xml:space="preserve"
  73 + style="font-size:42.00993729px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:0.19607843;stroke:none;font-family:FreeSans"
  74 + x="16.267668"
  75 + y="47.399117"
  76 + id="text3134"
  77 + sodipodi:linespacing="125%"><tspan
  78 + sodipodi:role="line"
  79 + id="tspan3136"
  80 + x="16.267668"
  81 + y="47.399117">4</tspan></text>
  82 + </g>
  83 +</svg>
... ...
public/images/control-panel/role-management.gif 0 → 100644

2.07 KB

public/images/control-panel/role-management.png 0 → 100644

3.63 KB

public/javascripts/application.js
... ... @@ -27,6 +27,7 @@
27 27 *= require manage-products.js
28 28 *= require catalog.js
29 29 *= require autogrow.js
  30 +*= require require_login.js
30 31 */
31 32  
32 33 // scope for noosfero stuff
... ...
public/javascripts/assign_role.js 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +(function($){
  2 + 'use strict';
  3 +
  4 + function toggle_assignment_method() {
  5 + if (this.value != "roles") {
  6 + $('.assign_by_roles').hide();
  7 + $('.assign_by_members').show();
  8 + } else {
  9 + $('.assign_by_members').hide();
  10 + $('.assign_by_roles').show();
  11 + }
  12 + }
  13 +
  14 + $(document).ready(function() {
  15 + $('.assign_by_roles').hide();
  16 + // Event triggers
  17 + $('.assign_role_by').click(toggle_assignment_method);
  18 + });
  19 +})(jQuery);
... ...
public/javascripts/manage-fields.js
... ... @@ -57,7 +57,7 @@ jQuery(document).ready(function(){
57 57 }
58 58  
59 59 var checkbox = jQuery(checkboxes[i+3]).attr("id").split("_")
60   - jQuery("#" + checkbox.first() + "_" + checkbox.last()).attr("checked", allchecked)
  60 + jQuery("#" + checkbox[0] + "_" + checkbox[checkbox.length-1]).attr("checked", allchecked)
61 61 }
62 62 }
63 63  
... ... @@ -74,10 +74,10 @@ jQuery(document).ready(function(){
74 74  
75 75 jQuery("input[type='checkbox']").click(function (){
76 76 var checkbox = jQuery(this).attr("id").split("_")
77   - verify_checked(checkbox.first())
  77 + verify_checked(checkbox[0])
78 78  
79 79 if(this.checked == false) {
80   - jQuery("#" + checkbox.first() + "_" + checkbox.last()).attr("checked", false)
  80 + jQuery("#" + checkbox[0] + "_" + checkbox[checkbox.length-1]).attr("checked", false)
81 81 }
82 82 })
83 83 })
... ...
public/javascripts/report-abuse.js
1 1 jQuery(function($) {
2 2 $('.report-abuse-action').live('click', function() {
3   - if($(this).attr('href')){
4   - noosfero.modal.inline($(this).attr('href'), {
5   - innerHeight: '300px',
6   - innerWidth: '445px'
7   - });
8   - }
  3 + if($(this).attr('href'))
  4 + noosfero.modal.url($(this).attr('href'));
  5 +
9 6 return false;
10 7 });
11 8  
... ...
public/javascripts/require_login.js 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +(function($) {
  2 + $(window).bind('userDataLoaded', function(event, data) {
  3 + $(".require-login-popup").live('click', function(){
  4 + clicked = $(this);
  5 + url = clicked.attr("href");
  6 + if(url!=undefined && url!='' && url!='#') {
  7 + if(!data.login) {
  8 + url = $.param.querystring(url, "require_login_popup=true");
  9 + }
  10 + loading_for_button(this);
  11 + $.post(url, function(data){
  12 + if(data.require_login_popup) {
  13 + $('#link_login').click(); //TODO see a better way to show login popup
  14 + }
  15 + }).complete(function() {
  16 + clicked.css("cursor","");
  17 + $(".small-loading").remove();
  18 + });
  19 + } else {
  20 + $('#link_login').click();
  21 + }
  22 + return false;
  23 + });
  24 + });
  25 +})(jQuery);
... ...
public/stylesheets/application.css
... ... @@ -1501,6 +1501,14 @@ a.comment-picture {
1501 1501 #content .title {
1502 1502 margin-bottom: 2px;
1503 1503 }
  1504 +.blog-post .post-pic {
  1505 + background-position: 50% 40%;
  1506 + background-size: cover;
  1507 + height: 150px;
  1508 +}
  1509 +.blog-post .post-pic.empty {
  1510 + display: none;
  1511 +}
1504 1512 .metadata, .blog-post .metadata {
1505 1513 display: block;
1506 1514 text-align: center;
... ... @@ -1834,6 +1842,9 @@ a.button.disabled, input.disabled {
1834 1842 #box-organizer div.box-3 {
1835 1843 background-image: url(../images/blocks/3.png);
1836 1844 }
  1845 +#box-organizer div.box-4 {
  1846 + background-image: url(../images/blocks/4.png);
  1847 +}
1837 1848 #box-organizer .block {
1838 1849 cursor: move;
1839 1850 }
... ... @@ -4677,6 +4688,12 @@ h1#agenda-title {
4677 4688 .controller-profile_editor a.control-panel-welcome-page {
4678 4689 background-image: url(../images/control-panel/welcome-page.png)
4679 4690 }
  4691 +.controller-profile_editor a.control-panel-roles {
  4692 + background-image: url(../images/control-panel/role-management.png)
  4693 +}
  4694 +.controller-profile_editor .msie6 a.control-panel-roles {
  4695 + background-image: url(../images/control-panel/role-management.gif)
  4696 +}
4680 4697 /* ==> public/stylesheets/controller_profile_members.css <== */
4681 4698 .controller-profile_members .no-boxes {
4682 4699 margin: 30px
... ...
script/install-dependencies/debian-wheezy.sh
1   -binary_packages='deb http://download.noosfero.org/debian/wheezy-1.1 ./'
  1 +binary_packages='deb http://download.noosfero.org/debian/wheezy-1.2 ./'
2 2  
3 3 source_packages=$(echo "$binary_packages" | sed -e 's/^deb/deb-src/')
4 4  
... ... @@ -53,6 +53,13 @@ FPQAoNmiMgP6zGF9rgOEWMEiFEryayrz
53 53 EOF
54 54 fi
55 55  
  56 +if grep -qrl wheezy /etc/apt/sources.list* && ! grep -qrl wheezy-backports /etc/apt/sources.list*; then
  57 + sudo tee /etc/apt/sources.list.d/backports.list <<EOF
  58 +deb http://httpredir.debian.org/debian wheezy-backports main
  59 +EOF
  60 +fi
  61 +
  62 +
56 63 if test -f tmp/debian/Release.gpg; then
57 64 echo "deb file://$(pwd)/tmp/debian/ ./" | sudo tee /etc/apt/sources.list.d/local.list
58 65 sudo apt-key add tmp/debian/signing-key.asc
... ... @@ -65,6 +72,9 @@ run sudo apt-get -qy dist-upgrade
65 72  
66 73 run sudo apt-get -y install dctrl-tools
67 74  
  75 +# *sigh* need ruby-rspec from backports
  76 +run sudo apt-get -y install -t wheezy-backports ruby-rspec
  77 +
68 78 # needed to run noosfero
69 79 packages=$(grep-dctrl -n -s Build-Depends,Depends,Recommends -S -X noosfero debian/control | sed -e '/^\s*#/d; s/([^)]*)//g; s/,\s*/\n/g' | grep -v 'memcached\|debconf\|dbconfig-common\|misc:Depends\|adduser\|mail-transport-agent')
70 80 run sudo apt-get -y install $packages
... ...
script/production
... ... @@ -85,7 +85,7 @@ environments_loop() {
85 85 }
86 86  
87 87 do_running() {
88   - pids=$(sed "s/.*/& /" tmp/pids/thin.*.pid | tr -d '\n' 2>/dev/null || true)
  88 + pids=$(sed "s/.*/& /" tmp/pids/thin.*.pid 2>/dev/null | tr -d '\n')
89 89 # passes if any of $pids exist, fails otherwise
90 90 kill -0 $pids > /dev/null 2>&1
91 91 }
... ...
test/factories.rb
... ... @@ -454,7 +454,7 @@ module Noosfero::Factory
454 454 end
455 455  
456 456 def defaults_for_suggest_article
457   - { :name => 'Sender', :email => 'sender@example.com', :article_name => 'Some title', :article_body => 'some body text', :article_abstract => 'some abstract text'}
  457 + { :name => 'Sender', :email => 'sender@example.com', :article => {:name => 'Some title', :body => 'some body text', :abstract => 'some abstract text'}}
458 458 end
459 459  
460 460 def defaults_for_comment(params = {})
... ...
test/functional/application_controller_test.rb
... ... @@ -578,4 +578,22 @@ class ApplicationControllerTest &lt; ActionController::TestCase
578 578 assert_response :success
579 579 end
580 580  
  581 + should "redirect to 404 if profile is '~' and user is not logged in" do
  582 + get :index, :profile => '~'
  583 + assert_response :missing
  584 + end
  585 +
  586 + should "redirect to action when profile is '~' " do
  587 + login_as('ze')
  588 + get :index, :profile => '~'
  589 + assert_response 302
  590 + end
  591 +
  592 + should "substitute '~' by current user and redirect properly " do
  593 + login_as('ze')
  594 + profile = Profile.where(:identifier => 'ze').first
  595 + get :index, :profile => '~'
  596 + assert_redirected_to :controller => 'test', :action => 'index', :profile => profile.identifier
  597 + end
  598 +
581 599 end
... ...
test/functional/cms_controller_test.rb
... ... @@ -1407,22 +1407,57 @@ class CmsControllerTest &lt; ActionController::TestCase
1407 1407 assert_template 'suggest_an_article'
1408 1408 end
1409 1409  
  1410 + should 'display name and email when a not logged in user suggest an article' do
  1411 + logout
  1412 + get :suggest_an_article, :profile => profile.identifier, :back_to => 'action_view'
  1413 +
  1414 + assert_select '#task_name'
  1415 + assert_select '#task_email'
  1416 + end
  1417 +
  1418 + should 'do not display name and email when a logged in user suggest an article' do
  1419 + get :suggest_an_article, :profile => profile.identifier, :back_to => 'action_view'
  1420 +
  1421 + assert_select '#task_name', 0
  1422 + assert_select '#task_email', 0
  1423 + end
  1424 +
  1425 + should 'display captcha when suggest an article for not logged in users' do
  1426 + logout
  1427 + get :suggest_an_article, :profile => profile.identifier, :back_to => 'action_view'
  1428 +
  1429 + assert_select '#dynamic_recaptcha'
  1430 + end
  1431 +
  1432 + should 'not display captcha when suggest an article for logged in users' do
  1433 + get :suggest_an_article, :profile => profile.identifier, :back_to => 'action_view'
  1434 +
  1435 + assert_select '#dynamic_recaptcha', 0
  1436 + end
  1437 +
1410 1438 should 'render TinyMce Editor on suggestion of article' do
1411 1439 logout
1412 1440 get :suggest_an_article, :profile => profile.identifier
1413 1441  
1414   - assert_tag :tag => 'textarea', :attributes => { :name => /article_abstract/, :class => 'mceEditor' }
1415   - assert_tag :tag => 'textarea', :attributes => { :name => /article_body/, :class => 'mceEditor' }
  1442 + assert_tag :tag => 'textarea', :attributes => { :name => /task\[article\]\[abstract\]/, :class => 'mceEditor' }
  1443 + assert_tag :tag => 'textarea', :attributes => { :name => /task\[article\]\[body\]/, :class => 'mceEditor' }
1416 1444 end
1417 1445  
1418 1446 should 'create a task suggest task to a profile' do
1419 1447 c = Community.create!(:name => 'test comm', :identifier => 'test_comm', :moderated_articles => true)
1420 1448  
1421 1449 assert_difference 'SuggestArticle.count' do
1422   - post :suggest_an_article, :profile => c.identifier, :back_to => 'action_view', :task => {:article_name => 'some name', :article_body => 'some body', :email => 'some@localhost.com', :name => 'some name'}
  1450 + post :suggest_an_article, :profile => c.identifier, :back_to => 'action_view', :task => {:article => {:name => 'some name', :body => 'some body'}, :email => 'some@localhost.com', :name => 'some name'}
1423 1451 end
1424 1452 end
1425 1453  
  1454 + should 'create suggest task with logged in user as the article author' do
  1455 + c = Community.create!(:name => 'test comm', :identifier => 'test_comm', :moderated_articles => true)
  1456 +
  1457 + post :suggest_an_article, :profile => c.identifier, :back_to => 'action_view', :task => {:article => {:name => 'some name', :body => 'some body'}}
  1458 + assert_equal profile, SuggestArticle.last.requestor
  1459 + end
  1460 +
1426 1461 should 'suggest an article from a profile' do
1427 1462 c = Community.create!(:name => 'test comm', :identifier => 'test_comm', :moderated_articles => true)
1428 1463 get :suggest_an_article, :profile => c.identifier, :back_to => c.identifier
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -175,7 +175,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
175 175 admin = fast_create(Person)
176 176 community.add_member(admin)
177 177  
178   - folder = fast_create(Folder, :profile_id => community.id, :published => false)
  178 + folder = fast_create(Folder, :profile_id => community.id, :published => false, :show_to_followers => false)
179 179 community.add_member(profile)
180 180 login_as(profile.identifier)
181 181  
... ... @@ -278,7 +278,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
278 278 should 'not give access to private articles if logged in and only member' do
279 279 person = create_user('test_user').person
280 280 profile = Profile.create!(:name => 'test profile', :identifier => 'test_profile')
281   - intranet = Folder.create!(:name => 'my_intranet', :profile => profile, :published => false)
  281 + intranet = Folder.create!(:name => 'my_intranet', :profile => profile, :published => false, :show_to_followers => false)
282 282 profile.affiliate(person, Profile::Roles.member(profile.environment.id))
283 283 login_as('test_user')
284 284  
... ... @@ -780,6 +780,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
780 780 assert_no_tag :tag => 'div', :attributes => { :class => 'short-post'}, :content => /Anything/
781 781 end
782 782  
  783 + should 'show only first paragraph with picture of posts if visualization_format is short+pic' do
  784 + login_as(profile.identifier)
  785 +
  786 + blog = Blog.create!(:name => 'A blog test', :profile => profile, :visualization_format => 'short+pic')
  787 +
  788 + blog.posts << TinyMceArticle.create!(:name => 'first post', :parent => blog, :profile => profile, :body => '<p>Content to be displayed.</p> <img src="pic.jpg">')
  789 +
  790 + get :view_page, :profile => profile.identifier, :page => blog.path
  791 +
  792 + assert_select '.blog-post .post-pic' do |el|
  793 + assert_match /background-image:url\(pic.jpg\)/, el.to_s
  794 + end
  795 + end
  796 +
783 797 should 'display link to edit blog for allowed' do
784 798 blog = fast_create(Blog, :profile_id => profile.id, :path => 'blog')
785 799 login_as(profile.identifier)
... ...
test/functional/profile_design_controller_test.rb
... ... @@ -737,9 +737,9 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
737 737 end
738 738 end
739 739  
740   - test 'should forbid POST to save for fixed blocks' do
  740 + test 'should forbid POST to save for uneditable blocks' do
741 741 block = profile.blocks.last
742   - block.fixed = true
  742 + block.edit_modes = "none"
743 743 block.save!
744 744  
745 745 post :save, id: block.id, profile: profile.identifier
... ... @@ -748,7 +748,7 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
748 748  
749 749 test 'should forbid POST to move_block for fixed blocks' do
750 750 block = profile.blocks.last
751   - block.fixed = true
  751 + block.move_modes = "none"
752 752 block.save!
753 753  
754 754 post :move_block, id: block.id, profile: profile.identifier, target: "end-of-box-#{@box3.id}"
... ...
test/functional/profile_roles_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,106 @@
  1 +require_relative "../test_helper"
  2 +require 'profile_roles_controller'
  3 +
  4 +class ProfileRolesControllerTest < ActionController::TestCase
  5 +
  6 + def setup
  7 + @controller = ProfileRolesController.new
  8 + @request = ActionController::TestRequest.new
  9 + @response = ActionController::TestResponse.new
  10 + @role = Role.find(:first)
  11 + end
  12 +
  13 + should 'create a custom role' do
  14 + community = fast_create(Community)
  15 + admin = create_user_with_permission('admin_user', 'manage_custom_roles', community)
  16 + login_as :admin_user
  17 + post :create, :profile => community.identifier, :role => {:name => "some_role", :permissions => ["edit_profile"] }
  18 + role = Role.where(:name => 'some_role').first
  19 +
  20 + assert_not_nil role
  21 + assert_equal community.id, role.profile_id
  22 + end
  23 +
  24 + should 'not create a custom role without permission' do
  25 + community = fast_create(Community)
  26 + moderator = create_user_with_permission('profile_admin', 'edit_profile', community)
  27 + login_as :profile_admin
  28 + post :create, :profile => community.identifier, :role => {:name => "new_admin", :permissions => ["edit_profile"] }
  29 +
  30 + assert_response 403
  31 + assert_template 'access_denied'
  32 +
  33 + role = Role.where(:name => 'new_admin')
  34 +
  35 + assert_empty role
  36 + end
  37 +
  38 +
  39 + should 'delete a custom role not used' do
  40 + community = fast_create(Community)
  41 + admin = create_user_with_permission('admin_user', 'manage_custom_roles', community)
  42 + login_as :admin_user
  43 + role = Role.create!({:name => 'delete_article', :key => 'profile_delete_article', :profile_id => community.id, :environment => Environment.default}, :without_protection => true)
  44 + post :remove , :profile => community.identifier, :id => role.id
  45 +
  46 + assert_response :redirect
  47 + assert_redirected_to :action => 'index'
  48 +
  49 + assert_not_includes Role.all, role
  50 + end
  51 +
  52 + should 'delete a custom role being used' do
  53 + community = fast_create(Community)
  54 + admin = create_user_with_permission('admin_user', 'manage_custom_roles', community)
  55 + login_as :admin_user
  56 + role = Role.create!({:name => 'delete_article', :key => 'profile_delete_article', :profile_id => community.id, :environment => Environment.default}, :without_protection => true)
  57 + admin.add_role(role, community)
  58 + moderator_role = Role.find_by_name("moderator")
  59 +
  60 + assert_not_includes community.members_by_role(moderator_role), admin
  61 +
  62 + post :remove , :profile => community.identifier, :id => role.id, :roles => [moderator_role.id]
  63 +
  64 + assert_response :redirect
  65 + assert_redirected_to :action => 'index'
  66 +
  67 + assert_not_includes Role.all, role
  68 + assert_includes community.members_by_role(moderator_role), admin
  69 + end
  70 +
  71 + should 'assign a custom role to single user' do
  72 + community = fast_create(Community)
  73 + admin = create_user_with_permission('admin_user', 'manage_custom_roles', community)
  74 + login_as :admin_user
  75 + role = Role.create!({:name => 'delete_article', :key => 'profile_delete_article', :profile_id => community.id, :environment => Environment.default}, :without_protection => true)
  76 +
  77 + assert_not_includes community.members_by_role(role), admin
  78 +
  79 + post :define, :profile => community.identifier, :id => role.id, :assign_role_by => "members", :person_id => admin.id
  80 +
  81 + assert_includes community.members_by_role(role), admin
  82 + end
  83 +
  84 + should 'replace a role with a custom role' do
  85 + community = fast_create(Community)
  86 + admin = create_user_with_permission('admin_user', 'manage_custom_roles', community)
  87 + moderator = create_user_with_permission('profile_admin', 'edit_profile', community)
  88 + login_as :admin_user
  89 + role = Role.create!({:name => 'delete_article', :key => 'profile_delete_article', :profile_id => community.id, :environment => Environment.default}, :without_protection => true)
  90 + moderator_role = Role.find_by_name("moderator")
  91 + admin.add_role(moderator_role, community)
  92 +
  93 + assert_not_includes community.members_by_role(role), admin
  94 +
  95 + assert_not_includes community.members_by_role(role), moderator
  96 + assert_not_includes community.members_by_role(moderator_role), moderator
  97 +
  98 + post :define, :profile => community.identifier, :id => role.id, :assign_role_by => "roles", :selected_role => moderator_role.id
  99 +
  100 + assert_not_includes community.members_by_role(moderator_role), admin
  101 + assert_includes community.members_by_role(role), admin
  102 +
  103 + assert_not_includes community.members_by_role(role), moderator
  104 + assert_not_includes community.members_by_role(moderator_role), moderator
  105 + end
  106 +end
... ...
test/functional/spam_controller_test.rb
... ... @@ -10,7 +10,7 @@ class SpamControllerTest &lt; ActionController::TestCase
10 10 @article = fast_create(TextileArticle, :profile_id => @community.id)
11 11 @spam_comment = fast_create(Comment, :source_id => @article.id, :spam => true, :name => 'foo', :email => 'foo@example.com')
12 12  
13   - @spam_suggest_article = SuggestArticle.create!(:name => 'spammer', :article_name => 'Spam article', :email => 'spammer@shady.place', :article_body => "Something you don't need", :target => @community, :spam => true)
  13 + @spam_suggest_article = SuggestArticle.create!(:name => 'spammer', :article => {:name => 'Spam article', :body => "Something you don't need"}, :email => 'spammer@shady.place', :target => @community, :spam => true)
14 14 login_as @profile.identifier
15 15 end
16 16  
... ...
test/functional/tasks_controller_test.rb
... ... @@ -264,11 +264,11 @@ class TasksControllerTest &lt; ActionController::TestCase
264 264 c = fast_create(Community)
265 265 c.add_admin profile
266 266 @controller.stubs(:profile).returns(c)
267   - t = SuggestArticle.create!(:article_name => 'test name', :article_abstract => 'test abstract', :article_body => 'test body', :name => 'some name', :email => 'test@localhost.com', :target => c)
  267 + t = SuggestArticle.create!(:article => {:name => 'test name', :abstract => 'test abstract', :body => 'test body'}, :name => 'some name', :email => 'test@localhost.com', :target => c)
268 268  
269 269 get :index
270   - assert_tag :tag => 'textarea', :content => /test abstract/, :attributes => { :name => /article_abstract/, :class => 'mceEditor' }
271   - assert_tag :tag => 'textarea', :content => /test body/, :attributes => { :name => /article_body/, :class => 'mceEditor' }
  270 + assert_tag :tag => 'textarea', :content => /test abstract/, :attributes => { :name => /tasks\[#{t.id}\]\[task\]\[article\]\[abstract\]/, :class => 'mceEditor' }
  271 + assert_tag :tag => 'textarea', :content => /test body/, :attributes => { :name => /tasks\[#{t.id}\]\[task\]\[article\]\[body\]/, :class => 'mceEditor' }
272 272 end
273 273  
274 274 should 'create TinyMceArticle article after finish approve suggested article task' do
... ... @@ -276,7 +276,7 @@ class TasksControllerTest &lt; ActionController::TestCase
276 276 c = fast_create(Community)
277 277 c.affiliate(profile, Profile::Roles.all_roles(profile.environment.id))
278 278 @controller.stubs(:profile).returns(c)
279   - t = SuggestArticle.create!(:article_name => 'test name', :article_body => 'test body', :name => 'some name', :email => 'test@localhost.com', :target => c)
  279 + t = SuggestArticle.create!(:article => {:name => 'test name', :body => 'test body'}, :name => 'some name', :email => 'test@localhost.com', :target => c)
280 280  
281 281 post :close, :tasks => {t.id => { :task => {}, :decision => "finish"}}
282 282 assert_not_nil TinyMceArticle.find(:first)
... ... @@ -288,16 +288,13 @@ class TasksControllerTest &lt; ActionController::TestCase
288 288 c.affiliate(profile, Profile::Roles.all_roles(profile.environment.id))
289 289 @controller.stubs(:profile).returns(c)
290 290 t = SuggestArticle.new
291   - t.article_name = 'test name'
292   - t.article_body = 'test body'
  291 + t.article = {:name => 'test name', :body => 'test body', :source => 'http://test.com', :source_name => 'some source name'}
293 292 t.name = 'some name'
294   - t.source = 'http://test.com'
295   - t.source_name = 'some source name'
296 293 t.email = 'test@localhost.com'
297 294 t.target = c
298 295 t.save!
299 296  
300   - post :close, :tasks => {t.id => { :task => {:article_name => 'new article name', :article_body => 'new body', :source => 'http://www.noosfero.com', :source_name => 'new source', :name => 'new name'}, :decision => "finish"}}
  297 + post :close, :tasks => {t.id => { :task => {:article => {:name => 'new article name', :body => 'new body', :source => 'http://www.noosfero.com', :source_name => 'new source'}, :name => 'new name'}, :decision => "finish"}}
301 298 assert_equal 'new article name', TinyMceArticle.find(:first).name
302 299 assert_equal 'new name', TinyMceArticle.find(:first).author_name
303 300 assert_equal 'new body', TinyMceArticle.find(:first).body
... ... @@ -305,6 +302,51 @@ class TasksControllerTest &lt; ActionController::TestCase
305 302 assert_equal 'new source', TinyMceArticle.find(:first).source_name
306 303 end
307 304  
  305 + should "display name from article suggestion when requestor was not setted" do
  306 + Task.destroy_all
  307 + c = fast_create(Community)
  308 + c.add_admin profile
  309 + @controller.stubs(:profile).returns(c)
  310 + t = SuggestArticle.create!(:article => {:name => 'test name', :abstract => 'test abstract', :body => 'test body'}, :name => 'some name', :email => 'test@localhost.com', :target => c)
  311 +
  312 + get :index
  313 + assert_select "#tasks_#{t.id}_task_name"
  314 + end
  315 +
  316 + should "append hidden tag with type value from article suggestion" do
  317 + Task.destroy_all
  318 + c = fast_create(Community)
  319 + c.add_admin profile
  320 + @controller.stubs(:profile).returns(c)
  321 + t = SuggestArticle.create!(:article => {:name => 'test name', :abstract => 'test abstract', :body => 'test body', :type => 'TextArticle'}, :name => 'some name', :email => 'test@localhost.com', :target => c)
  322 +
  323 + get :index
  324 + assert_select "#tasks_#{t.id}_task_article_type[value=TextArticle]"
  325 + end
  326 +
  327 + should "display parent_id selection from article suggestion with predefined value" do
  328 + Task.destroy_all
  329 + c = fast_create(Community)
  330 + c.add_admin profile
  331 + @controller.stubs(:profile).returns(c)
  332 + parent = fast_create(Folder, :profile_id => c.id)
  333 + t = SuggestArticle.create!(:article => {:name => 'test name', :abstract => 'test abstract', :body => 'test body', :parent_id => parent.id}, :name => 'some name', :email => 'test@localhost.com', :target => c)
  334 +
  335 + get :index
  336 + assert_select "#tasks_#{t.id}_task_article_parent_id option[value=#{parent.id}]"
  337 + end
  338 +
  339 + should "not display name from article suggestion when requestor was setted" do
  340 + Task.destroy_all
  341 + c = fast_create(Community)
  342 + c.add_admin profile
  343 + @controller.stubs(:profile).returns(c)
  344 + t = SuggestArticle.create!(:article => {:name => 'test name', :abstract => 'test abstract', :body => 'test body'}, :requestor => fast_create(Person), :target => c)
  345 +
  346 + get :index
  347 + assert_select "#tasks_#{t.id}_task_name", 0
  348 + end
  349 +
308 350 should "not crash if accessing close without tasks parameter" do
309 351 assert_nothing_raised do
310 352 post :close
... ...
test/integration/routing_test.rb
... ... @@ -272,4 +272,8 @@ class RoutingTest &lt; ActionController::IntegrationTest
272 272 assert_routing('/embed/block/12345', :controller => 'embed', :action => 'block', :id => '12345')
273 273 end
274 274  
  275 + should 'accept ~ as placeholder for current user' do
  276 + assert_routing('/profile/~', :controller => 'profile', :profile => '~', :action => 'index')
  277 + end
  278 +
275 279 end
... ...
test/unit/article_test.rb
... ... @@ -1715,6 +1715,18 @@ class ArticleTest &lt; ActiveSupport::TestCase
1715 1715 assert_equal 'bar.png', a.first_image
1716 1716 end
1717 1717  
  1718 + should 'get first image from having_image' do
  1719 + a = fast_create(Article,
  1720 + :body => '<p>Foo</p><p><img src="bar.png" /></p>',
  1721 + :abstract => '<p>Lead</p><p><img src="lead.png" /></p>'
  1722 + )
  1723 + img = {}
  1724 + img.expects(:present?).returns true
  1725 + img.expects(:public_filename).returns 'pic.jpg'
  1726 + a.expects(:image).at_least_once.returns img
  1727 + assert_equal 'pic.jpg', a.first_image
  1728 + end
  1729 +
1718 1730 should 'not get first image from anywhere' do
1719 1731 a = fast_create(Article, :body => '<p>Foo</p><p>Bar</p>')
1720 1732 assert_equal '', a.first_image
... ...
test/unit/block_test.rb
... ... @@ -35,6 +35,24 @@ class BlockTest &lt; ActiveSupport::TestCase
35 35 assert Block.new.editable?
36 36 end
37 37  
  38 + should 'be editable if edit modes is all' do
  39 + block = Block.new
  40 + block.edit_modes = 'all'
  41 +
  42 + assert block.editable?
  43 + end
  44 +
  45 + should 'be movable by default' do
  46 + assert Block.new.movable?
  47 + end
  48 +
  49 + should 'be movable if move modes is all' do
  50 + block = Block.new
  51 + block.move_modes = 'all'
  52 +
  53 + assert block.movable?
  54 + end
  55 +
38 56 should 'have default titles' do
39 57 b = Block.new
40 58 b.expects(:default_title).returns('my title')
... ... @@ -330,4 +348,11 @@ class BlockTest &lt; ActiveSupport::TestCase
330 348 block.save!
331 349 assert !block.display_to_user?(person_friend)
332 350 end
  351 +
  352 + should 'get limit as a number when limit is string' do
  353 + block = RecentDocumentsBlock.new
  354 + block.settings[:limit] = '5'
  355 + assert block.get_limit.is_a?(Fixnum)
  356 + end
  357 +
333 358 end
... ...
test/unit/blog_helper_test.rb
... ... @@ -20,30 +20,36 @@ class BlogHelperTest &lt; ActionView::TestCase
20 20 def _(s); s; end
21 21 def h(s); s; end
22 22  
23   - should 'list published posts with class blog-post' do
24   - blog.children << published_post = create(TextileArticle, :name => 'Post', :profile => profile, :parent => blog, :published => true)
25   -
26   - expects(:display_post).with(anything, anything).returns('POST')
27   - expects(:content_tag).with('div', "POST<br style=\"clear:both\"/>", :class => 'blog-post position-1 first last odd-post-inner', :id => "post-#{published_post.id}").returns('POST')
28   - expects(:content_tag).with('div', 'POST', {:class => 'odd-post'}).returns('RESULT')
29   -
30   - assert_equal 'RESULT', list_posts(blog.posts)
31   - end
32   -
33   - should 'list even/odd posts with a different class' do
34   - blog.children << older_post = create(TextileArticle, :name => 'First post', :profile => profile, :parent => blog, :published => true)
35   -
36   - blog.children << newer_post = create(TextileArticle, :name => 'Second post', :profile => profile, :parent => blog, :published => true)
37   -
38   - expects(:display_post).with(anything, anything).returns('POST').times(2)
39   -
40   - expects(:content_tag).with('div', "POST<br style=\"clear:both\"/>", :class => 'blog-post position-1 first odd-post-inner', :id => "post-#{newer_post.id}").returns('POST 1')
41   - expects(:content_tag).with('div', "POST 1", :class => 'odd-post').returns('ODD-POST')
42   -
43   - expects(:content_tag).with('div', "POST<br style=\"clear:both\"/>", :class => 'blog-post position-2 last even-post-inner', :id => "post-#{older_post.id}").returns('POST 2')
44   - expects(:content_tag).with('div', "POST 2", :class => 'even-post').returns('EVEN-POST')
45   -
46   - assert_equal "ODD-POST\n<hr class='sep-posts'/>\nEVEN-POST", list_posts(blog.posts)
  23 + should 'list blog posts with identifiers and classes' do
  24 + blog.children << older_post = create(TextileArticle, :name => 'First post',
  25 + :profile => profile, :parent => blog, :published => true)
  26 + blog.children << some_post = create(TextileArticle, :name => 'Some post',
  27 + :profile => profile, :parent => blog, :published => true)
  28 + blog.children << hidden_post = create(TextileArticle, :name => 'Hidden post',
  29 + :profile => profile, :parent => blog, :published => false)
  30 + blog.children << newer_post = create(TextileArticle, :name => 'Last post',
  31 + :profile => profile, :parent => blog, :published => true)
  32 +
  33 + def content_tag(tag, content_or_options_with_block = nil, options = nil, &block)
  34 + if block_given?
  35 + options = content_or_options_with_block
  36 + content = block.call
  37 + else
  38 + content = content_or_options_with_block
  39 + end
  40 + options ||= {}
  41 + "<#{tag}#{options.map{|k,v| " #{k}=\"#{[v].flatten.join(' ')}\""}.join}>#{content}</#{tag}>"
  42 + end
  43 +
  44 + html = HTML::Document.new(list_posts(blog.posts)).root
  45 + assert_select html, "div#post-#{newer_post.id}.blog-post.position-1.first.odd-post" +
  46 + " > div.odd-post-inner.blog-post-inner > .title", 'Last post'
  47 + assert_select html, "div#post-#{hidden_post.id}.blog-post.position-2.not-published.even-post" +
  48 + " > div.even-post-inner.blog-post-inner > .title", 'Hidden post'
  49 + assert_select html, "div#post-#{some_post.id}.blog-post.position-3.odd-post" +
  50 + " > div.odd-post-inner.blog-post-inner > .title", 'Some post'
  51 + assert_select html, "div#post-#{older_post.id}.blog-post.position-4.last.even-post" +
  52 + " > div.even-post-inner.blog-post-inner > .title", 'First post'
47 53 end
48 54  
49 55  
... ...
test/unit/boxes_helper_test.rb
... ... @@ -123,24 +123,24 @@ class BoxesHelperTest &lt; ActionView::TestCase
123 123 display_box_content(box, '')
124 124 end
125 125  
126   - should 'not show move options on block when block is fixed' do
  126 + should 'not show move options on block when block has no permission to edit' do
127 127 p = create_user_with_blocks
128 128  
129 129 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
130   - b.fixed = true
  130 + b.move_modes = "none"
131 131 b.save!
132 132  
133 133 stubs(:environment).returns(p.environment)
134 134 stubs(:user).returns(p)
135 135  
136   - assert_equal false, modifiable?(b)
  136 + assert_equal false, movable?(b)
137 137 end
138 138  
139   - should 'show move options on block when block is fixed and user is admin' do
  139 + should 'show move options on block when block has no permission to edit and user is admin' do
140 140 p = create_user_with_blocks
141 141  
142 142 b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
143   - b.fixed = true
  143 + b.edit_modes = "none"
144 144 b.save!
145 145  
146 146 p.environment.add_admin(p)
... ... @@ -148,7 +148,7 @@ class BoxesHelperTest &lt; ActionView::TestCase
148 148 stubs(:environment).returns(p.environment)
149 149 stubs(:user).returns(p)
150 150  
151   - assert_equal true, modifiable?(b)
  151 + assert_equal true, movable?(b)
152 152 end
153 153  
154 154 should 'consider boxes_limit without custom_design' do
... ... @@ -198,4 +198,16 @@ class BoxesHelperTest &lt; ActionView::TestCase
198 198 assert_no_tag_in_string block_edit_buttons(block), :tag => 'a', :attributes => {:class => 'button icon-button icon-embed '}
199 199 end
200 200  
  201 + should 'only show edit option on block' do
  202 + p = create_user_with_blocks
  203 +
  204 + b = p.blocks.select{|bk| !bk.kind_of?(MainBlock) }[0]
  205 + b.edit_modes = "only_edit"
  206 + b.save!
  207 +
  208 + stubs(:environment).returns(p.environment)
  209 + stubs(:user).returns(p)
  210 +
  211 + assert_equal false, b.editable?
  212 + end
201 213 end
... ...
test/unit/enterprise_test.rb
... ... @@ -188,7 +188,7 @@ class EnterpriseTest &lt; ActiveSupport::TestCase
188 188 inactive_template.save!
189 189  
190 190 active_template = create(Enterprise, :name => 'enteprise template', :identifier => 'enterprise_template')
191   - assert_equal 3, active_template.boxes.size
  191 + assert_equal 4, active_template.boxes.size
192 192  
193 193 e = Environment.default
194 194 e.inactive_enterprise_template = inactive_template
... ... @@ -400,7 +400,7 @@ class EnterpriseTest &lt; ActiveSupport::TestCase
400 400 e.save!
401 401  
402 402 ent = create(Enterprise, :name => 'test enteprise', :identifier => 'test_ent')
403   - assert_equal 3, ent.boxes.size
  403 + assert_equal 4, ent.boxes.size
404 404 end
405 405  
406 406 should 'collect the highlighted products with image' do
... ...
test/unit/highlights_block_test.rb
... ... @@ -109,33 +109,14 @@ class HighlightsBlockTest &lt; ActiveSupport::TestCase
109 109 end
110 110  
111 111 should 'list images randomically' do
112   - f1 = mock()
113   - f1.expects(:public_filename).returns('address')
114   - UploadedFile.expects(:find).with(1).returns(f1)
115   - f2 = mock()
116   - f2.expects(:public_filename).returns('address')
117   - UploadedFile.expects(:find).with(2).returns(f2)
118   - f3 = mock()
119   - f3.expects(:public_filename).returns('address')
120   - UploadedFile.expects(:find).with(3).returns(f3)
121   - f4 = mock()
122   - f4.expects(:public_filename).returns('address')
123   - UploadedFile.expects(:find).with(4).returns(f4)
124   - f5 = mock()
125   - f5.expects(:public_filename).returns('address')
126   - UploadedFile.expects(:find).with(5).returns(f5)
127 112 block = HighlightsBlock.new
128   - i1 = {:image_id => 1, :address => '/address', :position => 3, :title => 'address'}
129   - i2 = {:image_id => 2, :address => '/address', :position => 1, :title => 'address'}
130   - i3 = {:image_id => 3, :address => '/address', :position => 2, :title => 'address'}
131   - i4 = {:image_id => 4, :address => '/address', :position => 5, :title => 'address'}
132   - i5 = {:image_id => 5, :address => '/address', :position => 4, :title => 'address'}
133   - block.images = [i1,i2,i3,i4,i5]
134 113 block.shuffle = true
135   - block.save!
136   - block.reload
137   - assert_equal [i1,i2,i3,i4,i5], block.images
138   - assert_not_equal [i2,i3,i1,i4,i5], block.featured_images
  114 +
  115 + images = []
  116 + block.expects(:get_images).returns(images)
  117 + images.expects(:shuffle).returns(images)
  118 +
  119 + block.featured_images
139 120 end
140 121  
141 122 [Environment, Profile].each do |klass|
... ...
test/unit/layout_helper_test.rb
... ... @@ -30,4 +30,17 @@ class LayoutHelperTest &lt; ActionView::TestCase
30 30 assert_match /<link [^<]*href="\/designs\/themes\/my-theme\/global.css"/, css
31 31 end
32 32  
  33 + should 'append javascript files of enabled plugins in noosfero javascripts' do
  34 + plugin1 = Noosfero::Plugin.new
  35 + plugin1.expects(:js_files).returns(['plugin1.js'])
  36 + plugin2 = Noosfero::Plugin.new
  37 + plugin2.expects(:js_files).returns('plugin2.js')
  38 + @plugins = [plugin1, plugin2]
  39 + expects(:environment).returns(Environment.default).at_least_once
  40 + expects(:profile).returns(nil).at_least_once
  41 + js_tag = noosfero_javascript
  42 + assert_match /plugin1\.js/, js_tag
  43 + assert_match /plugin2\.js/, js_tag
  44 + end
  45 +
33 46 end
... ...
test/unit/profile_test.rb
... ... @@ -901,6 +901,8 @@ class ProfileTest &lt; ActiveSupport::TestCase
901 901  
902 902 should 'copy set of articles from a template' do
903 903 template = create_user('test_template').person
  904 + template.is_template = true
  905 + template.save
904 906 template.articles.destroy_all
905 907 a1 = fast_create(Article, :profile_id => template.id, :name => 'some xyz article')
906 908 a2 = fast_create(Article, :profile_id => template.id, :name => 'some child article', :parent_id => a1.id)
... ... @@ -934,6 +936,8 @@ class ProfileTest &lt; ActiveSupport::TestCase
934 936  
935 937 should 'copy homepage from template' do
936 938 template = create_user('test_template').person
  939 + template.is_template = true
  940 + template.save
937 941 template.articles.destroy_all
938 942 a1 = fast_create(Article, :profile_id => template.id, :name => 'some xyz article')
939 943 template.home_page = a1
... ... @@ -949,6 +953,8 @@ class ProfileTest &lt; ActiveSupport::TestCase
949 953  
950 954 should 'not advertise the articles copied from templates' do
951 955 template = create_user('test_template').person
  956 + template.is_template = true
  957 + template.save
952 958 template.articles.destroy_all
953 959 a = fast_create(Article, :profile_id => template.id, :name => 'some xyz article')
954 960  
... ... @@ -962,7 +968,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
962 968 end
963 969  
964 970 should 'copy set of boxes from profile template' do
965   - template = fast_create(Profile)
  971 + template = fast_create(Profile, :is_template => true)
966 972 template.boxes.destroy_all
967 973 template.boxes << Box.new
968 974 template.boxes[0].blocks << Block.new
... ... @@ -977,7 +983,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
977 983 end
978 984  
979 985 should 'copy layout template when applying template' do
980   - template = fast_create(Profile)
  986 + template = fast_create(Profile, :is_template => true)
981 987 template.layout_template = 'leftbar'
982 988 template.save!
983 989  
... ... @@ -989,7 +995,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
989 995 end
990 996  
991 997 should 'copy blocks when applying template' do
992   - template = fast_create(Profile)
  998 + template = fast_create(Profile, :is_template => true)
993 999 template.boxes.destroy_all
994 1000 template.boxes << Box.new
995 1001 template.boxes[0].blocks << Block.new
... ... @@ -1004,7 +1010,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1004 1010 end
1005 1011  
1006 1012 should 'copy articles when applying template' do
1007   - template = fast_create(Profile)
  1013 + template = fast_create(Profile, :is_template => true)
1008 1014 template.articles.create(:name => 'template article')
1009 1015 template.save!
1010 1016  
... ... @@ -1016,7 +1022,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1016 1022 end
1017 1023  
1018 1024 should 'rename existing articles when applying template' do
1019   - template = fast_create(Profile)
  1025 + template = fast_create(Profile, :is_template => true)
1020 1026 template.boxes.destroy_all
1021 1027 template.boxes << Box.new
1022 1028 template.boxes[0].blocks << Block.new
... ... @@ -1033,7 +1039,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1033 1039 end
1034 1040  
1035 1041 should 'copy header when applying template' do
1036   - template = fast_create(Profile)
  1042 + template = fast_create(Profile, :is_template => true)
1037 1043 template[:custom_header] = '{name}'
1038 1044 template.save!
1039 1045  
... ... @@ -1047,7 +1053,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1047 1053 end
1048 1054  
1049 1055 should 'copy footer when applying template' do
1050   - template = create(Profile, :address => 'Template address', :custom_footer => '{address}')
  1056 + template = create(Profile, :address => 'Template address', :custom_footer => '{address}', :is_template => true)
1051 1057  
1052 1058 p = create(Profile, :address => 'Profile address')
1053 1059 p.apply_template(template)
... ... @@ -1058,7 +1064,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1058 1064 end
1059 1065  
1060 1066 should 'ignore failing validation when applying template' do
1061   - template = create(Profile, :layout_template => 'leftbar', :custom_footer => 'my custom footer', :custom_header => 'my custom header')
  1067 + template = create(Profile, :layout_template => 'leftbar', :custom_footer => 'my custom footer', :custom_header => 'my custom header', :is_template => true)
1062 1068  
1063 1069 p = create(Profile)
1064 1070 def p.validate
... ... @@ -1074,7 +1080,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1074 1080 end
1075 1081  
1076 1082 should 'copy homepage when applying template' do
1077   - template = fast_create(Profile)
  1083 + template = fast_create(Profile, :is_template => true)
1078 1084 a1 = fast_create(Article, :profile_id => template.id, :name => 'some xyz article')
1079 1085 template.home_page = a1
1080 1086 template.save!
... ... @@ -1161,7 +1167,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1161 1167 end
1162 1168  
1163 1169 should 'copy public/private setting from template' do
1164   - template = fast_create(Profile, :public_profile => false)
  1170 + template = fast_create(Profile, :public_profile => false, :is_template => true)
1165 1171 p = fast_create(Profile)
1166 1172 p.apply_template(template)
1167 1173 assert_equal false, p.public_profile
... ...
test/unit/suggest_article_test.rb
... ... @@ -13,16 +13,9 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
13 13  
14 14 should 'have the article_name' do
15 15 t = SuggestArticle.new
16   - assert !t.errors[:article_name.to_s].present?
17   - t.valid?
18   - assert t.errors[:article_name.to_s].present?
19   - end
20   -
21   - should 'have the article_body' do
22   - t = SuggestArticle.new
23   - assert !t.errors[:article_body.to_s].present?
24   - t.valid?
25   - assert t.errors[:article_body.to_s].present?
  16 + assert !t.article_object.errors[:name].present?
  17 + t.article_object.valid?
  18 + assert t.article_object.errors[:name].present?
26 19 end
27 20  
28 21 should 'have the email' do
... ... @@ -46,19 +39,12 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
46 39 assert t.errors[:target_id.to_s].present?
47 40 end
48 41  
49   - should 'have the article_abstract' do
50   - t = SuggestArticle.new
51   - assert t.respond_to?(:article_abstract)
52   - end
53   -
54   - should 'have the article_parent_id' do
55   - t = SuggestArticle.new
56   - assert t.respond_to?(:article_parent_id)
57   - end
58   -
59   - should 'source be defined' do
  42 + should 'have the article' do
60 43 t = SuggestArticle.new
61   - assert t.respond_to?(:source)
  44 + assert t.respond_to?(:article_object)
  45 + assert !t.errors[:article_object].present?
  46 + t.valid?
  47 + assert t.errors[:article_object].present?
62 48 end
63 49  
64 50 should 'create an article on with perfom method' do
... ... @@ -66,9 +52,7 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
66 52 name = 'some name'
67 53 body = 'some body'
68 54 abstract = 'some abstract'
69   - t.article_name = name
70   - t.article_body = body
71   - t.article_abstract = abstract
  55 + t.article = {:name => name, :body => body, :abstract => abstract}
72 56 t.target = @profile
73 57 count = TinyMceArticle.count
74 58 t.perform
... ... @@ -77,8 +61,7 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
77 61  
78 62 should 'fill source name and URL into created article' do
79 63 t = build(SuggestArticle, :target => @profile)
80   - t.source_name = 'GNU project'
81   - t.source = 'http://www.gnu.org/'
  64 + t.article.merge!({:source_name => 'GNU project', :source => 'http://www.gnu.org/'})
82 65 t.perform
83 66  
84 67 article = TinyMceArticle.last
... ... @@ -95,7 +78,7 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
95 78  
96 79 should 'highlight created article' do
97 80 t = build(SuggestArticle, :target => @profile)
98   - t.highlighted = true
  81 + t.article[:highlighted] = true
99 82 t.perform
100 83  
101 84 article = TinyMceArticle.last(:conditions => { :name => t.article_name}) # just to be sure
... ... @@ -132,19 +115,19 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
132 115 end
133 116  
134 117 should 'have target notification message' do
135   - task = build(SuggestArticle, :target => @profile, :article_name => 'suggested article', :name => 'johndoe')
  118 + task = build(SuggestArticle, :target => @profile, :article => {:name => 'suggested article'}, :name => 'johndoe')
136 119  
137 120 assert_match(/#{task.name}.*suggested the publication of the article: #{task.subject}.*[\n]*.*to approve or reject/, task.target_notification_message)
138 121 end
139 122  
140 123 should 'have target notification description' do
141   - task = build(SuggestArticle,:target => @profile, :article_name => 'suggested article', :name => 'johndoe')
  124 + task = build(SuggestArticle,:target => @profile, :article => {:name => 'suggested article'}, :name => 'johndoe')
142 125  
143 126 assert_match(/#{task.name}.*suggested the publication of the article: #{task.subject}/, task.target_notification_description)
144 127 end
145 128  
146 129 should 'deliver target notification message' do
147   - task = build(SuggestArticle, :target => @profile, :article_name => 'suggested article', :name => 'johndoe', :email => 'johndoe@example.com')
  130 + task = build(SuggestArticle, :target => @profile, :article => {:name => 'suggested article'}, :name => 'johndoe', :email => 'johndoe@example.com')
148 131  
149 132 email = TaskMailer.target_notification(task, task.target_notification_message).deliver
150 133  
... ... @@ -160,7 +143,7 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
160 143 should 'delegate spam detection to plugins' do
161 144 Environment.default.enable_plugin(EverythingIsSpam)
162 145  
163   - t1 = build(SuggestArticle, :target => @profile, :article_name => 'suggested article', :name => 'johndoe', :email => 'johndoe@example.com')
  146 + t1 = build(SuggestArticle, :target => @profile, :article => {:name => 'suggested article'}, :name => 'johndoe', :email => 'johndoe@example.com')
164 147  
165 148 EverythingIsSpam.any_instance.expects(:check_for_spam)
166 149  
... ... @@ -189,7 +172,7 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
189 172 should 'notify plugins of suggest_articles being marked as spam' do
190 173 Environment.default.enable_plugin(SpamNotification)
191 174  
192   - t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com')
  175 + t = SuggestArticle.create!(:target => @profile, :article => {:name => 'suggested article', :body => 'wanna feel my body? my body baaaby'}, :name => 'johndoe', :email => 'johndoe@example.com')
193 176  
194 177 t.spam!
195 178 process_delayed_job_queue
... ... @@ -200,7 +183,7 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
200 183 should 'notify plugins of suggest_articles being marked as ham' do
201 184 Environment.default.enable_plugin(SpamNotification)
202 185  
203   - t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com')
  186 + t = SuggestArticle.create!(:target => @profile, :article => {:name => 'suggested article', :body => 'wanna feel my body? my body baaaby'}, :name => 'johndoe', :email => 'johndoe@example.com')
204 187  
205 188 t.ham!
206 189 process_delayed_job_queue
... ... @@ -219,10 +202,44 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
219 202 end
220 203  
221 204 should 'log spammer ip after marking comment as spam' do
222   - t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com', :ip_address => '192.168.0.1')
  205 + t = SuggestArticle.create!(:target => @profile, :article => {:name => 'suggested article', :body => 'wanna feel my body? my body baaaby'}, :name => 'johndoe', :email => 'johndoe@example.com', :ip_address => '192.168.0.1')
223 206 t.spam!
224 207 log = File.open('log/test_spammers.log')
225 208 assert_match "SuggestArticle-id: #{t.id} IP: 192.168.0.1", log.read
226 209 end
227 210  
  211 + should 'not require name and email when requestor is present' do
  212 + t = SuggestArticle.new(:requestor => fast_create(Person))
  213 + t.valid?
  214 + assert t.errors[:email].blank?
  215 + assert t.errors[:name].blank?
  216 + end
  217 +
  218 + should 'return name as sender when requestor is setted' do
  219 + person = fast_create(Person)
  220 + t = SuggestArticle.new(:requestor => person)
  221 + assert_equal person.name, t.sender
  222 + end
  223 +
  224 + should 'create an event on perfom method' do
  225 + t = SuggestArticle.new
  226 + t.article = {:name => 'name', :body => 'body', :type => 'Event'}
  227 + t.target = @profile
  228 + assert_difference "Event.count" do
  229 + t.perform
  230 + end
  231 + end
  232 +
  233 + should 'accept article type parameter' do
  234 + t = SuggestArticle.new
  235 + t.article = {:name => 'name', :body => 'body', :type => 'TextArticle'}
  236 + t.article_type == TextArticle
  237 + end
  238 +
  239 + should 'fallback to tinymce when type parameter is invalid' do
  240 + t = SuggestArticle.new
  241 + t.article = {:name => 'name', :body => 'body', :type => 'Comment'}
  242 + t.article_type == TinyMceArticle
  243 + end
  244 +
228 245 end
... ...
test/unit/user_mailer_test.rb
... ... @@ -18,6 +18,14 @@ class UserMailerTest &lt; ActiveSupport::TestCase
18 18 end
19 19 end
20 20  
  21 + should 'deliver profiles suggestions email' do
  22 + person = create_user('some-user').person
  23 + ProfileSuggestion.create!(:person => person, :suggestion =>
  24 +fast_create(Person))
  25 + email = UserMailer.profiles_suggestions_email(person).deliver
  26 + assert_match /profile\/some-user\/friends\/suggest/, email.body.to_s
  27 + end
  28 +
21 29 private
22 30  
23 31 def read_fixture(action)
... ...
test/unit/user_test.rb
... ... @@ -87,6 +87,14 @@ class UserTest &lt; ActiveSupport::TestCase
87 87 assert_equal person_count + 1, Person.count
88 88 end
89 89  
  90 + def test_should_create_person_with_identifier_different_from_login
  91 + user = User.create!(:login => 'new_user', :email => 'new_user@example.com', :password => 'test', :password_confirmation => 'test', :person_data => {:identifier => "new_test"})
  92 +
  93 + assert Person.exists?(['user_id = ?', user.id])
  94 +
  95 + assert user.login != user.person.identifier
  96 + end
  97 +
90 98 def test_login_validation
91 99 u = User.new
92 100 u.valid?
... ... @@ -355,7 +363,7 @@ class UserTest &lt; ActiveSupport::TestCase
355 363 Person.any_instance.stubs(:created_at).returns(DateTime.parse('16-08-2010'))
356 364 expected_hash = {
357 365 'login' => 'x_and_y', 'is_admin' => true, 'since_month' => 8,
358   - 'chat_enabled' => false, 'since_year' => 2010, 'email_domain' => nil,
  366 + 'chat_enabled' => false, 'since_year' => 2010, 'email_domain' => nil,
359 367 'amount_of_friends' => 0, 'friends_list' => {}, 'enterprises' => [],
360 368 }
361 369  
... ... @@ -546,6 +554,7 @@ class UserTest &lt; ActiveSupport::TestCase
546 554  
547 555 should 'delay activation check with custom time' do
548 556 NOOSFERO_CONF.stubs(:[]).with('hours_until_user_activation_check').returns(240)
  557 + NOOSFERO_CONF.stubs(:[]).with('exclude_profile_identifier_pattern')
549 558 user = new_user
550 559 job = Delayed::Job.last
551 560 assert_match /UserActivationJob/, job.handler
... ...
util/debian-install/Vagrantfile
... ... @@ -13,9 +13,11 @@ else
13 13 local_debs = Dir.glob('*.deb')
14 14 debs.each do |f|
15 15 fn = File.basename(f)
16   - if local_debs.include?(fn)
17   - local_debs.delete(fn)
18   - else
  16 +
  17 + local_debs.delete(fn)
  18 +
  19 + if File.stat(f) != File.stat(fn)
  20 + FileUtils::Verbose.rm_f(fn)
19 21 FileUtils::Verbose.ln f, '.'
20 22 end
21 23 end
... ...
util/debian-install/install
... ... @@ -61,19 +61,35 @@ deb http://download.noosfero.org/debian/wheezy-test ./
61 61 deb-src http://download.noosfero.org/debian/wheezy-test ./
62 62 EOF
63 63  
  64 +sed -e 's/wheezy/&-backports/' \
  65 + /etc/apt/sources.list > /etc/apt/sources.list.d/backports.list
  66 +
64 67 export DEBIAN_FRONTEND=noninteractive
65 68  
  69 +# local debs
  70 +if [ -n "$(find /vagrant -name '*.deb')" ]; then
  71 + apt-get install -qy apt-utils bzip2
  72 + (
  73 + rm -rf /opt/noosfero
  74 + mkdir /opt/noosfero
  75 + cp /vagrant/*.deb /opt/noosfero
  76 + cd /opt/noosfero
  77 + apt-ftparchive packages . > Packages
  78 + cat Packages | gzip - > Packages.gz
  79 + cat Packages | bzip2 - > Packages.bz2
  80 + apt-ftparchive release . > Release
  81 + echo 'deb [trusted=yes] file:///opt/noosfero ./' > /etc/apt/sources.list.d/local.list
  82 + )
  83 +else
  84 + rm -f /etc/apt/sources.list.d/local.list
  85 +fi
  86 +
66 87 apt-get update
67 88 apt-get dist-upgrade -qy
68   -apt-get install -qy postgresql ruby1.8
  89 +apt-get install -qy postgresql
69 90  
70   -if dpkg --unpack /vagrant/noosfero_*.deb /vagrant/noosfero-apache_*.deb; then
71   - apt-cache policy noosfero
72   - apt-get install -qyf
73   -else
74   - apt-cache policy noosfero
75   - apt-get install -qy noosfero noosfero-apache
76   -fi
  91 +apt-cache policy noosfero
  92 +apt-get install -qy noosfero noosfero-apache
77 93  
78 94 a2dissite 000-default
79 95 service apache2 reload
... ...
vendor/plugins/access_control/lib/role.rb
... ... @@ -4,6 +4,7 @@ class Role &lt; ActiveRecord::Base
4 4  
5 5 has_many :role_assignments, :dependent => :destroy
6 6 belongs_to :environment
  7 + belongs_to :organization
7 8 serialize :permissions, Array
8 9 validates_presence_of :name
9 10 validates_uniqueness_of :name, :scope => :environment_id
... ...