Commit 3779f8e0eb09dd2c5475fbb1e8b73a4772bbe94b

Authored by Leandro Santos
2 parents 7bc9af73 aa514668

Merge branch 'production' of gitlab.com:participa/noosfero into production

Showing 60 changed files with 906 additions and 220 deletions   Show diff stats
@@ -57,4 +57,4 @@ eval(File.read('config/Gemfile'), binding) rescue nil @@ -57,4 +57,4 @@ eval(File.read('config/Gemfile'), binding) rescue nil
57 # dependencies. 57 # dependencies.
58 Dir.glob('config/plugins/*/Gemfile').each do |gemfile| 58 Dir.glob('config/plugins/*/Gemfile').each do |gemfile|
59 eval File.read(gemfile) 59 eval File.read(gemfile)
60 -end 60 -end
  61 +end
61 \ No newline at end of file 62 \ No newline at end of file
INSTALL.chat.md
1 -XMPP/Chat Setup  
2 -=============== 1 +Automatic XMPP/Chat Setup
  2 +=========================
  3 +
  4 +Since Noosfero 1.2, the XMPP/Chat can be installed via `noosfero-chat` Debian
  5 +package. So you don't need to follow the manual instructions here if you
  6 +already have it installed on your system.
  7 +
  8 +But if you are going to install the `noosfero-chat` package on a system that
  9 +already has `noosfero` older 1.2 installed then you need to check if apache's
  10 +configuration file `/etc/apache2/sites-available/noosfero` has this line below:
  11 +
  12 + Include /usr/share/noosfero/util/chat/apache/xmpp.conf
  13 +
  14 +Manual XMPP/Chat Setup
  15 +======================
3 16
4 The samples of config file to configure a XMPP/BOSH server with ejabberd, 17 The samples of config file to configure a XMPP/BOSH server with ejabberd,
5 postgresql and apache2 can be found at util/chat directory. 18 postgresql and apache2 can be found at util/chat directory.
@@ -8,7 +21,7 @@ This setup supposes that you are using Noosfero installed via Debian package @@ -8,7 +21,7 @@ This setup supposes that you are using Noosfero installed via Debian package
8 in a production environment. 21 in a production environment.
9 22
10 Steps 23 Steps
11 -===== 24 +-----
12 25
13 This is a step-by-step guide to get a XMPP service working, in a Debian system. 26 This is a step-by-step guide to get a XMPP service working, in a Debian system.
14 27
@@ -144,15 +157,8 @@ You should see a page with a message like that: @@ -144,15 +157,8 @@ You should see a page with a message like that:
144 157
145 ## 9. Test chat session 158 ## 9. Test chat session
146 159
147 -Open Noosfero console and execute:  
148 -  
149 ->> environment = Environment.default  
150 ->> user = Person['guest']  
151 ->> password = user.user.crypted_password  
152 ->> login = user.jid  
153 ->> RubyBOSH.initialize_session(login, password, "http://#{environment.default_hostname}/http-bind", :wait => 30, :hold => 1, :window => 5  
154 -  
155 -If you have luck, should see something like that: 160 +Run `./script/noosfero-test-chat-session`. If you have luck, should see
  161 +something like that:
156 162
157 Ruby-BOSH - SEND 163 Ruby-BOSH - SEND
158 <body window="5" rid="60265" xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" to="vagrant-debian-squeeze.vagrantup.com" wait="30" xmpp:version="1.0" hold="1"/> 164 <body window="5" rid="60265" xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" to="vagrant-debian-squeeze.vagrantup.com" wait="30" xmpp:version="1.0" hold="1"/>
app/controllers/my_profile/cms_controller.rb
@@ -137,7 +137,13 @@ class CmsController &lt; MyProfileController @@ -137,7 +137,13 @@ class CmsController &lt; MyProfileController
137 article_data = environment.enabled?('articles_dont_accept_comments_by_default') ? { :accept_comments => false } : {} 137 article_data = environment.enabled?('articles_dont_accept_comments_by_default') ? { :accept_comments => false } : {}
138 article_data.merge!(params[:article]) if params[:article] 138 article_data.merge!(params[:article]) if params[:article]
139 article_data.merge!(:profile => profile) if profile 139 article_data.merge!(:profile => profile) if profile
140 - @article = klass.new(article_data) 140 +
  141 + @article = if params[:clone]
  142 + current_article = profile.articles.find(params[:id])
  143 + current_article.copy_without_save
  144 + else
  145 + klass.new(article_data)
  146 + end
141 147
142 parent = check_parent(params[:parent_id]) 148 parent = check_parent(params[:parent_id])
143 if parent 149 if parent
app/controllers/my_profile/tasks_controller.rb
1 class TasksController < MyProfileController 1 class TasksController < MyProfileController
2 2
3 protect 'perform_task', :profile 3 protect 'perform_task', :profile
4 - 4 +
5 def index 5 def index
6 - @filter_type = params[:filter_type] = params[:filter_type].blank? ? nil : params[:filter_type]  
7 - @filter_text = params[:filter_text].blank? ? nil : params[:filter_text] 6 + @filter_type = params[:filter_type].presence
  7 + @filter_text = params[:filter_text].presence
  8 + @filter_responsible = params[:filter_responsible]
8 @task_types = Task.pending_types_for(profile) 9 @task_types = Task.pending_types_for(profile)
9 - @tasks = Task.pending_all(profile,params).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page]) 10 +
  11 + @tasks = Task.pending_all(profile, @filter_type, @filter_text).order_by('created_at', 'asc')
  12 + @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present?
  13 + @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page])
  14 +
10 @failed = params ? params[:failed] : {} 15 @failed = params ? params[:failed] : {}
  16 +
  17 + @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task')})
11 end 18 end
12 19
13 def processed 20 def processed
14 @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) 21 @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at)
15 end 22 end
16 23
  24 + def change_responsible
  25 + task = profile.tasks.find(params[:task_id])
  26 +
  27 + if task.responsible.present? && task.responsible.id != params[:old_responsible_id].to_i
  28 + return render :json => {:notice => _('Task already assigned!'), :success => false, :current_responsible => task.responsible.id}
  29 + end
  30 +
  31 + responsible = profile.members.find(params[:responsible_id]) if params[:responsible_id].present?
  32 + task.responsible = responsible
  33 + task.save!
  34 + render :json => {:notice => _('Task responsible successfully updated!'), :success => true, :new_responsible => {:id => responsible.present? ? responsible.id : nil}}
  35 + end
  36 +
17 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] 37 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
18 38
19 def close 39 def close
20 failed = {} 40 failed = {}
  41 + save = false
21 42
22 if params[:tasks] 43 if params[:tasks]
23 params[:tasks].each do |id, value| 44 params[:tasks].each do |id, value|
24 decision = value[:decision] 45 decision = value[:decision]
25 - if request.post? && VALID_DECISIONS.include?(decision) && id && decision != 'skip' 46 +
  47 + if value[:task].is_a?(Hash) && value[:task][:tag_list]
  48 +
26 task = profile.find_in_all_tasks(id) 49 task = profile.find_in_all_tasks(id)
27 - begin  
28 - task.update_attributes(value[:task])  
29 - task.send(decision)  
30 - rescue Exception => ex  
31 - message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})"  
32 - failed[ex.message] ? failed[ex.message] << message : failed[ex.message] = [message] 50 + task.tag_list = value[:task][:tag_list]
  51 + value[:task].delete('tag_list')
  52 +
  53 + save = true
  54 + end
  55 +
  56 + if request.post?
  57 + if VALID_DECISIONS.include?(decision) && id && decision != 'skip'
  58 + task ||= profile.find_in_all_tasks(id)
  59 + begin
  60 + task.update_attributes(value[:task])
  61 + task.send(decision)
  62 + rescue Exception => ex
  63 + message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})"
  64 + failed[ex.message] ? failed[ex.message] << message : failed[ex.message] = [message]
  65 + end
  66 + elsif save
  67 + task.save!
33 end 68 end
34 end 69 end
35 end 70 end
app/helpers/application_helper.rb
@@ -941,6 +941,19 @@ module ApplicationHelper @@ -941,6 +941,19 @@ module ApplicationHelper
941 article_helper.cms_label_for_edit 941 article_helper.cms_label_for_edit
942 end 942 end
943 943
  944 + def label_for_clone_article(article)
  945 + translated_types = {
  946 + Folder => _('Folder'),
  947 + Blog => _('Blog'),
  948 + Event => _('Event'),
  949 + Forum => _('Forum')
  950 + }
  951 +
  952 + translated_type = translated_types[article.class] || _('Article')
  953 +
  954 + _('Clone %s') % translated_type
  955 + end
  956 +
944 def add_rss_feed_to_head(title, url) 957 def add_rss_feed_to_head(title, url)
945 content_for :feeds do 958 content_for :feeds do
946 tag(:link, :rel => 'alternate', :type => 'application/rss+xml', :title => title, :href => url_for(url)) 959 tag(:link, :rel => 'alternate', :type => 'application/rss+xml', :title => title, :href => url_for(url))
app/helpers/article_helper.rb
@@ -12,6 +12,7 @@ module ArticleHelper @@ -12,6 +12,7 @@ module ArticleHelper
12 @article = article 12 @article = article
13 13
14 visibility_options(@article, tokenized_children) + 14 visibility_options(@article, tokenized_children) +
  15 + topic_creation(@article) +
15 content_tag('h4', _('Options')) + 16 content_tag('h4', _('Options')) +
16 content_tag('div', 17 content_tag('div',
17 (article.profile.has_members? ? 18 (article.profile.has_members? ?
@@ -55,14 +56,7 @@ module ArticleHelper @@ -55,14 +56,7 @@ module ArticleHelper
55 'div', 56 'div',
56 check_box(:article, :display_versions) + 57 check_box(:article, :display_versions) +
57 content_tag('label', _('I want this article to display a link to older versions'), :for => 'article_display_versions') 58 content_tag('label', _('I want this article to display a link to older versions'), :for => 'article_display_versions')
58 - ) : '') +  
59 -  
60 - (article.forum? && article.profile.community? ?  
61 - content_tag(  
62 - 'div',  
63 - check_box(:article, :allows_members_to_create_topics) +  
64 - content_tag('label', _('Allow members to create topics'), :for => 'article_allows_members_to_create_topics')  
65 - ) : '') 59 + ) : '')
66 ) 60 )
67 end 61 end
68 62
@@ -81,6 +75,22 @@ module ArticleHelper @@ -81,6 +75,22 @@ module ArticleHelper
81 ) 75 )
82 end 76 end
83 77
  78 + def topic_creation(article)
  79 + return '' unless article.forum?
  80 +
  81 + general_options = Forum::TopicCreation.general_options(article)
  82 + slider_options = {:id => 'topic-creation-slider'}
  83 + slider_options = general_options.keys.inject(slider_options) do |result, option|
  84 + result.merge!({'data-'+option => general_options[option]})
  85 + end
  86 +
  87 + content_tag('h4', _('Topic creation')) +
  88 + content_tag( 'small', _('Who will be able to create new topics on this forum?')) +
  89 + content_tag('div', '', slider_options) +
  90 + hidden_field_tag('article[topic_creation]', article.topic_creation) +
  91 + javascript_include_tag('topic-creation-config')
  92 + end
  93 +
