[Git][noosfero/noosfero][stable-1.2] 11 commits: Require login for all pages when environment is private
Antonio Terceiro
gitlab at gitlab.com
Fri Oct 2 17:15:33 BRT 2015
Antonio Terceiro pushed to branch stable-1.2 at Noosfero / noosfero
Commits:
1fe26385 by Larissa Reis at 2015-10-02T14:47:43Z
Require login for all pages when environment is private
This fixes a bug in which some pages (eg. a profile page) were visible
to unlogged users even if the environment has enabled "show content
only to members".
The problem happened because some controllers use `before_filter
:login_required` so they can apply it to some specific methods,
effectively overriding the one set in `application_controller`. That
before filter set in `application_controller` is the one used to make
the environment private when that feature is enabled, so when a
controller overrides it, some methods are not required login even when
the environment is private. So I fixed the problem by using a
different `before_filter` to take care specifically of private
environments.
Now every page requires login when an environment is private, except
the pages in `account_controller` necessary for login and signup.
(cherry picked from commit 48f51755bc02fae929e8d62d7bf86aee8b2811ec)
- - - - -
0d4b92f1 by Arthur Del Esposte at 2015-10-02T14:48:00Z
Scoping environment_notification plugin's javascript functions
(cherry picked from commit 603ea7017cf4cfc41644976b694f1280d69db9e5)
- - - - -
4e78b4c4 by Arthur Del Esposte at 2015-10-02T14:48:00Z
Add environment_notification plugin's more specify classes name
(cherry picked from commit c838edfa0058fe706784fa5085f9570984b68373)
- - - - -
2ac1bd9a by Braulio Bhavamitra at 2015-10-02T14:48:00Z
environment_notifications: fix broken tests
The plugin still has some structural problems
(cherry picked from commit 1414c2864df4e2d73c8b01f1b7c0c437ee2279b5)
- - - - -
1265e3be by Antonio Terceiro at 2015-10-02T14:48:01Z
backup/restore: default `host` to localhost
(cherry picked from commit d89434517dff21b4fd682bd926d6279fe01cc77e)
- - - - -
f2d6b33e by Antonio Terceiro at 2015-10-02T14:48:01Z
backup/restore: omit host if absent from config
(cherry picked from commit b5fff02ebdc764821500a136bc838b5cf31513d4)
- - - - -
e5baee61 by Larissa Reis at 2015-10-02T14:48:01Z
newsletter: imported CSV can use semicolon as delimiter
Windows systems with pt_br have problem in exporting CSV with comma as
delimiter through Excel, so to improve compatibility I decided to loop
through delimiter types when parsing and importing recipients.
Since I try different types of delimiter and stick with the first one
that gives me a valid content, it's always possible (albeit unlikely)
the file can be parsed incorrectly.
(cherry picked from commit df2897b2afec602fbe3f9c6892589f5ef11a52af)
- - - - -
596f9f48 by Daniela Feitosa at 2015-10-02T14:51:09Z
Improve pagination default
Also %s/will_paginate/pagination_links to use the default on forum, blog
and cms
Signed-off-by: Tallys Martins <tallysmartins at yahoo.com.br>
Signed-off-by: Melissa Wen <melissa at colivre.coop.br>
Signed-off-by: Antonio Terceiro <terceiro at colivre.coop.br>
(cherry picked from commit 64b98a9ea0ee1ed54adbf3456730101bca04ef43;
required porting the CSS changes back to the huge application.css
present in 1.2)
- - - - -
ac24e531 by Antonio Terceiro at 2015-10-02T16:18:00Z
search: resolve translations at runtime
If you resolve the translation calls -- _("...") -- at load time they
will not be translated based on the current user's language.
SearchHelper::FILTERS_TRANSLATIONS was not used anywhere, so it got
removed.
(cherry picked from commit aed63bb6b431105d5ff2081b14f4c2d5d9dc0051)
- - - - -
3ac86dbb by Larissa Reis at 2015-10-02T16:40:23Z
Uses modal instead of deprecated colorbox
(cherry picked from commit edcc13059b31341eb78e58079ac2322f1142f206)
- - - - -
ab717cf3 by Arthur Del Esposte at 2015-10-02T16:42:07Z
Handle privacity in WorkAssignment submissions properly
(cherry picked from commit bb9580ee00c20caa533a40d40eae57bd35e9332d)
- - - - -
30 changed files:
- app/controllers/application_controller.rb
- app/controllers/public/account_controller.rb
- app/controllers/public/search_controller.rb
- app/helpers/application_helper.rb
- app/helpers/blog_helper.rb
- app/helpers/cms_helper.rb
- app/helpers/forum_helper.rb
- app/helpers/search_helper.rb
- app/views/cms/_published_media_items.html.erb
- app/views/content_viewer/_article_toolbar.html.erb
- lib/tasks/backup.rake
- plugins/environment_notification/controllers/environment_notification_plugin_admin_controller.rb
- plugins/environment_notification/controllers/public/environment_notification_plugin_public_controller.rb
- plugins/environment_notification/lib/environment_notification_helper.rb
- plugins/environment_notification/lib/environment_notification_plugin.rb
- − plugins/environment_notification/lib/ext/application_controller.rb
- plugins/environment_notification/public/environment_notification_plugin.js
- plugins/environment_notification/style.css
- plugins/environment_notification/test/functional/account_controller_test.rb
- plugins/environment_notification/views/environment_notification_plugin_admin/_form.html.erb
- plugins/environment_notification/views/environment_notification_plugin_admin/index.html.erb
- plugins/environment_notification/views/environment_notification_plugin_admin/show_notification.html.erb
- plugins/environment_notification/views/environment_notification_plugin_public/notifications_with_popup.html.erb
- plugins/newsletter/lib/newsletter_plugin/newsletter.rb
- plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb
- plugins/work_assignment/lib/ext/article.rb
- plugins/work_assignment/test/functional/cms_controller_test.rb
- public/stylesheets/application.css
- test/functional/account_controller_test.rb
- test/functional/profile_controller_test.rb
Changes:
=====================================
app/controllers/application_controller.rb
=====================================
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -7,10 +7,15 @@ class ApplicationController < ActionController::Base
before_filter :detect_stuff_by_domain
before_filter :init_noosfero_plugins
before_filter :allow_cross_domain_access
- before_filter :login_required, :if => :private_environment?
+ before_filter :require_login_for_environment, :if => :private_environment?
+
before_filter :verify_members_whitelist, :if => [:private_environment?, :user]
before_filter :redirect_to_current_user
+ def require_login_for_environment
+ login_required
+ end
+
def verify_members_whitelist
render_access_denied unless user.is_admin? || environment.in_whitelist?(user)
end
=====================================
app/controllers/public/account_controller.rb
=====================================
--- a/app/controllers/public/account_controller.rb
+++ b/app/controllers/public/account_controller.rb
@@ -2,7 +2,7 @@ class AccountController < ApplicationController
no_design_blocks
- before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise, :change_password]
+ before_filter :login_required, :require_login_for_environment, :only => [:activation_question, :accept_terms, :activate_enterprise, :change_password]
before_filter :redirect_if_logged_in, :only => [:login, :signup]
before_filter :protect_from_bots, :only => :signup
=====================================
app/controllers/public/search_controller.rb
=====================================
--- a/app/controllers/public/search_controller.rb
+++ b/app/controllers/public/search_controller.rb
@@ -178,22 +178,24 @@ class SearchController < PublicController
end
end
- AVAILABLE_SEARCHES = ActiveSupport::OrderedHash[
- :articles, _('Contents'),
- :people, _('People'),
- :communities, _('Communities'),
- :enterprises, _('Enterprises'),
- :products, _('Products and Services'),
- :events, _('Events'),
- ]
+ def available_searches
+ @available_searches ||= ActiveSupport::OrderedHash[
+ :articles, _('Contents'),
+ :people, _('People'),
+ :communities, _('Communities'),
+ :enterprises, _('Enterprises'),
+ :products, _('Products and Services'),
+ :events, _('Events'),
+ ]
+ end
def load_search_assets
- if AVAILABLE_SEARCHES.keys.include?(params[:action].to_sym) && environment.enabled?("disable_asset_#{params[:action]}")
+ if available_searches.keys.include?(params[:action].to_sym) && environment.enabled?("disable_asset_#{params[:action]}")
render_not_found
return
end
- @enabled_searches = AVAILABLE_SEARCHES.select {|key, name| environment.disabled?("disable_asset_#{key}") }
+ @enabled_searches = available_searches.select {|key, name| environment.disabled?("disable_asset_#{key}") }
@searching = {}
@titles = {}
@enabled_searches.each do |key, name|
@@ -205,7 +207,7 @@ class SearchController < PublicController
def load_order
@order = 'more_recent'
- if AVAILABLE_SEARCHES.keys.include?(@asset.to_sym)
+ if available_searches.keys.include?(@asset.to_sym)
available_orders = asset_class(@asset)::SEARCH_FILTERS[:order]
@order = params[:order] if available_orders.include?(params[:order])
end
=====================================
app/helpers/application_helper.rb
=====================================
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1131,7 +1131,7 @@ module ApplicationHelper
alias :browse_communities_menu :search_communities_menu
def pagination_links(collection, options={})
- options = {:previous_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options)
+ options = {:previous_label => content_tag(:span, '« ', :class => 'previous-arrow') + _('Previous'), :next_label => _('Next') + content_tag(:span, ' »', :class => 'next-arrow'), :inner_window => 1, :outer_window => 0 }.merge(options)
will_paginate(collection, options)
end
@@ -1316,7 +1316,12 @@ module ApplicationHelper
options[:class] = (options[:class] || '') + ' disabled'
content_tag('a', ' '+content_tag('span', content), options)
else
- link_to content, url, options
+ if options[:modal]
+ options.delete(:modal)
+ modal_link_to content, url, options
+ else
+ link_to content, url, options
+ end
end
end
=====================================
app/helpers/blog_helper.rb
=====================================
--- a/app/helpers/blog_helper.rb
+++ b/app/helpers/blog_helper.rb
@@ -24,10 +24,8 @@ module BlogHelper
end
def list_posts(articles, conf = { format: 'full', paginate: true })
- pagination = will_paginate(articles, {
+ pagination = pagination_links(articles, {
:param_name => 'npage',
- :previous_label => _('« Newer posts'),
- :next_label => _('Older posts »'),
:params => {:action=>"view_page",
:page=>articles.first.parent.path.split('/'),
:controller=>"content_viewer"}
=====================================
app/helpers/cms_helper.rb
=====================================
--- a/app/helpers/cms_helper.rb
+++ b/app/helpers/cms_helper.rb
@@ -9,11 +9,6 @@ module CmsHelper
mime_type.gsub('/', '_').gsub('-', '')
end
- def pagination_links(collection, options={})
- options = {:previous_label => '« ', :next_label => ' »', :page_links => false}.merge(options)
- will_paginate(collection, options)
- end
-
attr_reader :environment
def options_for_article(article, tokenized_children=nil)
@@ -35,7 +30,7 @@ module CmsHelper
end
def display_spread_button(article)
- expirable_button article, :spread, _('Spread this'), {:action => 'publish', :id => article.id}, {:class => 'colorbox'}
+ expirable_button article, :spread, _('Spread this'), {:action => 'publish', :id => article.id}, {:class => 'colorbox', :modal => true}
end
def display_delete_button(article)
=====================================
app/helpers/forum_helper.rb
=====================================
--- a/app/helpers/forum_helper.rb
+++ b/app/helpers/forum_helper.rb
@@ -10,10 +10,8 @@ module ForumHelper
end
def list_forum_posts(articles)
- pagination = will_paginate(articles, {
- :param_name => 'npage',
- :previous_label => _('« Newer posts'),
- :next_label => _('Older posts »')
+ pagination = pagination_links(articles, {
+ :param_name => 'npage'
})
content = [content_tag('tr',
content_tag('th', _('Discussion topic')) +
=====================================
app/helpers/search_helper.rb
=====================================
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -5,24 +5,21 @@ module SearchHelper
BLOCKS_SEARCH_LIMIT = 24
MULTIPLE_SEARCH_LIMIT = 8
- FILTERS_TRANSLATIONS = {
- :order => _('Order'),
- :display => _('Display')
- }
-
- FILTERS_OPTIONS_TRANSLATION = {
- :order => {
- 'more_popular' => _('More popular'),
- 'more_active' => _('More active'),
- 'more_recent' => _('More recent'),
- 'more_comments' => _('More comments')
- },
- :display => {
- 'map' => _('Map'),
- 'full' => _('Full'),
- 'compact' => _('Compact')
+ def filters_options_translation
+ @filters_options_translation ||= {
+ :order => {
+ 'more_popular' => _('More popular'),
+ 'more_active' => _('More active'),
+ 'more_recent' => _('More recent'),
+ 'more_comments' => _('More comments')
+ },
+ :display => {
+ 'map' => _('Map'),
+ 'full' => _('Full'),
+ 'compact' => _('Compact')
+ }
}
- }
+ end
COMMON_PROFILE_LIST_BLOCK = [
:enterprises,
@@ -100,7 +97,7 @@ module SearchHelper
if options.size <= 1
return
else
- options = options.map {|option| [FILTERS_OPTIONS_TRANSLATION[name][option], option]}
+ options = options.map {|option| [filters_options_translation[name][option], option]}
options = options_for_select(options, :selected => (params[name] || default))
select_tag(name, options)
end
=====================================
app/views/cms/_published_media_items.html.erb
=====================================
--- a/app/views/cms/_published_media_items.html.erb
+++ b/app/views/cms/_published_media_items.html.erb
@@ -4,7 +4,7 @@
<div class='section-title'>
<h3><%= header %></h3>
<% if @recent_files[key].total_pages > 1 %>
- <%= link_to(_('View all'), {:controller => 'cms', :action => 'view_all_media', :profile => profile.identifier, :key => key}, :class => 'view-all colorbox', 'data-key' => key) %>
+ <%= modal_link_to(_('View all'), {:controller => 'cms', :action => 'view_all_media', :profile => profile.identifier, :key => key}, { :class => 'view-all', 'data-key' => key }) %>
<% end %>
</div>
<%= render :partial => "cms/media_panel/list_published_media_items", :locals => { key: key, show_pagination_links: false } %>
=====================================
app/views/content_viewer/_article_toolbar.html.erb
=====================================
--- a/app/views/content_viewer/_article_toolbar.html.erb
+++ b/app/views/content_viewer/_article_toolbar.html.erb
@@ -18,7 +18,7 @@
<% if @page.allow_spread?(user) && !remove_content_button(:spread, @page) %>
<% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish', :id => @page.id }) %>
- <%= expirable_button @page, :spread, content_tag( 'span', _('Spread this') ), url, {:class => 'colorbox'} if url %>
+ <%= expirable_button @page, :spread, content_tag( 'span', _('Spread this') ), url, {:class => 'colorbox', :modal => true} if url %>
<% end %>
<% if !@page.gallery? && (@page.allow_create?(user) || (@page.parent && @page.parent.allow_create?(user))) %>
=====================================
lib/tasks/backup.rake
=====================================
--- a/lib/tasks/backup.rake
+++ b/lib/tasks/backup.rake
@@ -26,7 +26,8 @@ task :backup => :check_backup_support do
database = $config['production']['database']
host = $config['production']['host']
- sh "pg_dump -h #{host} #{database} > #{dump}"
+ host = host && "-h #{host}" || ""
+ sh "pg_dump #{host} #{database} > #{dump}"
sh 'tar', 'chaf', backup_file, dump, *dirs
rm_f dump
@@ -83,14 +84,15 @@ task :restore => :check_backup_support do
database = $config['production']['database']
username = $config['production']['username']
host = $config['production']['host']
+ host = host && "-h #{host}" || ""
puts "WARNING: backups should be restored to an empty database, otherwise"
puts "data from the backup may not be loaded properly."
puts
puts 'You can remove the existing database and create a new one with:'
puts
- puts "$ sudo -u postgres dropdb -h #{host} #{database}"
- puts "$ sudo -u postgres createdb -h #{host} #{database} --owner #{username}"
+ puts "$ sudo -u postgres dropdb #{host} #{database}"
+ puts "$ sudo -u postgres createdb #{host} #{database} --owner #{username}"
puts
print "Are you sure you want to continue (y/N)? "
response = $stdin.gets.strip
=====================================
plugins/environment_notification/controllers/environment_notification_plugin_admin_controller.rb
=====================================
--- a/plugins/environment_notification/controllers/environment_notification_plugin_admin_controller.rb
+++ b/plugins/environment_notification/controllers/environment_notification_plugin_admin_controller.rb
@@ -1,5 +1,10 @@
class EnvironmentNotificationPluginAdminController < AdminController
+
+ helper EnvironmentNotificationHelper
+ include EnvironmentNotificationHelper
+
before_filter :admin_required, :except => [:close_notification, :hide_notification]
+
def index
@notifications = environment.environment_notifications.order('updated_at DESC')
end
=====================================
plugins/environment_notification/controllers/public/environment_notification_plugin_public_controller.rb
=====================================
--- a/plugins/environment_notification/controllers/public/environment_notification_plugin_public_controller.rb
+++ b/plugins/environment_notification/controllers/public/environment_notification_plugin_public_controller.rb
@@ -1,4 +1,7 @@
class EnvironmentNotificationPluginPublicController < PublicController
+
+ helper EnvironmentNotificationHelper
+
def notifications_with_popup
@hide_notifications = hide_notifications
if params[:previous_path]
@@ -7,4 +10,5 @@ class EnvironmentNotificationPluginPublicController < PublicController
@previous_path = nil
end
end
+
end
=====================================
plugins/environment_notification/lib/environment_notification_helper.rb
=====================================
--- a/plugins/environment_notification/lib/environment_notification_helper.rb
+++ b/plugins/environment_notification/lib/environment_notification_helper.rb
@@ -1,4 +1,12 @@
module EnvironmentNotificationHelper
+
+ def hide_notifications
+ invalid_id = -1
+ hide_notifications_ids = [invalid_id]
+ hide_notifications_ids = JSON.parse(cookies[:hide_notifications]) unless cookies[:hide_notifications].blank?
+ hide_notifications_ids
+ end
+
def self.substitute_variables(message, user)
if user
message = message.gsub("%{email}", user.person.email).gsub("%{name}", user.person.name)
@@ -6,4 +14,5 @@ module EnvironmentNotificationHelper
message
end
-end
\ No newline at end of file
+
+end
=====================================
plugins/environment_notification/lib/environment_notification_plugin.rb
=====================================
--- a/plugins/environment_notification/lib/environment_notification_plugin.rb
+++ b/plugins/environment_notification/lib/environment_notification_plugin.rb
@@ -1,8 +1,5 @@
class EnvironmentNotificationPlugin < Noosfero::Plugin
- include ActionView::Helpers::JavaScriptHelper
- include ActionView::Helpers::TagHelper
-
def self.plugin_name
"Environment Notifications Plugin"
end
@@ -22,7 +19,10 @@ class EnvironmentNotificationPlugin < Noosfero::Plugin
end
def body_beginning
- expanded_template('environment_notification_plugin_admin/show_notification.html.erb')
+ lambda do
+ extend EnvironmentNotificationHelper
+ render template: 'environment_notification_plugin_admin/show_notification'
+ end
end
def admin_panel_links
=====================================
plugins/environment_notification/lib/ext/application_controller.rb deleted
=====================================
--- a/plugins/environment_notification/lib/ext/application_controller.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require_dependency 'application_controller'
-
-class ApplicationController
- def hide_notifications
- invalid_id = -1
- hide_notifications_ids = [-1]
- hide_notifications_ids = JSON.parse(cookies[:hide_notifications]) unless cookies[:hide_notifications].blank?
- hide_notifications_ids
- end
-end
=====================================
plugins/environment_notification/public/environment_notification_plugin.js
=====================================
--- a/plugins/environment_notification/public/environment_notification_plugin.js
+++ b/plugins/environment_notification/public/environment_notification_plugin.js
@@ -1,94 +1,98 @@
(function($) {
"use strict";
- function notificationBar() {
- var completeMessage = $(".notification-bar").remove();
- $("#content-inner").before(completeMessage);
- }
-
- function closeNotification(){
- var notification = $(this).parent();
- var id = notification.attr("data-notification");
-
- $.ajax({
- url: noosfero_root()+"/admin/plugin/environment_notification/close_notification",
- type: "POST",
- data: {notification_id: id},
- success: function(response) {
- notification.fadeOut();
- }
- });
- }
-
- function hideNotification(){
- var notification = $(this).parent();
- var id = notification.attr("data-notification");
-
- $.ajax({
- url: noosfero_root()+"/admin/plugin/environment_notification/hide_notification",
- type: "POST",
- data: {notification_id: id},
- success: function(response) {
- notification.fadeOut();
- }
- });
- }
+ var environment_notification_plugin = {
- function hideUserNotification(){
- var ids = $.cookie('hide_notifications');
- if(ids === null) {
- return null;
- }
- if(ids.startsWith('[') && ids.endsWith(']')){
- ids = ids.substring(1, ids.length - 1);
- ids = ids.split(",");
+ notificationBar: function() {
+ var completeMessage = $(".environment-notification-plugin-notification-bar").remove();
+ $("#content-inner").before(completeMessage);
+ },
- for(var i = 0; i < ids.length; i++) {
- $('[data-notification="' + ids[i] + '"]').fadeOut();
- }
- }
- }
-
- function mceRestrict() {
- tinyMCE.init({
- menubar : false,
- selector: "textarea",
- plugins: [
- "autolink link"
- ],
- toolbar: "bold italic underline | link"
- });
- }
-
- function showPopup() {
- if($('.action-home-index').length > 0) {
- jQuery(function($){
- $.colorbox({href: noosfero_root()+'/plugin/environment_notification/public/notifications_with_popup?previous_path=home'});
+ closeNotification: function(){
+ var notification = $(this).parent();
+ var id = notification.attr("data-notification");
+
+ $.ajax({
+ url: noosfero_root()+"/admin/plugin/environment_notification/close_notification",
+ type: "POST",
+ data: {notification_id: id},
+ success: function(response) {
+ notification.fadeOut();
+ }
});
- }
- else {
- jQuery(function($){
- $.colorbox({href: noosfero_root()+'/plugin/environment_notification/public/notifications_with_popup'});
+ },
+
+ hideNotification: function(){
+ var notification = $(this).parent();
+ var id = notification.attr("data-notification");
+
+ $.ajax({
+ url: noosfero_root()+"/admin/plugin/environment_notification/hide_notification",
+ type: "POST",
+ data: {notification_id: id},
+ success: function(response) {
+ notification.fadeOut();
+ }
});
- }
- }
+ },
+
+ hideUserNotification: function(){
+ var ids = $.cookie('hide_notifications');
+ if(ids === null) {
+ return null;
+ }
+
+ if(ids.startsWith('[') && ids.endsWith(']')){
+ ids = ids.substring(1, ids.length - 1);
+ ids = ids.split(",");
+
+ for(var i = 0; i < ids.length; i++) {
+ $('[data-notification="' + ids[i] + '"]').fadeOut();
+ }
+ }
+ },
+
+ mceRestrict: function() {
+ tinyMCE.init({
+ menubar : false,
+ selector: "textarea",
+ plugins: [
+ "autolink link"
+ ],
+ toolbar: "bold italic underline | link"
+ });
+ },
+
+ showPopup: function() {
+ if($('.action-home-index').length > 0) {
+ jQuery(function($){
+ $.colorbox({href: noosfero_root()+'/plugin/environment_notification/public/notifications_with_popup?previous_path=home'});
+ });
+ }
+ else {
+ jQuery(function($){
+ $.colorbox({href: noosfero_root()+'/plugin/environment_notification/public/notifications_with_popup'});
+ });
+ }
+ },
+ };
$(document).ready(function(){
- notificationBar();
- $(".notification-close").on("click", closeNotification);
- $(".notification-hide").on("click", hideNotification);
+ environment_notification_plugin.notificationBar();
+ $(".environment-notification-plugin-notification-bar .notification-close").on("click", environment_notification_plugin.closeNotification);
+ $(".environment-notification-plugin-notification-bar .notification-hide").on("click", environment_notification_plugin.hideNotification);
if($('.environment-notification-plugin-message').length > 0){
- mceRestrict();
+ environment_notification_plugin.mceRestrict();
}
- if($('.notification-bar').length > 0){
- hideUserNotification();
+ if($('.environment-notification-plugin-notification-bar').length > 0){
+ environment_notification_plugin.hideUserNotification();
}
- if($('[notification-display-popup="true"]').length > 0){
- showPopup();
+ if($('.environment-notification-plugin-notification-bar [notification-display-popup="true"]').length > 0){
+ environment_notification_plugin.showPopup();
}
});
=====================================
plugins/environment_notification/style.css
=====================================
--- a/plugins/environment_notification/style.css
+++ b/plugins/environment_notification/style.css
@@ -1,27 +1,33 @@
-.notification-bar {
+.environment-notification-plugin-notification-bar{
display: block;
}
-.notification:hover {
+.environment-notification-plugin-notification-bar .notification:hover,
+.environment-notification-plugin-notification-notification-modal .notification:hover{
opacity: 0.8;
}
-#notification-manager {
+#environment-notification-plugin-notification-manager{
overflow: auto;
}
-.notification .notification-close {
+.environment-notification-plugin-notification-bar .notification .notification-close,
+.environment-notification-plugin-notification-notification-modal .notification .notification-close{
background: url(public/images/close.png) no-repeat;
background-position: center;
width: 20px;
height: 20px;
}
-.warningnotification,
-.informationnotification,
-.successnotification,
-.dangernotification,
-.adminnotification {
+.environment-notification-plugin-notification-bar .warningnotification,
+.environment-notification-plugin-notification-bar .informationnotification,
+.environment-notification-plugin-notification-bar .successnotification,
+.environment-notification-plugin-notification-bar .dangernotification,
+.environment-notification-plugin-notification-bar .adminnotification,
+.environment-notification-plugin-notification-notification-modal .warningnotification,
+.environment-notification-plugin-notification-notification-modal .informationnotification,
+.environment-notification-plugin-notification-notification-modal .successnotification,
+.environment-notification-plugin-notification-notification-modal .dangernotification{
margin-bottom: 10px;
padding: 7px 10px;
border-radius: 5px;
@@ -31,87 +37,101 @@
overflow: auto;
}
-.warningnotification p,
-.informationnotification p,
-.successnotification p,
-.dangernotification p,
-.adminnotification p {
+.environment-notification-plugin-notification-bar .warningnotification p,
+.environment-notification-plugin-notification-bar .informationnotification p,
+.environment-notification-plugin-notification-bar .successnotification p,
+.environment-notification-plugin-notification-bar .dangernotification p,
+.environment-notification-plugin-notification-bar .adminnotification p,
+.environment-notification-plugin-notification-notification-modal .warningnotification p,
+.environment-notification-plugin-notification-notification-modal .informationnotification p,
+.environment-notification-plugin-notification-notification-modal .successnotification p,
+.environment-notification-plugin-notification-notification-modal .dangernotification p{
margin: 0px;
}
-.warningnotification {
+.environment-notification-plugin-notification-bar .warningnotification,
+.environment-notification-plugin-notification-notification-modal .warningnotification{
background: #fcf8e3;
border: 1px solid #faebcc;
color: #8a6d3b;
}
-.warningnotification p a{
+.environment-notification-plugin-notification-bar .warningnotification p a,
+.environment-notification-plugin-notification-notification-modal .warningnotification p a{
font-weight: bold;
color: #8a6d3b;
}
-.informationnotification {
+.environment-notification-plugin-notification-bar .informationnotification,
+.environment-notification-plugin-notification-notification-modal .informationnotification{
background: #d9edf7;
border: 1px solid #bce8f1;
color: #31708f;
}
-.informationnotification p a{
+.environment-notification-plugin-notification-bar .informationnotification p a,
+.environment-notification-plugin-notification-notification-modal .informationnotification p a{
font-weight: bold;
color: #31708f;
}
-.successnotification {
+.environment-notification-plugin-notification-bar .successnotification,
+.environment-notification-plugin-notification-notification-modal .successnotification{
background: #dff0d8;
border: 1px solid #d6e9c6;
color: #3c763d;
}
-.successnotification p a{
+.environment-notification-plugin-notification-bar .successnotification p a
+.environment-notification-plugin-notification-notification-modal .successnotification p a{
font-weight: bold;
color: #3c763d;
}
-.dangernotification {
+.environment-notification-plugin-notification-bar .dangernotification,
+.environment-notification-plugin-notification-notification-modal .dangernotification{
background: #f2dede;
border: 1px solid #ebccd1;
color: #a94442;
}
-.dangernotification p a{
+.environment-notification-plugin-notification-bar .dangernotification p a,
+.environment-notification-plugin-notification-notification-modal .dangernotification p a{
font-weight: bold;
color: #a94442;
}
-.adminnotification {
+.environment-notification-plugin-notification-bar .adminnotification,
+.environment-notification-plugin-notification-notification-modal .adminnotification{
background: #9a959a;
border: 1px solid #9a959a;
}
-.adminnotification p a{
+.environment-notification-plugin-notification-bar .adminnotification p a,
+.environment-notification-plugin-notification-notification-modal .adminnotification p a{
font-weight: bold;
color: white;
}
-a.button.icon-deactivate {
+#environment-notification-plugin-notification-manager a.button.icon-deactivate{
background: url(public/images/hide.png) no-repeat;
background-position: center;
}
-a.button.icon-activate {
+#environment-notification-plugin-notification-manager a.button.icon-activate{
background: url(public/images/show.png) no-repeat;
background-position: center;
}
-.notification-line {
+#environment-notification-plugin-notification-manager .notification-line{
display: inline;
padding-top: 10px;
vertical-align: middle;
border-bottom: 1px solid #ccc;
}
-.notification-title-bar {
+#environment-notification-plugin-notification-manager .notification-title-bar{
float: left;
width: 100%;
font-style: 14px;
@@ -120,24 +140,24 @@ a.button.icon-activate {
padding: 9px 0;
}
-.notification-title {
+#environment-notification-plugin-notification-manager .notification-title{
width: 80%;
float: left;
text-align: center;
}
-.notification-modal .notification-with-title {
+.environment-notification-plugin-notification-notification-modal .notification-with-title{
margin-bottom: 0px;
}
-.notification-modal .notification .notification-title {
+.environment-notification-plugin-notification-notification-modal .notification .notification-title{
width: 100%;
float: left;
font-weight: bold;
text-align: left;
}
-.notification-modal .notification-with-title-message {
+.environment-notification-plugin-notification-notification-modal .notification-with-title-message{
width: 100%;
float: left;
border-radius: 3px;
@@ -147,59 +167,60 @@ a.button.icon-activate {
overflow: auto;
}
-.notification-modal .notification-with-title-message p{
+.environment-notification-plugin-notification-notification-modal .notification-with-title-message p{
padding: 0px 7px;
}
-.notification-modal .notification-with-title-message p a{
+.environment-notification-plugin-notification-notification-modal .notification-with-title-message p a{
color: black;
font-weight: bold;
}
-.action-title {
+#environment-notification-plugin-notification-manager .action-title{
width: 20%;
float: left;
text-align: center;
}
-.notification-action {
+#environment-notification-plugin-notification-manager .notification-action{
width: 18%;
float: left;
height: 30px;
padding-top: 9px;
}
-.main-bar .button,
-.notification-action .button {
+#environment-notification-plugin-notification-manager .main-bar .button,
+#environment-notification-plugin-notification-manager .notification-action .button{
border-radius: 3px;
}
-.notification-message {
+#environment-notification-plugin-notification-manager .notification-message{
width: 82%;
float: left;
}
-.new-notification {
+#environment-notification-plugin-notification-manager .new-notification{
float: right;
width: auto;
}
-.back-button {
+#environment-notification-plugin-notification-manager .back-button{
float: left;
}
-.main-bar {
+#environment-notification-plugin-notification-manager .main-bar{
display: inline;
width: 100%;
}
-.notification-bar .notification .notification-message {
+.environment-notification-plugin-notification-bar .notification .notification-message,
+.environment-notification-plugin-notification-notification-modal .notification .notification-message{
width: 90%;
float: left;
}
-.notification-bar .notification .notification-close {
+.environment-notification-plugin-notification-bar .notification .notification-close{
background: url(public/images/redclose.png) no-repeat;
background-position: center;
width: 20px;
@@ -208,7 +229,7 @@ a.button.icon-activate {
cursor: pointer;
}
-.notification-bar .notification .notification-hide {
+.environment-notification-plugin-notification-bar .notification .notification-hide{
background: url(public/images/greenhide.png) no-repeat;
background-position: center;
width: 20px;
@@ -217,8 +238,13 @@ a.button.icon-activate {
cursor: pointer;
}
-.notification-modal {
+.environment-notification-plugin-notification-notification-modal{
display: block;
min-width: 400px;
max-width: 700px;
+}
+
+.environment-notification-plugin-form .notification-variables-options{
+ font-style: italic;
+ color: red;
}
\ No newline at end of file
=====================================
plugins/environment_notification/test/functional/account_controller_test.rb
=====================================
--- a/plugins/environment_notification/test/functional/account_controller_test.rb
+++ b/plugins/environment_notification/test/functional/account_controller_test.rb
@@ -1,7 +1,8 @@
require File.expand_path(File.dirname(__FILE__)) + '/../../../../test/test_helper'
require 'account_controller'
-class AccountController; def rescue_action(e) raise e end;
+class AccountController
+ include EnvironmentNotificationHelper
end
class AccountControllerTest < ActionController::TestCase
=====================================
plugins/environment_notification/views/environment_notification_plugin_admin/_form.html.erb
=====================================
--- a/plugins/environment_notification/views/environment_notification_plugin_admin/_form.html.erb
+++ b/plugins/environment_notification/views/environment_notification_plugin_admin/_form.html.erb
@@ -1,36 +1,38 @@
-<% abstract_options = {:value => @notification.message, :style => 'width: 100%; height: 200px;', :class => "environment-notification-plugin-message" } %>
+<div class="environment-notification-plugin-form">
+ <% abstract_options = {:value => @notification.message, :style => 'width: 100%; height: 200px;', :class => "environment-notification-plugin-message" } %>
-<%= button :back, _('Back'), :controller => 'environment_notification_plugin_admin' %>
+ <%= button :back, _('Back'), :controller => 'environment_notification_plugin_admin' %>
-<%= form_for :notifications do |f| %>
+ <%= form_for :notifications do |f| %>
- <%= render :file => 'shared/tiny_mce' %>
+ <%= render :file => 'shared/tiny_mce' %>
- <%= labelled_form_field(_("Optional Title:"), f.text_field(:title, value: @notification.title)) %>
+ <%= labelled_form_field(_("Optional Title:"), f.text_field(:title, value: @notification.title)) %>
- <%= labelled_form_field(_("Enter your message here:"), f.text_area(:message, abstract_options)) %>
- <small>
- <%= _("Obs: You can use %{name} and %{email} variables to put the user's name and email in the message.") %>
- </small>
+ <%= labelled_form_field(_("Enter your message here:"), f.text_area(:message, abstract_options)) %>
+ <small class="notification-variables-options">
+ <%= _("Obs: You can use %{name} and %{email} variables to put the user's name and email in the message.") %>
+ </small>
- <%= labelled_form_field(_('Notifications Status'), select(:notifications, :active, options_for_select_with_title({"Active" => true, "Inactive" => false}, @notification.active))) %>
+ <%= labelled_form_field(_('Notifications Status'), select(:notifications, :active, options_for_select_with_title({"Active" => true, "Inactive" => false}, @notification.active))) %>
- <%= labelled_form_field(_('Notifications Color/Type'), select(:notifications, :type, options_for_select_with_title({_("Blue - Information") => "EnvironmentNotificationPlugin::InformationNotification", _("Yellow - Warning") => "EnvironmentNotificationPlugin::WarningNotification", _("Green - Success") => "EnvironmentNotificationPlugin::SuccessNotification", _("Red - Danger") => "EnvironmentNotificationPlugin::DangerNotification"}, @notification.type))) %>
+ <%= labelled_form_field(_('Notifications Color/Type'), select(:notifications, :type, options_for_select_with_title({_("Blue - Information") => "EnvironmentNotificationPlugin::InformationNotification", _("Yellow - Warning") => "EnvironmentNotificationPlugin::WarningNotification", _("Green - Success") => "EnvironmentNotificationPlugin::SuccessNotification", _("Red - Danger") => "EnvironmentNotificationPlugin::DangerNotification"}, @notification.type))) %>
- <div>
- <%= labelled_check_box(_("Display only in the homepage"), 'notifications[display_only_in_homepage]', '1', @notification.display_only_in_homepage?) %>
- </div>
+ <div>
+ <%= labelled_check_box(_("Display only in the homepage"), 'notifications[display_only_in_homepage]', '1', @notification.display_only_in_homepage?) %>
+ </div>
- <div>
- <%= labelled_check_box(_("Display to not logged users too"), 'notifications[display_to_all_users]', '1', @notification.display_to_all_users?) %>
- </div>
+ <div>
+ <%= labelled_check_box(_("Display to not logged users too"), 'notifications[display_to_all_users]', '1', @notification.display_to_all_users?) %>
+ </div>
- <div>
- <%= labelled_check_box(_("Display popup until user close the notification"), 'notifications[display_popup]', '1', @notification.display_popup?) %>
- </div>
+ <div>
+ <%= labelled_check_box(_("Display popup until user close the notification"), 'notifications[display_popup]', '1', @notification.display_popup?) %>
+ </div>
- <% button_bar do %>
- <%= submit_button 'save', _('Save'), :cancel => { :action => 'index' } %>
- <% end %>
+ <% button_bar do %>
+ <%= submit_button 'save', _('Save'), :cancel => { :action => 'index' } %>
+ <% end %>
-<% end %>
+ <% end %>
+</div>
=====================================
plugins/environment_notification/views/environment_notification_plugin_admin/index.html.erb
=====================================
--- a/plugins/environment_notification/views/environment_notification_plugin_admin/index.html.erb
+++ b/plugins/environment_notification/views/environment_notification_plugin_admin/index.html.erb
@@ -1,4 +1,4 @@
-<div id="notification-manager">
+<div id="environment-notification-plugin-notification-manager">
<div class="notification-manager-title">
<h1><%= _("Environment Notifications") %></h1>
</div>
=====================================
plugins/environment_notification/views/environment_notification_plugin_admin/show_notification.html.erb
=====================================
--- a/plugins/environment_notification/views/environment_notification_plugin_admin/show_notification.html.erb
+++ b/plugins/environment_notification/views/environment_notification_plugin_admin/show_notification.html.erb
@@ -1,7 +1,7 @@
<% if current_user && current_user.person.is_admin? %>
<% active_notifications = EnvironmentNotificationPlugin::EnvironmentNotification.active(environment) %>
<% unless active_notifications.blank? %>
- <div class="notification-bar">
+ <div class="environment-notification-plugin-notification-bar">
<div class="adminnotification notification">
<div class="notification-message">
<p>
@@ -18,7 +18,7 @@
<% @notifications = EnvironmentNotificationPlugin::EnvironmentNotification.visibles(environment, current_user, controller_path).where("id NOT IN (?)", hide_notifications) %>
-<div class="notification-bar">
+<div class="environment-notification-plugin-notification-bar">
<% @notifications.each do |notification| %>
<div class="<%= notification.type.gsub("EnvironmentNotificationPlugin::", "").downcase %> notification" data-notification="<%=notification.id%>" notification-display-popup="<%=notification.display_popup?%>">
<div class="notification-message">
=====================================
plugins/environment_notification/views/environment_notification_plugin_public/notifications_with_popup.html.erb
=====================================
--- a/plugins/environment_notification/views/environment_notification_plugin_public/notifications_with_popup.html.erb
+++ b/plugins/environment_notification/views/environment_notification_plugin_public/notifications_with_popup.html.erb
@@ -1,6 +1,6 @@
<% @notifications = EnvironmentNotificationPlugin::EnvironmentNotification.with_popup(environment, current_user, @previous_path).where("id NOT IN (?)", @hide_notifications) %>
-<div class="notification-modal">
+<div class="environment-notification-plugin-notification-notification-modal">
<% @notifications.each do |notification| %>
<% if !notification.title.blank? %>
<div class="<%= notification.type.gsub("EnvironmentNotificationPlugin::", "").downcase %> notification notification-with-title" data-notification="<%=notification.id%>">
=====================================
plugins/newsletter/lib/newsletter_plugin/newsletter.rb
=====================================
--- a/plugins/newsletter/lib/newsletter_plugin/newsletter.rb
+++ b/plugins/newsletter/lib/newsletter_plugin/newsletter.rb
@@ -154,11 +154,14 @@ class NewsletterPlugin::Newsletter < Noosfero::Plugin::ActiveRecord
headers ||= false
if File.extname(file.original_filename) == '.csv'
- parsed_recipients = []
- CSV.foreach(file.path, headers: headers) do |row|
- parsed_recipients << {name: row[name_column.to_i - 1], email: row[email_column.to_i - 1]}
+ [",", ";", "\t"].each do |sep|
+ parsed_recipients = []
+ CSV.foreach(file.path, { headers: headers, col_sep: sep }) do |row|
+ parsed_recipients << {name: row[name_column.to_i - 1], email: row[email_column.to_i - 1]}
+ end
+ self.additional_recipients = parsed_recipients
+ break if self.valid? || !self.errors.include?(:additional_recipients)
end
- self.additional_recipients = parsed_recipients
else
#FIXME find a better way to deal with errors
self.errors.add(:additional_recipients, _("have unknown file type: %s" % file.original_filename))
=====================================
plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb
=====================================
--- a/plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb
+++ b/plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb
@@ -29,7 +29,7 @@ class NewsletterPluginNewsletterTest < ActiveSupport::TestCase
:person => fast_create(Person))
enabled_newsletters << newsletter.id if enabled
end
- assert_equal enabled_newsletters, NewsletterPlugin::Newsletter.enabled.map(&:id)
+ assert_equivalent enabled_newsletters, NewsletterPlugin::Newsletter.enabled.map(&:id)
end
should 'people of newsletters are the same environment members' do
@@ -214,6 +214,35 @@ EOS
assert_equivalent ["Coop1", "Coop2", "Coop3"], newsletter.additional_recipients.map { |recipient| recipient[:name] }
end
+ should 'provide flexibility for CSV file when parsing additional recipients' do
+ content_semicolon = <<-EOS
+Coop1;name1 at example.com
+Coop2;name2 at example.com
+Coop3;name3 at example.com
+EOS
+
+ content_tab = <<-EOS
+Coop1\tname1 at example.com
+Coop2\tname2 at example.com
+Coop3\tname3 at example.com
+EOS
+ [content_semicolon, content_tab].each do |content|
+ file = Tempfile.new(['recipients', '.csv'])
+ file.write(content)
+ file.rewind
+
+ environment = fast_create Environment
+ newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person))
+ newsletter.import_recipients(Rack::Test::UploadedFile.new(file, 'text/csv'))
+
+ file.close
+ file.unlink
+
+ assert_equivalent ["name1 at example.com", "name2 at example.com", "name3 at example.com"], newsletter.additional_recipients.map { |recipient| recipient[:email] }
+ assert_equivalent ["Coop1", "Coop2", "Coop3"], newsletter.additional_recipients.map { |recipient| recipient[:name] }
+ end
+ end
+
should 'retrieve blogs related to newsletter' do
environment = fast_create Environment
community = fast_create(Community, :environment_id => environment.id)
=====================================
plugins/work_assignment/lib/ext/article.rb
=====================================
--- a/plugins/work_assignment/lib/ext/article.rb
+++ b/plugins/work_assignment/lib/ext/article.rb
@@ -13,7 +13,13 @@ class Article
end
def work_assignment_change_visibility
- if self.parent && self.parent.parent && self.parent.parent.kind_of?(WorkAssignmentPlugin::WorkAssignment)
+ if WorkAssignmentPlugin.is_submission?(self)
+ related_work_assignment = self.parent.parent
+
+ if(!related_work_assignment.publish_submissions)
+ self.show_to_followers = false
+ end
+
self.published = self.parent.published
end
end
=====================================
plugins/work_assignment/test/functional/cms_controller_test.rb
=====================================
--- a/plugins/work_assignment/test/functional/cms_controller_test.rb
+++ b/plugins/work_assignment/test/functional/cms_controller_test.rb
@@ -71,6 +71,31 @@ class CmsControllerTest < ActionController::TestCase
assert_equal other_work_assignment.publish_submissions, submission.parent.published
end
+ should 'submission inherit Work Assignment "published" attribute and not be set as show_to_followers when it is not public' do
+ @organization.add_member(@person)
+ work_assignment = create_work_assignment('Work Assignment', @organization, false, nil)
+
+ assert !work_assignment.publish_submissions
+
+ post :upload_files, :profile => @organization.identifier, :parent_id => work_assignment.id, :uploaded_files => [fixture_file_upload('/files/test.txt', 'text/plain')]
+ submission = UploadedFile.last
+
+ assert !submission.show_to_followers?
+ assert_equal work_assignment.publish_submissions, submission.published
+ assert_equal work_assignment.publish_submissions, submission.parent.published
+
+ other_work_assignment = create_work_assignment('Other Work Assigment', @organization, true, nil)
+
+ assert_equal true, other_work_assignment.publish_submissions
+
+ post :upload_files, :profile => @organization.identifier, :parent_id => other_work_assignment.id, :uploaded_files => [fixture_file_upload('/files/test.txt', 'text/plain')]
+ submission = UploadedFile.last
+
+ assert submission.show_to_followers?
+ assert_equal other_work_assignment.publish_submissions, submission.published
+ assert_equal other_work_assignment.publish_submissions, submission.parent.published
+ end
+
private
def create_work_assignment(name = nil, profile = nil, publish_submissions = nil, allow_visibility_edition = nil)
@work_assignment = WorkAssignmentPlugin::WorkAssignment.create!(:name => name, :profile => profile, :publish_submissions => publish_submissions, :allow_visibility_edition => allow_visibility_edition)
=====================================
public/stylesheets/application.css
=====================================
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -1530,7 +1530,7 @@ a.comment-picture {
#content .blog-post .publishing-info {
text-align: left;
}
-#content #article .pagination .prev_page {
+#content #article .pagination .previous_page {
position: absolute;
left: 0;
}
@@ -1538,7 +1538,7 @@ a.comment-picture {
position: absolute;
right: 0;
}
-.msie6 #content #article .pagination .prev_page, .msie6 #content #article .pagination .next_page {
+.msie6 #content #article .pagination .previous_page, .msie6 #content #article .pagination .next_page {
position: relative;
display: inline;
}
@@ -3926,7 +3926,7 @@ table.cms-articles .icon:hover {
.image-gallery-item .uploaded-file {
background: transparent url('../images/icons-mime/empty.png') no-repeat 50% 43%;
}
-#content #article .image-gallery .pagination .prev_page {
+#content #article .image-gallery .pagination .previous_page {
position: relative;
left: auto;
right: auto;
=====================================
test/functional/account_controller_test.rb
=====================================
--- a/test/functional/account_controller_test.rb
+++ b/test/functional/account_controller_test.rb
@@ -1029,4 +1029,15 @@ class AccountControllerTest < ActionController::TestCase
:national_region_type_id => NationalRegionType::CITY,
:parent_national_region_code => parent_region.national_region_code)
end
+
+ should 'not lock users out of login if environment is restrict to members' do
+ Environment.default.enable(:restrict_to_members)
+ get :login
+ assert_response :success
+
+ post :login, :user => {:login => 'johndoe', :password => 'test'}
+ assert session[:user]
+ assert_response :redirect
+ end
+
end
=====================================
test/functional/profile_controller_test.rb
=====================================
--- a/test/functional/profile_controller_test.rb
+++ b/test/functional/profile_controller_test.rb
@@ -1748,4 +1748,10 @@ class ProfileControllerTest < ActionController::TestCase
assert_no_tag :tag => 'td', :descendant => { :tag => 'a', :content => /#{person.enterprises.count}/, :attributes => { :href => /profile\/#{person.identifier}\/enterprises$/ }}
end
+ should 'redirect to login if environment is restrict to members' do
+ Environment.default.enable(:restrict_to_members)
+ get :index
+ assert_redirected_to :controller => 'account', :action => 'login'
+ end
+
end
View it on GitLab: https://gitlab.com/noosfero/noosfero/compare/1225bf3e65f6a11b8b908edf3dbf64cf5d494302...ab717cf379017f122e181b34ed97ead9d824f15c
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listas.softwarelivre.org/pipermail/noosfero-dev/attachments/20151002/bf59774e/attachment-0001.html>
More information about the Noosfero-dev
mailing list