Commit bdd0bf0f0898ed7cef5d540eb05b013dc2562160
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'master' into AI3205-comment-paragraph
Showing
16 changed files
with
322 additions
and
4 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | +Using custom locales | |
| 2 | +==================== | |
| 3 | + | |
| 4 | +Personalized translations go into the `config/custom_locales/` directory. | |
| 5 | +Custom locales can be identified by the rails environment, schema name in a | |
| 6 | +multitenancy setup or domain name until the first dot (e.g env1.coop.br for the | |
| 7 | +example below). | |
| 8 | + | |
| 9 | +Currently, the only filename prefix for the localization file which is | |
| 10 | +processed is "environment". For instance, a POT file would be called | |
| 11 | +"environment.pot". | |
| 12 | + | |
| 13 | +The structure of an environment named env1 with custom translations for both | |
| 14 | +Portuguese and Spanish and an environment named env2 with custom Russian | |
| 15 | +translation would be: | |
| 16 | + | |
| 17 | + config/ | |
| 18 | + custom_locales/ | |
| 19 | + env1/ | |
| 20 | + environment.pot | |
| 21 | + pt/ | |
| 22 | + environment.po | |
| 23 | + es/ | |
| 24 | + environment.po | |
| 25 | + env2/ | |
| 26 | + environment.pot | |
| 27 | + ru/ | |
| 28 | + environment.po | |
| 29 | + | ... | ... |
app/controllers/application_controller.rb
| ... | ... | @@ -127,6 +127,9 @@ class ApplicationController < ActionController::Base |
| 127 | 127 | |
| 128 | 128 | # TODO: move this logic somewhere else (Domain class?) |
| 129 | 129 | def detect_stuff_by_domain |
| 130 | + # Sets text domain based on request host for custom internationalization | |
| 131 | + FastGettext.text_domain = Domain.custom_locale(request.host) | |
| 132 | + | |
| 130 | 133 | @domain = Domain.find_by_name(request.host) |
| 131 | 134 | if @domain.nil? |
| 132 | 135 | @environment = Environment.default | ... | ... |
app/helpers/layout_helper.rb
| ... | ... | @@ -9,6 +9,24 @@ module LayoutHelper |
| 9 | 9 | (!profile.nil? && profile.is_on_homepage?(request.path,@page) ? " profile-homepage" : "") |
| 10 | 10 | end |
| 11 | 11 | |
| 12 | + def html_tag_classes | |
| 13 | + [ | |
| 14 | + body_classes, ( | |
| 15 | + profile.blank? ? nil : [ | |
| 16 | + 'profile-type-is-' + profile.class.name.downcase, | |
| 17 | + 'profile-name-is-' + profile.identifier, | |
| 18 | + ] | |
| 19 | + ), 'theme-' + current_theme, | |
| 20 | + @plugins.dispatch(:html_tag_classes).map do |content| | |
| 21 | + if content.respond_to?(:call) | |
| 22 | + instance_exec(&content) | |
| 23 | + else | |
| 24 | + content.html_safe | |
| 25 | + end | |
| 26 | + end | |
| 27 | + ].flatten.compact.join(' ') | |
| 28 | + end | |
| 29 | + | |
| 12 | 30 | def noosfero_javascript |
| 13 | 31 | plugins_javascripts = @plugins.map { |plugin| [plugin.js_files].flatten.map { |js| plugin.class.public_path(js) } }.flatten |
| 14 | 32 | ... | ... |
app/models/domain.rb
| ... | ... | @@ -92,4 +92,11 @@ class Domain < ActiveRecord::Base |
| 92 | 92 | @hosting = {} |
| 93 | 93 | end |
| 94 | 94 | |
| 95 | + # Detects a domain's custom text domain chain if available based on a domain | |
| 96 | + # served on multitenancy configuration or a registered domain. | |
| 97 | + def self.custom_locale(domainname) | |
| 98 | + domain = Noosfero::MultiTenancy.mapping[domainname] || domainname[/(.*?)\./,1] | |
| 99 | + FastGettext.translation_repositories.keys.include?(domain) ? domain : FastGettext.default_text_domain | |
| 100 | + end | |
| 101 | + | |
| 95 | 102 | end | ... | ... |
app/views/layouts/application-ng.html.erb
| 1 | 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 2 | -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%= html_language %>" lang="<%= html_language %>"> | |
| 2 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%= html_language %>" lang="<%= html_language %>" class="<%= h html_tag_classes %>"> | |
| 3 | 3 | <head> |
| 4 | 4 | <title><%= h page_title %></title> |
| 5 | 5 | <%= yield(:feeds) %> | ... | ... |
app/views/layouts/application.html.erb
| 1 | 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 2 | -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%= html_language %>" lang="<%= html_language %>"> | |
| 2 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%= html_language %>" lang="<%= html_language %>" class="<%= h html_tag_classes %>"> | |
| 3 | 3 | <head> |
| 4 | 4 | <title><%= h page_title %></title> |
| 5 | 5 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ... | ... |
lib/noosfero/i18n.rb
| ... | ... | @@ -9,7 +9,8 @@ class Object |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | 11 | |
| 12 | -custom_locale_dir = Rails.root.join('custom_locales', Rails.env) | |
| 12 | +# Adds custom locales for a whole environment | |
| 13 | +custom_locale_dir = Rails.root.join('config', 'custom_locales', Rails.env) | |
| 13 | 14 | repos = [] |
| 14 | 15 | if File.exists?(custom_locale_dir) |
| 15 | 16 | repos << FastGettext::TranslationRepository.build('environment', :type => 'po', :path => custom_locale_dir) |
| ... | ... | @@ -29,3 +30,15 @@ end |
| 29 | 30 | |
| 30 | 31 | FastGettext.add_text_domain 'noosfero', :type => :chain, :chain => repos |
| 31 | 32 | FastGettext.default_text_domain = 'noosfero' |
| 33 | + | |
| 34 | +# Adds custom locales for specific domains; Domains are identified by the | |
| 35 | +# sequence before the first dot, while tenants are identified by schema name | |
| 36 | +hosted_environments = Noosfero::MultiTenancy.mapping.values | |
| 37 | +hosted_environments += Domain.all.map { |domain| domain.name[/(.*?)\./,1] } if Domain.table_exists? | |
| 38 | + | |
| 39 | +hosted_environments.uniq.each do |env| | |
| 40 | + custom_locale_dir = Rails.root.join('config', 'custom_locales', env) | |
| 41 | + if File.exists?(custom_locale_dir) | |
| 42 | + FastGettext.add_text_domain(env, :type => :chain, :chain => [FastGettext::TranslationRepository.build('environment', :type => 'po', :path => custom_locale_dir)] + repos) | |
| 43 | + end | |
| 44 | +end | ... | ... |
lib/noosfero/plugin.rb
| ... | ... | @@ -560,6 +560,12 @@ class Noosfero::Plugin |
| 560 | 560 | [] |
| 561 | 561 | end |
| 562 | 562 | |
| 563 | + # -> Adds css class to <html> tag | |
| 564 | + # returns = ['class1', 'class2'] | |
| 565 | + def html_tag_classes | |
| 566 | + nil | |
| 567 | + end | |
| 568 | + | |
| 563 | 569 | # -> Adds additional blocks to profiles and environments. |
| 564 | 570 | # Your plugin must implements a class method called 'extra_blocks' |
| 565 | 571 | # that returns a hash with the following syntax. | ... | ... |
| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | +Classify Members | |
| 2 | +Plugin that allows the association of communities with types of user profiles to classify and highlight them within the environment. | |
| 3 | + | |
| 4 | + * In a school env we want to visit a profile and know it is a teacher or a student. | |
| 5 | + * In a enterprise env we want to view a forum comment and know it comes from a director or a lawyer. | |
| 6 | + | |
| 7 | +This plugin will allow the admin to select communities for classification. When the user enter in one of these communities, its profile pages comes with a community related class allowing the theme to adapt it. Also the picture block and other places where the user picture is displayed will have labels like "Teacher" or "Lawyer", also themable with css. | ... | ... |
plugins/classify_members/controllers/classify_members_plugin_admin_controller.rb
0 → 100644
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +class ClassifyMembersPluginAdminController < PluginsController | |
| 2 | + def index | |
| 3 | + @settings ||= Noosfero::Plugin::Settings.new( | |
| 4 | + environment, ClassifyMembersPlugin, params[:settings] | |
| 5 | + ) | |
| 6 | + | |
| 7 | + if request.post? | |
| 8 | + @settings.save! | |
| 9 | + redirect_to :controller => 'plugins', :action => 'index' | |
| 10 | + end | |
| 11 | + end | |
| 12 | +end | ... | ... |
| ... | ... | @@ -0,0 +1,78 @@ |
| 1 | +class ClassifyMembersPlugin < Noosfero::Plugin | |
| 2 | + def self.plugin_name | |
| 3 | + _("Classify Members") | |
| 4 | + end | |
| 5 | + | |
| 6 | + def self.plugin_description | |
| 7 | + _("Allows the association of communities with types of user profiles to classify and highlight them within the environment.") | |
| 8 | + end | |
| 9 | + | |
| 10 | + def html_tag_classes | |
| 11 | + plugin = self | |
| 12 | + lambda do | |
| 13 | + if profile && profile.person? | |
| 14 | + plugin.find_community(profile).map do |community, community_label| | |
| 15 | + 'member-of-' + community.identifier | |
| 16 | + end | |
| 17 | + end | |
| 18 | + end | |
| 19 | + end | |
| 20 | + | |
| 21 | + def body_beginning | |
| 22 | + plugin = self | |
| 23 | + lambda do | |
| 24 | + if profile && profile.person? | |
| 25 | + javascript_tag(" | |
| 26 | + jQuery(function(){ | |
| 27 | + jQuery('<div class=\"cmm-member-tags\"><ul class=\"cmm-member-list\"></ul></div>').insertBefore( | |
| 28 | + '.profile-image-block .vcard .profile-info-options' | |
| 29 | + ); | |
| 30 | + });\n" + | |
| 31 | + plugin.find_community(profile).map do |community, community_label| | |
| 32 | + "jQuery(function(){ | |
| 33 | + jQuery('.cmm-member-list').prepend( | |
| 34 | + '<li>' + '#{link_to '<i></i>' + community_label, | |
| 35 | + {:profile => community.identifier, :controller => 'profile', :action => 'members'}, | |
| 36 | + :class => 'member-of-' + community.identifier}' + '</li>' | |
| 37 | + ); | |
| 38 | + });" | |
| 39 | + end.join("\n") | |
| 40 | + ) | |
| 41 | + else | |
| 42 | + '<!-- ClassCommunityPlugin not in a profile -->' | |
| 43 | + end | |
| 44 | + end | |
| 45 | + end | |
| 46 | + | |
| 47 | + def settings | |
| 48 | + @settings ||= Noosfero::Plugin::Settings.new( | |
| 49 | + context.environment, ClassifyMembersPlugin | |
| 50 | + ) | |
| 51 | + end | |
| 52 | + | |
| 53 | + def communities | |
| 54 | + communities = settings.communities | |
| 55 | + | |
| 56 | + return [] if communities.blank? | |
| 57 | + | |
| 58 | + communities.split(/\s*\n\s*/).map do |community| | |
| 59 | + community = community.split(/\s*:\s*/) | |
| 60 | + community[0] = Profile[community[0].to_s.strip] | |
| 61 | + community[1] = community[1].to_s.strip | |
| 62 | + | |
| 63 | + if community[0].blank? | |
| 64 | + nil | |
| 65 | + else | |
| 66 | + community[1] = community[0].name if community[1].blank? | |
| 67 | + community | |
| 68 | + end | |
| 69 | + end.compact | |
| 70 | + end | |
| 71 | + | |
| 72 | + def find_community(profile) | |
| 73 | + communities.map do |community| | |
| 74 | + profile.is_member_of?(community[0]) ? community : nil | |
| 75 | + end.compact | |
| 76 | + end | |
| 77 | + | |
| 78 | +end | ... | ... |
plugins/classify_members/test/functional/classify_members_plugin_test.rb
0 → 100644
| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
| 2 | + | |
| 3 | +# Re-raise errors caught by the controller. | |
| 4 | +class HomeController | |
| 5 | + def rescue_action(e) | |
| 6 | + raise e | |
| 7 | + end | |
| 8 | +end | |
| 9 | + | |
| 10 | +class ProfileControllerTest < ActionController::TestCase | |
| 11 | + def setup | |
| 12 | + @env = Environment.default | |
| 13 | + @env.enable_plugin('ClassifyMembersPlugin') | |
| 14 | + | |
| 15 | + @p1 = fast_create(Person, :environment_id => @env.id) | |
| 16 | + @p2 = fast_create(Person, :environment_id => @env.id) | |
| 17 | + @c1 = fast_create(Community, :environment_id => @env.id) | |
| 18 | + @c2 = fast_create(Community, :environment_id => @env.id) | |
| 19 | + | |
| 20 | + # Register cassification communities: | |
| 21 | + ClassifyMembersPlugin.new(self).settings.communities = "#{@c1.identifier}: Test-Tag" | |
| 22 | + @env.save! | |
| 23 | + | |
| 24 | + @c1.add_member @p1 | |
| 25 | + @c2.add_member @p1 | |
| 26 | + @c2.add_member @p2 | |
| 27 | + end | |
| 28 | + | |
| 29 | + def environment | |
| 30 | + @env | |
| 31 | + end | |
| 32 | + | |
| 33 | + should 'add classification to the <html>' do | |
| 34 | + get :index, :profile => @p1.identifier | |
| 35 | + | |
| 36 | + assert_select 'html.member-of-' + @c1.identifier | |
| 37 | + assert_select 'html.member-of-' + @c2.identifier, false | |
| 38 | + end | |
| 39 | + | |
| 40 | + should 'not add classification to a non member' do | |
| 41 | + get :index, :profile=>@p2.identifier | |
| 42 | + | |
| 43 | + assert_select 'html.member-of-' + @c1.identifier, false | |
| 44 | + end | |
| 45 | +end | ... | ... |
plugins/classify_members/test/unit/classify_members_plugin_test.rb
0 → 100644
| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | |
| 2 | + | |
| 3 | +class ClassifyMembersPluginTest < ActiveSupport::TestCase | |
| 4 | + def setup | |
| 5 | + @env = fast_create(Environment) | |
| 6 | + @p1 = fast_create(Person, :environment_id => @env.id) | |
| 7 | + @c1 = fast_create(Community, :environment_id => @env.id) | |
| 8 | + @c2 = fast_create(Community, :environment_id => @env.id) | |
| 9 | + @c3 = fast_create(Community, :environment_id => @env.id) | |
| 10 | + @plugin = ClassifyMembersPlugin.new self | |
| 11 | + end | |
| 12 | + | |
| 13 | + def environment | |
| 14 | + @env | |
| 15 | + end | |
| 16 | + | |
| 17 | + should 'not crash for nil setting' do | |
| 18 | + assert_equal [], @plugin.find_community(@p1) | |
| 19 | + end | |
| 20 | + | |
| 21 | + should 'list all classification communities' do | |
| 22 | + @plugin.settings.communities = " | |
| 23 | + #{@c1.identifier}: Tag1 | |
| 24 | + #{@c2.identifier} | |
| 25 | + " | |
| 26 | + @env.save! | |
| 27 | + | |
| 28 | + assert_equal [[@c1, 'Tag1'], [@c2, @c2.name]], @plugin.communities | |
| 29 | + end | |
| 30 | + | |
| 31 | + should 'list the classification communities for a person' do | |
| 32 | + @c1.add_member @p1 | |
| 33 | + @c2.add_member @p1 | |
| 34 | + @p1.stubs(:is_member_of?).returns(false) | |
| 35 | + @p1.stubs(:is_member_of?).with(@c1).returns(true) | |
| 36 | + @p1.stubs(:is_member_of?).with(@c2).returns(true) | |
| 37 | + @plugin.settings.communities = " | |
| 38 | + #{@c1.identifier}: Tag1 | |
| 39 | + #{@c2.identifier}: Tag2 | |
| 40 | + #{@c3.identifier}: Tag3 | |
| 41 | + " | |
| 42 | + @env.save! | |
| 43 | + | |
| 44 | + assert_equal [[@c1, 'Tag1'], [@c2, 'Tag2']], @plugin.find_community(@p1) | |
| 45 | + end | |
| 46 | +end | ... | ... |
plugins/classify_members/views/classify_members_plugin_admin/index.html.erb
0 → 100644
| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | +<h1><%= _("Classify Members Plugin's config") %></h1> | |
| 2 | + | |
| 3 | +<%= form_for(:settings) do |f| %> | |
| 4 | + | |
| 5 | + <%= labelled_form_field _('Communities to classify people:'), f.text_area(:communities) %> | |
| 6 | + | |
| 7 | + <p><%= | |
| 8 | + _('List of community identifiers and the applicable person label, line by line.') | |
| 9 | + %></p> | |
| 10 | + | |
| 11 | + <fieldset> | |
| 12 | + <legend><%=_('Example:')%></legend> | |
| 13 | + <%=_('teachers: Teacher')%> <br> | |
| 14 | + <%=_('office-lawyers: Lawyer')%> <br> | |
| 15 | + <%=_('salvador-ba: Soteropolitano')%> | |
| 16 | + </fieldset> | |
| 17 | + | |
| 18 | + <% button_bar do %> | |
| 19 | + <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %> | |
| 20 | + <% end %> | |
| 21 | + | |
| 22 | +<% end %> | ... | ... |
script/noosfero-plugins
| ... | ... | @@ -177,7 +177,7 @@ _new(){ |
| 177 | 177 | mkdir "$target" |
| 178 | 178 | |
| 179 | 179 | plugin_name=$(echo "$plugin" | sed -e 's/^./\u&/; s/_\(.\)/\u\1/g') |
| 180 | - for source_file in $(find "$template" -type f); do | |
| 180 | + for source_file in $(find "$template" -type f -and '(' -not -name '*.po' -and -not -name '*.mo' ')'); do | |
| 181 | 181 | target_file=$(echo "$source_file" | sed -e "s/template/$plugin/g") |
| 182 | 182 | mkdir -p $(dirname "$target_file") |
| 183 | 183 | sed "s/TemplatePlugin/${plugin_name}Plugin/g" "$source_file" > "$target_file" | ... | ... |
test/functional/home_controller_test.rb
| ... | ... | @@ -130,4 +130,36 @@ class HomeControllerTest < ActionController::TestCase |
| 130 | 130 | assert_no_tag :tag => 'a', :attributes => {:href => '/account/signup'} |
| 131 | 131 | end |
| 132 | 132 | |
| 133 | + should 'add class to the <html>' do | |
| 134 | + get :index | |
| 135 | + | |
| 136 | + # Where am i? | |
| 137 | + assert_select 'html.controller-home.action-home-index' | |
| 138 | + # What is the current layout? | |
| 139 | + assert_select 'html.template-default.theme-noosfero' | |
| 140 | + end | |
| 141 | + | |
| 142 | + should 'plugins add class to the <html>' do | |
| 143 | + class Plugin1 < Noosfero::Plugin | |
| 144 | + def html_tag_classes | |
| 145 | + lambda { ['t1', 't2'] } | |
| 146 | + end | |
| 147 | + end | |
| 148 | + | |
| 149 | + class Plugin2 < Noosfero::Plugin | |
| 150 | + def html_tag_classes | |
| 151 | + 'test' | |
| 152 | + end | |
| 153 | + end | |
| 154 | + | |
| 155 | + Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name]) | |
| 156 | + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new]) | |
| 157 | + | |
| 158 | + get :index | |
| 159 | + | |
| 160 | + # Where am i? | |
| 161 | + assert_select 'html.controller-home.action-home-index' | |
| 162 | + # There are plugin classes? | |
| 163 | + assert_select 'html.t1.t2.test' | |
| 164 | + end | |
| 133 | 165 | end | ... | ... |