84 def privacity_exceptions(article, tokenized_children) 94 def privacity_exceptions(article, tokenized_children)
85 content_tag('div', 95 content_tag('div',
86 content_tag('div', 96 content_tag('div',
app/helpers/tinymce_helper.rb
@@ -20,7 +20,7 @@ module TinymceHelper @@ -20,7 +20,7 @@ module TinymceHelper
20 :image_advtab => true, 20 :image_advtab => true,
21 :language => tinymce_language 21 :language => tinymce_language
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" 23 + options[:toolbar1] = "fullscreen | insertfile undo redo | copy paste | bold italic underline | styleselect fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
24 if options[:mode] == 'simple' 24 if options[:mode] == 'simple'
25 options[:menubar] = false 25 options[:menubar] = false
26 else 26 else
app/models/article.rb
@@ -508,13 +508,13 @@ class Article &lt; ActiveRecord::Base @@ -508,13 +508,13 @@ class Article &lt; ActiveRecord::Base
508 508
509 scope :display_filter, lambda {|user, profile| 509 scope :display_filter, lambda {|user, profile|
510 return published if (user.nil? && profile && profile.public?) 510 return published if (user.nil? && profile && profile.public?)
511 - return [] if user.nil? || profile.nil? || (profile && !profile.public? && !user.follows?(profile)) 511 + return [] if user.nil? || (profile && !profile.public? && !user.follows?(profile))
512 where( 512 where(
513 [ 513 [
514 "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ? 514 "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ?
515 OR (show_to_followers = ? AND ? AND profile_id = ?)", true, user.id, user.id, 515 OR (show_to_followers = ? AND ? AND profile_id = ?)", true, user.id, user.id,
516 profile.nil? ? false : user.has_permission?(:view_private_content, profile), 516 profile.nil? ? false : user.has_permission?(:view_private_content, profile),
517 - true, user.follows?(profile), profile.id 517 + true, user.follows?(profile), (profile.nil? ? nil : profile.id)
518 ] 518 ]
519 ) 519 )
520 } 520 }
@@ -587,25 +587,24 @@ class Article &lt; ActiveRecord::Base @@ -587,25 +587,24 @@ class Article &lt; ActiveRecord::Base
587 profile.visible? && profile.public? && published? 587 profile.visible? && profile.public? && published?
588 end 588 end
589 589
590 -  
591 - def copy(options = {}) 590 + def copy_without_save(options = {})
592 attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } 591 attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) }
593 attrs.merge!(options) 592 attrs.merge!(options)
594 object = self.class.new 593 object = self.class.new
595 attrs.each do |key, value| 594 attrs.each do |key, value|
596 object.send(key.to_s+'=', value) 595 object.send(key.to_s+'=', value)
597 end 596 end
  597 + object
  598 + end
  599 +
  600 + def copy(options = {})
  601 + object = copy_without_save(options)
598 object.save 602 object.save
599 object 603 object
600 end 604 end
601 605
602 def copy!(options = {}) 606 def copy!(options = {})
603 - attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) }  
604 - attrs.merge!(options)  
605 - object = self.class.new  
606 - attrs.each do |key, value|  
607 - object.send(key.to_s+'=', value)  
608 - end 607 + object = copy_without_save(options)
609 object.save! 608 object.save!
610 object 609 object
611 end 610 end
app/models/environment.rb
@@ -339,6 +339,16 @@ class Environment &lt; ActiveRecord::Base @@ -339,6 +339,16 @@ class Environment &lt; ActiveRecord::Base
339 self.save! 339 self.save!
340 end 340 end
341 341
  342 + def enable_all_plugins
  343 + Noosfero::Plugin.available_plugin_names.each do |plugin|
  344 + plugin_name = plugin.to_s + "Plugin"
  345 + unless self.enabled_plugins.include?(plugin_name)
  346 + self.enabled_plugins += [plugin_name]
  347 + end
  348 + end
  349 + self.save!
  350 + end
  351 +
342 # Disables a feature identified by its name 352 # Disables a feature identified by its name
343 def disable(feature, must_save=true) 353 def disable(feature, must_save=true)
344 self.settings["#{feature}_enabled".to_sym] = false 354 self.settings["#{feature}_enabled".to_sym] = false
app/models/forum.rb
@@ -3,11 +3,11 @@ class Forum &lt; Folder @@ -3,11 +3,11 @@ class Forum &lt; Folder
3 acts_as_having_posts :order => 'updated_at DESC' 3 acts_as_having_posts :order => 'updated_at DESC'
4 include PostsLimit 4 include PostsLimit
5 5
6 - attr_accessible :has_terms_of_use, :terms_of_use, :allows_members_to_create_topics 6 + attr_accessible :has_terms_of_use, :terms_of_use, :topic_creation
7 7
8 settings_items :terms_of_use, :type => :string, :default => "" 8 settings_items :terms_of_use, :type => :string, :default => ""
9 settings_items :has_terms_of_use, :type => :boolean, :default => false 9 settings_items :has_terms_of_use, :type => :boolean, :default => false
10 - settings_items :allows_members_to_create_topics, :type => :boolean, :default => false 10 + settings_items :topic_creation, :type => :string, :default => 'self'
11 has_and_belongs_to_many :users_with_agreement, :class_name => 'Person', :join_table => 'terms_forum_people' 11 has_and_belongs_to_many :users_with_agreement, :class_name => 'Person', :join_table => 'terms_forum_people'
12 12
13 before_save do |forum| 13 before_save do |forum|
@@ -33,6 +33,23 @@ class Forum &lt; Folder @@ -33,6 +33,23 @@ class Forum &lt; Folder
33 _('An internet forum, also called message board, where discussions can be held.') 33 _('An internet forum, also called message board, where discussions can be held.')
34 end 34 end
35 35
  36 + module TopicCreation
  37 + BASE = ActiveSupport::OrderedHash.new
  38 + BASE['users'] = _('Logged users')
  39 +
  40 + PERSON = ActiveSupport::OrderedHash.new
  41 + PERSON['self'] = _('Me')
  42 + PERSON['related'] = _('Friends')
  43 +
  44 + GROUP = ActiveSupport::OrderedHash.new
  45 + GROUP['self'] = _('Administrators')
  46 + GROUP['related'] = _('Members')
  47 +
  48 + def self.general_options(forum)
  49 + forum.profile.person? ? PERSON.merge(BASE) : GROUP.merge(BASE)
  50 + end
  51 + end
  52 +
36 include ActionView::Helpers::TagHelper 53 include ActionView::Helpers::TagHelper
37 def to_html(options = {}) 54 def to_html(options = {})
38 proc do 55 proc do
@@ -69,11 +86,17 @@ class Forum &lt; Folder @@ -69,11 +86,17 @@ class Forum &lt; Folder
69 self.users_with_agreement.exists? user 86 self.users_with_agreement.exists? user
70 end 87 end
71 88
72 - def can_create_topic?(user, profile)  
73 - return profile.community? && profile.members.include?(user) && self.allows_members_to_create_topics 89 + def can_create_topic?(user)
  90 + return true if user == profile || profile.admins.include?(user) || profile.environment.admins.include?(user)
  91 + case topic_creation
  92 + when 'related'
  93 + profile.person? ? profile.friends.include?(user) : profile.members.include?(user)
  94 + when 'users'
  95 + user.present?
  96 + end
74 end 97 end
75 98
76 def allow_create?(user) 99 def allow_create?(user)
77 - super || can_create_topic?(user, profile) 100 + super || can_create_topic?(user)
78 end 101 end
79 end 102 end
app/models/suggest_article.rb
@@ -25,7 +25,7 @@ class SuggestArticle &lt; Task @@ -25,7 +25,7 @@ class SuggestArticle &lt; Task
25 25
26 def article_object 26 def article_object
27 if @article_object.nil? 27 if @article_object.nil?
28 - @article_object = article_type.new(article.merge({:profile => target}).except(:type)) 28 + @article_object = article_type.new(article.merge(target.present? ? {:profile => target} : {}).except(:type))
29 if requestor.present? 29 if requestor.present?
30 @article_object.author = requestor 30 @article_object.author = requestor
31 else 31 else
app/models/task.rb
@@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
12 class Task < ActiveRecord::Base 12 class Task < ActiveRecord::Base
13 13
14 acts_as_having_settings :field => :data 14 acts_as_having_settings :field => :data
  15 + acts_as_ordered_taggable
15 16
16 module Status 17 module Status
17 # the status of tasks just created 18 # the status of tasks just created
@@ -33,6 +34,7 @@ class Task &lt; ActiveRecord::Base @@ -33,6 +34,7 @@ class Task &lt; ActiveRecord::Base
33 34
34 belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id 35 belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id
35 belongs_to :target, :foreign_key => :target_id, :polymorphic => true 36 belongs_to :target, :foreign_key => :target_id, :polymorphic => true
  37 + belongs_to :responsible, :class_name => 'Person', :foreign_key => :responsible_id
36 38
37 validates_uniqueness_of :code, :on => :create 39 validates_uniqueness_of :code, :on => :create
38 validates_presence_of :code 40 validates_presence_of :code
@@ -239,9 +241,9 @@ class Task &lt; ActiveRecord::Base @@ -239,9 +241,9 @@ class Task &lt; ActiveRecord::Base
239 scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } 241 scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] }
240 scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } 242 scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
241 scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } 243 scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
242 - scope :like, ->(field,value) { where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value}  
243 - scope :pending_all, ->(profile, params){  
244 - self.to(profile).without_spam.pending.of(params[:filter_type]).like('data', params[:filter_text]) 244 + scope :like, lambda { |field, value| where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value}
  245 + scope :pending_all, lambda { |profile, filter_type, filter_text|
  246 + self.to(profile).without_spam.pending.of(filter_type).like('data', filter_text)
245 } 247 }
246 248
247 scope :to, lambda { |profile| 249 scope :to, lambda { |profile|
app/views/cms/_forum.html.erb
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 2
3 <h1><%= _('My Forum') %></h1> 3 <h1><%= _('My Forum') %></h1>
4 4
  5 +<%= required_fields_message %>
  6 +
5 <%= render :file => 'shared/tiny_mce' %> 7 <%= render :file => 'shared/tiny_mce' %>
6 8
7 <%= required f.text_field(:name, :size => '64', :maxlength => 150, :onchange => "updateUrlField(this, 'article_slug')") %> 9 <%= required f.text_field(:name, :size => '64', :maxlength => 150, :onchange => "updateUrlField(this, 'article_slug')") %>
app/views/content_viewer/_article_toolbar.html.erb
1 <div<%= user && " class='logged-in'" %>> 1 <div<%= user && " class='logged-in'" %>>
2 <div id="article-actions"> 2 <div id="article-actions">
  3 +
3 <%= fullscreen_buttons('#article') %> 4 <%= fullscreen_buttons('#article') %>
  5 +
4 <% if @page.allow_edit?(user) && !remove_content_button(:edit, @page) %> 6 <% if @page.allow_edit?(user) && !remove_content_button(:edit, @page) %>
5 <% content = content_tag('span', label_for_edit_article(@page)) %> 7 <% content = content_tag('span', label_for_edit_article(@page)) %>
6 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %> 8 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %>
7 <%= expirable_button @page, :edit, content, url %> 9 <%= expirable_button @page, :edit, content, url %>
8 <% end %> 10 <% end %>
9 - 11 +
10 <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete, @page)%> 12 <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete, @page)%>
11 <% content = content_tag( 'span', _('Delete') ) %> 13 <% content = content_tag( 'span', _('Delete') ) %>
12 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %> 14 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %>
@@ -28,6 +30,10 @@ @@ -28,6 +30,10 @@
28 <% end %> 30 <% end %>
29 31
30 <%= modal_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) unless remove_content_button(:new, @page) %> 32 <%= modal_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) unless remove_content_button(:new, @page) %>
  33 +
  34 + <% content = content_tag('span', label_for_clone_article(@page)) %>
  35 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'new', :id => @page.id, :clone => true, :type => @page.class }) %>
  36 + <%= expirable_button @page, :clone, content, url %>
