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

Too many changes.

To preserve performance only 100 of 136 files displayed.

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>
... ...