Commit 3779f8e0eb09dd2c5475fbb1e8b73a4772bbe94b
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'production' of gitlab.com:participa/noosfero into production
Showing
60 changed files
with
906 additions
and
220 deletions
Show diff stats
Gemfile
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 | 17 | The samples of config file to configure a XMPP/BOSH server with ejabberd, |
| 5 | 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 | 21 | in a production environment. |
| 9 | 22 | |
| 10 | 23 | Steps |
| 11 | -===== | |
| 24 | +----- | |
| 12 | 25 | |
| 13 | 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 | 157 | |
| 145 | 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 | 163 | Ruby-BOSH - SEND |
| 158 | 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 < MyProfileController |
| 137 | 137 | article_data = environment.enabled?('articles_dont_accept_comments_by_default') ? { :accept_comments => false } : {} |
| 138 | 138 | article_data.merge!(params[:article]) if params[:article] |
| 139 | 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 | 148 | parent = check_parent(params[:parent_id]) |
| 143 | 149 | if parent | ... | ... |
app/controllers/my_profile/tasks_controller.rb
| 1 | 1 | class TasksController < MyProfileController |
| 2 | 2 | |
| 3 | 3 | protect 'perform_task', :profile |
| 4 | - | |
| 4 | + | |
| 5 | 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 | 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 | 15 | @failed = params ? params[:failed] : {} |
| 16 | + | |
| 17 | + @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task')}) | |
| 11 | 18 | end |
| 12 | 19 | |
| 13 | 20 | def processed |
| 14 | 21 | @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) |
| 15 | 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 | 37 | VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] |
| 18 | 38 | |
| 19 | 39 | def close |
| 20 | 40 | failed = {} |
| 41 | + save = false | |
| 21 | 42 | |
| 22 | 43 | if params[:tasks] |
| 23 | 44 | params[:tasks].each do |id, value| |
| 24 | 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 | 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 | 68 | end |
| 34 | 69 | end |
| 35 | 70 | end | ... | ... |
app/helpers/application_helper.rb
| ... | ... | @@ -941,6 +941,19 @@ module ApplicationHelper |
| 941 | 941 | article_helper.cms_label_for_edit |
| 942 | 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 | 957 | def add_rss_feed_to_head(title, url) |
| 945 | 958 | content_for :feeds do |
| 946 | 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 | 12 | @article = article |
| 13 | 13 | |
| 14 | 14 | visibility_options(@article, tokenized_children) + |
| 15 | + topic_creation(@article) + | |
| 15 | 16 | content_tag('h4', _('Options')) + |
| 16 | 17 | content_tag('div', |
| 17 | 18 | (article.profile.has_members? ? |
| ... | ... | @@ -55,14 +56,7 @@ module ArticleHelper |
| 55 | 56 | 'div', |
| 56 | 57 | check_box(:article, :display_versions) + |
| 57 | 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 | 61 | end |
| 68 | 62 | |
| ... | ... | @@ -81,6 +75,22 @@ module ArticleHelper |
| 81 | 75 | ) |
| 82 | 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 | 94 | def privacity_exceptions(article, tokenized_children) |
| 85 | 95 | content_tag('div', |
| 86 | 96 | content_tag('div', | ... | ... |
app/helpers/tinymce_helper.rb
| ... | ... | @@ -20,7 +20,7 @@ module TinymceHelper |
| 20 | 20 | :image_advtab => true, |
| 21 | 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 | 24 | if options[:mode] == 'simple' |
| 25 | 25 | options[:menubar] = false |
| 26 | 26 | else | ... | ... |
app/models/article.rb
| ... | ... | @@ -508,13 +508,13 @@ class Article < ActiveRecord::Base |
| 508 | 508 | |
| 509 | 509 | scope :display_filter, lambda {|user, profile| |
| 510 | 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 | 512 | where( |
| 513 | 513 | [ |
| 514 | 514 | "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ? |
| 515 | 515 | OR (show_to_followers = ? AND ? AND profile_id = ?)", true, user.id, user.id, |
| 516 | 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 < ActiveRecord::Base |
| 587 | 587 | profile.visible? && profile.public? && published? |
| 588 | 588 | end |
| 589 | 589 | |
| 590 | - | |
| 591 | - def copy(options = {}) | |
| 590 | + def copy_without_save(options = {}) | |
| 592 | 591 | attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } |
| 593 | 592 | attrs.merge!(options) |
| 594 | 593 | object = self.class.new |
| 595 | 594 | attrs.each do |key, value| |
| 596 | 595 | object.send(key.to_s+'=', value) |
| 597 | 596 | end |
| 597 | + object | |
| 598 | + end | |
| 599 | + | |
| 600 | + def copy(options = {}) | |
| 601 | + object = copy_without_save(options) | |
| 598 | 602 | object.save |
| 599 | 603 | object |
| 600 | 604 | end |
| 601 | 605 | |
| 602 | 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 | 608 | object.save! |
| 610 | 609 | object |
| 611 | 610 | end | ... | ... |
app/models/environment.rb
| ... | ... | @@ -339,6 +339,16 @@ class Environment < ActiveRecord::Base |
| 339 | 339 | self.save! |
| 340 | 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 | 352 | # Disables a feature identified by its name |
| 343 | 353 | def disable(feature, must_save=true) |
| 344 | 354 | self.settings["#{feature}_enabled".to_sym] = false | ... | ... |
app/models/forum.rb
| ... | ... | @@ -3,11 +3,11 @@ class Forum < Folder |
| 3 | 3 | acts_as_having_posts :order => 'updated_at DESC' |
| 4 | 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 | 8 | settings_items :terms_of_use, :type => :string, :default => "" |
| 9 | 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 | 11 | has_and_belongs_to_many :users_with_agreement, :class_name => 'Person', :join_table => 'terms_forum_people' |
| 12 | 12 | |
| 13 | 13 | before_save do |forum| |
| ... | ... | @@ -33,6 +33,23 @@ class Forum < Folder |
| 33 | 33 | _('An internet forum, also called message board, where discussions can be held.') |
| 34 | 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 | 53 | include ActionView::Helpers::TagHelper |
| 37 | 54 | def to_html(options = {}) |
| 38 | 55 | proc do |
| ... | ... | @@ -69,11 +86,17 @@ class Forum < Folder |
| 69 | 86 | self.users_with_agreement.exists? user |
| 70 | 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 | 97 | end |
| 75 | 98 | |
| 76 | 99 | def allow_create?(user) |
| 77 | - super || can_create_topic?(user, profile) | |
| 100 | + super || can_create_topic?(user) | |
| 78 | 101 | end |
| 79 | 102 | end | ... | ... |
app/models/suggest_article.rb
| ... | ... | @@ -25,7 +25,7 @@ class SuggestArticle < Task |
| 25 | 25 | |
| 26 | 26 | def article_object |
| 27 | 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 | 29 | if requestor.present? |
| 30 | 30 | @article_object.author = requestor |
| 31 | 31 | else | ... | ... |
app/models/task.rb
| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | class Task < ActiveRecord::Base |
| 13 | 13 | |
| 14 | 14 | acts_as_having_settings :field => :data |
| 15 | + acts_as_ordered_taggable | |
| 15 | 16 | |
| 16 | 17 | module Status |
| 17 | 18 | # the status of tasks just created |
| ... | ... | @@ -33,6 +34,7 @@ class Task < ActiveRecord::Base |
| 33 | 34 | |
| 34 | 35 | belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id |
| 35 | 36 | belongs_to :target, :foreign_key => :target_id, :polymorphic => true |
| 37 | + belongs_to :responsible, :class_name => 'Person', :foreign_key => :responsible_id | |
| 36 | 38 | |
| 37 | 39 | validates_uniqueness_of :code, :on => :create |
| 38 | 40 | validates_presence_of :code |
| ... | ... | @@ -239,9 +241,9 @@ class Task < ActiveRecord::Base |
| 239 | 241 | scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } |
| 240 | 242 | scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } |
| 241 | 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 | 249 | scope :to, lambda { |profile| | ... | ... |
app/views/cms/_forum.html.erb
app/views/content_viewer/_article_toolbar.html.erb
| 1 | 1 | <div<%= user && " class='logged-in'" %>> |
| 2 | 2 | <div id="article-actions"> |
| 3 | + | |
| 3 | 4 | <%= fullscreen_buttons('#article') %> |
| 5 | + | |
| 4 | 6 | <% if @page.allow_edit?(user) && !remove_content_button(:edit, @page) %> |
| 5 | 7 | <% content = content_tag('span', label_for_edit_article(@page)) %> |
| 6 | 8 | <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %> |
| 7 | 9 | <%= expirable_button @page, :edit, content, url %> |
| 8 | 10 | <% end %> |
| 9 | - | |
| 11 | + | |
| 10 | 12 | <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete, @page)%> |
| 11 | 13 | <% content = content_tag( 'span', _('Delete') ) %> |
| 12 | 14 | <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %> |
| ... | ... | @@ -28,6 +30,10 @@ |
| 28 | 30 | <% end %> |
| 29 | 31 | |
| 30 | 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 | 37 | <% end %> |
| 32 | 38 | |
| 33 | 39 | <% if @page.accept_uploads? && @page.allow_create?(user) %> |
| ... | ... | @@ -50,6 +56,7 @@ |
| 50 | 56 | <% end %> |
| 51 | 57 | |
| 52 | 58 | <%= report_abuse(profile, :link, @page) %> |
| 59 | + | |
| 53 | 60 | </div> |
| 54 | 61 | <div id="article-header"> |
| 55 | 62 | <% if @page.blog? and !@page.image.nil? %> | ... | ... |
| ... | ... | @@ -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 | 4 | <div class='pending-tasks'> |
| 5 | 5 | <h2><%= _('You have pending requests') %></h2> |
| 6 | 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 | 8 | </ul> |
| 9 | 9 | <%= button(:todo, _('Process requests'), :controller => 'tasks', :action => 'index') %> |
| 10 | 10 | </div> | ... | ... |
app/views/tasks/_task.html.erb
| ... | ... | @@ -2,6 +2,16 @@ |
| 2 | 2 | |
| 3 | 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 | 15 | <div class="task_decisions"> |
| 6 | 16 | <%= |
| 7 | 17 | labelled_radio_button(_("Accept"), "tasks[#{task.id}][decision]", 'finish', task.default_decision == 'accept', |
| ... | ... | @@ -22,6 +32,8 @@ |
| 22 | 32 | %> |
| 23 | 33 | </div><!-- class="task_decisions" --> |
| 24 | 34 | |
| 35 | + <div class="task_date"><%= show_time(task.created_at) %></div> | |
| 36 | + | |
| 25 | 37 | <%= render :partial => 'task_title', :locals => {:task => task} %> |
| 26 | 38 | |
| 27 | 39 | <div class="task_information"> |
| ... | ... | @@ -40,6 +52,13 @@ |
| 40 | 52 | <%= render :partial => partial_for_class(task.class, nil, :reject_details), :locals => {:task => task, :f => f} %> |
| 41 | 53 | </div> |
| 42 | 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 | 62 | <% end %> |
| 44 | 63 | |
| 45 | 64 | </div><!-- class="task_box" --> | ... | ... |
app/views/tasks/index.html.erb
| ... | ... | @@ -21,11 +21,27 @@ |
| 21 | 21 | </div> |
| 22 | 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 | 41 | <% if @tasks.empty? %> |
| 25 | 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 | 44 | </p> |
| 28 | - <em><%= _('No pending tasks for %s') % profile.name %></em> | |
| 29 | 45 | <% else %> |
| 30 | 46 | <%= form_tag :action => 'close' do%> |
| 31 | 47 | <% button_bar do %> |
| ... | ... | @@ -38,19 +54,24 @@ |
| 38 | 54 | |
| 39 | 55 | <ul class='task-list'> |
| 40 | 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 | 57 | <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "up-set-all-tasks-to") %> |
| 45 | 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 | 63 | <p> |
| 50 | 64 | <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "down-set-all-tasks-to") %> |
| 51 | 65 | </p> |
| 52 | 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 | 75 | <%= pagination_links(@tasks)%> |
| 55 | 76 | |
| 56 | 77 | <% button_bar do %> | ... | ... |
config/noosfero.yml.dist
| ... | ... | @@ -11,7 +11,14 @@ development: |
| 11 | 11 | max_upload_size: 5MB |
| 12 | 12 | hours_until_user_activation_check: 72 |
| 13 | 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 | 18 | test: |
| 16 | 19 | |
| 17 | 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 | 25 | \ No newline at end of file | ... | ... |
db/migrate/20150513213939_update_topic_creation_configuration.rb
0 → 100644
| ... | ... | @@ -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/schema.rb
| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | # |
| 12 | 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 | 16 | create_table "abuse_reports", :force => true do |t| |
| 17 | 17 | t.integer "reporter_id" | ... | ... |
debian/changelog
| 1 | -noosfero (1.2~0) UNRELEASED; urgency=medium | |
| 1 | +noosfero (1.2~1) UNRELEASED; urgency=medium | |
| 2 | 2 | |
| 3 | + [ Antonio Terceiro ] | |
| 3 | 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 | 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 | 85 | . |
| 86 | 86 | This package contains the configuration files needed to run Noosfero with the |
| 87 | 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. | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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.install
| ... | ... | @@ -17,6 +17,7 @@ debian/dbupgrade usr/lib/noosfero |
| 17 | 17 | debian/default/noosfero etc/default |
| 18 | 18 | debian/noosfero-check-dbconfig usr/sbin |
| 19 | 19 | debian/noosfero-console usr/sbin |
| 20 | +debian/noosfero-runner usr/sbin | |
| 20 | 21 | debian/noosfero.yml etc/noosfero |
| 21 | 22 | debian/thin.yml etc/noosfero |
| 22 | 23 | doc usr/share/noosfero | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +session required pam_limits.so | ... | ... |
features/forum.feature
| ... | ... | @@ -169,124 +169,3 @@ Feature: forum |
| 169 | 169 | | Post one | joaosilva | Hi all | Hi all | |
| 170 | 170 | When I go to /joaosilva/forum |
| 171 | 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 | 1 | require 'grape' |
| 2 | 2 | #require 'rack/contrib' |
| 3 | + | |
| 3 | 4 | Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/} |
| 4 | 5 | module Noosfero |
| 5 | 6 | module API |
| 6 | 7 | class API < Grape::API |
| 7 | 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 | 19 | end |
| 16 | 20 | |
| 17 | 21 | before { setup_multitenancy } |
| ... | ... | @@ -22,9 +26,9 @@ module Noosfero |
| 22 | 26 | prefix "api" |
| 23 | 27 | format :json |
| 24 | 28 | content_type :txt, "text/plain" |
| 25 | - | |
| 29 | + | |
| 26 | 30 | helpers APIHelpers |
| 27 | - | |
| 31 | + | |
| 28 | 32 | mount V1::Articles |
| 29 | 33 | mount V1::Comments |
| 30 | 34 | mount V1::Users |
| ... | ... | @@ -33,7 +37,7 @@ module Noosfero |
| 33 | 37 | mount V1::Enterprises |
| 34 | 38 | mount V1::Categories |
| 35 | 39 | mount Session |
| 36 | - | |
| 40 | + | |
| 37 | 41 | # hook point which allow plugins to add Grape::API extensions to API::API |
| 38 | 42 | #finds for plugins which has api mount points classes defined (the class should extends Grape::API) |
| 39 | 43 | @plugins = Noosfero::Plugin.all.map { |p| p.constantize } | ... | ... |
lib/noosfero/api/helpers.rb
| ... | ... | @@ -102,7 +102,21 @@ module Noosfero |
| 102 | 102 | end |
| 103 | 103 | attrs |
| 104 | 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 | 121 | # error helpers # |
| 108 | 122 | ########################################## | ... | ... |
lib/noosfero/api/request_logger.rb
| ... | ... | @@ -7,10 +7,10 @@ module Noosfero |
| 7 | 7 | def parameters(response, duration) |
| 8 | 8 | { |
| 9 | 9 | path: request.path, |
| 10 | - params: request.params.except('password'), | |
| 10 | + params: request.params.to_hash.except('password'), | |
| 11 | 11 | method: request.request_method, |
| 12 | 12 | total: (duration * 1000).round(2), |
| 13 | - db: request.env[:db_duration].round(2), | |
| 13 | + db: @db_duration.round(2), | |
| 14 | 14 | } |
| 15 | 15 | end |
| 16 | 16 | end | ... | ... |
lib/noosfero/api/session.rb
| 1 | +require "uri" | |
| 2 | + | |
| 1 | 3 | module Noosfero |
| 2 | 4 | module API |
| 3 | - | |
| 5 | + | |
| 4 | 6 | class Session < Grape::API |
| 5 | - | |
| 7 | + | |
| 6 | 8 | # Login to get token |
| 7 | 9 | # |
| 8 | 10 | # Parameters: |
| ... | ... | @@ -13,13 +15,13 @@ module Noosfero |
| 13 | 15 | # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin |
| 14 | 16 | post "/login" do |
| 15 | 17 | user ||= User.authenticate(params[:login], params[:password], environment) |
| 16 | - | |
| 18 | + | |
| 17 | 19 | return unauthorized! unless user |
| 18 | 20 | user.generate_private_token! |
| 19 | 21 | @current_user = user |
| 20 | 22 | present user, :with => Entities::UserLogin |
| 21 | 23 | end |
| 22 | - | |
| 24 | + | |
| 23 | 25 | # Create user. |
| 24 | 26 | # |
| 25 | 27 | # Parameters: |
| ... | ... | @@ -37,16 +39,20 @@ module Noosfero |
| 37 | 39 | unique_attributes! User, [:email, :login] |
| 38 | 40 | attrs = attributes_for_keys [:email, :login, :password] |
| 39 | 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 | 48 | user.activate |
| 43 | 49 | user.generate_private_token! |
| 44 | 50 | present user, :with => Entities::UserLogin |
| 45 | 51 | else |
| 46 | - something_wrong! | |
| 52 | + message = user.errors.to_json | |
| 53 | + render_api_error!(message, 400) | |
| 47 | 54 | end |
| 48 | 55 | end |
| 49 | - | |
| 50 | 56 | end |
| 51 | 57 | end |
| 52 | 58 | end | ... | ... |
lib/noosfero/api/v1/articles.rb
| ... | ... | @@ -34,8 +34,8 @@ module Noosfero |
| 34 | 34 | # FIXME verify allowed values |
| 35 | 35 | render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) |
| 36 | 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 | 39 | end |
| 40 | 40 | |
| 41 | 41 | get ':id/children' do | ... | ... |
lib/noosfero/plugin.rb
| ... | ... | @@ -731,7 +731,7 @@ class Noosfero::Plugin |
| 731 | 731 | def content_actions |
| 732 | 732 | #FIXME 'new' and 'upload' only works for content_remove. It should work for |
| 733 | 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 | 735 | end |
| 736 | 736 | |
| 737 | 737 | end | ... | ... |
plugins/tolerance_time/lib/tolerance_time_plugin.rb
| ... | ... | @@ -56,9 +56,18 @@ class ToleranceTimePlugin < Noosfero::Plugin |
| 56 | 56 | end |
| 57 | 57 | |
| 58 | 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 | 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 | 71 | end |
| 62 | 72 | end |
| 63 | - | |
| 64 | 73 | end | ... | ... |
public/designs/icons/tango/style.css
| ... | ... | @@ -115,6 +115,7 @@ |
| 115 | 115 | .icon-set-admin-role { background-image: url(mod/16x16/apps/user.png) } |
| 116 | 116 | .icon-reset-admin-role { background-image: url(../../../images/icons-app/person-icon.png) } |
| 117 | 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 | 120 | /******************LARGE ICONS********************/ |
| 120 | 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 | 1217 | } |
| 1218 | 1218 | |
| 1219 | 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 | 63 | |
| 64 | 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 | + | ... | ... |
| ... | ... | @@ -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 | 6238 | .forum-posts .pagination { |
| 6239 | 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 | 6255 | /* Task */ |
| 6242 | 6256 | |
| 6243 | 6257 | #user a#pending-tasks-count { | ... | ... |
public/stylesheets/tasks.css
| ... | ... | @@ -17,6 +17,11 @@ |
| 17 | 17 | font-size: 120%; |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | +.task_date { | |
| 21 | + color: gray; | |
| 22 | + font-size: 12px; | |
| 23 | +} | |
| 24 | + | |
| 20 | 25 | .task_icon { |
| 21 | 26 | float: left; |
| 22 | 27 | margin-right: 10px; |
| ... | ... | @@ -44,3 +49,27 @@ |
| 44 | 49 | text-decoration: underline; |
| 45 | 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 'virtualhosts' |
| 32 | 32 | puts " #{server_directive} #{domain.name}" |
| 33 | 33 | server_directive = 'ServerAlias' |
| 34 | 34 | end |
| 35 | + puts " Include /usr/share/noosfero/util/chat/apache/xmpp.conf" | |
| 35 | 36 | puts " Include /etc/noosfero/apache/virtualhost.conf" |
| 36 | 37 | puts "</VirtualHost>" |
| 37 | 38 | end | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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 < ActionController::TestCase |
| 1811 | 1811 | assert_equal 'first version', assigns(:article).name |
| 1812 | 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 | 1822 | should 'save article with content from older version' do |
| 1815 | 1823 | article = profile.articles.create(:name => 'first version') |
| 1816 | 1824 | article.name = 'second version'; article.save | ... | ... |
test/functional/content_viewer_controller_test.rb
| ... | ... | @@ -1252,9 +1252,11 @@ class ContentViewerControllerTest < ActionController::TestCase |
| 1252 | 1252 | should 'expire article actions button if any plugins says so' do |
| 1253 | 1253 | class Plugin1 < Noosfero::Plugin |
| 1254 | 1254 | def content_expire_edit(content); 'This button is expired.'; end |
| 1255 | + def content_expire_clone(content); 'This button is expired.'; end | |
| 1255 | 1256 | end |
| 1256 | 1257 | class Plugin2 < Noosfero::Plugin |
| 1257 | 1258 | def content_expire_edit(content); nil; end |
| 1259 | + def content_expire_clone(content); nil; end | |
| 1258 | 1260 | end |
| 1259 | 1261 | Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name]) |
| 1260 | 1262 | ... | ... |
test/functional/tasks_controller_test.rb
| ... | ... | @@ -80,6 +80,12 @@ class TasksControllerTest < ActionController::TestCase |
| 80 | 80 | assert_kind_of Array, assigns(:tasks) |
| 81 | 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 | 89 | should 'list processed tasks without spam' do |
| 84 | 90 | requestor = fast_create(Person) |
| 85 | 91 | task_spam = create(Task, :status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => true) |
| ... | ... | @@ -428,13 +434,13 @@ class TasksControllerTest < ActionController::TestCase |
| 428 | 434 | t2 = CleanHouse.create!(:requestor => requestor, :target => profile) |
| 429 | 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 | 439 | assert_includes assigns(:tasks), t1 |
| 434 | 440 | assert_not_includes assigns(:tasks), t2 |
| 435 | 441 | assert_not_includes assigns(:tasks), t3 |
| 436 | 442 | |
| 437 | - post :index | |
| 443 | + get :index | |
| 438 | 444 | |
| 439 | 445 | assert_includes assigns(:tasks), t1 |
| 440 | 446 | assert_includes assigns(:tasks), t2 |
| ... | ... | @@ -458,4 +464,82 @@ class TasksControllerTest < ActionController::TestCase |
| 458 | 464 | post :index, :page => 2 |
| 459 | 465 | assert_equal [t4], assigns(:tasks) |
| 460 | 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 | 545 | end | ... | ... |
test/unit/application_helper_test.rb
| ... | ... | @@ -1002,6 +1002,21 @@ class ApplicationHelperTest < ActionView::TestCase |
| 1002 | 1002 | assert_equal file, from_theme_include('atheme', 'afile')[:file] # exists? = true |
| 1003 | 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 | 1020 | protected |
| 1006 | 1021 | include NoosferoTestHelper |
| 1007 | 1022 | ... | ... |
test/unit/article_test.rb
| ... | ... | @@ -2040,6 +2040,17 @@ class ArticleTest < ActiveSupport::TestCase |
| 2040 | 2040 | assert_equal [], Article.display_filter(user, nil) |
| 2041 | 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 | 2054 | should 'display_filter do not show community private content to non members passing nil as profile parameter' do |
| 2044 | 2055 | user = create_user('someuser').person |
| 2045 | 2056 | p = fast_create(Community) |
| ... | ... | @@ -2049,6 +2060,16 @@ class ArticleTest < ActiveSupport::TestCase |
| 2049 | 2060 | assert_equal [], Article.display_filter(user, nil) |
| 2050 | 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 | 2073 | should 'display_filter show community public content of private community for user members' do |
| 2053 | 2074 | user = create_user('someuser').person |
| 2054 | 2075 | p = fast_create(Community, :public_profile => false) | ... | ... |
test/unit/environment_test.rb
| ... | ... | @@ -524,7 +524,7 @@ class EnvironmentTest < ActiveSupport::TestCase |
| 524 | 524 | p1= fast_create(Person, :is_template => true, :environment_id => e.id) |
| 525 | 525 | p2 = fast_create(Person, :environment_id => e.id) |
| 526 | 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 | 528 | end |
| 529 | 529 | |
| 530 | 530 | should 'person_templates return an empty array if there is no templates of person' do |
| ... | ... | @@ -532,7 +532,7 @@ class EnvironmentTest < ActiveSupport::TestCase |
| 532 | 532 | |
| 533 | 533 | fast_create(Person, :environment_id => e.id) |
| 534 | 534 | fast_create(Person, :environment_id => e.id) |
| 535 | - assert_equivalent [], e.person_templates | |
| 535 | + assert_equivalent [], e.person_templates | |
| 536 | 536 | end |
| 537 | 537 | |
| 538 | 538 | should 'person_default_template return the template defined as default' do |
| ... | ... | @@ -585,7 +585,7 @@ class EnvironmentTest < ActiveSupport::TestCase |
| 585 | 585 | c1= fast_create(Community, :is_template => true, :environment_id => e.id) |
| 586 | 586 | c2 = fast_create(Community, :environment_id => e.id) |
| 587 | 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 | 589 | end |
| 590 | 590 | |
| 591 | 591 | should 'community_templates return an empty array if there is no templates of community' do |
| ... | ... | @@ -646,7 +646,7 @@ class EnvironmentTest < ActiveSupport::TestCase |
| 646 | 646 | e1= fast_create(Enterprise, :is_template => true, :environment_id => env.id) |
| 647 | 647 | e2 = fast_create(Enterprise, :environment_id => env.id) |
| 648 | 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 | 650 | end |
| 651 | 651 | |
| 652 | 652 | should 'enterprise_templates return an empty array if there is no templates of enterprise' do |
| ... | ... | @@ -654,7 +654,7 @@ class EnvironmentTest < ActiveSupport::TestCase |
| 654 | 654 | |
| 655 | 655 | fast_create(Enterprise, :environment_id => env.id) |
| 656 | 656 | fast_create(Enterprise, :environment_id => env.id) |
| 657 | - assert_equivalent [], env.enterprise_templates | |
| 657 | + assert_equivalent [], env.enterprise_templates | |
| 658 | 658 | end |
| 659 | 659 | |
| 660 | 660 | should 'enterprise_default_template return the template defined as default' do |
| ... | ... | @@ -1428,6 +1428,36 @@ class EnvironmentTest < ActiveSupport::TestCase |
| 1428 | 1428 | assert !environment.plugin_enabled?(Plugin) |
| 1429 | 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 | 1461 | should 'have production costs' do |
| 1432 | 1462 | assert_respond_to Environment.default, :production_costs |
| 1433 | 1463 | end | ... | ... |
test/unit/forum_test.rb
| ... | ... | @@ -174,4 +174,70 @@ class ForumTest < ActiveSupport::TestCase |
| 174 | 174 | assert_equal true, Forum.find(forum.id).agrees_with_terms?(person) |
| 175 | 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 | 243 | end | ... | ... |
test/unit/task_test.rb
| ... | ... | @@ -444,6 +444,14 @@ class TaskTest < ActiveSupport::TestCase |
| 444 | 444 | assert t1.ham? |
| 445 | 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 | 455 | protected |
| 448 | 456 | |
| 449 | 457 | def sample_user | ... | ... |
util/chat/apache/xmpp.conf