31 <% end %> 37 <% end %>
32 38
33 <% if @page.accept_uploads? && @page.allow_create?(user) %> 39 <% if @page.accept_uploads? && @page.allow_create?(user) %>
@@ -50,6 +56,7 @@ @@ -50,6 +56,7 @@
50 <% end %> 56 <% end %>
51 57
52 <%= report_abuse(profile, :link, @page) %> 58 <%= report_abuse(profile, :link, @page) %>
  59 +
53 </div> 60 </div>
54 <div id="article-header"> 61 <div id="article-header">
55 <% if @page.blog? and !@page.image.nil? %> 62 <% if @page.blog? and !@page.image.nil? %>
app/views/content_viewer/_article_toolbar.html.erb.orig 0 → 100644
@@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
  1 +<div<%= user && " class='logged-in'" %>>
  2 + <div id="article-actions">
  3 +<<<<<<< HEAD
  4 + <%= fullscreen_buttons('#article') %>
  5 +=======
  6 +
  7 + <%= fullscreen_buttons('#article') %>
  8 +
  9 +>>>>>>> master
  10 + <% if @page.allow_edit?(user) && !remove_content_button(:edit, @page) %>
  11 + <% content = content_tag('span', label_for_edit_article(@page)) %>
  12 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %>
  13 + <%= expirable_button @page, :edit, content, url %>
  14 + <% end %>
  15 +
  16 + <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete, @page)%>
  17 + <% content = content_tag( 'span', _('Delete') ) %>
  18 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %>
  19 + <% options = {:method => :post, :confirm => delete_article_message(@page)} %>
  20 + <%= expirable_button @page, :delete, content, url, options %>
  21 + <% end %>
  22 +
  23 + <% if @page.allow_spread?(user) && !remove_content_button(:spread, @page) %>
  24 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish', :id => @page.id }) %>
  25 + <%= expirable_button @page, :spread, content_tag( 'span', _('Spread this') ), url, {:class => 'colorbox'} if url %>
  26 + <% end %>
  27 +
  28 + <% if !@page.gallery? && (@page.allow_create?(user) || (@page.parent && @page.parent.allow_create?(user))) %>
  29 + <% if @page.translatable? && !@page.native_translation.language.blank? && !remove_content_button(:locale, @page) %>
  30 + <% content = _('Add translation') %>
  31 + <% parent_id = (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)) %>
  32 + <% url = profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => parent_id, :type => @page.type, :article => { :translation_of_id => @page.native_translation.id })%>
  33 + <%= expirable_button @page, :locale, content, url %>
  34 + <% end %>
  35 +
  36 + <%= modal_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) unless remove_content_button(:new, @page) %>
  37 + <% end %>
  38 +
  39 + <% if @page.accept_uploads? && @page.allow_create?(user) %>
  40 + <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) unless remove_content_button(:upload, @page)%>
  41 + <% end %>
  42 +
  43 + <% if !@page.allow_create?(user) && profile.organization? && (@page.blog? || @page.parent && @page.parent.blog?) && !remove_content_button(:suggest, @page) %>
  44 + <% content = content_tag( 'span', _('Suggest an article') ) %>
  45 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}) %>
  46 + <% options = {:id => 'suggest-article-link'} %>
  47 + <%= expirable_button @page, :suggest, content, url, options %>
  48 + <% end %>
  49 +
  50 + <% if @page.display_versions? %>
  51 + <%= button(:clock, _('All versions'), {:controller => 'content_viewer', :profile => profile.identifier, :action => 'article_versions'}, :id => 'article-versions-link') %>
  52 + <% end %>
  53 +
  54 + <% plugins_toolbar_actions_for_article(@page).each do |plugin_button| %>
  55 + <%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url], plugin_button[:html_options] %>
  56 + <% end %>
  57 +
  58 + <%= report_abuse(profile, :link, @page) %>
  59 + </div>
  60 + <div id="article-header">
  61 + <% if @page.blog? and !@page.image.nil? %>
  62 + <div class="blog-cover"><%= image_tag(@page.image.public_filename())%></div>
  63 + <% end %>
  64 + <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
  65 + <%= @plugins.dispatch(:article_header_extra_contents, @page).collect { |content| instance_exec(&content) }.join("") %>
  66 + <%= article_title(@page, :no_link => true) %>
  67 + <%= article_translations(@page) %>
  68 + </div>
  69 +</div>
app/views/profile_editor/_pending_tasks.html.erb
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <div class='pending-tasks'> 4 <div class='pending-tasks'>
5 <h2><%= _('You have pending requests') %></h2> 5 <h2><%= _('You have pending requests') %></h2>
6 <ul> 6 <ul>
7 - <%= @pending_tasks.map {|task| content_tag('li', task_information(task))}.join %> 7 + <%= @pending_tasks.take(10).map {|task| content_tag('li', task_information(task))}.join %>
8 </ul> 8 </ul>
9 <%= button(:todo, _('Process requests'), :controller => 'tasks', :action => 'index') %> 9 <%= button(:todo, _('Process requests'), :controller => 'tasks', :action => 'index') %>
10 </div> 10 </div>
app/views/tasks/_task.html.erb
@@ -2,6 +2,16 @@ @@ -2,6 +2,16 @@
2 2
3 <%= render :partial => 'task_icon', :locals => {:task => task} %> 3 <%= render :partial => 'task_icon', :locals => {:task => task} %>
4 4
  5 + <% if profile.organization? && @responsible_candidates.present? %>
  6 + <div class="task_responsible">
  7 + <span class="label"><%= _('Assign to:') %></span>
  8 + <span>
  9 + <% change_responsible_url = url_for :action => :change_responsible, :controller => :tasks %>
  10 + <%= select_tag "tasks[#{task.id}][responsible]", options_from_collection_for_select(@responsible_candidates, :id, :name, task.responsible.present? ? task.responsible.id : nil), :include_blank => true, :onchange => "change_task_responsible(this);", 'data-old-responsible' => task.responsible.present? ? task.responsible.id : nil, 'data-task' => task.id, 'data-url' => change_responsible_url %>
  11 + </span>
  12 + </div>
  13 + <% end %>
  14 +
5 <div class="task_decisions"> 15 <div class="task_decisions">
6 <%= 16 <%=
7 labelled_radio_button(_("Accept"), "tasks[#{task.id}][decision]", 'finish', task.default_decision == 'accept', 17 labelled_radio_button(_("Accept"), "tasks[#{task.id}][decision]", 'finish', task.default_decision == 'accept',
@@ -22,6 +32,8 @@ @@ -22,6 +32,8 @@
22 %> 32 %>
23 </div><!-- class="task_decisions" --> 33 </div><!-- class="task_decisions" -->
24 34
  35 + <div class="task_date"><%= show_time(task.created_at) %></div>
  36 +
25 <%= render :partial => 'task_title', :locals => {:task => task} %> 37 <%= render :partial => 'task_title', :locals => {:task => task} %>
26 38
27 <div class="task_information"> 39 <div class="task_information">
@@ -40,6 +52,13 @@ @@ -40,6 +52,13 @@
40 <%= render :partial => partial_for_class(task.class, nil, :reject_details), :locals => {:task => task, :f => f} %> 52 <%= render :partial => partial_for_class(task.class, nil, :reject_details), :locals => {:task => task, :f => f} %>
41 </div> 53 </div>
42 <% end %> 54 <% end %>
  55 +
  56 + <div class="formfieldline">
  57 + <div class="formfield tag-list-fields">
  58 + <%= labelled_text_field(_('Tags'),"tasks[#{task.id}][task][tag_list]", task.tag_list.to_s, :size => 36, :class => 'tag-list') %>
  59 + </div>
  60 + </div>
  61 +
43 <% end %> 62 <% end %>
44 63
45 </div><!-- class="task_box" --> 64 </div><!-- class="task_box" -->
app/views/tasks/index.html.erb
@@ -21,11 +21,27 @@ @@ -21,11 +21,27 @@
21 </div> 21 </div>
22 <% end %> 22 <% end %>
23 23
  24 +<%= form_tag '#', :method => 'get' do %>
  25 + <%= field_set_tag _('Filter'), :class => 'filter_fields' do %>
  26 + <p>
  27 + <%= labelled_select(_('Type of task')+': ', :filter_type, :first, :last, @filter_type, type_collection, {:id => 'filter-type'}) %>
  28 + </p>
  29 + <p>
  30 + <%= labelled_text_field(_("Text filter")+': ', :filter_text, nil, {:id => 'filter-text',:value => @filter_text}) %>
  31 + </p>
  32 + <p>
  33 + <%= labelled_select(_('Assigned to')+': ', :filter_responsible, :id, :name, @filter_responsible, [OpenStruct.new(:name => _('All'), :id => nil), OpenStruct.new(:name => _('Unassigned'), :id => -1)] + @responsible_candidates) %>
  34 + </p>
  35 + <p>
  36 + <%= submit_button(:search, _('Search')) %>
  37 + </p>
  38 + <% end %>
  39 +<% end %>
  40 +
24 <% if @tasks.empty? %> 41 <% if @tasks.empty? %>
25 <p> 42 <p>
26 - <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :onchange => "document.location.href = '?filter_type='+this.value")%> 43 + <em><%= _('No pending tasks for %s') % profile.name %></em>
27 </p> 44 </p>
28 - <em><%= _('No pending tasks for %s') % profile.name %></em>  
29 <% else %> 45 <% else %>
30 <%= form_tag :action => 'close' do%> 46 <%= form_tag :action => 'close' do%>
31 <% button_bar do %> 47 <% button_bar do %>
@@ -38,19 +54,24 @@ @@ -38,19 +54,24 @@
38 54
39 <ul class='task-list'> 55 <ul class='task-list'>
40 <p> 56 <p>
41 - <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :onchange => "document.location.href = '?filter_type='+this.value") %>  
42 - </p>  
43 - <p>  
44 <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "up-set-all-tasks-to") %> 57 <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "up-set-all-tasks-to") %>
45 </p> 58 </p>
46 - <% @tasks.each do |task| %>  
47 - <%= render :partial => 'task', :locals => { :task => task } %>  
48 - <% end %> 59 +
  60 + <div class="task_boxes">
  61 + <%= render :partial => 'task', :collection => @tasks %>
  62 + </div>
49 <p> 63 <p>
50 <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "down-set-all-tasks-to") %> 64 <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "down-set-all-tasks-to") %>
51 </p> 65 </p>
52 </ul> 66 </ul>
53 67
  68 + <script>
  69 + jQuery('.tag-list').inputosaurus({
  70 + autoCompleteSource: <%= "'/myprofile/#{profile.identifier}/cms/search_tags'," %>
  71 + activateFinalResult : true
  72 + })
  73 + </script>
  74 +
54 <%= pagination_links(@tasks)%> 75 <%= pagination_links(@tasks)%>
55 76
56 <% button_bar do %> 77 <% button_bar do %>
config/noosfero.yml.dist
@@ -11,7 +11,14 @@ development: @@ -11,7 +11,14 @@ development:
11 max_upload_size: 5MB 11 max_upload_size: 5MB
12 hours_until_user_activation_check: 72 12 hours_until_user_activation_check: 72
13 exclude_profile_identifier_pattern: index(\..*)?|home(\..*)? 13 exclude_profile_identifier_pattern: index(\..*)?|home(\..*)?
  14 + api_recaptcha_site_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-'
  15 + api_recaptcha_private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY'
  16 + api_recaptcha_verify_uri: 'https://www.google.com/recaptcha/api/siteverify'
14 17
15 test: 18 test:
16 19
17 production: 20 production:
  21 + api_recaptcha_site_key: '6LcLPAcTAAAAAKsd0bxY_TArhD_A7OL19SRCW7_i'
  22 + api_recaptcha_private_key: '6LcLPAcTAAAAAE36SN1M2w1I7Hn8upwXYZ_YQZ5-'
  23 + api_recaptcha_verify_uri: 'https://www.google.com/recaptcha/api/siteverify'
  24 +
18 \ No newline at end of file 25 \ No newline at end of file
db/migrate/20150513213939_update_topic_creation_configuration.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class UpdateTopicCreationConfiguration < ActiveRecord::Migration
  2 + def up
  3 + Forum.where("setting LIKE '%:allows_members_to_create_topics: true%'").find_each do |forum|
  4 + forum.setting.delete(:allows_members_to_create_topics)
  5 + forum.setting.merge!(:topic_creation => 'related')
  6 + forum.save
  7 + end
  8 + end
  9 +
  10 + def down
  11 + say "this migration can't be reverted"
  12 + end
  13 +end
db/migrate/20150525101430_add_responsible_to_task.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +class AddResponsibleToTask < ActiveRecord::Migration
  2 +
  3 + def change
  4 + add_column :tasks, :responsible_id, :integer
  5 + end
  6 +
  7 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20150408231524) do 14 +ActiveRecord::Schema.define(:version => 20150513213939) do
15 15
16 create_table "abuse_reports", :force => true do |t| 16 create_table "abuse_reports", :force => true do |t|
17 t.integer "reporter_id" 17 t.integer "reporter_id"
debian/apache2/conf.d/noosfero-chat 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<IfModule mpm_worker_module>
  2 + StartServers 8
  3 + MinSpareThreads 25
  4 + MaxSpareThreads 75
  5 + ThreadLimit 128
  6 + ThreadsPerChild 128
  7 + MaxClients 2048
  8 + MaxRequestsPerChild 0
  9 +</IfModule>
debian/changelog
1 -noosfero (1.2~0) UNRELEASED; urgency=medium 1 +noosfero (1.2~1) UNRELEASED; urgency=medium
2 2
  3 + [ Antonio Terceiro ]
3 * Temporary version in heavy development 4 * Temporary version in heavy development
4 5
5 - -- Antonio Terceiro <terceiro@debian.org> Fri, 08 May 2015 16:08:18 -0300 6 + [ Joenio Costa ]
  7 + * Build noosfero-chat package
  8 +
  9 + -- Joenio Costa <joenio@colivre.coop.br> Mon, 18 May 2015 14:32:21 -0300
6 10
7 noosfero (1.1) wheezy; urgency=low 11 noosfero (1.1) wheezy; urgency=low
8 12
debian/control
@@ -85,3 +85,14 @@ Description: free web-based platform for social networks (apache frontend) @@ -85,3 +85,14 @@ Description: free web-based platform for social networks (apache frontend)
85 . 85 .
86 This package contains the configuration files needed to run Noosfero with the 86 This package contains the configuration files needed to run Noosfero with the
87 Apache HTTPD server as frontend. 87 Apache HTTPD server as frontend.
  88 +
  89 +Package: noosfero-chat
  90 +Architecture: all
  91 +Depends: noosfero (>= 1.2), ejabberd, odbc-postgresql, pidgin-data
  92 +Description: free web-based platform for social networks (ejabberd based chat)
  93 + Noosfero is a web platform for social and solidarity economy networks with
  94 + blog, e-Porfolios, CMS, RSS, thematic discussion, events agenda and collective
  95 + inteligence for solidarity economy in the same system.
  96 + .
  97 + This package contains the configuration files needed to run Noosfero with the
  98 + Ejabberd XMPP chat server.
debian/default/noosfero-chat 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +POLL=true
  2 +SMP=auto
debian/noosfero-chat.install 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +debian/default/noosfero-chat etc/default
  2 +util/chat/ejabberd.cfg etc/ejabberd
  3 +debian/update-noosfero-odbc usr/sbin
  4 +etc/security/limits.d/noosfero-chat.conf etc/security/limits.d
  5 +etc/pam.d/noosfero-chat etc/pam.d
  6 +debian/apache2/conf.d/noosfero-chat etc/apache2/conf.d
debian/noosfero-chat.postinst 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +#!/bin/bash
  2 +
  3 +set -e
  4 +
  5 +. /usr/share/debconf/confmodule
  6 +
  7 +echo -n "Fetching noosfero domain ..."
  8 +domain=$(noosfero-runner 'puts Environment.default.default_hostname(true)')
  9 +echo " [domain = $domain]"
  10 +
  11 +ejabberd_config='/etc/ejabberd/ejabberd.cfg'
  12 +if test -f $ejabberd_config; then
  13 + sed -i "s/acl, *\([^,]*\), *{user, *\([^,]*\), *[^}]*/acl, \1, {user, \2, \"$domain\"/" /etc/ejabberd/ejabberd.cfg
  14 + sed -i "s/hosts, *\[[^]]*/hosts, [\"$domain\"/" /etc/ejabberd/ejabberd.cfg
  15 +fi
  16 +
  17 +echo -n 'Fetching noosfero database name ...'
  18 +noosfero_database=$(noosfero-runner 'puts Environment.connection_config[:database]')
  19 +echo " [database = $noosfero_database]"
  20 +
  21 +echo 'Creating ejabberd schema ...'
  22 +. /etc/default/noosfero
  23 +noosfero_user="$NOOSFERO_USER"
  24 +su - postgres -c "psql -c 'GRANT CREATE ON DATABASE $noosfero_database TO $noosfero_user;' > /dev/null"
  25 +su - $noosfero_user -c 'rails dbconsole production < /usr/share/noosfero/util/chat/postgresql/ejabberd.sql > /dev/null'
  26 +
  27 +if which update-noosfero-odbc > /dev/null ; then
  28 + update-noosfero-odbc
  29 +fi
  30 +
  31 +ejabberd_default='/etc/default/ejabberd'
  32 +noosfero_chat_default='/etc/default/noosfero-chat'
  33 +if test -f $ejabberd_default; then
  34 + if ! cat $ejabberd_default | grep "^\. $noosfero_chat_default" > /dev/null ; then
  35 + echo 'Extending ejabberd defaults with noosfero-chat defaults ...'
  36 + echo ". $noosfero_chat_default" >> $ejabberd_default
  37 + fi
  38 +fi
  39 +
  40 +a2enmod proxy_http
  41 +
  42 +invoke-rc.d ejabberd restart
  43 +invoke-rc.d noosfero restart
  44 +invoke-rc.d apache2 restart
  45 +
  46 +# stop debconf to avoid the problem with infinite hanging, cfe
  47 +# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=295477
  48 +db_stop
  49 +
  50 +#DEBHELPER#
debian/noosfero-runner 0 → 100755
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +#!/bin/sh
  2 +
  3 +set -e
  4 +
  5 +environment="$2"
  6 +test -z "$environment" && environment=production
  7 +
  8 +su - noosfero -c "rails runner -e $environment '$1'"
debian/noosfero.install
@@ -17,6 +17,7 @@ debian/dbupgrade usr/lib/noosfero @@ -17,6 +17,7 @@ debian/dbupgrade usr/lib/noosfero
17 debian/default/noosfero etc/default 17 debian/default/noosfero etc/default
18 debian/noosfero-check-dbconfig usr/sbin 18 debian/noosfero-check-dbconfig usr/sbin
19 debian/noosfero-console usr/sbin 19 debian/noosfero-console usr/sbin
  20 +debian/noosfero-runner usr/sbin
20 debian/noosfero.yml etc/noosfero 21 debian/noosfero.yml etc/noosfero
21 debian/thin.yml etc/noosfero 22 debian/thin.yml etc/noosfero
22 doc usr/share/noosfero 23 doc usr/share/noosfero
debian/update-noosfero-odbc 0 → 100755
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +#!/bin/sh
  2 +
  3 +set -e
  4 +
  5 +# automatically update configuration, but if package noosfero is also installed
  6 +if test -x /usr/share/noosfero/script/odbcconf; then
  7 + config_file="/etc/odbc.ini"
  8 + if test -e "$config_file"; then
  9 + echo "Overwriting $config_file ..."
  10 + fi
  11 + /usr/share/noosfero/script/odbcconf > "$config_file"
  12 +
  13 + echo 'Noosfero ODBC configuration updated.'
  14 +fi
etc/pam.d/noosfero-chat 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +session required pam_limits.so
etc/security/limits.d/noosfero-chat.conf 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +ejabberd hard nofile 65536
  2 +ejabberd soft nofile 65536
features/forum.feature
@@ -169,124 +169,3 @@ Feature: forum @@ -169,124 +169,3 @@ Feature: forum
169 | Post one | joaosilva | Hi all | Hi all | 169 | Post one | joaosilva | Hi all | Hi all |
170 When I go to /joaosilva/forum 170 When I go to /joaosilva/forum
171 Then I should see "Joao Silva" within ".forum-post-last-answer" 171 Then I should see "Joao Silva" within ".forum-post-last-answer"
172 -  
173 - @selenium  
174 - Scenario: community member should be able to see the discussion topic button  
175 - Given the following community  
176 - | identifier | name | owner |  
177 - | sample-community | Sample Community | joaosilva |  
178 - And the following forums  
179 - | owner | name |  
180 - | sample-community | Forum |  
181 - And the following users  
182 - | login | name |  
183 - | mariasilva | Maria Silva|  
184 - And "Maria Silva" is a member of "Sample Community"  
185 - And I am logged in as "joaosilva"  
186 - When I go to /sample-community/forum  
187 - And I follow "Configure forum"  
188 - And I check "Allow members to create topics"  
189 - And I press "Save"  
190 - And I am logged in as "mariasilva"  
191 - And I go to /sample-community/forum  
192 - Then I should see "New discussion topic"  
193 -  
194 - @selenium  
195 - Scenario: a non community member should not be able to see the discussion topic button  
196 - Given the following community  
197 - | identifier | name | owner |  
198 - | sample-community | Sample Community | joaosilva |  
199 - And the following forums  
200 - | owner | name |  
201 - | sample-community | Forum |  
202 - And the following users  
203 - | login | name |  
204 - | mariasilva | Maria Silva|  
205 - And I am logged in as "joaosilva"  
206 - When I go to /sample-community/forum  
207 - And I follow "Configure forum"  
208 - And I check "Allow members to create topics"  
209 - And I press "Save"  
210 - And I am logged in as "mariasilva"  
211 - And I go to /sample-community/forum  
212 - Then I should not see "New discussion topic"  
213 -  
214 - @selenium  
215 - Scenario: community member should not be able to see the discussion topic button  
216 - Given the following community  
217 - | identifier | name | owner |  
218 - | sample-community | Sample Community | joaosilva |  
219 - And the following forums  
220 - | owner | name |  
221 - | sample-community | Forum |  
222 - And the following users  
223 - | login | name |  
224 - | mariasilva | Maria Silva|  
225 - And "Maria Silva" is a member of "Sample Community"  
226 - And I am logged in as "joaosilva"  
227 - When I go to /sample-community/forum  
228 - And I follow "Configure forum"  
229 - And I uncheck "Allow members to create topics"  
230 - And I press "Save"  
231 - And I am logged in as "mariasilva"  
232 - And I go to /sample-community/forum  
233 - Then I should not see "New discussion topic"  
234 -  
235 - @selenium  
236 - Scenario: community member should be able to create a topic with the discussion topic button  
237 - Given the following community  
238 - | identifier | name | owner |  
239 - | sample-community | Sample Community | joaosilva |  
240 - And the following forums  
241 - | owner | name |  
242 - | sample-community | Forum |  
243 - And the following users  
244 - | login | name |  
245 - | mariasilva | Maria Silva|  
246 - And "Maria Silva" is a member of "Sample Community"  
247 - And I am logged in as "joaosilva"  
248 - When I go to /sample-community/forum  
249 - And I follow "Configure forum"  
250 - And I check "Allow members to create topics"  
251 - And I press "Save"  
252 - And I am logged in as "mariasilva"  
253 - And I go to /sample-community/forum  
254 - And I follow "New discussion topic"  
255 - And I should see "Text article with visual editor"  
256 - And I follow "Text article with visual editor"  
257 - And I fill in "Title" with "Test"  
258 - And I press "Save"  
259 - Then I should see "Test"  
260 -  
261 - @selenium  
262 - Scenario: community member should be able to create a topic on a topic page  
263 - Given the following community  
264 - | identifier | name | owner |  
265 - | sample-community | Sample Community | joaosilva |  
266 - And the following forums  
267 - | owner | name |  
268 - | sample-community | Forum |  
269 - And the following users  
270 - | login | name |  
271 - | mariasilva | Maria Silva|  
272 - And "Maria Silva" is a member of "Sample Community"  
273 - And I am logged in as "joaosilva"  
274 - When I go to /sample-community/forum  
275 - And I follow "Configure forum"  
276 - And I check "Allow members to create topics"  
277 - And I press "Save"  
278 - And I am logged in as "mariasilva"  
279 - And I go to /sample-community/forum  
280 - And I follow "New discussion topic"  
281 - And I should see "Text article with visual editor"  
282 - And I follow "Text article with visual editor"  
283 - And I fill in "Title" with "Test"  
284 - And I press "Save"  
285 - And I go to /sample-community/forum/test  
286 - And I follow "New discussion topic"  
287 - And I should see "Text article with visual editor"  
288 - And I follow "Text article with visual editor"  
289 - And I fill in "Title" with "Test inside the topic page"  
290 - And I press "Save"  
291 - And I go to /sample-community/forum  
292 - Then I should see "Test inside the topic page"  
lib/noosfero/api/api.rb
1 require 'grape' 1 require 'grape'
2 #require 'rack/contrib' 2 #require 'rack/contrib'
  3 +
3 Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/} 4 Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/}
4 module Noosfero 5 module Noosfero
5 module API 6 module API
6 class API < Grape::API 7 class API < Grape::API
7 use Rack::JSONP 8 use Rack::JSONP
8 -  
9 - logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log"))  
10 - logger.formatter = GrapeLogging::Formatters::Default.new  
11 - use RequestLogger, { logger: logger }  
12 -  
13 - rescue_from :all do |e|  
14 - logger.error e 9 +
  10 + @@NOOSFERO_CONF = nil
  11 +
  12 + def self.NOOSFERO_CONF
  13 + if @@NOOSFERO_CONF
  14 + @@NOOSFERO_CONF
  15 + else
  16 + file = Rails.root.join('config', 'noosfero.yml')
  17 + @@NOOSFERO_CONF = File.exists?(file) ? YAML.load_file(file)[Rails.env] || {} : {}
  18 + end
15 end 19 end
16 20
17 before { setup_multitenancy } 21 before { setup_multitenancy }
@@ -22,9 +26,9 @@ module Noosfero @@ -22,9 +26,9 @@ module Noosfero
22 prefix "api" 26 prefix "api"
23 format :json 27 format :json
24 content_type :txt, "text/plain" 28 content_type :txt, "text/plain"
25 - 29 +
26 helpers APIHelpers 30 helpers APIHelpers
27 - 31 +
28 mount V1::Articles 32 mount V1::Articles
29 mount V1::Comments 33 mount V1::Comments
30 mount V1::Users 34 mount V1::Users
@@ -33,7 +37,7 @@ module Noosfero @@ -33,7 +37,7 @@ module Noosfero
33 mount V1::Enterprises 37 mount V1::Enterprises
34 mount V1::Categories 38 mount V1::Categories
35 mount Session 39 mount Session
36 - 40 +
37 # hook point which allow plugins to add Grape::API extensions to API::API 41 # hook point which allow plugins to add Grape::API extensions to API::API
38 #finds for plugins which has api mount points classes defined (the class should extends Grape::API) 42 #finds for plugins which has api mount points classes defined (the class should extends Grape::API)
39 @plugins = Noosfero::Plugin.all.map { |p| p.constantize } 43 @plugins = Noosfero::Plugin.all.map { |p| p.constantize }
lib/noosfero/api/helpers.rb
@@ -102,7 +102,21 @@ module Noosfero @@ -102,7 +102,21 @@ module Noosfero
102 end 102 end
103 attrs 103 attrs
104 end 104 end
105 - 105 +
  106 + def verify_recaptcha_v2(remote_ip, g_recaptcha_response, private_key, api_recaptcha_verify_uri)
  107 + verify_hash = {
  108 + "secret" => private_key,
  109 + "remoteip" => remote_ip,
  110 + "response" => g_recaptcha_response
  111 + }
  112 + uri = URI(api_recaptcha_verify_uri)
  113 + https = Net::HTTP.new(uri.host, uri.port)
  114 + https.use_ssl = true
  115 + request = Net::HTTP::Post.new(uri.path)
  116 + request.set_form_data(verify_hash)
  117 + JSON.parse(https.request(request).body)
  118 + end
  119 +
106 ########################################## 120 ##########################################
107 # error helpers # 121 # error helpers #
108 ########################################## 122 ##########################################
lib/noosfero/api/request_logger.rb
@@ -7,10 +7,10 @@ module Noosfero @@ -7,10 +7,10 @@ module Noosfero
7 def parameters(response, duration) 7 def parameters(response, duration)
8 { 8 {
9 path: request.path, 9 path: request.path,
10 - params: request.params.except('password'), 10 + params: request.params.to_hash.except('password'),
11 method: request.request_method, 11 method: request.request_method,
12 total: (duration * 1000).round(2), 12 total: (duration * 1000).round(2),
13 - db: request.env[:db_duration].round(2), 13 + db: @db_duration.round(2),
14 } 14 }
15 end 15 end
16 end 16 end
lib/noosfero/api/session.rb
  1 +require "uri"
  2 +
1 module Noosfero 3 module Noosfero
2 module API 4 module API
3 - 5 +
4 class Session < Grape::API 6 class Session < Grape::API
5 - 7 +
6 # Login to get token 8 # Login to get token
7 # 9 #
8 # Parameters: 10 # Parameters:
@@ -13,13 +15,13 @@ module Noosfero @@ -13,13 +15,13 @@ module Noosfero
13 # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin 15 # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin
14 post "/login" do 16 post "/login" do
15 user ||= User.authenticate(params[:login], params[:password], environment) 17 user ||= User.authenticate(params[:login], params[:password], environment)
16 - 18 +
17 return unauthorized! unless user 19 return unauthorized! unless user
18 user.generate_private_token! 20 user.generate_private_token!
19 @current_user = user 21 @current_user = user
20 present user, :with => Entities::UserLogin 22 present user, :with => Entities::UserLogin
21 end 23 end
22 - 24 +
23 # Create user. 25 # Create user.
24 # 26 #
25 # Parameters: 27 # Parameters:
@@ -37,16 +39,20 @@ module Noosfero @@ -37,16 +39,20 @@ module Noosfero
37 unique_attributes! User, [:email, :login] 39 unique_attributes! User, [:email, :login]
38 attrs = attributes_for_keys [:email, :login, :password] 40 attrs = attributes_for_keys [:email, :login, :password]
39 attrs[:password_confirmation] = attrs[:password] 41 attrs[:password_confirmation] = attrs[:password]
40 - user = User.new(attrs)  
41 - if user.save 42 + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
  43 + private_key = API.NOOSFERO_CONF['api_recaptcha_private_key']
  44 + api_recaptcha_verify_uri = API.NOOSFERO_CONF['api_recaptcha_verify_uri']
  45 + captcha_result = verify_recaptcha_v2(remote_ip, params['g-recaptcha-response'], private_key, api_recaptcha_verify_uri)
  46 + user = User.new(attrs)
  47 + if captcha_result["success"] and user.save!
42 user.activate 48 user.activate
43 user.generate_private_token! 49 user.generate_private_token!
44 present user, :with => Entities::UserLogin 50 present user, :with => Entities::UserLogin
45 else 51 else
46 - something_wrong! 52 + message = user.errors.to_json
  53 + render_api_error!(message, 400)
47 end 54 end
48 end 55 end
49 -  
50 end 56 end
51 end 57 end
52 end 58 end
lib/noosfero/api/v1/articles.rb
@@ -34,8 +34,8 @@ module Noosfero @@ -34,8 +34,8 @@ module Noosfero
34 # FIXME verify allowed values 34 # FIXME verify allowed values
35 render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) 35 render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value)
36 article = find_article(environment.articles, params[:id]) 36 article = find_article(environment.articles, params[:id])
37 - Vote.create!(:voteable => article, :voter => nil, :vote => value)  
38 - {:vote => true} 37 + vote = Vote.new(:voteable => article, :voter => current_person, :vote => value)
  38 + {:vote => vote.save}
39 end 39 end
40 40
41 get ':id/children' do 41 get ':id/children' do
lib/noosfero/plugin.rb
@@ -731,7 +731,7 @@ class Noosfero::Plugin @@ -731,7 +731,7 @@ class Noosfero::Plugin
731 def content_actions 731 def content_actions
732 #FIXME 'new' and 'upload' only works for content_remove. It should work for 732 #FIXME 'new' and 'upload' only works for content_remove. It should work for
733 #content_expire too. 733 #content_expire too.
734 - %w[edit delete spread locale suggest home new upload undo] 734 + %w[edit delete spread locale suggest home new upload undo clone]
735 end 735 end
736 736
737 end 737 end
lib/tasks/enable_plugins.rake 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +namespace :noosfero do
  2 + namespace :plugins do
  3 + task :enable_all => :environment do
  4 + Environment.all.each do |env|
  5 + puts "Plugins Activated on #{env.name}" if env.enable_all_plugins
  6 + end
  7 + end
  8 + end
  9 +end
plugins/tolerance_time/lib/tolerance_time_plugin.rb
@@ -56,9 +56,18 @@ class ToleranceTimePlugin &lt; Noosfero::Plugin @@ -56,9 +56,18 @@ class ToleranceTimePlugin &lt; Noosfero::Plugin
56 end 56 end
57 57
58 def content_expire_edit(content) 58 def content_expire_edit(content)
  59 + content_expire_for(content, _('editing'))
  60 + end
  61 +
  62 + def content_expire_clone(content)
  63 + content_expire_for(content, _('cloning'))
  64 + end
  65 +
  66 + private
  67 +
  68 + def content_expire_for(content, action)
59 if ToleranceTimePlugin.expired?(content) 69 if ToleranceTimePlugin.expired?(content)
60 - _('The tolerance time for editing this content is over.') 70 + _('The tolerance time for %s this content is over.') % action
61 end 71 end
62 end 72 end
63 -  
64 end 73 end
public/designs/icons/tango/style.css
@@ -115,6 +115,7 @@ @@ -115,6 +115,7 @@
115 .icon-set-admin-role { background-image: url(mod/16x16/apps/user.png) } 115 .icon-set-admin-role { background-image: url(mod/16x16/apps/user.png) }
116 .icon-reset-admin-role { background-image: url(../../../images/icons-app/person-icon.png) } 116 .icon-reset-admin-role { background-image: url(../../../images/icons-app/person-icon.png) }
117 .icon-clock { background-image: url(Tango/16x16/actions/appointment.png) } 117 .icon-clock { background-image: url(Tango/16x16/actions/appointment.png) }
  118 +.icon-fullscreen { background-image: url(Tango/16x16/actions/view-fullscreen.png) }
118 119
119 /******************LARGE ICONS********************/ 120 /******************LARGE ICONS********************/
120 .image-gallery-item .folder { background-image: url(mod/96x96/places/folder.png) } 121 .image-gallery-item .folder { background-image: url(mod/96x96/places/folder.png) }
public/javascripts/application.js
@@ -1217,3 +1217,35 @@ function add_new_file_fields() { @@ -1217,3 +1217,35 @@ function add_new_file_fields() {
1217 } 1217 }
1218 1218
1219 window.isHidden = function isHidden() { return (typeof(document.hidden) != 'undefined') ? document.hidden : !document.hasFocus() }; 1219 window.isHidden = function isHidden() { return (typeof(document.hidden) != 'undefined') ? document.hidden : !document.hasFocus() };
  1220 +
  1221 +function $_GET(id){
  1222 + var a = new RegExp(id+"=([^&#=]*)");
  1223 + return decodeURIComponent(a.exec(window.location.search)[1]);
  1224 +}
  1225 +
  1226 +var fullwidth=false;
  1227 +function toggle_fullwidth(itemId){
  1228 + if(fullwidth){
  1229 + jQuery(itemId).removeClass("fullwidth");
  1230 + jQuery("#fullscreen-btn").show()
  1231 + jQuery("#exit-fullscreen-btn").hide()
  1232 + fullwidth = false;
  1233 + }
  1234 + else{
  1235 + jQuery(itemId).addClass("fullwidth");
  1236 + jQuery("#exit-fullscreen-btn").show()
  1237 + jQuery("#fullscreen-btn").hide()
  1238 + fullwidth = true;
  1239 + }
  1240 + jQuery(window).trigger("toggleFullwidth", fullwidth);
  1241 +}
  1242 +
  1243 +function fullscreenPageLoad(itemId){
  1244 + jQuery(document).ready(function(){
  1245 +
  1246 + if ($_GET('fullscreen') == 1){
  1247 + toggle_fullwidth(itemId);
  1248 + }
  1249 + });
  1250 +}
  1251 +
public/javascripts/tasks.js
@@ -63,3 +63,19 @@ @@ -63,3 +63,19 @@
63 63
64 })(jQuery) 64 })(jQuery)
65 65
  66 +function change_task_responsible(el) {
  67 + jQuery.post($(el).data('url'), {task_id: $(el).data('task'),
  68 + responsible_id: $(el).val(),
  69 + old_responsible_id: $(el).data('old-responsible')}, function(data) {
  70 + if (data.success) {
  71 + $(el).effect("highlight");
  72 + $(el).data('old-responsible', data.new_responsible.id);
  73 + } else {
  74 + $(el).effect("highlight", {color: 'red'});
  75 + }
  76 + if (data.notice) {
  77 + display_notice(data.notice);
  78 + }
  79 + });
  80 +}
  81 +
public/javascripts/topic-creation-config.js 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +var values_map = {2: 'self', 1: 'related', 0: 'users'};
  2 +var keys_map = {};
  3 +Object.keys(values_map).forEach(function(value){
  4 + keys_map[values_map[value]] = value;
  5 +});
  6 +var s = jQuery('#topic-creation-slider');
  7 +
  8 +function setValue(event, ui){
  9 + jQuery('#article_topic_creation').val(values_map[ui.value]);
  10 +}
  11 +
  12 +s.slider({
  13 + orientation: 'vertical',
  14 + min: 0,
  15 + max: 2,
  16 + step: 1,
  17 + value: keys_map[jQuery('#article_topic_creation').val()],
  18 + range: 'max',
  19 + change: setValue
  20 +}).each(function() {
  21 + var opt = jQuery(this).data()['ui-slider'].options;
  22 + var vals = opt.max - opt.min;
  23 +
  24 + for (var i = 0; i <= vals; i++) {
  25 + var n = vals - i;
  26 + var el = jQuery('<label>' + s.data(values_map[i]) + '</label>').css('top', ((n/vals*100) - 7 - n) + '%');
  27 + s.append(el);
  28 + }
  29 +});
  30 +
public/stylesheets/application.css
@@ -6238,6 +6238,20 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img { @@ -6238,6 +6238,20 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img {
6238 .forum-posts .pagination { 6238 .forum-posts .pagination {
6239 margin-top: 20px; 6239 margin-top: 20px;
6240 } 6240 }
  6241 +
  6242 +#topic-creation-slider{
  6243 + margin-top: 15px;
  6244 +}
  6245 +
  6246 +#topic-creation-slider .ui-slider-range {
  6247 + background: #73D216;
  6248 +}
  6249 +
  6250 +#topic-creation-slider label {
  6251 + left: 20px;
  6252 + position: absolute;
  6253 + width: 200px;
  6254 +}
6241 /* Task */ 6255 /* Task */
6242 6256
6243 #user a#pending-tasks-count { 6257 #user a#pending-tasks-count {
public/stylesheets/tasks.css
@@ -17,6 +17,11 @@ @@ -17,6 +17,11 @@
17 font-size: 120%; 17 font-size: 120%;
18 } 18 }
19 19
  20 +.task_date {
  21 + color: gray;
  22 + font-size: 12px;
  23 +}
  24 +
20 .task_icon { 25 .task_icon {
21 float: left; 26 float: left;
22 margin-right: 10px; 27 margin-right: 10px;
@@ -44,3 +49,27 @@ @@ -44,3 +49,27 @@
44 text-decoration: underline; 49 text-decoration: underline;
45 font-weight: bold; 50 font-weight: bold;
46 } 51 }
  52 +
  53 +.task_responsible {
  54 + text-align: right;
  55 +}
  56 +
  57 +/* Task tags list */
  58 +
  59 +.formfieldline label {
  60 + font-weight: bold;
  61 + margin: 0 5px 0 10px;
  62 +}
  63 +
  64 +.formfieldline .inputosaurus-container {
  65 + vertical-align: middle;
  66 +}
  67 +
  68 +.tag-list-fields input {
  69 + background-color: #fff !important;
  70 +}
  71 +
  72 +.formfield.tag-list-fields .ui-autocomplete-loading {
  73 + background: url('/images/loading-small.gif') right center no-repeat !important;
  74 + background-color: #fff !important;
  75 +}
script/apacheconf
@@ -32,6 +32,7 @@ when &#39;virtualhosts&#39; @@ -32,6 +32,7 @@ when &#39;virtualhosts&#39;
32 puts " #{server_directive} #{domain.name}" 32 puts " #{server_directive} #{domain.name}"
33 server_directive = 'ServerAlias' 33 server_directive = 'ServerAlias'
34 end 34 end
  35 + puts " Include /usr/share/noosfero/util/chat/apache/xmpp.conf"
35 puts " Include /etc/noosfero/apache/virtualhost.conf" 36 puts " Include /etc/noosfero/apache/virtualhost.conf"
36 puts "</VirtualHost>" 37 puts "</VirtualHost>"
37 end 38 end
script/noosfero-test-chat-session 0 → 100755
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +#!/usr/bin/env ruby
  2 +require File.dirname(__FILE__) + '/../config/environment'
  3 +
  4 +environment = Environment.default
  5 +person = Person.first
  6 +password = person.user.crypted_password
  7 +login = person.jid
  8 +
  9 +begin
  10 + RubyBOSH.initialize_session(
  11 + login,
  12 + password,
  13 + "http://#{environment.default_hostname}/http-bind",
  14 + :wait => 30,
  15 + :hold => 1,
  16 + :window => 5
  17 + )
  18 +rescue Exception => e
  19 + puts ""
  20 + puts "[ERROR] XMPP/Chat setup isn't working"
  21 + puts "-------------------------------------"
  22 + puts e.to_s
  23 + exit 1
  24 +else
  25 + puts ""
  26 + puts "[OK] XMPP/Chat setup is working"
  27 + exit 0
  28 +end
script/odbcconf 0 → 100755
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +require 'yaml'
  4 +config = YAML.load_file(File.dirname(__FILE__) + '/../config/database.yml')['production']
  5 +
  6 +
  7 +puts "[PostgreSQLEjabberdNoosfero]"
  8 +puts "Description = PostgreSQL Noosfero ejabberd database"
  9 +puts "Driver = PostgreSQL Unicode"
  10 +puts "Trace = No"
  11 +puts "TraceFile = /tmp/psqlodbc.log"
  12 +puts "Database = #{config['database']}"
  13 +puts "Servername = #{config['host'] || 'localhost'}"
  14 +puts "UserName = #{config['username']}"
  15 +puts "Password = #{config['password']}"
  16 +puts "Port = #{config['port'] || '5432'}"
  17 +puts "ReadOnly = No"
  18 +puts "RowVersioning = No"
  19 +puts "ShowSystemTables = No"
  20 +puts "ShowOidColumn = No"
  21 +puts "FakeOidIndex = No"
  22 +puts "ConnSettings = SET search_path TO ejabberd"
test/functional/cms_controller_test.rb
@@ -1811,6 +1811,14 @@ class CmsControllerTest &lt; ActionController::TestCase @@ -1811,6 +1811,14 @@ class CmsControllerTest &lt; ActionController::TestCase
1811 assert_equal 'first version', assigns(:article).name 1811 assert_equal 'first version', assigns(:article).name
1812 end 1812 end
1813 1813
  1814 + should 'clone article with its content' do
  1815 + article = profile.articles.create(:name => 'first version')
  1816 +
  1817 + get :new, :profile => profile.identifier, :id => article.id, :clone => true, :type => 'TinyMceArticle'
  1818 +
  1819 + assert_match article.name, @response.body
  1820 + end
  1821 +
1814 should 'save article with content from older version' do 1822 should 'save article with content from older version' do
1815 article = profile.articles.create(:name => 'first version') 1823 article = profile.articles.create(:name => 'first version')
1816 article.name = 'second version'; article.save 1824 article.name = 'second version'; article.save
test/functional/content_viewer_controller_test.rb
@@ -1252,9 +1252,11 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1252,9 +1252,11 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1252 should 'expire article actions button if any plugins says so' do 1252 should 'expire article actions button if any plugins says so' do
1253 class Plugin1 < Noosfero::Plugin 1253 class Plugin1 < Noosfero::Plugin
1254 def content_expire_edit(content); 'This button is expired.'; end 1254 def content_expire_edit(content); 'This button is expired.'; end
  1255 + def content_expire_clone(content); 'This button is expired.'; end
1255 end 1256 end
1256 class Plugin2 < Noosfero::Plugin 1257 class Plugin2 < Noosfero::Plugin
1257 def content_expire_edit(content); nil; end 1258 def content_expire_edit(content); nil; end
  1259 + def content_expire_clone(content); nil; end
1258 end 1260 end
1259 Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name]) 1261 Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name])
1260 1262
test/functional/tasks_controller_test.rb
@@ -80,6 +80,12 @@ class TasksControllerTest &lt; ActionController::TestCase @@ -80,6 +80,12 @@ class TasksControllerTest &lt; ActionController::TestCase
80 assert_kind_of Array, assigns(:tasks) 80 assert_kind_of Array, assigns(:tasks)
81 end 81 end
82 82
  83 + should 'display task created_at' do
  84 + Task.create!(:requestor => fast_create(Person), :target => profile, :spam => false)
  85 + get :index
  86 + assert_select '.task_date'
  87 + end
  88 +
83 should 'list processed tasks without spam' do 89 should 'list processed tasks without spam' do
84 requestor = fast_create(Person) 90 requestor = fast_create(Person)
85 task_spam = create(Task, :status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => true) 91 task_spam = create(Task, :status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => true)
@@ -428,13 +434,13 @@ class TasksControllerTest &lt; ActionController::TestCase @@ -428,13 +434,13 @@ class TasksControllerTest &lt; ActionController::TestCase
428 t2 = CleanHouse.create!(:requestor => requestor, :target => profile) 434 t2 = CleanHouse.create!(:requestor => requestor, :target => profile)
429 t3 = FeedDog.create!(:requestor => requestor, :target => profile) 435 t3 = FeedDog.create!(:requestor => requestor, :target => profile)
430 436
431 - post :index, :filter_type => t1.type, :filter_text => 'test' 437 + get :index, :filter_type => t1.type, :filter_text => 'test'
432 438
433 assert_includes assigns(:tasks), t1 439 assert_includes assigns(:tasks), t1
434 assert_not_includes assigns(:tasks), t2 440 assert_not_includes assigns(:tasks), t2
435 assert_not_includes assigns(:tasks), t3 441 assert_not_includes assigns(:tasks), t3
436 442
437 - post :index 443 + get :index
438 444
439 assert_includes assigns(:tasks), t1 445 assert_includes assigns(:tasks), t1
440 assert_includes assigns(:tasks), t2 446 assert_includes assigns(:tasks), t2
@@ -458,4 +464,82 @@ class TasksControllerTest &lt; ActionController::TestCase @@ -458,4 +464,82 @@ class TasksControllerTest &lt; ActionController::TestCase
458 post :index, :page => 2 464 post :index, :page => 2
459 assert_equal [t4], assigns(:tasks) 465 assert_equal [t4], assigns(:tasks)
460 end 466 end
  467 +
  468 + should 'filter tasks by responsible' do
  469 + Task.stubs(:per_page).returns(3)
  470 + requestor = fast_create(Person)
  471 + responsible = fast_create(Person)
  472 + t1 = Task.create!(:requestor => requestor, :target => profile, :responsible => responsible)
  473 + t2 = Task.create!(:requestor => requestor, :target => profile, :responsible => responsible)
  474 + t3 = Task.create!(:requestor => requestor, :target => profile)
  475 +
  476 + get :index, :filter_responsible => responsible.id
  477 +
  478 + assert_includes assigns(:tasks), t1
  479 + assert_includes assigns(:tasks), t2
  480 + assert_not_includes assigns(:tasks), t3
  481 +
  482 + get :index
  483 +
  484 + assert_includes assigns(:tasks), t1
  485 + assert_includes assigns(:tasks), t2
  486 + assert_includes assigns(:tasks), t3
  487 + end
  488 +
  489 + should 'do not display responsible assignment if profile is not an organization' do
  490 + profile = create_user('personprofile').person
  491 + t1 = Task.create!(:requestor => profile, :target => profile)
  492 + @controller.stubs(:profile).returns(profile)
  493 + login_as profile.user.login
  494 + get :index
  495 +
  496 + assert_select "#task-#{t1.id}"
  497 + assert_select '.task_responsible', 0
  498 + end
  499 +
  500 + should 'display responsible assignment if profile is an organization' do
  501 + profile = fast_create(Community)
  502 + person1 = create_user('person1').person
  503 + person2 = create_user('person2').person
  504 + person3 = create_user('person3').person
  505 + profile.add_admin(person1)
  506 + profile.add_admin(person2)
  507 + profile.add_member(person3)
  508 + Task.create!(:requestor => person3, :target => profile)
  509 + @controller.stubs(:profile).returns(profile)
  510 +
  511 + login_as person1.user.login
  512 + get :index
  513 + assert_equivalent [person1, person2], assigns(:responsible_candidates)
  514 + assert_select '.task_responsible'
  515 + end
  516 +
  517 + should 'change task responsible' do
  518 + profile = fast_create(Community)
  519 + @controller.stubs(:profile).returns(profile)
  520 + person = create_user('person1').person
  521 + profile.add_admin(person)
  522 + task = Task.create!(:requestor => person, :target => profile)
  523 +
  524 + assert_equal nil, task.responsible
  525 + login_as person.user.login
  526 + post :change_responsible, :task_id => task.id, :responsible_id => person.id
  527 + assert_equal person, task.reload.responsible
  528 + end
  529 +
  530 + should 'not change task responsible if old responsible is not the current' do
  531 + profile = fast_create(Community)
  532 + @controller.stubs(:profile).returns(profile)
  533 + person1 = create_user('person1').person
  534 + person2 = create_user('person2').person
  535 + profile.add_admin(person1)
  536 + task = Task.create!(:requestor => person1, :target => profile, :responsible => person1)
  537 +
  538 + login_as person1.user.login
  539 + post :change_responsible, :task_id => task.id, :responsible_id => person2.id, :old_responsible => nil
  540 + assert_equal person1, task.reload.responsible
  541 + json_response = ActiveSupport::JSON.decode(response.body)
  542 + assert !json_response['success']
  543 + end
  544 +
461 end 545 end
test/unit/application_helper_test.rb
@@ -1002,6 +1002,21 @@ class ApplicationHelperTest &lt; ActionView::TestCase @@ -1002,6 +1002,21 @@ class ApplicationHelperTest &lt; ActionView::TestCase
1002 assert_equal file, from_theme_include('atheme', 'afile')[:file] # exists? = true 1002 assert_equal file, from_theme_include('atheme', 'afile')[:file] # exists? = true
1003 end 1003 end
1004 1004
  1005 + should 'enable fullscreen buttons' do
  1006 + html = fullscreen_buttons("#article")
  1007 + assert html.include?("<script>fullscreenPageLoad('#article')</script>")
  1008 + assert html.include?("class=\"button with-text icon-fullscreen\"")
  1009 + assert html.include?("onClick=\"toggle_fullwidth('#article')\"")
  1010 + end
  1011 +
  1012 + should "return the related class string" do
  1013 + assert_equal "Clone Folder", label_for_clone_article(Folder.new)
  1014 + assert_equal "Clone Blog", label_for_clone_article(Blog.new)
  1015 + assert_equal "Clone Event", label_for_clone_article(Event.new)
  1016 + assert_equal "Clone Forum", label_for_clone_article(Forum.new)
  1017 + assert_equal "Clone Article", label_for_clone_article(TinyMceArticle.new)
  1018 + end
  1019 +
1005 protected 1020 protected
1006 include NoosferoTestHelper 1021 include NoosferoTestHelper
1007 1022
test/unit/article_test.rb
@@ -2040,6 +2040,17 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -2040,6 +2040,17 @@ class ArticleTest &lt; ActiveSupport::TestCase
2040 assert_equal [], Article.display_filter(user, nil) 2040 assert_equal [], Article.display_filter(user, nil)
2041 end 2041 end
2042 2042
  2043 + should 'display_filter show person public content to non friends passing nil as profile parameter' do
  2044 + user = create_user('someuser').person
  2045 + p = fast_create(Person)
  2046 + assert !p.is_a_friend?(user)
  2047 + assert !user.is_admin?
  2048 + Article.delete_all
  2049 + a1 = fast_create(Article, :profile_id => p.id)
  2050 + a2 = fast_create(Article)
  2051 + assert_equivalent [a1,a2], Article.display_filter(user, nil)
  2052 + end
  2053 +
2043 should 'display_filter do not show community private content to non members passing nil as profile parameter' do 2054 should 'display_filter do not show community private content to non members passing nil as profile parameter' do
2044 user = create_user('someuser').person 2055 user = create_user('someuser').person
2045 p = fast_create(Community) 2056 p = fast_create(Community)
@@ -2049,6 +2060,16 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -2049,6 +2060,16 @@ class ArticleTest &lt; ActiveSupport::TestCase
2049 assert_equal [], Article.display_filter(user, nil) 2060 assert_equal [], Article.display_filter(user, nil)
2050 end 2061 end
2051 2062
  2063 + should 'display_filter show community public content to non members passing nil as profile parameter' do
  2064 + user = create_user('someuser').person
  2065 + p = fast_create(Community)
  2066 + assert !user.is_member_of?(p)
  2067 + Article.delete_all
  2068 + a1 = fast_create(Article, :profile_id => p.id)
  2069 + a2 = fast_create(Article)
  2070 + assert_equivalent [a1,a2], Article.display_filter(user, nil)
  2071 + end
  2072 +
2052 should 'display_filter show community public content of private community for user members' do 2073 should 'display_filter show community public content of private community for user members' do
2053 user = create_user('someuser').person 2074 user = create_user('someuser').person
2054 p = fast_create(Community, :public_profile => false) 2075 p = fast_create(Community, :public_profile => false)
test/unit/environment_test.rb
@@ -524,7 +524,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -524,7 +524,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
524 p1= fast_create(Person, :is_template => true, :environment_id => e.id) 524 p1= fast_create(Person, :is_template => true, :environment_id => e.id)
525 p2 = fast_create(Person, :environment_id => e.id) 525 p2 = fast_create(Person, :environment_id => e.id)
526 p3 = fast_create(Person, :is_template => true, :environment_id => e.id) 526 p3 = fast_create(Person, :is_template => true, :environment_id => e.id)
527 - assert_equivalent [p1,p3], e.person_templates 527 + assert_equivalent [p1,p3], e.person_templates
528 end 528 end
529 529
530 should 'person_templates return an empty array if there is no templates of person' do 530 should 'person_templates return an empty array if there is no templates of person' do
@@ -532,7 +532,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -532,7 +532,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
532 532
533 fast_create(Person, :environment_id => e.id) 533 fast_create(Person, :environment_id => e.id)
534 fast_create(Person, :environment_id => e.id) 534 fast_create(Person, :environment_id => e.id)
535 - assert_equivalent [], e.person_templates 535 + assert_equivalent [], e.person_templates
536 end 536 end
537 537
538 should 'person_default_template return the template defined as default' do 538 should 'person_default_template return the template defined as default' do
@@ -585,7 +585,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -585,7 +585,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
585 c1= fast_create(Community, :is_template => true, :environment_id => e.id) 585 c1= fast_create(Community, :is_template => true, :environment_id => e.id)
586 c2 = fast_create(Community, :environment_id => e.id) 586 c2 = fast_create(Community, :environment_id => e.id)
587 c3 = fast_create(Community, :is_template => true, :environment_id => e.id) 587 c3 = fast_create(Community, :is_template => true, :environment_id => e.id)
588 - assert_equivalent [c1,c3], e.community_templates 588 + assert_equivalent [c1,c3], e.community_templates
589 end 589 end
590 590
591 should 'community_templates return an empty array if there is no templates of community' do 591 should 'community_templates return an empty array if there is no templates of community' do
@@ -646,7 +646,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -646,7 +646,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
646 e1= fast_create(Enterprise, :is_template => true, :environment_id => env.id) 646 e1= fast_create(Enterprise, :is_template => true, :environment_id => env.id)
647 e2 = fast_create(Enterprise, :environment_id => env.id) 647 e2 = fast_create(Enterprise, :environment_id => env.id)
648 e3 = fast_create(Enterprise, :is_template => true, :environment_id => env.id) 648 e3 = fast_create(Enterprise, :is_template => true, :environment_id => env.id)
649 - assert_equivalent [e1,e3], env.enterprise_templates 649 + assert_equivalent [e1,e3], env.enterprise_templates
650 end 650 end
651 651
652 should 'enterprise_templates return an empty array if there is no templates of enterprise' do 652 should 'enterprise_templates return an empty array if there is no templates of enterprise' do
@@ -654,7 +654,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -654,7 +654,7 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
654 654
655 fast_create(Enterprise, :environment_id => env.id) 655 fast_create(Enterprise, :environment_id => env.id)
656 fast_create(Enterprise, :environment_id => env.id) 656 fast_create(Enterprise, :environment_id => env.id)
657 - assert_equivalent [], env.enterprise_templates 657 + assert_equivalent [], env.enterprise_templates
658 end 658 end
659 659
660 should 'enterprise_default_template return the template defined as default' do 660 should 'enterprise_default_template return the template defined as default' do
@@ -1428,6 +1428,36 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -1428,6 +1428,36 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
1428 assert !environment.plugin_enabled?(Plugin) 1428 assert !environment.plugin_enabled?(Plugin)
1429 end 1429 end
1430 1430
  1431 + should 'activate on database all available plugins' do
  1432 + plugins_enable = ["Statistics", "Foo", "PeopleBlock"]
  1433 + Noosfero::Plugins.stubs(:available_plugin_names).returns(plugins_enable)
  1434 + env1 = Environment.create(:name => "Test")
  1435 + env2 = Environment.create(:name => "Test 2")
  1436 +
  1437 + env1.enable_all_plugins
  1438 + env2.enable_all_plugins
  1439 +
  1440 + plugins = ["PeopleBlockPlugin", "StatisticsPlugin", "FooPlugin"]
  1441 + plugins.each do |plugin|
  1442 + assert env1.enabled_plugins.include?(plugin)
  1443 + assert env2.enabled_plugins.include?(plugin)
  1444 + end
  1445 + end
  1446 +
  1447 + should 'dont activate plugins that are not available' do
  1448 + env1 = Environment.create(:name => "Test")
  1449 + env2 = Environment.create(:name => "Test 2")
  1450 +
  1451 + env1.enable_all_plugins
  1452 + env2.enable_all_plugins
  1453 +
  1454 + plugins = ["SomePlugin", "OtherPlugin", "ThirdPlugin"]
  1455 + plugins.each do |plugin|
  1456 + assert_equal false, env1.enabled_plugins.include?(plugin)
  1457 + assert_equal false, env2.enabled_plugins.include?(plugin)
  1458 + end
  1459 + end
  1460 +
1431 should 'have production costs' do 1461 should 'have production costs' do
1432 assert_respond_to Environment.default, :production_costs 1462 assert_respond_to Environment.default, :production_costs
1433 end 1463 end
test/unit/forum_test.rb
@@ -174,4 +174,70 @@ class ForumTest &lt; ActiveSupport::TestCase @@ -174,4 +174,70 @@ class ForumTest &lt; ActiveSupport::TestCase
174 assert_equal true, Forum.find(forum.id).agrees_with_terms?(person) 174 assert_equal true, Forum.find(forum.id).agrees_with_terms?(person)
175 end 175 end
176 176
  177 + should 'always allow topic creation to the person himself' do
  178 + person = fast_create(Person)
  179 + someone = fast_create(Person)
  180 + forum = Forum.new(:profile => person)
  181 +
  182 + assert forum.can_create_topic?(person)
  183 + assert !forum.can_create_topic?(someone)
  184 + end
  185 +
  186 + should 'always allow topic creation to profile admins' do
  187 + admin = fast_create(Person)
  188 + someone = fast_create(Person)
  189 + profile = fast_create(Profile)
  190 + admins = [admin]
  191 + profile.stubs(:admins).returns(admins)
  192 + forum = Forum.new(:profile => profile)
  193 +
  194 + assert forum.can_create_topic?(admin)
  195 + assert !forum.can_create_topic?(someone)
  196 + end
  197 +
  198 + should 'always allow topic creation to environment admins' do
  199 + admin = fast_create(Person)
  200 + someone = fast_create(Person)
  201 + profile = fast_create(Profile)
  202 + admins = [admin]
  203 + environment = profile.environment
  204 + environment.stubs(:admins).returns(admins)
  205 + forum = Forum.new(:profile => profile)
  206 +
  207 + assert forum.can_create_topic?(admin)
  208 + assert !forum.can_create_topic?(someone)
  209 + end
  210 +
  211 + should 'allow only person friends to create topics when topic_creation is related' do
  212 + person = fast_create(Person)
  213 + friend = fast_create(Person)
  214 + someone = fast_create(Person)
  215 + friends = [friend]
  216 + person.stubs(:friends).returns(friends)
  217 + forum = Forum.new(:profile => person, :topic_creation => 'related')
  218 +
  219 + assert forum.can_create_topic?(friend)
  220 + assert !forum.can_create_topic?(someone)
  221 + end
  222 +
  223 + should 'allow only group members to create topics when topic_creation is related' do
  224 + organization = fast_create(Organization)
  225 + member = fast_create(Person)
  226 + someone = fast_create(Person)
  227 + members = [member]
  228 + organization.stubs(:members).returns(members)
  229 + forum = Forum.new(:profile => organization, :topic_creation => 'related')
  230 +
  231 + assert forum.can_create_topic?(member)
  232 + assert !forum.can_create_topic?(someone)
  233 + end
  234 +
  235 + should 'allow every user to create topics when topic_creation is users' do
  236 + profile = fast_create(Profile)
  237 + user = fast_create(Person)
  238 + forum = Forum.new(:profile => profile, :topic_creation => 'users')
  239 +
  240 + assert forum.can_create_topic?(user)
  241 + assert !forum.can_create_topic?(nil)
  242 + end
177 end 243 end
test/unit/task_test.rb
@@ -444,6 +444,14 @@ class TaskTest &lt; ActiveSupport::TestCase @@ -444,6 +444,14 @@ class TaskTest &lt; ActiveSupport::TestCase
444 assert t1.ham? 444 assert t1.ham?
445 end 445 end
446 446
  447 + should 'be able to assign a responsible to a task' do
  448 + person = fast_create(Person)
  449 + task = fast_create(Task)
  450 + task.responsible = person
  451 + task.save!
  452 + assert_equal person, task.responsible
  453 + end
  454 +
447 protected 455 protected
448 456
449 def sample_user 457 def sample_user
util/chat/apache/xmpp.conf
1 # If your XMPP XMPP/BOSH isn't in localhost, change the config below to correct 1 # If your XMPP XMPP/BOSH isn't in localhost, change the config below to correct
2 # point to address 2 # point to address
3 3
  4 + RewriteEngine On
4 RewriteRule /http-bind http://localhost:5280/http-bind [P,QSA,L] 5 RewriteRule /http-bind http://localhost:5280/http-bind [P,QSA,L]
5 <Proxy http://localhost:5280/http-bind> 6 <Proxy http://localhost:5280/http-bind>
6 Order Allow,Deny 7 Order Allow,Deny