Commit 619223c680bd8fcedc16ed16ceb432223922df94

Authored by Rodrigo Souto
Committed by Joenio Costa
1 parent 4fe87ea2

Bsc Plugin

* Refactoring profile management to use tokeninput
  * Also, adding support of tokeninput to the core
* Adding attributes validated and cnpj to enterprise
* Environment option to define enterprise as validated by defaul
* Making profile namedscopes consider subclasses too
* Adapting enterprise icon and identification to meet Bsc expectations
* Adapting profile info actions to meet Bsc expectations
* Adapting control panel buttons to meet Bsc expectations
* Adapting core product to meed Bsc expectations
* Adding and fixing plugin hotspots

(ActionItem2085, ActionItem2087, ActionItem2086, ActionItem2088)
Showing 104 changed files with 6763 additions and 434 deletions   Show diff stats
app/controllers/application.rb
... ... @@ -128,6 +128,21 @@ class ApplicationController < ActionController::Base
128 128  
129 129 def init_noosfero_plugins
130 130 @plugins = Noosfero::Plugin::Manager.new(self)
  131 + @plugins.enabled_plugins.map(&:class).each do |plugin|
  132 + prepend_view_path(plugin.view_path)
  133 + end
  134 + init_noosfero_plugins_controller_filters
  135 + end
  136 +
  137 + # This is a generic method that initialize any possible filter defined by a
  138 + # plugin to the current controller being initialized.
  139 + def init_noosfero_plugins_controller_filters
  140 + @plugins.enabled_plugins.each do |plugin|
  141 + plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter|
  142 + self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {}))
  143 + self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block])
  144 + end
  145 + end
131 146 end
132 147  
133 148 def load_terminology
... ...
app/controllers/my_profile/manage_products_controller.rb
... ... @@ -4,6 +4,7 @@ class ManageProductsController < ApplicationController
4 4 protect 'manage_products', :profile, :except => [:show]
5 5 before_filter :check_environment_feature
6 6 before_filter :login_required, :except => [:show]
  7 + before_filter :create_product?, :only => [:new]
7 8  
8 9 protected
9 10  
... ... @@ -14,6 +15,13 @@ class ManageProductsController < ApplicationController
14 15 end
15 16 end
16 17  
  18 + def create_product?
  19 + if !profile.create_product?
  20 + render_access_denied
  21 + return
  22 + end
  23 + end
  24 +
17 25 public
18 26  
19 27 def index
... ...
app/controllers/my_profile/profile_members_controller.rb
1 1 class ProfileMembersController < MyProfileController
2 2 protect 'manage_memberships', :profile
3   - no_design_blocks
4 3  
5 4 def index
6 5 @members = profile.members
... ... @@ -15,29 +14,23 @@ class ProfileMembersController &lt; MyProfileController
15 14 rescue ActiveRecord::RecordNotFound
16 15 @person = nil
17 16 end
18   - if !params[:confirmation] && @person && @person.is_last_admin_leaving?(profile, @roles)
19   - redirect_to :action => :last_admin, :roles => params[:roles], :person => @person
20   - else
21   - if @person && @person.define_roles(@roles, profile)
  17 +
  18 + if @person
  19 + if@person.is_last_admin_leaving?(profile, @roles)
  20 + redirect_to :action => :last_admin
  21 + elsif @person.define_roles(@roles, profile)
22 22 session[:notice] = _('Roles successfuly updated')
  23 + redirect_to :controller => 'profile_editor'
23 24 else
24 25 session[:notice] = _('Couldn\'t change the roles')
25   - end
26   - if params[:confirmation]
27   - redirect_to profile.url
28   - else
29   - redirect_to :action => :index
  26 + redirect_to :action => 'index'
30 27 end
31 28 end
32 29 end
33 30  
34 31 def last_admin
35   - @person = params[:person]
36   - @roles = params[:roles] || []
37   - @members = profile.members.select {|member| !profile.admins.include?(member)}
38   - @title = _('Current admins')
39   - @collection = :profile_admins
40   - @remove_action = {:action => 'remove_admin'}
  32 + @roles = [Profile::Roles.admin(environment.id)]
  33 + @pre_population = [].to_json
41 34 end
42 35  
43 36 def add_role
... ... @@ -90,6 +83,7 @@ class ProfileMembersController &lt; MyProfileController
90 83 end
91 84  
92 85 def add_members
  86 + @roles = Profile::Roles.organization_member_roles(environment.id)
93 87 end
94 88  
95 89 def add_member
... ... @@ -122,19 +116,42 @@ class ProfileMembersController &lt; MyProfileController
122 116 render :layout => false
123 117 end
124 118  
125   - def find_users
126   - if !params[:query] || params[:query].length <= 2
127   - @users_found = []
128   - elsif params[:scope] == 'all_users'
129   - @users_found = Person.find_by_contents(params[:query] + '*').select {|user| !profile.members.include?(user)}
130   - @button_alt = _('Add member')
131   - @add_action = {:action => 'add_member'}
132   - elsif params[:scope] == 'new_admins'
133   - @users_found = Person.find_by_contents(params[:query] + '*').select {|user| profile.members.include?(user) && !profile.admins.include?(user)}
134   - @button_alt = _('Add member')
135   - @add_action = {:action => 'add_admin'}
  119 + def search_user
  120 + role = Role.find(params[:role])
  121 + render :text => environment.people.find(:all, :conditions => ['LOWER(name) LIKE ? OR LOWER(identifier) LIKE ?', "%#{params['q_'+role.key]}%", "%#{params['q_'+role.key]}%"]).
  122 + select { |person| !profile.members_by_role(role).include?(person) }.
  123 + map {|person| {:id => person.id, :name => person.name} }.
  124 + to_json
  125 + end
  126 +
  127 + def save_associations
  128 + error = false
  129 + roles = Profile::Roles.organization_member_roles(environment.id)
  130 + roles.select { |role| params['q_'+role.key] }.each do |role|
  131 + people = [Person.find(params['q_'+role.key].split(','))].flatten
  132 + to_remove = profile.members_by_role(role) - people
  133 + to_add = people - profile.members_by_role(role)
  134 +
  135 + begin
  136 + to_remove.each { |person| profile.disaffiliate(person, role) }
  137 + to_add.each { |person| profile.affiliate(person, role) }
  138 + rescue Exception => ex
  139 + logger.info ex
  140 + error = true
  141 + end
  142 + end
  143 +
  144 + if error
  145 + session[:notice] = _('The members list couldn\'t be updated. Please contact the administrator.')
  146 + redirect_to :action => 'add_members'
  147 + else
  148 + if profile.admins.blank? && !params[:last_admin]
  149 + redirect_to :action => 'last_admin'
  150 + else
  151 + session[:notice] = _('The members list was updated.')
  152 + redirect_to :controller => 'profile_editor'
  153 + end
136 154 end
137   - render :layout => false
138 155 end
139 156  
140 157 def send_mail
... ...
app/helpers/application_helper.rb
... ... @@ -298,6 +298,17 @@ module ApplicationHelper
298 298 partial_for_task_class(klass.superclass, action)
299 299 end
300 300  
  301 + def view_for_profile_actions(klass)
  302 + raise ArgumentError, 'No profile actions view for this class.' if klass.nil?
  303 +
  304 + name = klass.name.underscore
  305 + VIEW_EXTENSIONS.each do |ext|
  306 + return "blocks/profile_info_actions/"+name+ext if File.exists?(File.join(RAILS_ROOT, 'app', 'views', 'blocks', 'profile_info_actions', name+ext))
  307 + end
  308 +
  309 + view_for_profile_actions(klass.superclass)
  310 + end
  311 +
301 312 def user
302 313 @controller.send(:user)
303 314 end
... ... @@ -970,7 +981,8 @@ module ApplicationHelper
970 981 'lightbox',
971 982 'colorpicker',
972 983 pngfix_stylesheet_path,
973   - ]
  984 + ] +
  985 + tokeninput_stylesheets
974 986 end
975 987  
976 988 # DEPRECATED. Do not use this·
... ... @@ -982,6 +994,10 @@ module ApplicationHelper
982 994 'iepngfix/iepngfix.css'
983 995 end
984 996  
  997 + def tokeninput_stylesheets
  998 + ['token-input', 'token-input-facebook', 'token-input-mac']
  999 + end
  1000 +
985 1001 def noosfero_layout_features
986 1002 render :file => 'shared/noosfero_layout_features'
987 1003 end
... ... @@ -1127,7 +1143,7 @@ module ApplicationHelper
1127 1143 def manage_enterprises
1128 1144 if user && !user.enterprises.empty?
1129 1145 enterprises_link = user.enterprises.map do |enterprise|
1130   - link_to(content_tag('strong', [_('<span>Manage</span> %s') % enterprise.short_name(25)]), @environment.top_url + "/myprofile/#{enterprise.identifier}", :class => "icon-menu-enterprise", :title => [_('Manage %s') % enterprise.short_name])
  1146 + link_to(content_tag('strong', [_('<span>Manage</span> %s') % enterprise.short_name(25)]), @environment.top_url + "/myprofile/#{enterprise.identifier}", :class => "icon-menu-"+enterprise.class.identification.underscore, :title => [_('Manage %s') % enterprise.short_name])
1131 1147 end
1132 1148 render :partial => 'shared/manage_enterprises', :locals => {:enterprises_link => enterprises_link}
1133 1149 end
... ...
app/helpers/catalog_helper.rb
... ... @@ -6,14 +6,17 @@ include ManageProductsHelper
6 6 def display_products_list(profile, products)
7 7 data = ''
8 8 extra_content = []
  9 + extra_content_list = []
9 10 products.each { |product|
10 11 extra_content = @plugins.map(:catalog_item_extras, product).collect { |content| instance_eval(&content) } if @plugins
  12 + extra_content_list = @plugins.map(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } if @plugins
11 13 data << content_tag('li',
12 14 link_to_product(product, :class => 'product-pic', :style => 'background-image:url(%s)' % product.default_image(:portrait) ) +
13 15 content_tag('h3', link_to_product(product)) +
14 16 content_tag('ul',
15 17 (product.price ? content_tag('li', _('Price: %s') % ( "%.2f" % product.price), :class => 'product_price') : '') +
16   - content_tag('li', product_category_name(profile, product.product_category), :class => 'product_category')
  18 + content_tag('li', product_category_name(profile, product.product_category), :class => 'product_category') +
  19 + extra_content_list.map { |content| content_tag('li', content)}.join("\n")
17 20 ) +
18 21 (product.description ? content_tag('div',
19 22 txt2html(product.description),
... ...
app/models/community.rb
... ... @@ -75,4 +75,8 @@ class Community &lt; Organization
75 75 end
76 76 end
77 77  
  78 + def control_panel_settings_button
  79 + {:title => __('Community Info and settings'), :icon => 'edit-profile-group'}
  80 + end
  81 +
78 82 end
... ...
app/models/enterprise.rb
... ... @@ -147,6 +147,7 @@ class Enterprise &lt; Organization
147 147 end
148 148  
149 149 before_create do |enterprise|
  150 + enterprise.validated = enterprise.environment.enabled?('enterprises_are_validated_when_created')
150 151 if enterprise.environment.enabled?('enterprises_are_disabled_when_created')
151 152 enterprise.enabled = false
152 153 end
... ... @@ -167,4 +168,12 @@ class Enterprise &lt; Organization
167 168 enable_contact_us
168 169 end
169 170  
  171 + def control_panel_settings_button
  172 + {:title => __('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'}
  173 + end
  174 +
  175 + def create_product?
  176 + true
  177 + end
  178 +
170 179 end
... ...
app/models/environment.rb
... ... @@ -117,6 +117,7 @@ class Environment &lt; ActiveRecord::Base
117 117 'enable_organization_url_change' => _("Allow organizations to change their URL"),
118 118 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"),
119 119 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'),
  120 + 'enterprises_are_validated_when_created' => __('Enterprises are validated when created'),
120 121 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'),
121 122 'xmpp_chat' => _('XMPP/Jabber based chat'),
122 123 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images')
... ... @@ -255,11 +256,22 @@ class Environment &lt; ActiveRecord::Base
255 256 self.settings["#{feature}_enabled".to_sym] = true
256 257 end
257 258  
  259 + def enable_plugin(plugin)
  260 + self.enabled_plugins += [plugin]
  261 + self.enabled_plugins.uniq!
  262 + self.save!
  263 + end
  264 +
258 265 # Disables a feature identified by its name
259 266 def disable(feature)
260 267 self.settings["#{feature}_enabled".to_sym] = false
261 268 end
262 269  
  270 + def disable_plugin(plugin)
  271 + self.enabled_plugins.delete(plugin)
  272 + self.save!
  273 + end
  274 +
263 275 # Tells if a feature, identified by its name, is enabled
264 276 def enabled?(feature)
265 277 self.settings["#{feature}_enabled".to_sym] == true
... ...
app/models/organization.rb
... ... @@ -95,12 +95,12 @@ class Organization &lt; Profile
95 95 []
96 96 end
97 97  
98   - N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Validated'); N_('Tag list')
99   - settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information, :validated, :cnpj
  98 + N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Tag list')
  99 + settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information
100 100  
101 101 validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT
102   -
103 102 validates_format_of :contact_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |org| !org.contact_email.blank? })
  103 + validates_as_cnpj :cnpj
104 104  
105 105 xss_terminate :only => [ :acronym, :contact_person, :contact_email, :legal_form, :economic_activity, :management_information ], :on => 'validation'
106 106  
... ... @@ -149,4 +149,12 @@ class Organization &lt; Profile
149 149 false
150 150 end
151 151  
  152 + def members_to_json
  153 + members.map { |member| {:id => member.id, :name => member.name} }.to_json
  154 + end
  155 +
  156 + def members_by_role_to_json(role)
  157 + members_by_role(role).map { |member| {:id => member.id, :name => member.name} }.to_json
  158 + end
  159 +
152 160 end
... ...
app/models/person.rb
... ... @@ -389,6 +389,10 @@ class Person &lt; Profile
389 389 leave_hash.to_json
390 390 end
391 391  
  392 + def control_panel_settings_button
  393 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  394 + end
  395 +
392 396 protected
393 397  
394 398 def followed_by?(profile)
... ...
app/models/product.rb
... ... @@ -149,4 +149,8 @@ class Product &lt; ActiveRecord::Base
149 149 unit.blank? ? name : "#{name} - #{unit.name.downcase}"
150 150 end
151 151  
  152 + def display_supplier_on_search?
  153 + true
  154 + end
  155 +
152 156 end
... ...
app/models/profile.rb
... ... @@ -52,8 +52,9 @@ class Profile &lt; ActiveRecord::Base
52 52 acts_as_accessible
53 53  
54 54 named_scope :memberships_of, lambda { |person| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.accessor_type = ? AND role_assignments.accessor_id = ?', person.class.base_class.name, person.id ] } }
55   - named_scope :enterprises, :conditions => "profiles.type = 'Enterprise'"
56   - named_scope :communities, :conditions => "profiles.type = 'Community'"
  55 + #FIXME: these will work only if the subclass is already loaded
  56 + named_scope :enterprises, lambda { {:conditions => (Enterprise.send(:subclasses).map(&:name) << 'Enterprise').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} }
  57 + named_scope :communities, lambda { {:conditions => (Community.send(:subclasses).map(&:name) << 'Community').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} }
57 58  
58 59 def members
59 60 Person.members_of(self)
... ... @@ -564,9 +565,7 @@ private :generate_url, :url_options
564 565 if self.closed? && members_count > 0
565 566 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
566 567 else
567   - if members_count == 0
568   - self.affiliate(person, Profile::Roles.admin(environment.id))
569   - end
  568 + self.affiliate(person, Profile::Roles.admin(environment.id)) if members_count == 0
570 569 self.affiliate(person, Profile::Roles.member(environment.id))
571 570 end
572 571 else
... ... @@ -817,6 +816,14 @@ private :generate_url, :url_options
817 816 end
818 817 end
819 818  
  819 + def control_panel_settings_button
  820 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  821 + end
  822 +
  823 + def self.identification
  824 + name
  825 + end
  826 +
820 827 protected
821 828  
822 829 def followed_by?(person)
... ...
app/views/admin_panel/index.rhtml
... ... @@ -16,4 +16,7 @@
16 16 <tr><td><%= link_to _('Manage Fields'), :controller => 'features', :action => 'manage_fields' %></td></tr>
17 17 <tr><td><%= link_to _('Set Portal'), :action => 'set_portal_community' %></td></tr>
18 18 <tr><td><%= link_to _('Terms of use'), :action => 'terms_of_use' %></td></tr>
  19 + <% @plugins.map(:admin_panel_links).each do |link| %>
  20 + <tr><td><%= link_to link[:title], link[:url] %></td></tr>
  21 + <% end %>
19 22 </table>
... ...
app/views/layouts/_javascript.rhtml
1   -<%= javascript_include_tag :defaults, 'jquery-latest.js', 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery.cookie', 'reflection', 'add-and-join', :cache => 'cache-general' %>
  1 +<%= javascript_include_tag :defaults, 'jquery-latest.js', 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery.cookie', 'reflection', 'add-and-join', 'jquery.tokeninput', :cache => 'cache-general' %>
... ...
app/views/manage_products/index.rhtml
... ... @@ -25,6 +25,6 @@
25 25 <%= pagination_links @products %>
26 26  
27 27 <% button_bar do %>
28   - <%= button :add, _('New product or service'), :action => 'new' %>
  28 + <%= button :add, _('New product or service'), :action => 'new' if @profile.create_product? %>
29 29 <%= button :back, _('Back'), { :controller => 'profile_editor', :profile => @profile.identifier, :action => 'index' } %>
30 30 <% end %>
... ...
app/views/memberships/index.rhtml
... ... @@ -17,7 +17,7 @@
17 17 <span class='profile-details'>
18 18 <strong><%= membership.name %></strong><br/>
19 19 <%= _('Role: %s') % rolename_for(profile, membership) %> <br/>
20   - <%= _('Type: %s') % getterm(membership.class.name) %> <br/>
  20 + <%= _('Type: %s') % getterm(membership.class.identification) %> <br/>
21 21 <%= _('Description: %s') % membership.description + '<br/>' if membership.community? %>
22 22 <%= _('Members: %s') % membership.members.size.to_s %> <br/>
23 23 <%= _('Created at: %s') % show_date(membership.created_at) unless membership.enterprise? %> <br/>
... ...
app/views/profile/_profile.rhtml
1 1 <tr>
2 2 <td colspan='2'>
3 3  
4   - <% plugins_tabs = @plugins.map(:profile_tabs) %>
  4 + <% plugins_tabs = @plugins.map(:profile_tabs).
  5 + map { |tab| {:title => tab[:title], :id => tab[:id], :content => instance_eval(&tab[:content]), :start => tab[:title]} }%>
5 6  
6 7 <% tabs = plugins_tabs.select { |tab| tab[:start] } %>
7 8  
... ...
app/views/profile/profile_info.rjs
... ... @@ -3,4 +3,4 @@ if !user.nil? and user.has_permission?(&#39;edit_profile&#39;, profile)
3 3 else
4 4 page.hide "profile-admin-url-#{@block.id}"
5 5 end
6   -page.replace_html "profile-info-options-#{@block.id}", :file => 'blocks/profile_info_actions/' + @block.owner.class.name.underscore
  6 +page.replace_html "profile-info-options-#{@block.id}", :file => view_for_profile_actions(@block.owner.class)
... ...
app/views/profile_editor/_moderation.html.erb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +<h2><%= _('Moderation options') %></h2>
  2 +<% if profile.community? %>
  3 + <div style='margin-bottom: 1em'>
  4 + <%= _('New members must be approved:')%>
  5 + </div>
  6 + <div style='margin-bottom: 0.5em'>
  7 + <%= radio_button 'profile_data', 'closed', 'true', :style => 'float: left' %>
  8 + <div style='margin-left: 30px'>
  9 + <%= _('<strong>Before</strong> joining this group (a moderator has to accept the member in pending request before member can access the intranet and/or the website).') %>
  10 + </div>
  11 + </div>
  12 + <div>
  13 + <%= radio_button 'profile_data', 'closed', 'false', :style => 'float: left' %>
  14 + <div style='margin-left: 30px'>
  15 + <%= _('<strong>After</strong> joining this group (a moderator can always desactivate access for users later).') %>
  16 + </div>
  17 + </div>
  18 + <br>
  19 +<% end %>
  20 +<div style='margin-bottom: 1em'>
  21 + <%= _('New articles posted by members of this group must be approved:')%>
  22 +
  23 +</div>
  24 +<div style='margin-bottom: 0.5em'>
  25 + <%= radio_button 'profile_data', 'moderated_articles', 'true', :style => 'float: left' %>
  26 + <div style='margin-left: 30px'>
  27 + <%= _('<strong>Before</strong> being published in this group (a moderator has to accept the article in pending request before the article be listed as a article of this group).') %>
  28 + </div>
  29 +</div>
  30 +<div>
  31 + <%= radio_button 'profile_data', 'moderated_articles', 'false', :style => 'float: left' %>
  32 + <div style='margin-left: 30px'>
  33 + <%= _('<strong>After</strong> being published in this group (a moderator can always remove publicated articles later).') %>
  34 + </div>
  35 +</div>
... ...
app/views/profile_editor/_organization.rhtml
... ... @@ -58,42 +58,8 @@
58 58 </div>
59 59 <% end %>
60 60  
61   - <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'profile_data', :profile => @profile } %>
  61 +<%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'profile_data', :profile => @profile } %>
62 62  
63   - <%= labelled_check_box(_('Enable "contact us"'), 'profile_data[enable_contact_us]', "1", @profile.enable_contact_us) if @profile.enterprise? %>
  63 +<%= labelled_check_box(_('Enable "contact us"'), 'profile_data[enable_contact_us]', "1", @profile.enable_contact_us) if @profile.enterprise? %>
64 64  
65   - <h1><%= _('Moderation options') %></h1>
66   - <% if profile.community? %>
67   - <div style='margin-bottom: 1em'>
68   - <%= _('New members must be approved:')%>
69   - </div>
70   - <div style='margin-bottom: 0.5em'>
71   - <%= radio_button 'profile_data', 'closed', 'true', :style => 'float: left' %>
72   - <div style='margin-left: 30px'>
73   - <%= _('<strong>Before</strong> joining this group (a moderator has to accept the member in pending request before member can access the intranet and/or the website).') %>
74   - </div>
75   - </div>
76   - <div>
77   - <%= radio_button 'profile_data', 'closed', 'false', :style => 'float: left' %>
78   - <div style='margin-left: 30px'>
79   - <%= _('<strong>After</strong> joining this group (a moderator can always desactivate access for users later).') %>
80   - </div>
81   - </div>
82   - <br>
83   - <% end %>
84   - <div style='margin-bottom: 1em'>
85   - <%= _('New articles posted by members of this group must be approved:')%>
86   -
87   - </div>
88   - <div style='margin-bottom: 0.5em'>
89   - <%= radio_button 'profile_data', 'moderated_articles', 'true', :style => 'float: left' %>
90   - <div style='margin-left: 30px'>
91   - <%= _('<strong>Before</strong> being published in this group (a moderator has to accept the article in pending request before the article be listed as a article of this group).') %>
92   - </div>
93   - </div>
94   - <div>
95   - <%= radio_button 'profile_data', 'moderated_articles', 'false', :style => 'float: left' %>
96   - <div style='margin-left: 30px'>
97   - <%= _('<strong>After</strong> being published in this group (a moderator can always remove publicated articles later).') %>
98   - </div>
99   - </div>
  65 +<%= render :partial => 'moderation', :locals => { :profile => @profile } %>
... ...
app/views/profile_editor/index.rhtml
... ... @@ -10,9 +10,9 @@
10 10  
11 11 <% control_panel do %>
12 12  
13   - <%= control_panel_button(_('Profile Info and settings'), 'edit-profile', :controller => 'profile_editor', :action => 'edit') if profile.person? %>
14   - <%= control_panel_button(__('Community Info and settings'), 'edit-profile-group', :controller => 'profile_editor', :action => 'edit') if profile.community? %>
15   - <%= control_panel_button(__('Enterprise Info and settings'), 'edit-profile-enterprise', :controller => 'profile_editor', :action => 'edit') if profile.enterprise? %>
  13 + <%= control_panel_button(profile.control_panel_settings_button[:title],
  14 + profile.control_panel_settings_button[:icon],
  15 + :controller => 'profile_editor', :action => 'edit') %>
16 16  
17 17 <%= control_panel_button(_('Location'), 'edit-location', :controller => 'maps', :action => 'edit_location') %>
18 18  
... ... @@ -58,9 +58,9 @@
58 58  
59 59 <% if profile.enterprise? %>
60 60 <% if profile.enabled? %>
61   - <%= control_panel_button(__('Disable Enterprise'), 'disable', :action => 'disable') %>
  61 + <%= control_panel_button(__('Disable'), 'disable', :action => 'disable') %>
62 62 <% else %>
63   - <%= control_panel_button(__('Enable Enterprise'), 'enable', :action => 'enable') %>
  63 + <%= control_panel_button(__('Enable'), 'enable', :action => 'enable') %>
64 64 <% end %>
65 65 <% end %>
66 66  
... ...
app/views/profile_members/_find_users.rhtml
... ... @@ -1 +0,0 @@
1   -find_users.rhtml
2 0 \ No newline at end of file
app/views/profile_members/_index_buttons.rhtml
... ... @@ -5,4 +5,7 @@
5 5 <%= button :search, _('Invite your friends to join %s') % profile.short_name, :controller => 'invite', :action => 'select_address_book' %>
6 6 <% end %>
7 7 <%= button :send, _('Send e-mail to members'), :action => 'send_mail' %>
  8 + <% @plugins.map(:manage_members_extra_buttons).each do |plugin_button| %>
  9 + <%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url] %>
  10 + <% end %>
8 11 <% end %>
... ...
app/views/profile_members/_manage_roles.html.erb 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +<% form_tag :action => 'save_associations' do %>
  2 + <% @roles.each do |role|%>
  3 + <%= content_tag('p', content_tag('b', role.name.pluralize+':'), :style => 'margin-top: 30px; margin-bottom: 0px;') %>
  4 + <%= text_field_tag('q_'+role.key, nil, :id => 'search_'+role.key) %>
  5 + <%= hidden_field_tag(:last_admin, true) if from == 'last_admin'%>
  6 + <% end %>
  7 +
  8 + <% button_bar(:style => 'margin-top: 30px;') do %>
  9 + <%= submit_button('save', _('Save'))%>
  10 + <%= button('cancel', _('Cancel'), {:controller => 'profile_editor'})%>
  11 + <% end %>
  12 +<% end %>
  13 +
  14 +<% @roles.each do |role| %>
  15 + <% search_url = url_for(:action => 'search_user', :profile => profile.identifier, :role => role.id) %>
  16 + <% @pre_population ||= profile.members_by_role_to_json(role) %>
  17 + <script type="text/javascript">
  18 + jQuery(<%= ('#search_' + role.key).to_json %>)
  19 + .tokenInput("<%= search_url %>", {
  20 + prePopulate: <%= @pre_population %>,
  21 + hintText: <%= _('Type in a search term for users').to_json %>,
  22 + noResultsText: <%= _('No results').to_json %>,
  23 + searchingText: <%= _('Searching...').to_json %>,
  24 + searchDelay: 500,
  25 + backspaceDeleteItem: false,
  26 + preventDuplicates: true,
  27 + queryParam: <%= ('q_'+role.key).to_json %>
  28 + });
  29 +
  30 + jQuery("#token-input-search-enterprises")
  31 + .live("keydown", function(event){
  32 + if(event.keyCode == "13") return false;
  33 + });
  34 + </script>
  35 +<% end %>
  36 +
  37 +<script>
  38 + jQuery(function(){
  39 + jQuery('.token-input-input-token input').blur();
  40 + jQuery('.token-input-input-token input')[0].focus();
  41 + });
  42 +</script>
... ...
app/views/profile_members/add_members.rhtml
1   -<h2><%= _('Add members to %s') % profile.name %></h2>
  1 +<h1><%= _('Add members to %s') % profile.name %></h1>
2 2  
3   -<% form_remote_tag :url => {:action => 'find_users', :profile => profile.identifier, :scope => 'all_users'}, :update => 'users-list', :loading => '$("users-list").addClassName("loading")', :complete => '$("users-list").removeClassName("loading")' do %>
4   - <%= text_field_tag('query', '', :autocomplete => 'off') %>
5   - <%= submit_tag(_('Search')) %>
6   -<% end %>
7   -
8   -<%= observe_field('query', :url => {:action => 'find_users', :profile => profile.identifier, :scope => 'all_users'}, :update => 'users-list', :frequency => 1, :with => 'query', :condition => '$("query").value.length > 2', :loading => '$("users-list").addClassName("loading")', :complete => '$("users-list").removeClassName("loading")') %>
9   -<%= observe_field('query', :frequency => 1, :condition => '$("query").value.length <= 2', :function => '$("users-list").update($("empty-query").innerHTML)') %>
10   -
11   -<div id="users-list">
12   - <%= render :partial => 'find_users' %>
13   -</div>
14   -
15   -<div id='empty-query' style='display: none'>
16   - <%= render :partial => 'find_users' %>
17   -</div>
18   -
19   -<div id="members-list" class="add-members">
20   - <%= render :partial => 'members_list' %>
21   -</div>
22   -<%= drop_receiving_element('members-list',
23   - :url => {:action => 'add_member', :profile => profile.identifier},
24   - :before => '$("tr-" + element.id).hide()',
25   - :loading => '$("members-list").addClassName("loading")',
26   - :update => 'members-list',
27   - :success => '$("tr-" + element.id).hide(); $(element.id).show();',
28   - :complete => '$("members-list").removeClassName("loading")') %>
29   -
30   -<br style="clear:both" />
  3 +<%= render :partial => 'manage_roles', :locals => {:from => 'add_members'} %>
... ...
app/views/profile_members/find_users.rhtml
... ... @@ -1,35 +0,0 @@
1   -<h3> <%= _('Users') %> </h3>
2   -<table>
3   - <tr><th><%= _('Name') %></th><th></th></tr>
4   - <% @users_found.each do |user| %>
5   - <tr id="tr-<%= user.identifier %>">
6   - <td>
7   - <div id="<%= user.identifier %>" class="draggable-user">
8   - <%= image_tag('/images/grip-clue.png') %>
9   - <%= profile_image(user, :icon) %>
10   - <%= [link_to_profile(user.short_name + " (#{user.identifier})", user.identifier, :title => user.name),
11   - (user.sex ? gettext(user.sex.capitalize) : _('Sex not informed')),
12   - user.location.empty? ? nil : user.location ].compact.join(' &#151; ') %>
13   - </div>
14   - <%= draggable_element(user.identifier, :revert => true) %>
15   - </td>
16   - <td>
17   - <%= button_to_remote_without_text(:add, @button_alt,
18   - { :loading => '$("members-list").addClassName("loading")',
19   - :update => 'members-list',
20   - :url => {:id => user.id, :profile => profile.identifier}.merge(@add_action),
21   - :success => "$('tr-#{user.identifier}').hide()",
22   - :complete => '$("members-list").removeClassName("loading")'}) %>
23   -
24   -
25   - </td>
26   - </tr>
27   - <% end if @users_found %>
28   - <% if !params[:query] || params[:query].length <= 2 %>
29   - <tr>
30   - <td style='text-align: center'>
31   - <em><%= _('You must type at least 3 characters') %></em>
32   - </td>
33   - </tr>
34   - <% end %>
35   -</table>
app/views/profile_members/last_admin.rhtml
1 1 <h1><%= _('Last administrator leaving %s') % profile.name %></h1>
2 2  
3   -<% if profile.members_count > 1 %>
4   - <div id='last-admin-message'>
5   - <%= _('Since you are the last administrator, you must choose at least one member to administer this community.') % profile.name %>
6   - </div>
  3 +<div id='last-admin-message'>
  4 + <%= _('Since you are the last administrator, you must choose at least one member to administer this community.') % profile.name %>
  5 +</div>
7 6  
8   - <%= render :partial => 'add_admins' %>
  7 +<%= render :partial => 'manage_roles', :locals => {:from => 'last_admin'} %>
9 8  
10   - <% form_tag :action => 'update_roles', :roles => @roles, :person => @person, :confirmation => profile.admins.count > 1 do %>
11   - <% button_bar do %>
12   - <%= submit_button(:save, _("Leave administration and save"), :cancel => {:action => 'index'}) %>
13   - <% end %>
14   - <% end %>
15   -
16   -<% else %>
17   -
18   - <div id='last-admin-message'>
19   - <%= _('Since you are the last administrator and there is no other member in this community, the next user to join this community will assume the administrator role.') % profile.name %>
20   - </div>
21   -
22   - <% form_tag :action => 'update_roles', :roles => @roles, :person => @person, :confirmation => true do %>
23   - <% button_bar do %>
24   - <%= submit_button(:ok, _("Ok, I want to leave"), :cancel => {:action => 'index'}) %>
25   - <% end %>
26   - <% end %>
27   -<% end %>
... ...
app/views/search/_product.rhtml
... ... @@ -6,6 +6,7 @@ product_item_pos += 1
6 6 %>
7 7  
8 8 <% extra_content = @plugins.map(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
  9 +<% extra_properties = @plugins.map(:asset_product_properties, product)%>
9 10  
10 11 <li class="product-item <%= ( pos % 2 == 0 ) ? 'odd' : 'even' %>">
11 12 <%= link_to_product product, :class => 'product-pic', :style => 'background-image:url(%s)' % product.default_image(:minor) %>
... ... @@ -14,10 +15,15 @@ product_item_pos += 1
14 15 </strong>
15 16 <ul>
16 17 <li> <%= _('Price: %s') % (product.price ? product.price : _('Not informed')) %> </li>
17   - <% if product.enterprise %>
18   - <li> <%= _('Suplier: %s') % link_to_homepage(product.enterprise.name, product.enterprise.identifier) %> </li>
  18 + <% if product.enterprise && product.display_supplier_on_search? %>
  19 + <li> <%= _('Supplier: %s') % link_to_homepage(product.enterprise.name, product.enterprise.identifier) %> </li>
  20 + <% end %>
  21 +
  22 + <li> <%= _('Category:') + ' ' + link_to_product_category(product.product_category) %> </li>
  23 +
  24 + <% extra_properties.each do |property| %>
  25 + <li><%= property[:name] + ': ' + instance_eval(&property[:content]) %></li>
19 26 <% end %>
20   - <li> <%=_('Category:') + ' ' + link_to_product_category(product.product_category) %> </li>
21 27 </ul>
22 28  
23 29 <%= extra_content.join('\n') %>
... ...
db/migrate/20111004184103_add_field_validated_to_enterprises.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddFieldValidatedToEnterprises < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :profiles, :validated, :boolean, :default => true
  4 + end
  5 +
  6 + def self.down
  7 + add_column :profiles, :validated
  8 + end
  9 +end
... ...
db/migrate/20111004184104_add_cnpj_to_enterprises.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddCnpjToEnterprises < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :profiles, :cnpj, :string
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :profiles, :cnpj
  8 + end
  9 +end
... ...
db/schema.rb
... ... @@ -9,7 +9,7 @@
9 9 #
10 10 # It's strongly recommended to check this file into your version control system.
11 11  
12   -ActiveRecord::Schema.define(:version => 20110824192153) do
  12 +ActiveRecord::Schema.define(:version => 20111004184104) do
13 13  
14 14 create_table "action_tracker", :force => true do |t|
15 15 t.integer "user_id"
... ... @@ -378,6 +378,8 @@ ActiveRecord::Schema.define(:version =&gt; 20110824192153) do
378 378 t.datetime "updated_at"
379 379 t.boolean "visible", :default => true
380 380 t.integer "image_id"
  381 + t.boolean "validated", :default => true
  382 + t.string "cnpj"
381 383 end
382 384  
383 385 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id"
... ...
features/activate_enterprise.feature
... ... @@ -55,7 +55,7 @@ Feature: activate enterprise
55 55 Given feature "enterprise_activation" is enabled on environment
56 56 And the following enterprises
57 57 | identifier | name | enabled | cnpj |
58   - | services-provider | Services Provider | false | 00000000000000 |
  58 + | services-provider | Services Provider | false | 94.132.024/0001-48 |
59 59 And I am on Joao Silva's control panel
60 60 And I fill in "Enterprise activation code" with code of "Services Provider"
61 61 And I press "Activate"
... ...
features/bsc.feature 0 → 100644
... ... @@ -0,0 +1,164 @@
  1 +Feature: bsc
  2 +
  3 + Background:
  4 + Given "Bsc" plugin is enabled
  5 +
  6 + Scenario: display link to bsc creation on admin panel when bsc plugin active
  7 + Given I am logged in as admin
  8 + When I am on the environment control panel
  9 + Then I should see "Create Bsc"
  10 + When "Bsc" plugin is disabled
  11 + And I am on the environment control panel
  12 + Then I should not see "Create Bsc"
  13 +
  14 + Scenario: be able to create a bsc
  15 + Given I am logged in as admin
  16 + And I am on the environment control panel
  17 + And I follow "Create Bsc"
  18 + And I fill in the following:
  19 + | Business name | Sample Bsc |
  20 + | Company name | Sample Bsc |
  21 + | profile_data_identifier | sample-identifier |
  22 + | Cnpj | 07.970.746/0001-77 |
  23 + When I press "Save"
  24 + Then there should be a profile named "Sample Bsc"
  25 +
  26 + Scenario: display a button on bsc control panel to manage associated enterprises
  27 + Given the folllowing "bsc" from "bsc_plugin"
  28 + | business_name | identifier | company_name | cnpj |
  29 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 |
  30 + And I am logged in as admin
  31 + When I am on Bsc Test's control panel
  32 + Then I should see "Manage associated enterprises"
  33 +
  34 + Scenario: display a button on bsc control panel to transfer ownership
  35 + Given the folllowing "bsc" from "bsc_plugin"
  36 + | business_name | identifier | company_name | cnpj |
  37 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 |
  38 + And I am logged in as admin
  39 + When I am on Bsc Test's control panel
  40 + Then I should see "Transfer ownership"
  41 +
  42 + Scenario: create a new enterprise already associated with a bsc
  43 + Given the following user
  44 + | login | name |
  45 + | pedro-silva | Pedro Silva |
  46 + And the folllowing "bsc" from "bsc_plugin"
  47 + | business_name | identifier | company_name | cnpj | owner |
  48 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 | pedro-silva |
  49 + And organization_approval_method is "none" on environment
  50 + And I am logged in as "pedro-silva"
  51 + And I am on Bsc Test's control panel
  52 + And I follow "Manage associated enterprises"
  53 + And I follow "Add new enterprise"
  54 + And I fill in the following:
  55 + | Name | Associated Enterprise |
  56 + | Address | associated-enterprise |
  57 + When I press "Save"
  58 + Then "Associated Enterprise" should be associated with "Bsc Test"
  59 +
  60 + Scenario: do not display "add new product" button
  61 + Given the following user
  62 + | login | name |
  63 + | pedro-silva | Pedro Silva |
  64 + And the folllowing "bsc" from "bsc_plugin"
  65 + | business_name | identifier | company_name | cnpj | owner |
  66 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 | pedro-silva |
  67 + And feature "disable_products_for_enterprises" is disabled on environment
  68 + And I am logged in as "pedro-silva"
  69 + And I am on Bsc Test's control panel
  70 + When I follow "Manage Products and Services"
  71 + Then I should not see "New product or service"
  72 +
  73 + Scenario: display bsc's enterprises' products name on the bsc catalog
  74 + Given the following user
  75 + | login | name |
  76 + | pedro-silva | Pedro Silva |
  77 + And the folllowing "bsc" from "bsc_plugin"
  78 + | business_name | identifier | company_name | cnpj | owner |
  79 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 | pedro-silva |
  80 + And the following enterprise
  81 + | identifier | name |
  82 + | sample-enterprise | Sample Enterprise |
  83 + And the following product_category
  84 + | name |
  85 + | bike |
  86 + And the following products
  87 + | owner | category | name |
  88 + | sample-enterprise | bike | Master Bike |
  89 + And "Sample Enterprise" is associated with "Bsc Test"
  90 + And I am logged in as "pedro-silva"
  91 + When I go to Bsc Test's products page
  92 + Then I should see "Master Bike"
  93 + And I should see "Sample Enterprise"
  94 +
  95 + Scenario: display enterprise name linked only if person is member of any Bsc
  96 + Given the folllowing "bsc" from "bsc_plugin"
  97 + | business_name | identifier | company_name | cnpj |
  98 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 |
  99 + | Another Bsc | another-bsc | Another Bsc Test Ltda | 07.970.746/0001-77 |
  100 + And the following enterprise
  101 + | identifier | name |
  102 + | sample-enterprise | Sample Enterprise |
  103 + And the following product_category
  104 + | name |
  105 + | bike |
  106 + And the following products
  107 + | owner | category | name |
  108 + | sample-enterprise | bike | Master Bike |
  109 + And "Sample Enterprise" is associated with "Bsc Test"
  110 + And the folllowing "bsc" from "bsc_plugin"
  111 + | business_name | identifier | company_name | cnpj |
  112 + And the following user
  113 + | login | name |
  114 + | pedro | Pedro Souto |
  115 + | maria | Maria Souto |
  116 + And pedro is member of another-bsc
  117 + And I am logged in as "pedro"
  118 + When I go to Bsc Test's products page
  119 + Then I should see "Sample Enterprise"
  120 + And I should see "Sample Enterprise" within "a.bsc-catalog-enterprise-link"
  121 + But I am logged in as "maria"
  122 + When I go to Bsc Test's products page
  123 + Then I should see "Sample Enterprise"
  124 + #TODO -> test that it's not a link
  125 +
  126 + Scenario: allow only environment administrators to delete bsc profile
  127 + Given the folllowing "bsc" from "bsc_plugin"
  128 + | business_name | identifier | company_name | cnpj |
  129 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 |
  130 + And the following user
  131 + | login | name |
  132 + | pedro | Pedro Souto |
  133 + And "Pedro Souto" is admin of "Bsc Test"
  134 + And I am logged in as "pedro"
  135 + And I am on Bsc Test's control panel
  136 + And I follow "Bsc info and settings"
  137 + When I follow "Delete profile"
  138 + Then I should see "Access denied"
  139 + And "Bsc Test" profile should exist
  140 + But I am logged in as admin
  141 + And I am on Bsc Test's control panel
  142 + And I follow "Bsc info and settings"
  143 + When I follow "Delete profile"
  144 + Then I should see "Deleting profile Bsc Test"
  145 + And I follow "Yes, I am sure"
  146 + Then "Bsc Test" profile should not exist
  147 +
  148 + # Like we can believe that selenium is going to work...
  149 + @selenium
  150 + Scenario: list already associated enterprises on manage associated enterprises
  151 + Given the folllowing "bsc" from "bsc_plugin"
  152 + | business_name | identifier | company_name | cnpj |
  153 + | Bsc Test | bsc-test | Bsc Test Ltda | 94.132.024/0001-48 |
  154 + And the following enterprises
  155 + | identifier | name |
  156 + | enterprise-1 | Enterprise 1 |
  157 + | enterprise-2 | Enterprise 2 |
  158 + And "Enterprise 1" is associated with "Bsc Test"
  159 + And "Enterprise 2" is associated with "Bsc Test"
  160 + And I am logged in as admin
  161 + And I am on Bsc Test's control panel
  162 + When I follow "Manage associated enterprises"
  163 + Then I should see "Enterprise 1"
  164 + And I should see "Enterprise 2"
... ...
features/last_administrator_leaving.feature
... ... @@ -11,7 +11,7 @@ Feature: remove administrator role
11 11 And the following community
12 12 | name | identifier |
13 13 | Nice people | nice-people |
14   - And "Joao Silva" is a member of "Nice people"
  14 + And "Joao Silva" is admin of "Nice people"
15 15 And I am logged in as "joaosilva"
16 16  
17 17 Scenario: the last administrator removes his administrator role and must choose the new administrator
... ... @@ -28,8 +28,8 @@ Feature: remove administrator role
28 28 And I uncheck "Profile Administrator"
29 29 And I uncheck "Profile Member"
30 30 When I press "Save changes"
31   - Then I should see "Since you are the last administrator and there is no other member in this community"
32   - And I press "Ok, I want to leave"
  31 + Then I should see "Since you are the last administrator, you must choose at least one member to administer this community."
  32 + And I press "Save"
33 33 And I am logged in as "mariasouza"
34 34 When I go to Nice people's join page
35 35 Then "Maria Souza" should be admin of "Nice people"
... ... @@ -41,8 +41,8 @@ Feature: remove administrator role
41 41 And I uncheck "Profile Administrator"
42 42 And I uncheck "Profile Member"
43 43 When I press "Save changes"
44   - Then I should see "Since you are the last administrator and there is no other member in this community"
45   - And I press "Ok, I want to leave"
  44 + Then I should see "Since you are the last administrator, you must choose at least one member to administer this community."
  45 + And I press "Save"
46 46 And I am logged in as "mariasouza"
47 47 When I go to Nice people's join page
48 48 Then "Maria Souza" should be admin of "Nice people"
... ...
features/manage_products.feature
... ... @@ -11,6 +11,12 @@ Feature: manage products
11 11 | redemoinho | joaosilva | Rede Moinho | true |
12 12 And feature "disable_products_for_enterprises" is disabled on environment
13 13  
  14 + Scenario: display "create new product" button
  15 + Given I am logged in as "joaosilva"
  16 + And I am on Rede Moinho's control panel
  17 + When I follow "Manage Products and Services"
  18 + Then I should see "New product or service"
  19 +
14 20 Scenario: paginate public listing products and services
15 21 Given the following product_category
16 22 | name |
... ...
features/plugins.feature
... ... @@ -19,7 +19,7 @@ Feature: plugins
19 19 And the following events of TestPlugin
20 20 | event | body |
21 21 | control_panel_buttons | lambda { {:title => 'Test plugin button', :icon => '', :url => ''} } |
22   - | profile_tabs | lambda { {:title => 'Test plugin tab', :id => 'test_plugin', :content => 'Test plugin random content'} } |
  22 + | profile_tabs | lambda { {:title => 'Test plugin tab', :id => 'test_plugin', :content => lambda {'Test plugin random content'} } } |
23 23  
24 24 Scenario: a user must see the plugin\'s button in the control panel if the plugin is enabled
25 25 Given plugin TestPlugin is enabled on environment
... ...
features/step_definitions/noosfero_steps.rb
... ... @@ -36,6 +36,36 @@ Given /^the following (community|communities|enterprises?|organizations?)$/ do |
36 36 end
37 37 end
38 38  
  39 +Given /^"([^\"]*)" is associated with "([^\"]*)"$/ do |enterprise, bsc|
  40 + enterprise = Enterprise.find_by_name(enterprise) || Enterprise[enterprise]
  41 + bsc = BscPlugin::Bsc.find_by_name(bsc) || BscPlugin::Bsc[bsc]
  42 +
  43 + bsc.enterprises << enterprise
  44 +end
  45 +
  46 +Then /^"([^\"]*)" should be associated with "([^\"]*)"$/ do |enterprise, bsc|
  47 + enterprise = Enterprise.find_by_name(enterprise) || Enterprise[enterprise]
  48 + bsc = BscPlugin::Bsc.find_by_name(bsc) || BscPlugin::Bsc[bsc]
  49 +
  50 + bsc.enterprises.should include(enterprise)
  51 +end
  52 +
  53 +Given /^the folllowing "([^\"]*)" from "([^\"]*)"$/ do |kind, plugin, table|
  54 + klass = (plugin.camelize+'::'+kind.singularize.camelize).constantize
  55 + table.hashes.each do |row|
  56 + owner = row.delete("owner")
  57 + domain = row.delete("domain")
  58 + organization = klass.create!(row)
  59 + if owner
  60 + organization.add_admin(Profile[owner])
  61 + end
  62 + if domain
  63 + d = Domain.new :name => domain, :owner => organization
  64 + d.save(false)
  65 + end
  66 + end
  67 +end
  68 +
39 69 Given /^the following blocks$/ do |table|
40 70 table.hashes.map{|item| item.dup}.each do |item|
41 71 klass = item.delete('type')
... ... @@ -298,12 +328,12 @@ Given /^&quot;(.+)&quot; is friend of &quot;(.+)&quot;$/ do |person, friend|
298 328 Person[person].add_friend(Person[friend])
299 329 end
300 330  
301   -Given /^(.+) is blocked$/ do |enterprise_name|
  331 +Given /^enterprise "([^\"]*)" is blocked$/ do |enterprise_name|
302 332 enterprise = Enterprise.find_by_name(enterprise_name)
303 333 enterprise.block
304 334 end
305 335  
306   -Given /^(.+) is disabled$/ do |enterprise_name|
  336 +Given /^enterprise "([^\"]*)" is disabled$/ do |enterprise_name|
307 337 enterprise = Enterprise.find_by_name(enterprise_name)
308 338 enterprise.enabled = false
309 339 enterprise.save
... ... @@ -428,3 +458,33 @@ Then /^I should receive an e-mail on (.*)$/ do |address|
428 458 last_mail = ActionMailer::Base.deliveries.last
429 459 last_mail['to'].to_s.should == address
430 460 end
  461 +
  462 +Given /^"([^\"]*)" plugin is (enabled|disabled)$/ do |plugin_name, status|
  463 + environment = Environment.default
  464 + environment.send(status.chop + '_plugin', plugin_name+'Plugin')
  465 +end
  466 +
  467 +Then /^there should be an? (.+) named "([^\"]*)"$/ do |klass_name, profile_name|
  468 + klass = klass_name.camelize.constantize
  469 + klass.find_by_name(profile_name).nil?.should be_false
  470 +end
  471 +
  472 +Then /^"([^\"]*)" profile should exist$/ do |profile_selector|
  473 + profile = nil
  474 + begin
  475 + profile = Profile.find_by_name(profile_selector)
  476 + profile.nil?.should be_false
  477 + rescue
  478 + profile.nil?.should be_false
  479 + end
  480 +end
  481 +
  482 +Then /^"([^\"]*)" profile should not exist$/ do |profile_selector|
  483 + profile = nil
  484 + begin
  485 + profile = Profile.find_by_name(profile_selector)
  486 + profile.nil?.should be_true
  487 + rescue
  488 + profile.nil?.should be_true
  489 + end
  490 +end
... ...
features/unblock_button.feature
... ... @@ -10,11 +10,11 @@ Feature: unblock button
10 10 And the following blocks
11 11 | owner | type |
12 12 | sample-enterprise | DisabledEnterpriseMessageBlock |
13   - And Sample Enterprise is disabled
  13 + And enterprise "Sample Enterprise" is disabled
14 14  
15 15 Scenario: the environment administrator unblocks a blocked enterprise
16 16 Given I am logged in as admin
17   - And Sample Enterprise is blocked
  17 + And enterprise "Sample Enterprise" is blocked
18 18 And I am on Sample Enterprise's homepage
19 19 When I follow "Unblock"
20 20 Then I should not see "Unblock"
... ... @@ -24,7 +24,7 @@ Feature: unblock button
24 24 | login | name |
25 25 | joaosilva | Joao Silva |
26 26 And I am logged in as "joaosilva"
27   - And Sample Enterprise is blocked
  27 + And enterprise "Sample Enterprise" is blocked
28 28 When I am on Sample Enterprise's homepage
29 29 Then I should not see "Unblock"
30 30  
... ...
lib/noosfero/plugin.rb
... ... @@ -40,7 +40,11 @@ class Noosfero::Plugin
40 40 end
41 41  
42 42 def root_path
43   - Rails.root+'/plugins/'+public_name
  43 + File.join(RAILS_ROOT, 'plugins', public_name)
  44 + end
  45 +
  46 + def view_path
  47 + File.join(root_path,'views')
44 48 end
45 49  
46 50 # Here the developer should specify the meta-informations that the plugin can
... ... @@ -61,11 +65,6 @@ class Noosfero::Plugin
61 65 end
62 66 end
63 67  
64   - def expanded_template(file_path, locals = {})
65   - views_path = "#{RAILS_ROOT}/plugins/#{self.class.public_name}/views"
66   - ERB.new(File.read("#{views_path}/#{file_path}")).result(binding)
67   - end
68   -
69 68 # Here the developer may specify the events to which the plugins can
70 69 # register and must return true or false. The default value must be false.
71 70  
... ... @@ -92,7 +91,7 @@ class Noosfero::Plugin
92 91 # returns = { :title => title, :id => id, :content => content, :start => start }
93 92 # title = name that will be displayed.
94 93 # id = div id.
95   - # content = content of the tab (use expanded_template method to import content from another file).
  94 + # content = lambda block that creates a html code.
96 95 # start = boolean that specifies if the tab must come before noosfero tabs (optional).
97 96 def profile_tabs
98 97 nil
... ... @@ -104,6 +103,12 @@ class Noosfero::Plugin
104 103 nil
105 104 end
106 105  
  106 + # -> Adds content to calalog list item
  107 + # returns = lambda block that creates a html code
  108 + def catalog_list_item_extras(item)
  109 + nil
  110 + end
  111 +
107 112 # -> Adds content to products info
108 113 # returns = lambda block that creates a html code
109 114 def product_info_extras(product)
... ... @@ -116,13 +121,21 @@ class Noosfero::Plugin
116 121 nil
117 122 end
118 123  
  124 + # -> Adds a property to the product on asset products
  125 + # returns = {:name => name, :content => content}
  126 + # name = Name of the property
  127 + # content = lambda block that creates an html
  128 + def asset_product_properties(product)
  129 + nil
  130 + end
  131 +
119 132 # -> Adds content to the beginning of the page
120 133 # returns = lambda block that creates html code or raw rhtml/html.erb
121 134 def body_beginning
122 135 nil
123 136 end
124 137  
125   - # -> Add plugins' javascript files to application
  138 + # -> Adds plugins' javascript files to application
126 139 # returns = ['example1.js', 'javascripts/example2.js', 'example3.js']
127 140 def js_files
128 141 []
... ... @@ -140,4 +153,43 @@ class Noosfero::Plugin
140 153 raw_content
141 154 end
142 155  
  156 + # -> Adds links to the admin panel
  157 + # returns = {:title => title, :url => url}
  158 + # title = name that will be displayed in the link
  159 + # url = url or route to which the link will redirect to.
  160 + def admin_panel_links
  161 + nil
  162 + end
  163 +
  164 + # -> Adds buttons to manage members page
  165 + # returns = { :title => title, :icon => icon, :url => url }
  166 + # title = name that will be displayed.
  167 + # icon = css class name (for customized icons include them in a css file).
  168 + # url = url or route to which the button will redirect.
  169 + def manage_members_extra_buttons
  170 + nil
  171 + end
  172 +
  173 + # This is a generic hotspot for all controllers on Noosfero.
  174 + # If any plugin wants to define filters to run on any controller, the name of
  175 + # the hotspot must be in the following form: <underscored_controller_name>_filters.
  176 + # Example: for ProfileController the hotspot is profile_controller_filters
  177 + #
  178 + # -> Adds a filter to a controller
  179 + # returns = { :type => type,
  180 + # :method_name => method_name,
  181 + # :options => {:opt1 => opt1, :opt2 => opt2},
  182 + # :block => Proc or lambda block}
  183 + # type = 'before_filter' or 'after_filter'
  184 + # method_name = The name of the filter
  185 + # option = Filter options, like :only or :except
  186 + # block = Block that the filter will call
  187 + def method_missing(method, *args, &block)
  188 + if method.to_s =~ /^(.+)_controller_filters$/
  189 + []
  190 + else
  191 + super
  192 + end
  193 + end
  194 +
143 195 end
... ...
lib/noosfero/plugin/context.rb
... ... @@ -30,4 +30,8 @@ class Noosfero::Plugin::Context
30 30 @session ||= @controller.send(:session)
31 31 end
32 32  
  33 + def user
  34 + @user ||= @controller.send(:user)
  35 + end
  36 +
33 37 end
... ...
plugins/bsc/controllers/bsc_plugin_environment_controller.rb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +class BscPluginEnvironmentController < AdminController
  2 +
  3 + def new
  4 + @bsc = BscPlugin::Bsc.new(params[:profile_data])
  5 + if request.post? && @bsc.valid?
  6 + @bsc.user = current_user
  7 + @bsc.save!
  8 + @bsc.add_admin(user)
  9 + session[:notice] = _('Your Bsc was created.')
  10 + redirect_to :controller => 'profile_editor', :profile => @bsc.identifier
  11 + end
  12 + end
  13 +
  14 + def save_validations
  15 + enterprises = [Enterprise.find(params[:q].split(','))].flatten
  16 +
  17 + begin
  18 + enterprises.each { |enterprise| enterprise.validated = true ; enterprise.save! }
  19 + session[:notice] = _('Enterprises validated.')
  20 + redirect_to :controller => 'admin_panel'
  21 + rescue Exception => ex
  22 + session[:notice] = _('Enterprise validations couldn\'t be saved.')
  23 + logger.info ex
  24 + redirect_to :action => 'validate_enterprises'
  25 + end
  26 + end
  27 +
  28 + def search_enterprise
  29 + render :text => Enterprise.not_validated.find(:all, :conditions => ["type <> 'BscPlugin::Bsc' AND (name LIKE ? OR identifier LIKE ?)", "%#{params[:q]}%", "%#{params[:q]}%"]).
  30 + map {|enterprise| {:id => enterprise.id, :name => enterprise.name} }.
  31 + to_json
  32 + end
  33 +
  34 +end
  35 +
... ...
plugins/bsc/controllers/bsc_plugin_myprofile_controller.rb 0 → 100644
... ... @@ -0,0 +1,88 @@
  1 +class BscPluginMyprofileController < MyProfileController
  2 +
  3 + def manage_associated_enterprises
  4 + @associated_enterprises = profile.enterprises
  5 + @pending_enterprises = profile.enterprise_requests.pending.map(&:enterprise)
  6 + end
  7 +
  8 + def search_enterprise
  9 + render :text => environment.enterprises.find(:all, :conditions => ["type <> 'BscPlugin::Bsc' AND (LOWER(name) LIKE ? OR LOWER(identifier) LIKE ?)", "%#{params[:q]}%", "%#{params[:q]}%"]).
  10 + select { |enterprise| enterprise.bsc.nil? && !profile.already_requested?(enterprise)}.
  11 + map {|enterprise| {:id => enterprise.id, :name => enterprise.name} }.
  12 + to_json
  13 + end
  14 +
  15 + def save_associations
  16 + enterprises = [Enterprise.find(params[:q].split(','))].flatten
  17 + to_remove = profile.enterprises - enterprises
  18 + to_add = enterprises - profile.enterprises
  19 +
  20 + to_remove.each do |enterprise|
  21 + enterprise.bsc = nil
  22 + enterprise.save!
  23 + profile.enterprises.delete(enterprise)
  24 + end
  25 +
  26 + to_add.each do |enterprise|
  27 + if enterprise.enabled
  28 + BscPlugin::AssociateEnterprise.create!(:requestor => user, :target => enterprise, :bsc => profile)
  29 + else
  30 + enterprise.bsc = profile
  31 + enterprise.save!
  32 + profile.enterprises << enterprise
  33 + end
  34 + end
  35 +
  36 + session[:notice] = _('This Bsc associations were saved successfully.')
  37 + begin
  38 + redirect_to :controller => 'profile_editor'
  39 + rescue Exception => ex
  40 + session[:notice] = _('This Bsc associations couldn\'t be saved.')
  41 + logger.info ex
  42 + redirect_to :action => 'manage_associated_enterprises'
  43 + end
  44 + end
  45 +
  46 + def similar_enterprises
  47 + name = params[:name]
  48 + city = params[:city]
  49 +
  50 + result = []
  51 + if !name.blank?
  52 + enterprises = (profile.environment.enterprises - profile.enterprises).select { |enterprise| enterprise.bsc_id.nil? && enterprise.city == city && enterprise.name.downcase.include?(name.downcase)}
  53 + result = enterprises.inject(result) {|result, enterprise| result << [enterprise.id, enterprise.name]}
  54 + end
  55 + render :text => result.to_json
  56 + end
  57 +
  58 + def transfer_ownership
  59 + role = Profile::Roles.admin(profile.environment.id)
  60 + @roles = [role]
  61 + if request.post?
  62 + person = Person.find(params['q_'+role.key])
  63 +
  64 + profile.admins.map { |admin| profile.remove_admin(admin) }
  65 + profile.add_admin(person)
  66 +
  67 + BscPlugin::Mailer.deliver_admin_notification(person, profile)
  68 +
  69 + session[:notice] = _('Enterprise ownership transferred.')
  70 + redirect_to :controller => 'profile_editor'
  71 + end
  72 + end
  73 +
  74 + def create_enterprise
  75 + @create_enterprise = CreateEnterprise.new(params[:create_enterprise])
  76 + @create_enterprise.requestor = user
  77 + @create_enterprise.target = environment
  78 + @create_enterprise.bsc_id = profile.id
  79 + @create_enterprise.enabled = true
  80 + @create_enterprise.validated = false
  81 + if request.post? && @create_enterprise.valid?
  82 + @create_enterprise.perform
  83 + session[:notice] = _('Enterprise was created in association with %s.') % profile.name
  84 + redirect_to :controller => 'profile_editor', :profile => @create_enterprise.identifier
  85 + end
  86 + end
  87 +
  88 +end
... ...
plugins/bsc/db/migrate/20110609143043_add_bsc_to_enterprise.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddBscToEnterprise < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :profiles, :bsc_id, :integer
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :profiles, :bsc_id
  8 + end
  9 +end
... ...
plugins/bsc/db/migrate/20110610145112_add_bsc_fields.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddBscFields < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :profiles, :company_name, :string
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :profiles, :company_name
  8 + end
  9 +end
... ...
plugins/bsc/db/migrate/20110614183624_add_bsc_to_tasks.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddBscToTasks < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :tasks, :bsc_id, :integer
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :tasks, :bsc_id
  8 + end
  9 +end
... ...
plugins/bsc/lib/bsc_plugin.rb 0 → 100644
... ... @@ -0,0 +1,121 @@
  1 +require_dependency 'ext/enterprise'
  2 +require_dependency 'ext/product'
  3 +
  4 +class BscPlugin < Noosfero::Plugin
  5 +
  6 + Bsc
  7 +
  8 + def self.plugin_name
  9 + "Bsc"
  10 + end
  11 +
  12 + def self.plugin_description
  13 + _("Adds the Bsc feature")
  14 + end
  15 +
  16 + def admin_panel_links
  17 + [{:title => _('Create Bsc'), :url => {:controller => 'bsc_plugin_environment', :action => 'new'}},
  18 + {:title => _('Validate Enterprises'), :url => {:controller => 'bsc_plugin_environment', :action => 'validate_enterprises'}} ]
  19 + end
  20 +
  21 + def control_panel_buttons
  22 + buttons = []
  23 + buttons << {:title => _("Manage associated enterprises"), :icon => 'bsc-enterprises', :url => {:controller => 'bsc_plugin_myprofile', :action => 'manage_associated_enterprises'}} if bsc?(context.profile)
  24 + buttons << {:title => _('Transfer ownership'), :icon => 'transfer-enterprise-ownership', :url => {:controller => 'bsc_plugin_myprofile', :action => 'transfer_ownership'}} if context.profile.enterprise?
  25 + buttons
  26 + end
  27 +
  28 + def manage_members_extra_buttons
  29 + {:title => _('Transfer ownership'), :icon => '', :url => {:controller => 'bsc_plugin_myprofile', :action => 'transfer_enterprises_management'}} if context.profile.enterprise?
  30 + end
  31 +
  32 + def stylesheet?
  33 + true
  34 + end
  35 +
  36 + def catalog_list_item_extras(product)
  37 + if bsc?(context.profile)
  38 + enterprise = product.enterprise
  39 + if is_member_of_any_bsc?(context.user)
  40 + lambda {link_to(enterprise.short_name, enterprise.url, :class => 'bsc-catalog-enterprise-link')}
  41 + else
  42 + lambda {enterprise.short_name}
  43 + end
  44 + end
  45 + end
  46 +
  47 + def profile_controller_filters
  48 + if profile
  49 + special_enterprise = profile.enterprise? && !profile.validated && profile.bsc
  50 + is_member_of_any_bsc = is_member_of_any_bsc?(context.user)
  51 + block = lambda {
  52 + render_access_denied if special_enterprise && !is_member_of_any_bsc
  53 + }
  54 +
  55 + [{ :type => 'before_filter', :method_name => 'bsc_access', :block => block }]
  56 + else
  57 + []
  58 + end
  59 + end
  60 +
  61 + def content_viewer_controller_filters
  62 + if profile
  63 + special_enterprise = profile.enterprise? && !profile.validated && profile.bsc
  64 + is_member_of_any_bsc = is_member_of_any_bsc?(context.user)
  65 + block = lambda {
  66 + render_access_denied if special_enterprise && !is_member_of_any_bsc
  67 + }
  68 +
  69 + [{ :type => 'before_filter', :method_name => 'bsc_access', :block => block }]
  70 + else
  71 + []
  72 + end
  73 + end
  74 +
  75 + def profile_editor_controller_filters
  76 + if context.user
  77 + is_not_admin = !context.environment.admins.include?(context.user)
  78 + [{ :type => 'before_filter',
  79 + :method_name => 'bsc_destroy_access',
  80 + :options => {:only => :destroy_profile},
  81 + :block => lambda { render_access_denied if is_not_admin } }]
  82 + else
  83 + []
  84 + end
  85 + end
  86 +
  87 + def asset_product_properties(product)
  88 + properties = []
  89 + properties << { :name => _('Bsc'), :content => lambda { link_to(product.bsc.name, product.bsc.url) } } if product.bsc
  90 + if product.enterprise.validated || is_member_of_any_bsc?(context.user)
  91 + content = lambda { link_to_homepage(product.enterprise.name, product.enterprise.identifier) }
  92 + else
  93 + content = lambda { product.enterprise.name }
  94 + end
  95 + properties << { :name => _('Supplier'), :content => content }
  96 + end
  97 +
  98 + def profile_tabs
  99 + if bsc?(context.profile)
  100 + { :title => _("Contact"),
  101 + :id => 'bsc-contact',
  102 + :content => lambda { render :partial => 'profile_tab' },
  103 + :start => true }
  104 + end
  105 + end
  106 +
  107 + private
  108 +
  109 + def bsc?(profile)
  110 + profile.kind_of?(BscPlugin::Bsc)
  111 + end
  112 +
  113 + def is_member_of_any_bsc?(user)
  114 + BscPlugin::Bsc.all.any? { |bsc| bsc.members.include?(user) }
  115 + end
  116 +
  117 + def profile
  118 + context.environment.profiles.find_by_identifier(context.params[:profile])
  119 + end
  120 +
  121 +end
... ...
plugins/bsc/lib/bsc_plugin/associate_enterprise.rb 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +class BscPlugin::AssociateEnterprise < Task
  2 +
  3 + alias :enterprise :target
  4 +
  5 + belongs_to :bsc, :class_name => 'BscPlugin::Bsc'
  6 +
  7 + validates_presence_of :bsc
  8 +
  9 + def title
  10 + _("BSC association")
  11 + end
  12 +
  13 + def linked_subject
  14 + {:text => bsc.name, :url => bsc.url}
  15 + end
  16 +
  17 + def information
  18 + {:message => _('%{requestor} wants to associate this enterprise with %{linked_subject}.')}
  19 + end
  20 +
  21 + def icon
  22 + src = bsc.image ? bsc.image.public_filename(:minor) : '/images/icons-app/enterprise-minor.png'
  23 + {:type => :defined_image, :src => src, :name => bsc.name}
  24 + end
  25 +
  26 + def reject_details
  27 + true
  28 + end
  29 +
  30 + def perform
  31 + bsc.enterprises << enterprise
  32 + end
  33 +
  34 + def task_finished_message
  35 + _('%{enterprise} accepted your request to associate it with %{bsc}.') % {:enterprise => enterprise.name, :bsc => bsc.name}
  36 + end
  37 +
  38 + def task_cancelled_message
  39 + message = _("%{enterprise} rejected your request to associate it with %{bsc}.") % {:enterprise => enterprise.name, :bsc => bsc.name}
  40 + if !reject_explanation.blank?
  41 + message += " " + _("Here is the reject explanation left by the administrator:\n\n%{reject_explanation}") % {:reject_explanation => reject_explanation}
  42 + end
  43 + end
  44 +
  45 + def target_notification_message
  46 + _('%{requestor} wants assoaciate %{bsc} as your BSC.') % {:requestor => requestor.name, :enterprise => enterprise.name, :bsc => bsc.name}
  47 + end
  48 +
  49 +end
... ...
plugins/bsc/lib/bsc_plugin/bsc.rb 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +class BscPlugin::Bsc < Enterprise
  2 +
  3 + has_many :enterprises
  4 + has_many :enterprise_requests, :class_name => 'BscPlugin::AssociateEnterprise'
  5 +
  6 + validates_presence_of :nickname
  7 + validates_presence_of :company_name
  8 + validates_presence_of :cnpj
  9 + validates_uniqueness_of :nickname
  10 + validates_uniqueness_of :company_name
  11 + validates_uniqueness_of :cnpj
  12 +
  13 + before_validation do |bsc|
  14 + bsc.name = bsc.business_name || 'Sample name'
  15 + end
  16 +
  17 + def already_requested?(enterprise)
  18 + enterprise_requests.pending.map(&:enterprise).include?(enterprise)
  19 + end
  20 +
  21 + def enterprises_to_json
  22 + enterprises.map { |enterprise| {:id => enterprise.id, :name => enterprise.name} }.to_json
  23 + end
  24 +
  25 + def control_panel_settings_button
  26 + {:title => _('Bsc info and settings'), :icon => 'edit-profile-enterprise'}
  27 + end
  28 +
  29 + def products(reload_flag=false)
  30 + reload if reload_flag
  31 + enterprises.map { |enterprise| enterprise.products }.flatten
  32 + end
  33 +
  34 + def create_product?
  35 + false
  36 + end
  37 +
  38 + def self.identification
  39 + 'Bsc'
  40 + end
  41 +
  42 +end
... ...
plugins/bsc/lib/bsc_plugin/mailer.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class BscPlugin::Mailer < Noosfero::Plugin::MailerBase
  2 +
  3 + prepend_view_path(BscPlugin.root_path+'/views')
  4 +
  5 + def admin_notification(admin, bsc)
  6 + domain = bsc.hostname || bsc.environment.default_hostname
  7 + recipients admin.contact_email
  8 + from 'no-reply@' + domain
  9 + subject _("[%s] Bsc management transferred to you.") % bsc.name
  10 + content_type 'text/html'
  11 + body :bsc => bsc
  12 + end
  13 +end
... ...
plugins/bsc/lib/ext/enterprise.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +require_dependency 'enterprise'
  2 +
  3 +class Enterprise
  4 + belongs_to :bsc, :class_name => 'BscPlugin::Bsc'
  5 + FIELDS << 'bsc_id'
  6 + FIELDS << 'enabled'
  7 + FIELDS << 'validated'
  8 +
  9 + named_scope :validated, :conditions => {:validated => true}
  10 + named_scope :not_validated, :conditions => {:validated => false}
  11 +end
... ...
plugins/bsc/lib/ext/product.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +require_dependency 'product'
  2 +
  3 +class Product
  4 + def bsc
  5 + enterprise.bsc if enterprise
  6 + end
  7 +
  8 + def display_supplier_on_search?
  9 + false
  10 + end
  11 +end
... ...
plugins/bsc/public/images/manage-bsc-enterprises-icon.png 0 → 100644

978 Bytes

plugins/bsc/public/images/manage-bsc-enterprises.gif 0 → 100644

2.05 KB

plugins/bsc/public/images/manage-bsc-enterprises.png 0 → 100644

3.84 KB

plugins/bsc/public/images/manage-bsc-enterprises.svg 0 → 100644
... ... @@ -0,0 +1,1309 @@
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<svg
  3 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  4 + xmlns:cc="http://creativecommons.org/ns#"
  5 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  6 + xmlns:svg="http://www.w3.org/2000/svg"
  7 + xmlns="http://www.w3.org/2000/svg"
  8 + xmlns:xlink="http://www.w3.org/1999/xlink"
  9 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  10 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  11 + width="48"
  12 + height="48"
  13 + overflow="visible"
  14 + enable-background="new 0 0 128 129.396"
  15 + xml:space="preserve"
  16 + id="svg2"
  17 + sodipodi:version="0.32"
  18 + inkscape:version="0.47 r22583"
  19 + sodipodi:docname="BSC.svg"
  20 + version="1.0"
  21 + inkscape:export-filename="/home/tigert/My Downloads/go-home.png"
  22 + inkscape:export-xdpi="90.000000"
  23 + inkscape:export-ydpi="90.000000"><metadata
  24 + id="metadata367"><rdf:RDF><cc:Work
  25 + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
  26 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><cc:license
  27 + rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" /><dc:title /><dc:creator><cc:Agent><dc:title>Jakub Steiner</dc:title></cc:Agent></dc:creator><dc:source>http://jimmac.musichall.cz</dc:source><dc:subject><rdf:Bag><rdf:li>home</rdf:li><rdf:li>return</rdf:li><rdf:li>go</rdf:li><rdf:li>default</rdf:li><rdf:li>user</rdf:li><rdf:li>directory</rdf:li></rdf:Bag></dc:subject><dc:contributor><cc:Agent><dc:title>Tuomas Kuosmanen</dc:title></cc:Agent></dc:contributor></cc:Work><cc:License
  28 + rdf:about="http://creativecommons.org/licenses/by-sa/2.0/"><cc:permits
  29 + rdf:resource="http://web.resource.org/cc/Reproduction" /><cc:permits
  30 + rdf:resource="http://web.resource.org/cc/Distribution" /><cc:requires
  31 + rdf:resource="http://web.resource.org/cc/Notice" /><cc:requires
  32 + rdf:resource="http://web.resource.org/cc/Attribution" /><cc:permits
  33 + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /><cc:requires
  34 + rdf:resource="http://web.resource.org/cc/ShareAlike" /></cc:License></rdf:RDF></metadata><defs
  35 + id="defs365"><linearGradient
  36 + inkscape:collect="always"
  37 + id="linearGradient3177"><stop
  38 + style="stop-color:#729fcf;stop-opacity:1"
  39 + offset="0"
  40 + id="stop3179" /><stop
  41 + style="stop-color:#204a87;stop-opacity:1"
  42 + offset="1"
  43 + id="stop3181" /></linearGradient><linearGradient
  44 + id="linearGradient3171"><stop
  45 + id="stop3173"
  46 + offset="0"
  47 + style="stop-color:#d3d7cf;stop-opacity:1" /><stop
  48 + id="stop3175"
  49 + offset="1"
  50 + style="stop-color:#888a85;stop-opacity:1" /></linearGradient><linearGradient
  51 + inkscape:collect="always"
  52 + id="linearGradient5161"><stop
  53 + style="stop-color:#c17d11;stop-opacity:1;"
  54 + offset="0"
  55 + id="stop5163" /><stop
  56 + style="stop-color:#8f5902;stop-opacity:1"
  57 + offset="1"
  58 + id="stop5165" /></linearGradient><linearGradient
  59 + inkscape:collect="always"
  60 + id="linearGradient3771"><stop
  61 + style="stop-color:#ffffff;stop-opacity:1;"
  62 + offset="0"
  63 + id="stop3773" /><stop
  64 + style="stop-color:#ffffff;stop-opacity:0;"
  65 + offset="1"
  66 + id="stop3775" /></linearGradient><linearGradient
  67 + gradientTransform="matrix(0.314683,0.000000,0.000000,0.314683,4.128264,3.742874)"
  68 + y2="59.7995"
  69 + x2="48.046001"
  70 + y1="117.5205"
  71 + x1="80.223602"
  72 + gradientUnits="userSpaceOnUse"
  73 + id="linearGradient3736">
  74 + <stop
  75 + id="stop3738"
  76 + style="stop-color:#CCCCCC"
  77 + offset="0" />
  78 + <stop
  79 + id="stop3740"
  80 + style="stop-color:#FFFFFF"
  81 + offset="0.81071424" />
  82 + <midPointStop
  83 + id="midPointStop3742"
  84 + style="stop-color:#CCCCCC"
  85 + offset="0" />
  86 + <midPointStop
  87 + id="midPointStop3744"
  88 + style="stop-color:#CCCCCC"
  89 + offset="0.5" />
  90 + <midPointStop
  91 + id="midPointStop3746"
  92 + style="stop-color:#FFFFFF"
  93 + offset="0.9831" />
  94 + </linearGradient><inkscape:perspective
  95 + sodipodi:type="inkscape:persp3d"
  96 + inkscape:vp_x="0 : 24 : 1"
  97 + inkscape:vp_y="0 : 1000 : 0"
  98 + inkscape:vp_z="48 : 24 : 1"
  99 + inkscape:persp3d-origin="24 : 16 : 1"
  100 + id="perspective92" /><linearGradient
  101 + inkscape:collect="always"
  102 + id="linearGradient5060"><stop
  103 + style="stop-color:black;stop-opacity:1;"
  104 + offset="0"
  105 + id="stop5062" /><stop
  106 + style="stop-color:black;stop-opacity:0;"
  107 + offset="1"
  108 + id="stop5064" /></linearGradient><linearGradient
  109 + id="linearGradient5048"><stop
  110 + style="stop-color:black;stop-opacity:0;"
  111 + offset="0"
  112 + id="stop5050" /><stop
  113 + id="stop5056"
  114 + offset="0.5"
  115 + style="stop-color:black;stop-opacity:1;" /><stop
  116 + style="stop-color:black;stop-opacity:0;"
  117 + offset="1"
  118 + id="stop5052" /></linearGradient><linearGradient
  119 + id="linearGradient2406"><stop
  120 + style="stop-color:#7c7e79;stop-opacity:1;"
  121 + offset="0"
  122 + id="stop2408" /><stop
  123 + id="stop2414"
  124 + offset="0.1724138"
  125 + style="stop-color:#848681;stop-opacity:1;" /><stop
  126 + style="stop-color:#898c86;stop-opacity:1;"
  127 + offset="1"
  128 + id="stop2410" /></linearGradient><linearGradient
  129 + inkscape:collect="always"
  130 + id="linearGradient2390"><stop
  131 + style="stop-color:#919191;stop-opacity:1;"
  132 + offset="0"
  133 + id="stop2392" /><stop
  134 + style="stop-color:#919191;stop-opacity:0;"
  135 + offset="1"
  136 + id="stop2394" /></linearGradient><linearGradient
  137 + inkscape:collect="always"
  138 + id="linearGradient2378"><stop
  139 + style="stop-color:#575757;stop-opacity:1;"
  140 + offset="0"
  141 + id="stop2380" /><stop
  142 + style="stop-color:#575757;stop-opacity:0;"
  143 + offset="1"
  144 + id="stop2382" /></linearGradient><linearGradient
  145 + inkscape:collect="always"
  146 + id="linearGradient2368"><stop
  147 + style="stop-color:#ffffff;stop-opacity:1;"
  148 + offset="0"
  149 + id="stop2370" /><stop
  150 + style="stop-color:#ffffff;stop-opacity:0;"
  151 + offset="1"
  152 + id="stop2372" /></linearGradient><linearGradient
  153 + inkscape:collect="always"
  154 + id="linearGradient2349"><stop
  155 + style="stop-color:#000000;stop-opacity:1;"
  156 + offset="0"
  157 + id="stop2351" /><stop
  158 + style="stop-color:#000000;stop-opacity:0;"
  159 + offset="1"
  160 + id="stop2353" /></linearGradient><linearGradient
  161 + id="linearGradient2341"><stop
  162 + id="stop2343"
  163 + offset="0"
  164 + style="stop-color:#000000;stop-opacity:1;" /><stop
  165 + id="stop2345"
  166 + offset="1"
  167 + style="stop-color:#000000;stop-opacity:0;" /></linearGradient><linearGradient
  168 + id="linearGradient2329"><stop
  169 + style="stop-color:#000000;stop-opacity:0.18556701;"
  170 + offset="0"
  171 + id="stop2331" /><stop
  172 + style="stop-color:#ffffff;stop-opacity:1;"
  173 + offset="1"
  174 + id="stop2333" /></linearGradient><linearGradient
  175 + inkscape:collect="always"
  176 + id="linearGradient2319"><stop
  177 + style="stop-color:#000000;stop-opacity:1;"
  178 + offset="0"
  179 + id="stop2321" /><stop
  180 + style="stop-color:#000000;stop-opacity:0;"
  181 + offset="1"
  182 + id="stop2323" /></linearGradient><linearGradient
  183 + id="linearGradient2307"><stop
  184 + style="stop-color:#edd400;stop-opacity:1;"
  185 + offset="0"
  186 + id="stop2309" /><stop
  187 + style="stop-color:#998800;stop-opacity:1;"
  188 + offset="1"
  189 + id="stop2311" /></linearGradient><linearGradient
  190 + inkscape:collect="always"
  191 + id="linearGradient2299"><stop
  192 + style="stop-color:#ffffff;stop-opacity:1;"
  193 + offset="0"
  194 + id="stop2301" /><stop
  195 + style="stop-color:#ffffff;stop-opacity:0;"
  196 + offset="1"
  197 + id="stop2303" /></linearGradient><linearGradient
  198 + id="XMLID_2_"
  199 + gradientUnits="userSpaceOnUse"
  200 + x1="80.223602"
  201 + y1="117.5205"
  202 + x2="48.046001"
  203 + y2="59.7995"
  204 + gradientTransform="matrix(0.314683,0.000000,0.000000,0.314683,4.128264,3.742874)">
  205 + <stop
  206 + offset="0"
  207 + style="stop-color:#CCCCCC"
  208 + id="stop17" />
  209 + <stop
  210 + offset="0.9831"
  211 + style="stop-color:#FFFFFF"
  212 + id="stop19" />
  213 + <midPointStop
  214 + offset="0"
  215 + style="stop-color:#CCCCCC"
  216 + id="midPointStop48" />
  217 + <midPointStop
  218 + offset="0.5"
  219 + style="stop-color:#CCCCCC"
  220 + id="midPointStop50" />
  221 + <midPointStop
  222 + offset="0.9831"
  223 + style="stop-color:#FFFFFF"
  224 + id="midPointStop52" />
  225 + </linearGradient><linearGradient
  226 + id="XMLID_39_"
  227 + gradientUnits="userSpaceOnUse"
  228 + x1="64.387703"
  229 + y1="65.124001"
  230 + x2="64.387703"
  231 + y2="35.569"
  232 + gradientTransform="matrix(0.354101,0.000000,0.000000,0.354101,1.638679,-8.364921e-2)">
  233 + <stop
  234 + offset="0"
  235 + style="stop-color:#FFFFFF"
  236 + id="stop336" />
  237 + <stop
  238 + offset="0.8539"
  239 + style="stop-color:#FF6200"
  240 + id="stop338" />
  241 + <stop
  242 + offset="1"
  243 + style="stop-color:#F25D00"
  244 + id="stop340" />
  245 + <midPointStop
  246 + offset="0"
  247 + style="stop-color:#FFFFFF"
  248 + id="midPointStop335" />
  249 + <midPointStop
  250 + offset="0.5"
  251 + style="stop-color:#FFFFFF"
  252 + id="midPointStop337" />
  253 + <midPointStop
  254 + offset="0.8539"
  255 + style="stop-color:#FF6200"
  256 + id="midPointStop339" />
  257 + <midPointStop
  258 + offset="0.5"
  259 + style="stop-color:#FF6200"
  260 + id="midPointStop341" />
  261 + <midPointStop
  262 + offset="1"
  263 + style="stop-color:#F25D00"
  264 + id="midPointStop343" />
  265 + </linearGradient><radialGradient
  266 + inkscape:collect="always"
  267 + xlink:href="#linearGradient2307"
  268 + id="radialGradient2313"
  269 + cx="19.985598"
  270 + cy="36.77816"
  271 + fx="19.985598"
  272 + fy="36.77816"
  273 + r="1.0821035"
  274 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.565787)"
  275 + gradientUnits="userSpaceOnUse" /><radialGradient
  276 + inkscape:collect="always"
  277 + xlink:href="#linearGradient2319"
  278 + id="radialGradient2325"
  279 + cx="20.443665"
  280 + cy="37.425829"
  281 + fx="20.443665"
  282 + fy="37.425829"
  283 + r="1.0821035"
  284 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.731106)"
  285 + gradientUnits="userSpaceOnUse" /><radialGradient
  286 + inkscape:collect="always"
  287 + xlink:href="#linearGradient2349"
  288 + id="radialGradient2355"
  289 + cx="24.023088"
  290 + cy="40.56913"
  291 + fx="24.023088"
  292 + fy="40.56913"
  293 + r="16.28684"
  294 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.431250,1.157278e-15,23.07369)"
  295 + gradientUnits="userSpaceOnUse" /><linearGradient
  296 + inkscape:collect="always"
  297 + xlink:href="#linearGradient2390"
  298 + id="linearGradient2396"
  299 + x1="30.603519"
  300 + y1="37.337803"
  301 + x2="30.603519"
  302 + y2="36.112415"
  303 + gradientUnits="userSpaceOnUse"
  304 + gradientTransform="matrix(1.263867,0,0,0.859794,-6.499556,8.390924)" /><linearGradient
  305 + inkscape:collect="always"
  306 + xlink:href="#linearGradient5048"
  307 + id="linearGradient3710"
  308 + gradientUnits="userSpaceOnUse"
  309 + gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
  310 + x1="302.85715"
  311 + y1="366.64789"
  312 + x2="302.85715"
  313 + y2="609.50507" /><radialGradient
  314 + inkscape:collect="always"
  315 + xlink:href="#linearGradient5060"
  316 + id="radialGradient3712"
  317 + gradientUnits="userSpaceOnUse"
  318 + gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
  319 + cx="605.71429"
  320 + cy="486.64789"
  321 + fx="605.71429"
  322 + fy="486.64789"
  323 + r="117.14286" /><radialGradient
  324 + inkscape:collect="always"
  325 + xlink:href="#linearGradient5060"
  326 + id="radialGradient3714"
  327 + gradientUnits="userSpaceOnUse"
  328 + gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
  329 + cx="605.71429"
  330 + cy="486.64789"
  331 + fx="605.71429"
  332 + fy="486.64789"
  333 + r="117.14286" /><linearGradient
  334 + inkscape:collect="always"
  335 + xlink:href="#XMLID_2_"
  336 + id="linearGradient3716"
  337 + gradientUnits="userSpaceOnUse"
  338 + gradientTransform="matrix(0.336922,0,0,0.166888,-42.01712,15.46151)"
  339 + x1="52.006104"
  340 + y1="166.1331"
  341 + x2="14.049017"
  342 + y2="-42.218513" /><linearGradient
  343 + inkscape:collect="always"
  344 + xlink:href="#linearGradient2329"
  345 + id="linearGradient3718"
  346 + gradientUnits="userSpaceOnUse"
  347 + gradientTransform="matrix(0.898789,0,0,1.071914,-59.521975,-2.080838)"
  348 + x1="17.602522"
  349 + y1="26.057423"
  350 + x2="17.682528"
  351 + y2="32.654099" /><radialGradient
  352 + inkscape:collect="always"
  353 + xlink:href="#linearGradient2378"
  354 + id="radialGradient3720"
  355 + gradientUnits="userSpaceOnUse"
  356 + gradientTransform="matrix(1.125263,-3.585417e-8,4.269819e-8,1.340059,-63.006704,1.355395)"
  357 + cx="24.195112"
  358 + cy="10.577631"
  359 + fx="24.195112"
  360 + fy="10.577631"
  361 + r="15.242914" /><linearGradient
  362 + inkscape:collect="always"
  363 + xlink:href="#linearGradient2406"
  364 + id="linearGradient3722"
  365 + gradientUnits="userSpaceOnUse"
  366 + gradientTransform="matrix(0.888785,0,0,1.08932,-57.58901,-1.524336)"
  367 + x1="17.850183"
  368 + y1="28.939463"
  369 + x2="19.040216"
  370 + y2="41.03223" /><radialGradient
  371 + inkscape:collect="always"
  372 + xlink:href="#linearGradient2319"
  373 + id="radialGradient3724"
  374 + gradientUnits="userSpaceOnUse"
  375 + gradientTransform="matrix(1.125263,0,0,0.982744,-63.428678,0.731106)"
  376 + cx="20.443665"
  377 + cy="37.425829"
  378 + fx="20.443665"
  379 + fy="37.425829"
  380 + r="1.0821035" /><radialGradient
  381 + inkscape:collect="always"
  382 + xlink:href="#linearGradient2307"
  383 + id="radialGradient3726"
  384 + gradientUnits="userSpaceOnUse"
  385 + gradientTransform="matrix(1.125263,0,0,0.982744,-63.428678,0.565787)"
  386 + cx="19.985598"
  387 + cy="36.77816"
  388 + fx="19.985598"
  389 + fy="36.77816"
  390 + r="1.0821035" /><linearGradient
  391 + inkscape:collect="always"
  392 + xlink:href="#XMLID_39_"
  393 + id="linearGradient3728"
  394 + gradientUnits="userSpaceOnUse"
  395 + gradientTransform="matrix(0.354101,0,0,0.354101,-58.361321,-0.08364921)"
  396 + x1="64.387703"
  397 + y1="65.124001"
  398 + x2="64.387703"
  399 + y2="35.569" /><radialGradient
  400 + inkscape:collect="always"
  401 + xlink:href="#linearGradient2299"
  402 + id="radialGradient3730"
  403 + gradientUnits="userSpaceOnUse"
  404 + gradientTransform="matrix(4.100086,0,0,4.201322,-85.41506,-78.53967)"
  405 + cx="7.5326638"
  406 + cy="24.202574"
  407 + fx="7.5326638"
  408 + fy="24.202574"
  409 + r="8.2452128" /><radialGradient
  410 + inkscape:collect="always"
  411 + xlink:href="#linearGradient2341"
  412 + id="radialGradient3732"
  413 + gradientUnits="userSpaceOnUse"
  414 + gradientTransform="matrix(4.100086,0,0,-4.201322,-65.198109,105.3535)"
  415 + cx="11.68129"
  416 + cy="19.554111"
  417 + fx="11.68129"
  418 + fy="19.554111"
  419 + r="8.2452126" /><radialGradient
  420 + inkscape:collect="always"
  421 + xlink:href="#linearGradient2368"
  422 + id="radialGradient3734"
  423 + gradientUnits="userSpaceOnUse"
  424 + gradientTransform="matrix(3.751495,0,0,3.147818,-142.00907,-65.70704)"
  425 + cx="29.913452"
  426 + cy="30.442923"
  427 + fx="29.913452"
  428 + fy="30.442923"
  429 + r="4.0018832" /><linearGradient
  430 + inkscape:collect="always"
  431 + xlink:href="#linearGradient3736"
  432 + id="linearGradient4355"
  433 + gradientUnits="userSpaceOnUse"
  434 + gradientTransform="matrix(0.336922,0.000000,0.000000,0.166888,17.98288,15.46151)"
  435 + x1="49.023571"
  436 + y1="173.99986"
  437 + x2="19.343113"
  438 + y2="-5.7614088" /><radialGradient
  439 + inkscape:collect="always"
  440 + xlink:href="#linearGradient2378"
  441 + id="radialGradient4379"
  442 + gradientUnits="userSpaceOnUse"
  443 + gradientTransform="matrix(-2.7847815e-8,0.91845951,-1.1152723,-3.3815205e-8,39.60096,1.4161083)"
  444 + cx="28.399611"
  445 + cy="15.333439"
  446 + fx="28.399611"
  447 + fy="15.333439"
  448 + r="15.242914" /><radialGradient
  449 + inkscape:collect="always"
  450 + xlink:href="#linearGradient2341"
  451 + id="radialGradient4473"
  452 + gradientUnits="userSpaceOnUse"
  453 + gradientTransform="matrix(-2.0618025,-2.0407128e-7,-3.2411324e-7,3.2746279,61.522713,-37.078328)"
  454 + cx="11.651313"
  455 + cy="20.026194"
  456 + fx="11.651313"
  457 + fy="20.026194"
  458 + r="8.2452126" /><radialGradient
  459 + inkscape:collect="always"
  460 + xlink:href="#linearGradient2299"
  461 + id="radialGradient4657"
  462 + gradientUnits="userSpaceOnUse"
  463 + gradientTransform="matrix(3.39591,-4.6697806e-7,4.6697814e-7,3.3959105,-19.762019,-53.978222)"
  464 + cx="8.027895"
  465 + cy="24.287516"
  466 + fx="8.027895"
  467 + fy="24.287516"
  468 + r="8.2452128" /><linearGradient
  469 + inkscape:collect="always"
  470 + xlink:href="#linearGradient2329"
  471 + id="linearGradient4661"
  472 + gradientUnits="userSpaceOnUse"
  473 + gradientTransform="matrix(0.67409175,0,0,0.92339382,5.1085187,4.2256015)"
  474 + x1="15.415529"
  475 + y1="23.833527"
  476 + x2="16.899008"
  477 + y2="33.219193" /><linearGradient
  478 + inkscape:collect="always"
  479 + xlink:href="#linearGradient3771"
  480 + id="linearGradient4665"
  481 + gradientUnits="userSpaceOnUse"
  482 + gradientTransform="translate(-1,1)"
  483 + x1="26.5"
  484 + y1="36.5"
  485 + x2="24.5"
  486 + y2="28.5" /><radialGradient
  487 + inkscape:collect="always"
  488 + xlink:href="#linearGradient2368"
  489 + id="radialGradient4669"
  490 + gradientUnits="userSpaceOnUse"
  491 + gradientTransform="matrix(0.11004748,1.8133708,-1.8164852,0.18222563,79.460078,-26.062392)"
  492 + cx="29.913452"
  493 + cy="30.442923"
  494 + fx="29.913452"
  495 + fy="30.442923"
  496 + r="4.0018832" /><radialGradient
  497 + inkscape:collect="always"
  498 + xlink:href="#linearGradient5060"
  499 + id="radialGradient4693"
  500 + gradientUnits="userSpaceOnUse"
  501 + gradientTransform="matrix(-2.6052648,0,0,1.9930785,128.02878,-872.62632)"
  502 + cx="605.71429"
  503 + cy="486.64789"
  504 + fx="605.71429"
  505 + fy="486.64789"
  506 + r="117.14286" /><linearGradient
  507 + inkscape:collect="always"
  508 + xlink:href="#linearGradient5048"
  509 + id="linearGradient4697"
  510 + gradientUnits="userSpaceOnUse"
  511 + gradientTransform="matrix(2.3780477,0,0,1.969706,-1808.0185,-845.99583)"
  512 + x1="304.19855"
  513 + y1="357.48697"
  514 + x2="304.19855"
  515 + y2="603.21515" /><radialGradient
  516 + inkscape:collect="always"
  517 + xlink:href="#linearGradient5060"
  518 + id="radialGradient4701"
  519 + gradientUnits="userSpaceOnUse"
  520 + gradientTransform="matrix(2.6052656,0,0,1.9930785,-2039.1167,-872.62632)"
  521 + cx="605.71429"
  522 + cy="486.64789"
  523 + fx="605.71429"
  524 + fy="486.64789"
  525 + r="117.14286" /><linearGradient
  526 + inkscape:collect="always"
  527 + xlink:href="#linearGradient5048"
  528 + id="linearGradient4765"
  529 + gradientUnits="userSpaceOnUse"
  530 + gradientTransform="matrix(2.3780477,0,0,1.969706,-1808.0185,-845.99583)"
  531 + x1="304.19855"
  532 + y1="357.48697"
  533 + x2="304.19855"
  534 + y2="603.21515" /><radialGradient
  535 + inkscape:collect="always"
  536 + xlink:href="#linearGradient5060"
  537 + id="radialGradient4767"
  538 + gradientUnits="userSpaceOnUse"
  539 + gradientTransform="matrix(2.6052656,0,0,1.9930785,-2039.1167,-872.62632)"
  540 + cx="605.71429"
  541 + cy="486.64789"
  542 + fx="605.71429"
  543 + fy="486.64789"
  544 + r="117.14286" /><radialGradient
  545 + inkscape:collect="always"
  546 + xlink:href="#linearGradient5060"
  547 + id="radialGradient4769"
  548 + gradientUnits="userSpaceOnUse"
  549 + gradientTransform="matrix(-2.6052648,0,0,1.9930785,128.02878,-872.62632)"
  550 + cx="605.71429"
  551 + cy="486.64789"
  552 + fx="605.71429"
  553 + fy="486.64789"
  554 + r="117.14286" /><linearGradient
  555 + inkscape:collect="always"
  556 + xlink:href="#linearGradient3736"
  557 + id="linearGradient4771"
  558 + gradientUnits="userSpaceOnUse"
  559 + gradientTransform="matrix(0.336922,0,0,0.166888,17.98288,15.46151)"
  560 + x1="49.023571"
  561 + y1="173.99986"
  562 + x2="19.343113"
  563 + y2="-5.7614088" /><linearGradient
  564 + inkscape:collect="always"
  565 + xlink:href="#linearGradient2329"
  566 + id="linearGradient4773"
  567 + gradientUnits="userSpaceOnUse"
  568 + gradientTransform="matrix(0.67409175,0,0,0.92339382,5.1085187,4.2256015)"
  569 + x1="15.415529"
  570 + y1="23.833527"
  571 + x2="16.899008"
  572 + y2="33.219193" /><radialGradient
  573 + inkscape:collect="always"
  574 + xlink:href="#linearGradient2319"
  575 + id="radialGradient4775"
  576 + gradientUnits="userSpaceOnUse"
  577 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.731106)"
  578 + cx="20.443665"
  579 + cy="37.425829"
  580 + fx="20.443665"
  581 + fy="37.425829"
  582 + r="1.0821035" /><radialGradient
  583 + inkscape:collect="always"
  584 + xlink:href="#linearGradient2307"
  585 + id="radialGradient4777"
  586 + gradientUnits="userSpaceOnUse"
  587 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.565787)"
  588 + cx="19.985598"
  589 + cy="36.77816"
  590 + fx="19.985598"
  591 + fy="36.77816"
  592 + r="1.0821035" /><radialGradient
  593 + inkscape:collect="always"
  594 + xlink:href="#linearGradient2368"
  595 + id="radialGradient4779"
  596 + gradientUnits="userSpaceOnUse"
  597 + gradientTransform="matrix(0.11004748,1.8133708,-1.8164852,0.18222563,79.460078,-26.062392)"
  598 + cx="29.913452"
  599 + cy="30.442923"
  600 + fx="29.913452"
  601 + fy="30.442923"
  602 + r="4.0018832" /><linearGradient
  603 + inkscape:collect="always"
  604 + xlink:href="#linearGradient3771"
  605 + id="linearGradient4781"
  606 + gradientUnits="userSpaceOnUse"
  607 + gradientTransform="translate(-1,1)"
  608 + x1="26.5"
  609 + y1="36.5"
  610 + x2="24.5"
  611 + y2="28.5" /><radialGradient
  612 + inkscape:collect="always"
  613 + xlink:href="#linearGradient2378"
  614 + id="radialGradient4783"
  615 + gradientUnits="userSpaceOnUse"
  616 + gradientTransform="matrix(-2.7847815e-8,0.91845951,-1.1152723,-3.3815205e-8,39.60096,1.4161083)"
  617 + cx="28.399611"
  618 + cy="15.333439"
  619 + fx="28.399611"
  620 + fy="15.333439"
  621 + r="15.242914" /><radialGradient
  622 + inkscape:collect="always"
  623 + xlink:href="#linearGradient2299"
  624 + id="radialGradient4785"
  625 + gradientUnits="userSpaceOnUse"
  626 + gradientTransform="matrix(3.39591,-4.6697806e-7,4.6697814e-7,3.3959105,-19.762019,-53.978222)"
  627 + cx="8.027895"
  628 + cy="24.287516"
  629 + fx="8.027895"
  630 + fy="24.287516"
  631 + r="8.2452128" /><radialGradient
  632 + inkscape:collect="always"
  633 + xlink:href="#linearGradient2341"
  634 + id="radialGradient4787"
  635 + gradientUnits="userSpaceOnUse"
  636 + gradientTransform="matrix(-2.0618025,-2.0407128e-7,-3.2411324e-7,3.2746279,61.522713,-37.078328)"
  637 + cx="11.651313"
  638 + cy="20.026194"
  639 + fx="11.651313"
  640 + fy="20.026194"
  641 + r="8.2452126" /><linearGradient
  642 + inkscape:collect="always"
  643 + xlink:href="#linearGradient5048"
  644 + id="linearGradient5003"
  645 + gradientUnits="userSpaceOnUse"
  646 + gradientTransform="matrix(2.3780477,0,0,1.969706,-1808.0185,-845.99583)"
  647 + x1="304.19855"
  648 + y1="357.48697"
  649 + x2="304.19855"
  650 + y2="603.21515" /><radialGradient
  651 + inkscape:collect="always"
  652 + xlink:href="#linearGradient5060"
  653 + id="radialGradient5005"
  654 + gradientUnits="userSpaceOnUse"
  655 + gradientTransform="matrix(2.6052656,0,0,1.9930785,-2039.1167,-872.62632)"
  656 + cx="605.71429"
  657 + cy="486.64789"
  658 + fx="605.71429"
  659 + fy="486.64789"
  660 + r="117.14286" /><radialGradient
  661 + inkscape:collect="always"
  662 + xlink:href="#linearGradient5060"
  663 + id="radialGradient5007"
  664 + gradientUnits="userSpaceOnUse"
  665 + gradientTransform="matrix(-2.6052648,0,0,1.9930785,128.02878,-872.62632)"
  666 + cx="605.71429"
  667 + cy="486.64789"
  668 + fx="605.71429"
  669 + fy="486.64789"
  670 + r="117.14286" /><linearGradient
  671 + inkscape:collect="always"
  672 + xlink:href="#linearGradient3736"
  673 + id="linearGradient5009"
  674 + gradientUnits="userSpaceOnUse"
  675 + gradientTransform="matrix(0.336922,0,0,0.166888,17.98288,15.46151)"
  676 + x1="49.023571"
  677 + y1="173.99986"
  678 + x2="19.343113"
  679 + y2="-5.7614088" /><linearGradient
  680 + inkscape:collect="always"
  681 + xlink:href="#linearGradient2329"
  682 + id="linearGradient5011"
  683 + gradientUnits="userSpaceOnUse"
  684 + gradientTransform="matrix(0.67409175,0,0,0.92339382,5.1085187,4.2256015)"
  685 + x1="15.415529"
  686 + y1="23.833527"
  687 + x2="16.899008"
  688 + y2="33.219193" /><radialGradient
  689 + inkscape:collect="always"
  690 + xlink:href="#linearGradient2319"
  691 + id="radialGradient5013"
  692 + gradientUnits="userSpaceOnUse"
  693 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.731106)"
  694 + cx="20.443665"
  695 + cy="37.425829"
  696 + fx="20.443665"
  697 + fy="37.425829"
  698 + r="1.0821035" /><radialGradient
  699 + inkscape:collect="always"
  700 + xlink:href="#linearGradient2307"
  701 + id="radialGradient5015"
  702 + gradientUnits="userSpaceOnUse"
  703 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,2.0807369)"
  704 + cx="19.985598"
  705 + cy="36.77816"
  706 + fx="19.985598"
  707 + fy="36.77816"
  708 + r="1.0821035" /><radialGradient
  709 + inkscape:collect="always"
  710 + xlink:href="#linearGradient2368"
  711 + id="radialGradient5017"
  712 + gradientUnits="userSpaceOnUse"
  713 + gradientTransform="matrix(0.11004748,1.8133708,-1.8164852,0.18222563,79.460078,-26.062392)"
  714 + cx="29.913452"
  715 + cy="30.442923"
  716 + fx="29.913452"
  717 + fy="30.442923"
  718 + r="4.0018832" /><linearGradient
  719 + inkscape:collect="always"
  720 + xlink:href="#linearGradient3771"
  721 + id="linearGradient5019"
  722 + gradientUnits="userSpaceOnUse"
  723 + gradientTransform="translate(-1,1)"
  724 + x1="26.5"
  725 + y1="36.5"
  726 + x2="24.5"
  727 + y2="28.5" /><radialGradient
  728 + inkscape:collect="always"
  729 + xlink:href="#linearGradient2378"
  730 + id="radialGradient5021"
  731 + gradientUnits="userSpaceOnUse"
  732 + gradientTransform="matrix(-2.7847815e-8,0.91845951,-1.1152723,-3.3815205e-8,39.60096,1.4161083)"
  733 + cx="28.399611"
  734 + cy="15.333439"
  735 + fx="28.399611"
  736 + fy="15.333439"
  737 + r="15.242914" /><radialGradient
  738 + inkscape:collect="always"
  739 + xlink:href="#linearGradient2299"
  740 + id="radialGradient5023"
  741 + gradientUnits="userSpaceOnUse"
  742 + gradientTransform="matrix(3.39591,-4.6697806e-7,4.6697814e-7,3.3959105,-19.762019,-53.978222)"
  743 + cx="8.027895"
  744 + cy="24.287516"
  745 + fx="8.027895"
  746 + fy="24.287516"
  747 + r="8.2452128" /><radialGradient
  748 + inkscape:collect="always"
  749 + xlink:href="#linearGradient2341"
  750 + id="radialGradient5025"
  751 + gradientUnits="userSpaceOnUse"
  752 + gradientTransform="matrix(-2.0618025,-2.0407128e-7,-3.2411324e-7,3.2746279,61.522713,-37.078328)"
  753 + cx="11.651313"
  754 + cy="20.026194"
  755 + fx="11.651313"
  756 + fy="20.026194"
  757 + r="8.2452126" /><linearGradient
  758 + inkscape:collect="always"
  759 + xlink:href="#linearGradient5048"
  760 + id="linearGradient5077"
  761 + gradientUnits="userSpaceOnUse"
  762 + gradientTransform="matrix(2.1515669,0,0,1.969706,-1773.1654,-845.99583)"
  763 + x1="304.19855"
  764 + y1="357.48697"
  765 + x2="304.19855"
  766 + y2="603.21515" /><radialGradient
  767 + inkscape:collect="always"
  768 + xlink:href="#linearGradient5060"
  769 + id="radialGradient5079"
  770 + gradientUnits="userSpaceOnUse"
  771 + gradientTransform="matrix(2.6052656,0,0,1.9930785,-2131.7867,-872.62632)"
  772 + cx="605.71429"
  773 + cy="486.64789"
  774 + fx="605.71429"
  775 + fy="486.64789"
  776 + r="117.14286" /><radialGradient
  777 + inkscape:collect="always"
  778 + xlink:href="#linearGradient5060"
  779 + id="radialGradient5081"
  780 + gradientUnits="userSpaceOnUse"
  781 + gradientTransform="matrix(-2.6052648,0,0,1.9930785,128.02878,-872.62632)"
  782 + cx="605.71429"
  783 + cy="486.64789"
  784 + fx="605.71429"
  785 + fy="486.64789"
  786 + r="117.14286" /><linearGradient
  787 + inkscape:collect="always"
  788 + xlink:href="#linearGradient3736"
  789 + id="linearGradient5083"
  790 + gradientUnits="userSpaceOnUse"
  791 + gradientTransform="matrix(0.336922,0,0,0.166888,17.98288,15.46151)"
  792 + x1="49.023571"
  793 + y1="173.99986"
  794 + x2="19.343113"
  795 + y2="-5.7614088" /><linearGradient
  796 + inkscape:collect="always"
  797 + xlink:href="#linearGradient2329"
  798 + id="linearGradient5085"
  799 + gradientUnits="userSpaceOnUse"
  800 + gradientTransform="matrix(0.59919267,0,0,0.92339382,6.9853499,4.2256015)"
  801 + x1="15.415529"
  802 + y1="23.833527"
  803 + x2="16.899008"
  804 + y2="33.219193" /><radialGradient
  805 + inkscape:collect="always"
  806 + xlink:href="#linearGradient2319"
  807 + id="radialGradient5087"
  808 + gradientUnits="userSpaceOnUse"
  809 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.731106)"
  810 + cx="20.443665"
  811 + cy="37.425829"
  812 + fx="20.443665"
  813 + fy="37.425829"
  814 + r="1.0821035" /><radialGradient
  815 + inkscape:collect="always"
  816 + xlink:href="#linearGradient2368"
  817 + id="radialGradient5091"
  818 + gradientUnits="userSpaceOnUse"
  819 + gradientTransform="matrix(0.11004748,1.8133708,-1.8164852,0.18222563,79.460078,-26.062392)"
  820 + cx="29.913452"
  821 + cy="30.442923"
  822 + fx="29.913452"
  823 + fy="30.442923"
  824 + r="4.0018832" /><linearGradient
  825 + inkscape:collect="always"
  826 + xlink:href="#linearGradient3771"
  827 + id="linearGradient5093"
  828 + gradientUnits="userSpaceOnUse"
  829 + gradientTransform="translate(-1,1)"
  830 + x1="26.5"
  831 + y1="36.5"
  832 + x2="24.5"
  833 + y2="28.5" /><radialGradient
  834 + inkscape:collect="always"
  835 + xlink:href="#linearGradient2378"
  836 + id="radialGradient5095"
  837 + gradientUnits="userSpaceOnUse"
  838 + gradientTransform="matrix(-2.7847815e-8,0.91845951,-1.1152723,-3.3815205e-8,39.60096,1.4161083)"
  839 + cx="29.48839"
  840 + cy="15.333439"
  841 + fx="29.48839"
  842 + fy="15.333439"
  843 + r="15.242914" /><radialGradient
  844 + inkscape:collect="always"
  845 + xlink:href="#linearGradient2299"
  846 + id="radialGradient5097"
  847 + gradientUnits="userSpaceOnUse"
  848 + gradientTransform="matrix(3.39591,-4.6697806e-7,4.6697814e-7,3.3959105,-19.762019,-52.978222)"
  849 + cx="8.027895"
  850 + cy="24.287516"
  851 + fx="8.027895"
  852 + fy="24.287516"
  853 + r="8.2452128" /><radialGradient
  854 + inkscape:collect="always"
  855 + xlink:href="#linearGradient2341"
  856 + id="radialGradient5099"
  857 + gradientUnits="userSpaceOnUse"
  858 + gradientTransform="matrix(-2.0618025,-2.0407128e-7,-3.2411324e-7,3.2746279,61.522713,-36.078328)"
  859 + cx="11.651313"
  860 + cy="20.026194"
  861 + fx="11.651313"
  862 + fy="20.026194"
  863 + r="8.2452126" /><linearGradient
  864 + inkscape:collect="always"
  865 + xlink:href="#linearGradient3177"
  866 + id="linearGradient3259"
  867 + gradientUnits="userSpaceOnUse"
  868 + x1="25.621262"
  869 + y1="33.817276"
  870 + x2="28.5"
  871 + y2="36.5" /><linearGradient
  872 + inkscape:collect="always"
  873 + xlink:href="#linearGradient5048"
  874 + id="linearGradient3297"
  875 + gradientUnits="userSpaceOnUse"
  876 + gradientTransform="matrix(2.1515669,0,0,1.969706,-1773.1654,-845.99583)"
  877 + x1="304.19855"
  878 + y1="357.48697"
  879 + x2="304.19855"
  880 + y2="603.21515" /><radialGradient
  881 + inkscape:collect="always"
  882 + xlink:href="#linearGradient5060"
  883 + id="radialGradient3299"
  884 + gradientUnits="userSpaceOnUse"
  885 + gradientTransform="matrix(2.6052656,0,0,1.9930785,-2131.7867,-872.62632)"
  886 + cx="605.71429"
  887 + cy="486.64789"
  888 + fx="605.71429"
  889 + fy="486.64789"
  890 + r="117.14286" /><radialGradient
  891 + inkscape:collect="always"
  892 + xlink:href="#linearGradient5060"
  893 + id="radialGradient3301"
  894 + gradientUnits="userSpaceOnUse"
  895 + gradientTransform="matrix(-2.6052648,0,0,1.9930785,128.02878,-872.62632)"
  896 + cx="605.71429"
  897 + cy="486.64789"
  898 + fx="605.71429"
  899 + fy="486.64789"
  900 + r="117.14286" /><linearGradient
  901 + inkscape:collect="always"
  902 + xlink:href="#linearGradient3736"
  903 + id="linearGradient3303"
  904 + gradientUnits="userSpaceOnUse"
  905 + gradientTransform="matrix(0.336922,0,0,0.166888,17.98288,15.46151)"
  906 + x1="49.023571"
  907 + y1="173.99986"
  908 + x2="19.343113"
  909 + y2="-5.7614088" /><linearGradient
  910 + inkscape:collect="always"
  911 + xlink:href="#linearGradient2329"
  912 + id="linearGradient3305"
  913 + gradientUnits="userSpaceOnUse"
  914 + gradientTransform="matrix(0.59919267,0,0,0.92339382,6.9853499,4.2256015)"
  915 + x1="15.415529"
  916 + y1="23.833527"
  917 + x2="16.899008"
  918 + y2="33.219193" /><radialGradient
  919 + inkscape:collect="always"
  920 + xlink:href="#linearGradient2319"
  921 + id="radialGradient3307"
  922 + gradientUnits="userSpaceOnUse"
  923 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,0.731106)"
  924 + cx="20.443665"
  925 + cy="37.425829"
  926 + fx="20.443665"
  927 + fy="37.425829"
  928 + r="1.0821035" /><linearGradient
  929 + inkscape:collect="always"
  930 + xlink:href="#linearGradient5161"
  931 + id="linearGradient3309"
  932 + gradientUnits="userSpaceOnUse"
  933 + x1="17.5"
  934 + y1="35.5"
  935 + x2="20.5"
  936 + y2="39.5" /><radialGradient
  937 + inkscape:collect="always"
  938 + xlink:href="#linearGradient3171"
  939 + id="radialGradient3311"
  940 + gradientUnits="userSpaceOnUse"
  941 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.3489438,1.0441923)"
  942 + cx="19.985598"
  943 + cy="36.77816"
  944 + fx="19.985598"
  945 + fy="36.77816"
  946 + r="1.0821035" /><linearGradient
  947 + inkscape:collect="always"
  948 + xlink:href="#linearGradient3771"
  949 + id="linearGradient3315"
  950 + gradientUnits="userSpaceOnUse"
  951 + gradientTransform="translate(-1,1)"
  952 + x1="26.5"
  953 + y1="36.5"
  954 + x2="24.5"
  955 + y2="28.5" /><radialGradient
  956 + inkscape:collect="always"
  957 + xlink:href="#linearGradient2378"
  958 + id="radialGradient3317"
  959 + gradientUnits="userSpaceOnUse"
  960 + gradientTransform="matrix(-2.7847815e-8,0.91845951,-1.1152723,-3.3815205e-8,39.60096,1.4161083)"
  961 + cx="29.48839"
  962 + cy="15.333439"
  963 + fx="29.48839"
  964 + fy="15.333439"
  965 + r="15.242914" /><radialGradient
  966 + inkscape:collect="always"
  967 + xlink:href="#linearGradient2299"
  968 + id="radialGradient3319"
  969 + gradientUnits="userSpaceOnUse"
  970 + gradientTransform="matrix(3.39591,-4.6697806e-7,4.6697814e-7,3.3959105,-19.762019,-52.978222)"
  971 + cx="8.027895"
  972 + cy="24.287516"
  973 + fx="8.027895"
  974 + fy="24.287516"
  975 + r="8.2452128" /><radialGradient
  976 + inkscape:collect="always"
  977 + xlink:href="#linearGradient2341"
  978 + id="radialGradient3321"
  979 + gradientUnits="userSpaceOnUse"
  980 + gradientTransform="matrix(-2.0618025,-2.0407128e-7,-3.2411324e-7,3.2746279,61.522713,-36.078328)"
  981 + cx="11.651313"
  982 + cy="20.026194"
  983 + fx="11.651313"
  984 + fy="20.026194"
  985 + r="8.2452126" /><inkscape:perspective
  986 + id="perspective3331"
  987 + inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
  988 + inkscape:vp_z="1 : 0.5 : 1"
  989 + inkscape:vp_y="0 : 1000 : 0"
  990 + inkscape:vp_x="0 : 0.5 : 1"
  991 + sodipodi:type="inkscape:persp3d" /><radialGradient
  992 + inkscape:collect="always"
  993 + xlink:href="#linearGradient2368-5"
  994 + id="radialGradient5017-4"
  995 + gradientUnits="userSpaceOnUse"
  996 + gradientTransform="matrix(0.11004748,1.8133708,-1.8164852,0.18222563,79.460078,-26.062392)"
  997 + cx="29.913452"
  998 + cy="30.442923"
  999 + fx="29.913452"
  1000 + fy="30.442923"
  1001 + r="4.001883" /><linearGradient
  1002 + inkscape:collect="always"
  1003 + id="linearGradient2368-5"><stop
  1004 + style="stop-color:#ffffff;stop-opacity:1;"
  1005 + offset="0"
  1006 + id="stop2370-2" /><stop
  1007 + style="stop-color:#ffffff;stop-opacity:0;"
  1008 + offset="1"
  1009 + id="stop2372-0" /></linearGradient><radialGradient
  1010 + r="4.001883"
  1011 + fy="30.442923"
  1012 + fx="29.913452"
  1013 + cy="30.442923"
  1014 + cx="29.913452"
  1015 + gradientTransform="matrix(0.11004748,1.8133708,-1.8164852,0.18222563,79.460083,-26.062392)"
  1016 + gradientUnits="userSpaceOnUse"
  1017 + id="radialGradient3340"
  1018 + xlink:href="#linearGradient2368-5"
  1019 + inkscape:collect="always" /><inkscape:perspective
  1020 + id="perspective4157"
  1021 + inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
  1022 + inkscape:vp_z="1 : 0.5 : 1"
  1023 + inkscape:vp_y="0 : 1000 : 0"
  1024 + inkscape:vp_x="0 : 0.5 : 1"
  1025 + sodipodi:type="inkscape:persp3d" /><radialGradient
  1026 + inkscape:collect="always"
  1027 + xlink:href="#linearGradient2307-6"
  1028 + id="radialGradient5015-8"
  1029 + gradientUnits="userSpaceOnUse"
  1030 + gradientTransform="matrix(1.125263,0,0,0.982744,-4.428678,2.0807369)"
  1031 + cx="20.09285"
  1032 + cy="35.716652"
  1033 + fx="20.09285"
  1034 + fy="35.716652"
  1035 + r="1.0821035" /><linearGradient
  1036 + id="linearGradient2307-6"><stop
  1037 + style="stop-color:#edd400;stop-opacity:1;"
  1038 + offset="0"
  1039 + id="stop2309-3" /><stop
  1040 + style="stop-color:#998800;stop-opacity:1;"
  1041 + offset="1"
  1042 + id="stop2311-5" /></linearGradient><radialGradient
  1043 + r="1.0821035"
  1044 + fy="36.77816"
  1045 + fx="19.985598"
  1046 + cy="36.77816"
  1047 + cx="19.985598"
  1048 + gradientTransform="matrix(1.125263,0,0,0.982744,-21.672305,-35.366438)"
  1049 + gradientUnits="userSpaceOnUse"
  1050 + id="radialGradient4166"
  1051 + xlink:href="#linearGradient2307-6"
  1052 + inkscape:collect="always" /></defs><sodipodi:namedview
  1053 + inkscape:cy="24"
  1054 + inkscape:cx="24"
  1055 + inkscape:zoom="12.541667"
  1056 + inkscape:window-height="827"
  1057 + inkscape:window-width="1440"
  1058 + inkscape:pageshadow="2"
  1059 + inkscape:pageopacity="0.0"
  1060 + borderopacity="0.21568627"
  1061 + bordercolor="#666666"
  1062 + pagecolor="#ffffff"
  1063 + id="base"
  1064 + inkscape:showpageshadow="false"
  1065 + inkscape:window-x="0"
  1066 + inkscape:window-y="25"
  1067 + inkscape:current-layer="svg2"
  1068 + fill="#555753"
  1069 + showgrid="true"
  1070 + stroke="#a40000"
  1071 + showguides="true"
  1072 + inkscape:guide-bbox="true"
  1073 + inkscape:snap-grids="true"
  1074 + inkscape:window-maximized="1"><inkscape:grid
  1075 + type="xygrid"
  1076 + id="grid3670"
  1077 + empspacing="5"
  1078 + visible="true"
  1079 + enabled="true"
  1080 + snapvisiblegridlinesonly="true"
  1081 + originx="0.5px"
  1082 + originy="0.5px" /></sodipodi:namedview>
  1083 +
  1084 +<g
  1085 + transform="translate(15,-6)"
  1086 + id="use5027"><g
  1087 + transform="matrix(0.02158196,0,0,0.01859457,39.12251,27.63767)"
  1088 + id="g3263"
  1089 + style="display:inline"><rect
  1090 + style="opacity:0.40206185;color:#000000;fill:url(#linearGradient3297);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1091 + id="rect3265"
  1092 + width="880.36487"
  1093 + height="484.0123"
  1094 + x="-1442.0613"
  1095 + y="-141.85165" /><path
  1096 + style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3299);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1097 + d="m -561.69644,-141.85163 c 0,0 0,484.00662 0,484.00662 134.16473,0.91119 324.34504,-108.44127 324.34497,-242.03439 0,-133.593226 -149.71775,-241.97221 -324.34497,-241.97223 z"
  1098 + id="path3267"
  1099 + sodipodi:nodetypes="cccc" /><path
  1100 + sodipodi:nodetypes="cccc"
  1101 + id="path3269"
  1102 + d="m -1442.0613,-141.85163 c 0,0 0,484.00662 0,484.00662 -134.1648,0.91119 -324.345,-108.44127 -324.345,-242.03439 0,-133.593226 149.7177,-241.97221 324.345,-241.97223 z"
  1103 + style="opacity:0.40206185;color:#000000;fill:url(#radialGradient3301);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g><g
  1104 + id="g3271"
  1105 + transform="translate(-5.9999999,-13)"><path
  1106 + style="color:#000000;fill:url(#linearGradient3303);fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1107 + d="m 35.5,26.5 0,16 c 0,0.905333 -0.160268,1 -1,1 l -22,0 c -0.839733,0 -1,-0.09467 -1,-1 l 0,-16 24,0 z"
  1108 + id="path3273"
  1109 + sodipodi:nodetypes="ccccccc" /><path
  1110 + sodipodi:nodetypes="ccccc"
  1111 + id="path3277"
  1112 + d="m 34.5,26.5 0,16 -22,0 0,-16 22,0 z"
  1113 + style="opacity:0.35;color:#000000;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /><rect
  1114 + style="color:#000000;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1115 + id="rect3285"
  1116 + width="7"
  1117 + height="6"
  1118 + x="24.5"
  1119 + y="32.5" /><rect
  1120 + y="31.5"
  1121 + x="23.5"
  1122 + height="8"
  1123 + width="9"
  1124 + id="rect3287"
  1125 + style="opacity:0.5;color:#000000;fill:none;stroke:url(#linearGradient3315);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1126 + ry="1"
  1127 + rx="1" /><path
  1128 + clip-rule="evenodd"
  1129 + d="m 12.5,31.5 0,5 13,-6 9,5 0,-4 -10,-7 -12,7 z"
  1130 + id="path3289"
  1131 + style="opacity:0.20000000000000001;fill:url(#radialGradient3317);fill-opacity:1;fill-rule:evenodd"
  1132 + sodipodi:nodetypes="ccccccc" /><path
  1133 + style="fill:#ef2929;stroke:#a40000;stroke-linecap:round;stroke-linejoin:round"
  1134 + id="path3291"
  1135 + d="m 8.4999999,27.5 -1e-7,5 1.5,2 14.5000002,-7 12.5,7 1.5,-2 0,-5 -14,-8 -16.0000001,8 z"
  1136 + sodipodi:nodetypes="ccccccccc" /><path
  1137 + style="opacity:0.40909089;color:#000000;fill:url(#radialGradient3319);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1138 + d="m 9.0215946,27.818937 -10e-8,4 L 24.5,24.5 l 0,-4.318937 -15.4784054,7.637874 z"
  1139 + id="path3293"
  1140 + sodipodi:nodetypes="ccccc" /><path
  1141 + sodipodi:nodetypes="ccccc"
  1142 + id="path3295"
  1143 + d="m 24.5,24.5 0,-4.318937 13.478405,7.637874 0,4 L 24.5,24.5 z"
  1144 + style="opacity:0.13636367;color:#000000;fill:url(#radialGradient3321);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /><path
  1145 + style="opacity:0.39772728;color:#000000;fill:url(#radialGradient3340);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999958;marker:none;visibility:visible;display:inline;overflow:visible"
  1146 + d="m 25,36.954984 c 2.841896,0.362925 3.37612,-1.584298 6,-1.701594 L 31,33 l -6,0 0,3.954959 0,2.5e-5 z"
  1147 + id="rect2363-2"
  1148 + sodipodi:nodetypes="cccccc" /></g></g><g
  1149 + id="g4807"
  1150 + transform="translate(-1.9999999,-1.0000001)"><g
  1151 + style="display:inline"
  1152 + id="g4729"
  1153 + transform="matrix(0.02158196,0,0,0.01859457,39.12251,28.63767)"><rect
  1154 + y="-141.85165"
  1155 + x="-1442.0613"
  1156 + height="484.0123"
  1157 + width="880.36487"
  1158 + id="rect4731"
  1159 + style="opacity:0.40206185;color:#000000;fill:url(#linearGradient5077);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /><path
  1160 + sodipodi:nodetypes="cccc"
  1161 + id="path4733"
  1162 + d="m -561.69644,-141.85163 c 0,0 0,484.00662 0,484.00662 134.16473,0.91119 324.34504,-108.44127 324.34497,-242.03439 0,-133.593226 -149.71775,-241.97221 -324.34497,-241.97223 z"
  1163 + style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5079);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /><path
  1164 + style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5081);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1165 + d="m -1442.0613,-141.85163 c 0,0 0,484.00662 0,484.00662 -134.1648,0.91119 -324.345,-108.44127 -324.345,-242.03439 0,-133.593226 149.7177,-241.97221 324.345,-241.97223 z"
  1166 + id="path4735"
  1167 + sodipodi:nodetypes="cccc" /></g><g
  1168 + transform="translate(-5.9999999,-13)"
  1169 + id="g4737"><path
  1170 + sodipodi:nodetypes="ccccccc"
  1171 + id="path4739"
  1172 + d="m 35.5,26.5 0,17 c 0,0.905333 -0.160268,1 -1,1 l -22,0 c -0.839733,0 -1,-0.09467 -1,-1 l 0,-17 24,0 z"
  1173 + style="color:#000000;fill:url(#linearGradient5083);fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /><path
  1174 + sodipodi:nodetypes="ccccc"
  1175 + clip-rule="evenodd"
  1176 + d="M 22,31 21.969511,44 14.074317,44 14,31 l 8,0 z"
  1177 + id="path4741"
  1178 + style="fill:url(#linearGradient5085);fill-opacity:1;fill-rule:evenodd" /><path
  1179 + style="opacity:0.35;color:#000000;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1180 + d="m 34.5,26.5 0,17 -22,0 0,-17 22,0 z"
  1181 + id="path4743"
  1182 + sodipodi:nodetypes="ccccc" /><path
  1183 + clip-rule="evenodd"
  1184 + d="m 18.576856,36.44767 c 0.67279,0 1.216616,0.474605 1.216616,1.058507 0,0.589811 -0.543826,1.068355 -1.216616,1.068355 -0.672272,0 -1.218686,-0.478544 -1.218686,-1.068355 5.15e-4,-0.583902 0.546414,-1.058507 1.218686,-1.058507 z"
  1185 + id="path4745"
  1186 + style="opacity:0.40909089;fill:url(#radialGradient5087);fill-opacity:1;fill-rule:evenodd" /><rect
  1187 + y="32"
  1188 + x="15"
  1189 + height="12"
  1190 + width="6"
  1191 + id="rect4747"
  1192 + style="color:#000000;fill:#8f5902;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /><path
  1193 + style="color:#000000;fill:url(#radialGradient5015-8);fill-opacity:1;fill-rule:evenodd;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  1194 + id="path4749"
  1195 + d="m 18.542048,36.410633 c 0.672789,0 1.216615,0.474605 1.216615,1.058507 0,0.589809 -0.543826,1.068353 -1.216615,1.068353 -0.672273,0 -1.218687,-0.478544 -1.218687,-1.068353 5.15e-4,-0.583902 0.546414,-1.058507 1.218687,-1.058507 z"
  1196 + clip-rule="evenodd" /><rect
  1197 + y="32.5"
  1198 + x="24.5"
  1199 + height="6.999999"
  1200 + width="8"
  1201 + id="rect4751"
  1202 + style="color:#000000;fill:url(#linearGradient3259);fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /><rect
  1203 + rx="1"
  1204 + ry="1"
  1205 + style="opacity:0.5;color:#000000;fill:none;stroke:url(#linearGradient5093);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1206 + id="rect4755"
  1207 + width="10"
  1208 + height="9"
  1209 + x="23.5"
  1210 + y="31.5" /><path
  1211 + sodipodi:nodetypes="ccccccc"
  1212 + style="opacity:0.20000000000000001;fill:url(#radialGradient5095);fill-opacity:1;fill-rule:evenodd"
  1213 + id="path4757"
  1214 + d="m 12.5,31.5 0,5 13,-6 9,5 0,-5 -10,-6 -12,7 z"
  1215 + clip-rule="evenodd" /><path
  1216 + sodipodi:nodetypes="ccccccccc"
  1217 + d="m 8.4999999,27.5 -1e-7,5 1.5,2 14.5000002,-7 12.5,7 1.5,-2 0,-5 -14,-8 -16.0000001,8 z"
  1218 + id="path4759"
  1219 + style="fill:#ef2929;stroke:#a40000;stroke-linecap:round;stroke-linejoin:round" /><path
  1220 + sodipodi:nodetypes="ccccc"
  1221 + id="path4761"
  1222 + d="m 9.0215946,27.818937 -10e-8,4 L 24.5,24.5 l 0,-4.318937 -15.4784054,7.637874 z"
  1223 + style="opacity:0.40909089;color:#000000;fill:url(#radialGradient5097);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /><path
  1224 + style="opacity:0.13636367;color:#000000;fill:url(#radialGradient5099);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1225 + d="m 24.5,24.5 0,-4.318937 13.478405,7.637874 0,4 L 24.5,24.5 z"
  1226 + id="path4763"
  1227 + sodipodi:nodetypes="ccccc" /></g></g><g
  1228 + id="g3829"
  1229 + transform="translate(2.0000001,0)"><g
  1230 + transform="matrix(0.02158196,0,0,0.01859457,44.12251,41.63767)"
  1231 + id="g5022"
  1232 + style="display:inline"><rect
  1233 + style="opacity:0.40206185;color:#000000;fill:url(#linearGradient5003);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1234 + id="rect4173"
  1235 + width="973.03491"
  1236 + height="484.0123"
  1237 + x="-1442.0613"
  1238 + y="-141.85165" /><path
  1239 + style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5005);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1240 + d="m -469.02645,-141.85163 c 0,0 0,484.00662 0,484.00662 134.16473,0.91119 324.34504,-108.44127 324.34497,-242.03439 0,-133.593226 -149.71775,-241.97221 -324.34497,-241.97223 z"
  1241 + id="path5058"
  1242 + sodipodi:nodetypes="cccc" /><path
  1243 + sodipodi:nodetypes="cccc"
  1244 + id="path5018"
  1245 + d="m -1442.0613,-141.85163 c 0,0 0,484.00662 0,484.00662 -134.1648,0.91119 -324.345,-108.44127 -324.345,-242.03439 0,-133.593226 149.7177,-241.97221 324.345,-241.97223 z"
  1246 + style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5007);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g><g
  1247 + id="g3812"><path
  1248 + style="color:#000000;fill:url(#linearGradient5009);fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1249 + d="m 36.5,25.5 0,18 c 0,0.905333 -0.160268,1 -1,1 l -24,0 c -0.839733,0 -1,-0.09467 -1,-1 l 0,-18 26,0 z"
  1250 + id="rect1512"
  1251 + sodipodi:nodetypes="ccccccc" /><path
  1252 + style="fill:url(#linearGradient5011);fill-opacity:1;fill-rule:evenodd"
  1253 + id="path2327"
  1254 + d="M 22,31 21.9657,44 13.083607,44 13,31 l 9,0 z"
  1255 + clip-rule="evenodd"
  1256 + sodipodi:nodetypes="ccccc" /><path
  1257 + sodipodi:nodetypes="ccccc"
  1258 + id="path2357"
  1259 + d="m 35.5,25.5 0,18 -24,0 0,-18 24,0 z"
  1260 + style="opacity:0.35;color:#000000;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /><path
  1261 + style="opacity:0.40909089;fill:url(#radialGradient5013);fill-opacity:1;fill-rule:evenodd"
  1262 + id="path2315"
  1263 + d="m 18.576856,36.44767 c 0.67279,0 1.216616,0.474605 1.216616,1.058507 0,0.589811 -0.543826,1.068355 -1.216616,1.068355 -0.672272,0 -1.218686,-0.478544 -1.218686,-1.068355 5.15e-4,-0.583902 0.546414,-1.058507 1.218686,-1.058507 z"
  1264 + clip-rule="evenodd" /><rect
  1265 + style="color:#000000;fill:#8f5902;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
  1266 + id="rect3851"
  1267 + width="7"
  1268 + height="12"
  1269 + x="14"
  1270 + y="32" /><path
  1271 + clip-rule="evenodd"
  1272 + d="m 18.462314,37.447175 c 0.672789,0 1.216615,0.474605 1.216615,1.058507 0,0.589809 -0.543826,1.068353 -1.216615,1.068353 -0.672273,0 -1.218687,-0.478544 -1.218687,-1.068353 5.15e-4,-0.583902 0.546414,-1.058507 1.218687,-1.058507 z"
  1273 + id="path217"
  1274 + style="fill:url(#radialGradient5015);fill-opacity:1;fill-rule:evenodd" /><rect
  1275 + style="color:#000000;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1276 + id="rect3263"
  1277 + width="8"
  1278 + height="6.999999"
  1279 + x="24.5"
  1280 + y="32.5" /><path
  1281 + style="opacity:0.39772728;color:#000000;fill:url(#radialGradient5017);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999958;marker:none;visibility:visible;display:inline;overflow:visible"
  1282 + d="m 25.014716,36.954984 c 2.841896,0.362925 4.341979,-1.584298 6.965859,-1.701594 L 32,33 l -7,0 0.01472,3.954959 -4e-6,2.5e-5 z"
  1283 + id="rect2363"
  1284 + sodipodi:nodetypes="cccccc" /><rect
  1285 + y="31.5"
  1286 + x="23.5"
  1287 + height="9"
  1288 + width="10"
  1289 + id="rect3748"
  1290 + style="opacity:0.5;color:#000000;fill:none;stroke:url(#linearGradient5019);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1291 + ry="1"
  1292 + rx="1" /><path
  1293 + clip-rule="evenodd"
  1294 + d="m 11.5,31.5 0,5 14,-7 10,6 0,-4 -11,-7 -13,7 z"
  1295 + id="path23"
  1296 + style="opacity:0.2;fill:url(#radialGradient5021);fill-opacity:1;fill-rule:evenodd"
  1297 + sodipodi:nodetypes="ccccccc" /><path
  1298 + style="fill:#ef2929;stroke:#a40000;stroke-linecap:round;stroke-linejoin:round"
  1299 + id="path362"
  1300 + d="m 7.5,26.5 0,6 1.5,2 15.5,-8 13.5,8 1.5,-2 0,-6 -15,-9 -17,9 z"
  1301 + sodipodi:nodetypes="ccccccccc" /><path
  1302 + style="opacity:0.40909089;color:#000000;fill:url(#radialGradient5023);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
  1303 + d="m 7.9418605,26.898671 0,5 L 24.5,23.5 l 0,-5.318937 -16.5581395,8.717608 z"
  1304 + id="path1536"
  1305 + sodipodi:nodetypes="ccccc" /><path
  1306 + sodipodi:nodetypes="ccccc"
  1307 + id="path2337"
  1308 + d="m 24.5,23.5 0,-5.318937 14.558139,8.637874 0,5 L 24.5,23.5 z"
  1309 + style="opacity:0.13636367;color:#000000;fill:url(#radialGradient5025);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /></g></g></svg>
0 1310 \ No newline at end of file
... ...
plugins/bsc/public/images/transfer-ownership.png 0 → 100644

3.99 KB

plugins/bsc/public/images/transfer-ownership.svg 0 → 100644
... ... @@ -0,0 +1,1965 @@
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +
  4 +<svg
  5 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  6 + xmlns:cc="http://creativecommons.org/ns#"
  7 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8 + xmlns:svg="http://www.w3.org/2000/svg"
  9 + xmlns="http://www.w3.org/2000/svg"
  10 + xmlns:xlink="http://www.w3.org/1999/xlink"
  11 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  12 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  13 + width="48"
  14 + height="48"
  15 + id="svg2"
  16 + version="1.1"
  17 + inkscape:version="0.47 r22583"
  18 + sodipodi:docname="Ícone BSC 2.svg"
  19 + inkscape:export-filename="/home/caiosba/Colivre/Documentos/Ícones BSC 2.png"
  20 + inkscape:export-xdpi="90"
  21 + inkscape:export-ydpi="90">
  22 + <defs
  23 + id="defs4">
  24 + <inkscape:perspective
  25 + sodipodi:type="inkscape:persp3d"
  26 + inkscape:vp_x="0 : 526.18109 : 1"
  27 + inkscape:vp_y="0 : 1000 : 0"
  28 + inkscape:vp_z="744.09448 : 526.18109 : 1"
  29 + inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
  30 + id="perspective10" />
  31 + <linearGradient
  32 + id="linearGradient5036">
  33 + <stop
  34 + id="stop5038"
  35 + offset="0"
  36 + style="stop-color:#f5f5f5;stop-opacity:0.09;" />
  37 + <stop
  38 + style="stop-color:#ffffff;stop-opacity:0.89999998;"
  39 + offset="0.2631579"
  40 + id="stop5044" />
  41 + <stop
  42 + id="stop5088"
  43 + offset="0.74792242"
  44 + style="stop-color:#c7c7c7;stop-opacity:0.46000001;" />
  45 + <stop
  46 + id="stop5040"
  47 + offset="1"
  48 + style="stop-color:#ffffff;stop-opacity:0.78039217;" />
  49 + </linearGradient>
  50 + <linearGradient
  51 + id="linearGradient5058">
  52 + <stop
  53 + id="stop5060"
  54 + offset="0"
  55 + style="stop-color:#959791;stop-opacity:1;" />
  56 + <stop
  57 + style="stop-color:#f8f8f8;stop-opacity:1;"
  58 + offset="0.5"
  59 + id="stop5066" />
  60 + <stop
  61 + id="stop5062"
  62 + offset="1"
  63 + style="stop-color:#8c8c8c;stop-opacity:1;" />
  64 + </linearGradient>
  65 + <linearGradient
  66 + id="linearGradient5068">
  67 + <stop
  68 + id="stop5070"
  69 + offset="0"
  70 + style="stop-color:#ffffff;stop-opacity:1;" />
  71 + <stop
  72 + style="stop-color:#ffffff;stop-opacity:0.69;"
  73 + offset="0.32894737"
  74 + id="stop5078" />
  75 + <stop
  76 + style="stop-color:#c2c2c2;stop-opacity:0.34;"
  77 + offset="0.65789473"
  78 + id="stop5076" />
  79 + <stop
  80 + id="stop5072"
  81 + offset="1"
  82 + style="stop-color:#ffffff;stop-opacity:0;" />
  83 + </linearGradient>
  84 + <linearGradient
  85 + id="linearGradient2966">
  86 + <stop
  87 + id="stop2968"
  88 + offset="0"
  89 + style="stop-color:#ffd1d1;stop-opacity:1;" />
  90 + <stop
  91 + style="stop-color:#ff1d1d;stop-opacity:1;"
  92 + offset="0.5"
  93 + id="stop3006" />
  94 + <stop
  95 + id="stop2970"
  96 + offset="1"
  97 + style="stop-color:#6f0000;stop-opacity:1;" />
  98 + </linearGradient>
  99 + <linearGradient
  100 + id="linearGradient2974">
  101 + <stop
  102 + id="stop2976"
  103 + offset="0"
  104 + style="stop-color:#c1c1c1;stop-opacity:1;" />
  105 + <stop
  106 + id="stop2978"
  107 + offset="1"
  108 + style="stop-color:#acacac;stop-opacity:1;" />
  109 + </linearGradient>
  110 + <linearGradient
  111 + id="linearGradient2994">
  112 + <stop
  113 + id="stop2996"
  114 + offset="0"
  115 + style="stop-color:#000000;stop-opacity:1;" />
  116 + <stop
  117 + id="stop2998"
  118 + offset="1"
  119 + style="stop-color:#c9c9c9;stop-opacity:1;" />
  120 + </linearGradient>
  121 + <linearGradient
  122 + id="linearGradient9910">
  123 + <stop
  124 + id="stop9912"
  125 + offset="0"
  126 + style="stop-color:#729fcf;stop-opacity:1;" />
  127 + <stop
  128 + style="stop-color:#a5bfda;stop-opacity:1;"
  129 + offset="0.31578946"
  130 + id="stop9918" />
  131 + <stop
  132 + id="stop9914"
  133 + offset="1"
  134 + style="stop-color:#376ca4;stop-opacity:1;" />
  135 + </linearGradient>
  136 + <linearGradient
  137 + id="linearGradient9920">
  138 + <stop
  139 + style="stop-color:#5b90c8;stop-opacity:1;"
  140 + offset="0"
  141 + id="stop9922" />
  142 + <stop
  143 + id="stop9924"
  144 + offset="0.31578946"
  145 + style="stop-color:#8fb0d1;stop-opacity:1;" />
  146 + <stop
  147 + style="stop-color:#34679d;stop-opacity:1;"
  148 + offset="1"
  149 + id="stop9926" />
  150 + </linearGradient>
  151 + <inkscape:perspective
  152 + id="perspective90"
  153 + inkscape:persp3d-origin="24 : 16 : 1"
  154 + inkscape:vp_z="48 : 24 : 1"
  155 + inkscape:vp_y="0 : 1000 : 0"
  156 + inkscape:vp_x="0 : 24 : 1"
  157 + sodipodi:type="inkscape:persp3d" />
  158 + <linearGradient
  159 + id="linearGradient7121">
  160 + <stop
  161 + id="stop7123"
  162 + offset="0"
  163 + style="stop-color:#ffffff;stop-opacity:1;" />
  164 + <stop
  165 + id="stop7125"
  166 + offset="1"
  167 + style="stop-color:#8ae234;stop-opacity:1" />
  168 + </linearGradient>
  169 + <inkscape:perspective
  170 + id="perspective27321"
  171 + inkscape:persp3d-origin="24 : 16 : 1"
  172 + inkscape:vp_z="48 : 24 : 1"
  173 + inkscape:vp_y="0 : 1000 : 0"
  174 + inkscape:vp_x="0 : 24 : 1"
  175 + sodipodi:type="inkscape:persp3d" />
  176 + <linearGradient
  177 + y2="50.939667"
  178 + x2="45.380436"
  179 + y1="45.264122"
  180 + x1="46.834816"
  181 + gradientUnits="userSpaceOnUse"
  182 + id="linearGradient7186"
  183 + xlink:href="#linearGradient2871"
  184 + inkscape:collect="always" />
  185 + <linearGradient
  186 + y2="26.649363"
  187 + x2="53.588623"
  188 + y1="23.667896"
  189 + x1="18.935766"
  190 + gradientUnits="userSpaceOnUse"
  191 + id="linearGradient7184"
  192 + xlink:href="#linearGradient2402"
  193 + inkscape:collect="always" />
  194 + <linearGradient
  195 + y2="50.939667"
  196 + x2="45.380436"
  197 + y1="45.264122"
  198 + x1="46.834816"
  199 + gradientUnits="userSpaceOnUse"
  200 + id="linearGradient7182"
  201 + xlink:href="#linearGradient2871"
  202 + inkscape:collect="always" />
  203 + <linearGradient
  204 + y2="20.60858"
  205 + x2="15.984863"
  206 + y1="36.061237"
  207 + x1="62.513836"
  208 + gradientUnits="userSpaceOnUse"
  209 + id="linearGradient7180"
  210 + xlink:href="#linearGradient2380"
  211 + inkscape:collect="always" />
  212 + <linearGradient
  213 + gradientTransform="matrix(-1,0,0,-1,47.93934,50.02474)"
  214 + y2="23.554308"
  215 + x2="22.374878"
  216 + y1="13.604306"
  217 + x1="13.435029"
  218 + gradientUnits="userSpaceOnUse"
  219 + id="linearGradient7189"
  220 + xlink:href="#linearGradient7179"
  221 + inkscape:collect="always" />
  222 + <linearGradient
  223 + gradientUnits="userSpaceOnUse"
  224 + y2="23.554308"
  225 + x2="22.374878"
  226 + y1="13.604306"
  227 + x1="13.435029"
  228 + id="linearGradient7185"
  229 + xlink:href="#linearGradient7179"
  230 + inkscape:collect="always" />
  231 + <linearGradient
  232 + id="linearGradient1322">
  233 + <stop
  234 + style="stop-color:#729fcf"
  235 + offset="0.0000000"
  236 + id="stop1324" />
  237 + <stop
  238 + style="stop-color:#5187d6;stop-opacity:1.0000000;"
  239 + offset="1.0000000"
  240 + id="stop1326" />
  241 + </linearGradient>
  242 + <linearGradient
  243 + id="linearGradient2316">
  244 + <stop
  245 + id="stop2318"
  246 + offset="0"
  247 + style="stop-color:#000000;stop-opacity:1;" />
  248 + <stop
  249 + id="stop2320"
  250 + offset="1"
  251 + style="stop-color:#ffffff;stop-opacity:0.65979379;" />
  252 + </linearGradient>
  253 + <linearGradient
  254 + id="linearGradient7179"
  255 + inkscape:collect="always">
  256 + <stop
  257 + id="stop7181"
  258 + offset="0"
  259 + style="stop-color:#ffffff;stop-opacity:1;" />
  260 + <stop
  261 + id="stop7183"
  262 + offset="1"
  263 + style="stop-color:#ffffff;stop-opacity:0;" />
  264 + </linearGradient>
  265 + <linearGradient
  266 + y2="26.048164"
  267 + x2="52.854095"
  268 + y1="26.048164"
  269 + x1="5.9649177"
  270 + gradientUnits="userSpaceOnUse"
  271 + id="linearGradient1491"
  272 + xlink:href="#linearGradient2797"
  273 + inkscape:collect="always" />
  274 + <linearGradient
  275 + id="linearGradient2797"
  276 + inkscape:collect="always">
  277 + <stop
  278 + id="stop2799"
  279 + offset="0"
  280 + style="stop-color:#ffffff;stop-opacity:1;" />
  281 + <stop
  282 + id="stop2801"
  283 + offset="1"
  284 + style="stop-color:#ffffff;stop-opacity:0;" />
  285 + </linearGradient>
  286 + <linearGradient
  287 + y2="26.048164"
  288 + x2="52.854095"
  289 + y1="26.048164"
  290 + x1="5.9649177"
  291 + gradientUnits="userSpaceOnUse"
  292 + id="linearGradient1493"
  293 + xlink:href="#linearGradient2797"
  294 + inkscape:collect="always" />
  295 + <linearGradient
  296 + id="linearGradient2402">
  297 + <stop
  298 + id="stop2404"
  299 + offset="0"
  300 + style="stop-color:#729fcf;stop-opacity:1;" />
  301 + <stop
  302 + id="stop2406"
  303 + offset="1"
  304 + style="stop-color:#528ac5;stop-opacity:1;" />
  305 + </linearGradient>
  306 + <linearGradient
  307 + id="linearGradient2871"
  308 + inkscape:collect="always">
  309 + <stop
  310 + id="stop2873"
  311 + offset="0"
  312 + style="stop-color:#3465a4;stop-opacity:1;" />
  313 + <stop
  314 + id="stop2875"
  315 + offset="1"
  316 + style="stop-color:#3465a4;stop-opacity:1" />
  317 + </linearGradient>
  318 + <linearGradient
  319 + gradientTransform="translate(-48.77039,-5.765705)"
  320 + gradientUnits="userSpaceOnUse"
  321 + y2="24.842253"
  322 + x2="37.124462"
  323 + y1="30.748846"
  324 + x1="32.647972"
  325 + id="linearGradient2696"
  326 + xlink:href="#linearGradient2690"
  327 + inkscape:collect="always" />
  328 + <linearGradient
  329 + id="linearGradient2690"
  330 + inkscape:collect="always">
  331 + <stop
  332 + id="stop2692"
  333 + offset="0"
  334 + style="stop-color:#c4d7eb;stop-opacity:1;" />
  335 + <stop
  336 + id="stop2694"
  337 + offset="1"
  338 + style="stop-color:#c4d7eb;stop-opacity:0;" />
  339 + </linearGradient>
  340 + <linearGradient
  341 + gradientTransform="translate(-48.77039,-5.765705)"
  342 + gradientUnits="userSpaceOnUse"
  343 + y2="24.842253"
  344 + x2="37.124462"
  345 + y1="31.455952"
  346 + x1="36.713837"
  347 + id="linearGradient2688"
  348 + xlink:href="#linearGradient2682"
  349 + inkscape:collect="always" />
  350 + <linearGradient
  351 + id="linearGradient2682">
  352 + <stop
  353 + id="stop2684"
  354 + offset="0"
  355 + style="stop-color:#3977c3;stop-opacity:1;" />
  356 + <stop
  357 + id="stop2686"
  358 + offset="1"
  359 + style="stop-color:#89aedc;stop-opacity:0;" />
  360 + </linearGradient>
  361 + <linearGradient
  362 + id="linearGradient2380">
  363 + <stop
  364 + id="stop2382"
  365 + offset="0"
  366 + style="stop-color:#b9cfe7;stop-opacity:1" />
  367 + <stop
  368 + id="stop2384"
  369 + offset="1"
  370 + style="stop-color:#729fcf;stop-opacity:1" />
  371 + </linearGradient>
  372 + <linearGradient
  373 + y2="19.115122"
  374 + x2="15.419417"
  375 + y1="10.612206"
  376 + x1="13.478554"
  377 + gradientTransform="translate(-48.30498,-6.043298)"
  378 + gradientUnits="userSpaceOnUse"
  379 + id="linearGradient1486"
  380 + xlink:href="#linearGradient2831"
  381 + inkscape:collect="always" />
  382 + <linearGradient
  383 + id="linearGradient2831">
  384 + <stop
  385 + id="stop2833"
  386 + offset="0"
  387 + style="stop-color:#3465a4;stop-opacity:1;" />
  388 + <stop
  389 + style="stop-color:#5b86be;stop-opacity:1;"
  390 + offset="0.33333334"
  391 + id="stop2855" />
  392 + <stop
  393 + id="stop2835"
  394 + offset="1"
  395 + style="stop-color:#83a8d8;stop-opacity:0;" />
  396 + </linearGradient>
  397 + <linearGradient
  398 + y2="26.194071"
  399 + x2="37.065414"
  400 + y1="29.729605"
  401 + x1="37.128052"
  402 + gradientTransform="matrix(-1,0,0,-1,-1.24248,40.0817)"
  403 + gradientUnits="userSpaceOnUse"
  404 + id="linearGradient1488"
  405 + xlink:href="#linearGradient2847"
  406 + inkscape:collect="always" />
  407 + <linearGradient
  408 + id="linearGradient2847"
  409 + inkscape:collect="always">
  410 + <stop
  411 + id="stop2849"
  412 + offset="0"
  413 + style="stop-color:#3465a4;stop-opacity:1;" />
  414 + <stop
  415 + id="stop2851"
  416 + offset="1"
  417 + style="stop-color:#3465a4;stop-opacity:0;" />
  418 + </linearGradient>
  419 + <radialGradient
  420 + r="15.644737"
  421 + fy="36.421127"
  422 + fx="24.837126"
  423 + cy="36.421127"
  424 + cx="24.837126"
  425 + gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
  426 + gradientUnits="userSpaceOnUse"
  427 + id="radialGradient1503"
  428 + xlink:href="#linearGradient8662"
  429 + inkscape:collect="always" />
  430 + <linearGradient
  431 + id="linearGradient8662"
  432 + inkscape:collect="always">
  433 + <stop
  434 + id="stop8664"
  435 + offset="0"
  436 + style="stop-color:#000000;stop-opacity:1;" />
  437 + <stop
  438 + id="stop8666"
  439 + offset="1"
  440 + style="stop-color:#000000;stop-opacity:0;" />
  441 + </linearGradient>
  442 + <linearGradient
  443 + id="linearGradient7916">
  444 + <stop
  445 + id="stop7918"
  446 + offset="0"
  447 + style="stop-color:#ffffff;stop-opacity:1;" />
  448 + <stop
  449 + id="stop7920"
  450 + offset="1.0000000"
  451 + style="stop-color:#ffffff;stop-opacity:0.34020618;" />
  452 + </linearGradient>
  453 + <inkscape:perspective
  454 + id="perspective70"
  455 + inkscape:persp3d-origin="24 : 16 : 1"
  456 + inkscape:vp_z="48 : 24 : 1"
  457 + inkscape:vp_y="0 : 1000 : 0"
  458 + inkscape:vp_x="0 : 24 : 1"
  459 + sodipodi:type="inkscape:persp3d" />
  460 + <linearGradient
  461 + y2="50.939667"
  462 + x2="45.380436"
  463 + y1="45.264122"
  464 + x1="46.834816"
  465 + gradientUnits="userSpaceOnUse"
  466 + id="linearGradient7186-5"
  467 + xlink:href="#linearGradient2871-7"
  468 + inkscape:collect="always" />
  469 + <linearGradient
  470 + y2="26.649363"
  471 + x2="53.588623"
  472 + y1="23.667896"
  473 + x1="18.935766"
  474 + gradientUnits="userSpaceOnUse"
  475 + id="linearGradient7184-5"
  476 + xlink:href="#linearGradient2402-4"
  477 + inkscape:collect="always" />
  478 + <linearGradient
  479 + y2="50.939667"
  480 + x2="45.380436"
  481 + y1="45.264122"
  482 + x1="46.834816"
  483 + gradientUnits="userSpaceOnUse"
  484 + id="linearGradient7182-1"
  485 + xlink:href="#linearGradient2871-7"
  486 + inkscape:collect="always" />
  487 + <linearGradient
  488 + y2="20.60858"
  489 + x2="15.984863"
  490 + y1="36.061237"
  491 + x1="62.513836"
  492 + gradientUnits="userSpaceOnUse"
  493 + id="linearGradient7180-4"
  494 + xlink:href="#linearGradient2380-9"
  495 + inkscape:collect="always" />
  496 + <linearGradient
  497 + gradientTransform="matrix(-1,0,0,-1,47.93934,50.02474)"
  498 + y2="23.554308"
  499 + x2="22.374878"
  500 + y1="13.604306"
  501 + x1="13.435029"
  502 + gradientUnits="userSpaceOnUse"
  503 + id="linearGradient7189-5"
  504 + xlink:href="#linearGradient7179-1"
  505 + inkscape:collect="always" />
  506 + <linearGradient
  507 + gradientUnits="userSpaceOnUse"
  508 + y2="23.554308"
  509 + x2="22.374878"
  510 + y1="13.604306"
  511 + x1="13.435029"
  512 + id="linearGradient7185-1"
  513 + xlink:href="#linearGradient7179-1"
  514 + inkscape:collect="always" />
  515 + <linearGradient
  516 + id="linearGradient1322-5">
  517 + <stop
  518 + style="stop-color:#729fcf"
  519 + offset="0.0000000"
  520 + id="stop1324-3" />
  521 + <stop
  522 + style="stop-color:#5187d6;stop-opacity:1.0000000;"
  523 + offset="1.0000000"
  524 + id="stop1326-9" />
  525 + </linearGradient>
  526 + <linearGradient
  527 + id="linearGradient2316-6">
  528 + <stop
  529 + id="stop2318-0"
  530 + offset="0"
  531 + style="stop-color:#000000;stop-opacity:1;" />
  532 + <stop
  533 + id="stop2320-8"
  534 + offset="1"
  535 + style="stop-color:#ffffff;stop-opacity:0.65979379;" />
  536 + </linearGradient>
  537 + <linearGradient
  538 + id="linearGradient7179-1"
  539 + inkscape:collect="always">
  540 + <stop
  541 + id="stop7181-7"
  542 + offset="0"
  543 + style="stop-color:#ffffff;stop-opacity:1;" />
  544 + <stop
  545 + id="stop7183-0"
  546 + offset="1"
  547 + style="stop-color:#ffffff;stop-opacity:0;" />
  548 + </linearGradient>
  549 + <linearGradient
  550 + y2="26.048164"
  551 + x2="52.854095"
  552 + y1="26.048164"
  553 + x1="5.9649177"
  554 + gradientUnits="userSpaceOnUse"
  555 + id="linearGradient1491-9"
  556 + xlink:href="#linearGradient2797-9"
  557 + inkscape:collect="always" />
  558 + <linearGradient
  559 + id="linearGradient2797-9"
  560 + inkscape:collect="always">
  561 + <stop
  562 + id="stop2799-7"
  563 + offset="0"
  564 + style="stop-color:#ffffff;stop-opacity:1;" />
  565 + <stop
  566 + id="stop2801-4"
  567 + offset="1"
  568 + style="stop-color:#ffffff;stop-opacity:0;" />
  569 + </linearGradient>
  570 + <linearGradient
  571 + y2="26.048164"
  572 + x2="52.854095"
  573 + y1="26.048164"
  574 + x1="5.9649177"
  575 + gradientUnits="userSpaceOnUse"
  576 + id="linearGradient1493-7"
  577 + xlink:href="#linearGradient2797-9"
  578 + inkscape:collect="always" />
  579 + <linearGradient
  580 + id="linearGradient2402-4">
  581 + <stop
  582 + id="stop2404-6"
  583 + offset="0"
  584 + style="stop-color:#729fcf;stop-opacity:1;" />
  585 + <stop
  586 + id="stop2406-3"
  587 + offset="1"
  588 + style="stop-color:#528ac5;stop-opacity:1;" />
  589 + </linearGradient>
  590 + <linearGradient
  591 + id="linearGradient2871-7"
  592 + inkscape:collect="always">
  593 + <stop
  594 + id="stop2873-5"
  595 + offset="0"
  596 + style="stop-color:#3465a4;stop-opacity:1;" />
  597 + <stop
  598 + id="stop2875-6"
  599 + offset="1"
  600 + style="stop-color:#3465a4;stop-opacity:1" />
  601 + </linearGradient>
  602 + <linearGradient
  603 + gradientTransform="translate(-48.77039,-5.765705)"
  604 + gradientUnits="userSpaceOnUse"
  605 + y2="24.842253"
  606 + x2="37.124462"
  607 + y1="30.748846"
  608 + x1="32.647972"
  609 + id="linearGradient2696-9"
  610 + xlink:href="#linearGradient2690-0"
  611 + inkscape:collect="always" />
  612 + <linearGradient
  613 + id="linearGradient2690-0"
  614 + inkscape:collect="always">
  615 + <stop
  616 + id="stop2692-2"
  617 + offset="0"
  618 + style="stop-color:#c4d7eb;stop-opacity:1;" />
  619 + <stop
  620 + id="stop2694-2"
  621 + offset="1"
  622 + style="stop-color:#c4d7eb;stop-opacity:0;" />
  623 + </linearGradient>
  624 + <linearGradient
  625 + gradientTransform="translate(-48.77039,-5.765705)"
  626 + gradientUnits="userSpaceOnUse"
  627 + y2="24.842253"
  628 + x2="37.124462"
  629 + y1="31.455952"
  630 + x1="36.713837"
  631 + id="linearGradient2688-1"
  632 + xlink:href="#linearGradient2682-8"
  633 + inkscape:collect="always" />
  634 + <linearGradient
  635 + id="linearGradient2682-8">
  636 + <stop
  637 + id="stop2684-0"
  638 + offset="0"
  639 + style="stop-color:#3977c3;stop-opacity:1;" />
  640 + <stop
  641 + id="stop2686-8"
  642 + offset="1"
  643 + style="stop-color:#89aedc;stop-opacity:0;" />
  644 + </linearGradient>
  645 + <linearGradient
  646 + id="linearGradient2380-9">
  647 + <stop
  648 + id="stop2382-6"
  649 + offset="0"
  650 + style="stop-color:#b9cfe7;stop-opacity:1" />
  651 + <stop
  652 + id="stop2384-4"
  653 + offset="1"
  654 + style="stop-color:#729fcf;stop-opacity:1" />
  655 + </linearGradient>
  656 + <linearGradient
  657 + y2="19.115122"
  658 + x2="15.419417"
  659 + y1="10.612206"
  660 + x1="13.478554"
  661 + gradientTransform="translate(-48.30498,-6.043298)"
  662 + gradientUnits="userSpaceOnUse"
  663 + id="linearGradient1486-1"
  664 + xlink:href="#linearGradient2831-1"
  665 + inkscape:collect="always" />
  666 + <linearGradient
  667 + id="linearGradient2831-1">
  668 + <stop
  669 + id="stop2833-6"
  670 + offset="0"
  671 + style="stop-color:#3465a4;stop-opacity:1;" />
  672 + <stop
  673 + style="stop-color:#5b86be;stop-opacity:1;"
  674 + offset="0.33333334"
  675 + id="stop2855-6" />
  676 + <stop
  677 + id="stop2835-2"
  678 + offset="1"
  679 + style="stop-color:#83a8d8;stop-opacity:0;" />
  680 + </linearGradient>
  681 + <linearGradient
  682 + y2="26.194071"
  683 + x2="37.065414"
  684 + y1="29.729605"
  685 + x1="37.128052"
  686 + gradientTransform="matrix(-1,0,0,-1,-1.24248,40.0817)"
  687 + gradientUnits="userSpaceOnUse"
  688 + id="linearGradient1488-5"
  689 + xlink:href="#linearGradient2847-4"
  690 + inkscape:collect="always" />
  691 + <linearGradient
  692 + id="linearGradient2847-4"
  693 + inkscape:collect="always">
  694 + <stop
  695 + id="stop2849-0"
  696 + offset="0"
  697 + style="stop-color:#3465a4;stop-opacity:1;" />
  698 + <stop
  699 + id="stop2851-8"
  700 + offset="1"
  701 + style="stop-color:#3465a4;stop-opacity:0;" />
  702 + </linearGradient>
  703 + <radialGradient
  704 + r="15.644737"
  705 + fy="36.421127"
  706 + fx="24.837126"
  707 + cy="36.421127"
  708 + cx="24.837126"
  709 + gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
  710 + gradientUnits="userSpaceOnUse"
  711 + id="radialGradient1503-0"
  712 + xlink:href="#linearGradient8662-1"
  713 + inkscape:collect="always" />
  714 + <linearGradient
  715 + id="linearGradient8662-1"
  716 + inkscape:collect="always">
  717 + <stop
  718 + id="stop8664-1"
  719 + offset="0"
  720 + style="stop-color:#000000;stop-opacity:1;" />
  721 + <stop
  722 + id="stop8666-7"
  723 + offset="1"
  724 + style="stop-color:#000000;stop-opacity:0;" />
  725 + </linearGradient>
  726 + <linearGradient
  727 + id="linearGradient7916-1">
  728 + <stop
  729 + id="stop7918-7"
  730 + offset="0"
  731 + style="stop-color:#ffffff;stop-opacity:1;" />
  732 + <stop
  733 + id="stop7920-1"
  734 + offset="1.0000000"
  735 + style="stop-color:#ffffff;stop-opacity:0.34020618;" />
  736 + </linearGradient>
  737 + <inkscape:perspective
  738 + id="perspective70-5"
  739 + inkscape:persp3d-origin="24 : 16 : 1"
  740 + inkscape:vp_z="48 : 24 : 1"
  741 + inkscape:vp_y="0 : 1000 : 0"
  742 + inkscape:vp_x="0 : 24 : 1"
  743 + sodipodi:type="inkscape:persp3d" />
  744 + <inkscape:perspective
  745 + id="perspective29237"
  746 + inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
  747 + inkscape:vp_z="1 : 0.5 : 1"
  748 + inkscape:vp_y="0 : 1000 : 0"
  749 + inkscape:vp_x="0 : 0.5 : 1"
  750 + sodipodi:type="inkscape:persp3d" />
  751 + <linearGradient
  752 + inkscape:collect="always"
  753 + id="linearGradient6998-2">
  754 + <stop
  755 + style="stop-color:#000000;stop-opacity:1;"
  756 + offset="0"
  757 + id="stop7000-7" />
  758 + <stop
  759 + style="stop-color:#000000;stop-opacity:0;"
  760 + offset="1"
  761 + id="stop7002-1" />
  762 + </linearGradient>
  763 + <radialGradient
  764 + inkscape:collect="always"
  765 + xlink:href="#linearGradient6998-2"
  766 + id="radialGradient29477"
  767 + gradientUnits="userSpaceOnUse"
  768 + gradientTransform="matrix(1,0,0,0.3222749,0,20.728756)"
  769 + cx="19.688505"
  770 + cy="30.585787"
  771 + fx="19.688505"
  772 + fy="30.585787"
  773 + r="4.6624851" />
  774 + <radialGradient
  775 + inkscape:collect="always"
  776 + xlink:href="#linearGradient6998-2"
  777 + id="radialGradient29479"
  778 + gradientUnits="userSpaceOnUse"
  779 + gradientTransform="matrix(1,0,0,0.3222749,0,20.728756)"
  780 + cx="19.688505"
  781 + cy="30.585787"
  782 + fx="19.688505"
  783 + fy="30.585787"
  784 + r="4.6624851" />
  785 + <linearGradient
  786 + y2="35.739632"
  787 + x2="21.408455"
  788 + y1="36.390400"
  789 + x1="22.686766"
  790 + gradientTransform="matrix(-0.977685,0.210075,0.210075,0.977685,41.80576,-11.11866)"
  791 + gradientUnits="userSpaceOnUse"
  792 + id="linearGradient4374"
  793 + xlink:href="#linearGradient4356"
  794 + inkscape:collect="always" />
  795 + <linearGradient
  796 + y2="36.217758"
  797 + x2="22.626925"
  798 + y1="35.817974"
  799 + x1="20.661695"
  800 + gradientTransform="matrix(0.983375,0.181588,-0.181588,0.983375,-7.072120,-9.824920)"
  801 + gradientUnits="userSpaceOnUse"
  802 + id="linearGradient4372"
  803 + xlink:href="#linearGradient4356"
  804 + inkscape:collect="always" />
  805 + <radialGradient
  806 + gradientUnits="userSpaceOnUse"
  807 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.681917,0.000000,8.233773)"
  808 + r="13.565360"
  809 + fy="19.836468"
  810 + fx="16.214741"
  811 + cy="19.836468"
  812 + cx="16.214741"
  813 + id="radialGradient4350"
  814 + xlink:href="#linearGradient4344"
  815 + inkscape:collect="always" />
  816 + <linearGradient
  817 + gradientTransform="translate(-13.12500,-7.000000)"
  818 + y2="35.803486"
  819 + x2="30.935921"
  820 + y1="29.553486"
  821 + x1="30.935921"
  822 + gradientUnits="userSpaceOnUse"
  823 + id="linearGradient4332"
  824 + xlink:href="#linearGradient3824"
  825 + inkscape:collect="always" />
  826 + <linearGradient
  827 + y2="35.803486"
  828 + x2="30.935921"
  829 + y1="29.553486"
  830 + x1="30.935921"
  831 + gradientTransform="translate(-12.41789,-7.000000)"
  832 + gradientUnits="userSpaceOnUse"
  833 + id="linearGradient4326"
  834 + xlink:href="#linearGradient3824"
  835 + inkscape:collect="always" />
  836 + <linearGradient
  837 + gradientTransform="translate(0.707108,0.000000)"
  838 + y2="35.803486"
  839 + x2="30.935921"
  840 + y1="29.553486"
  841 + x1="30.935921"
  842 + gradientUnits="userSpaceOnUse"
  843 + id="linearGradient4175"
  844 + xlink:href="#linearGradient3824"
  845 + inkscape:collect="always" />
  846 + <radialGradient
  847 + gradientUnits="userSpaceOnUse"
  848 + r="9.1620579"
  849 + fy="17.064077"
  850 + fx="29.344931"
  851 + cy="17.064077"
  852 + cx="29.344931"
  853 + id="radialGradient3806"
  854 + xlink:href="#linearGradient3800"
  855 + inkscape:collect="always" />
  856 + <linearGradient
  857 + id="linearGradient3800">
  858 + <stop
  859 + id="stop3802"
  860 + offset="0.0000000"
  861 + style="stop-color:#f4d9b1;stop-opacity:1.0000000;" />
  862 + <stop
  863 + id="stop3804"
  864 + offset="1.0000000"
  865 + style="stop-color:#df9725;stop-opacity:1.0000000;" />
  866 + </linearGradient>
  867 + <linearGradient
  868 + id="linearGradient3816"
  869 + inkscape:collect="always">
  870 + <stop
  871 + id="stop3818"
  872 + offset="0"
  873 + style="stop-color:#000000;stop-opacity:1;" />
  874 + <stop
  875 + id="stop3820"
  876 + offset="1"
  877 + style="stop-color:#000000;stop-opacity:0;" />
  878 + </linearGradient>
  879 + <linearGradient
  880 + id="linearGradient3824">
  881 + <stop
  882 + id="stop3826"
  883 + offset="0"
  884 + style="stop-color:#ffffff;stop-opacity:1;" />
  885 + <stop
  886 + id="stop3828"
  887 + offset="1.0000000"
  888 + style="stop-color:#c9c9c9;stop-opacity:1.0000000;" />
  889 + </linearGradient>
  890 + <linearGradient
  891 + id="linearGradient4163">
  892 + <stop
  893 + id="stop4165"
  894 + offset="0.0000000"
  895 + style="stop-color:#3b74bc;stop-opacity:1.0000000;" />
  896 + <stop
  897 + id="stop4167"
  898 + offset="1.0000000"
  899 + style="stop-color:#2d5990;stop-opacity:1.0000000;" />
  900 + </linearGradient>
  901 + <linearGradient
  902 + id="linearGradient4338">
  903 + <stop
  904 + style="stop-color:#e9b15e;stop-opacity:1.0000000;"
  905 + offset="0.0000000"
  906 + id="stop4340" />
  907 + <stop
  908 + style="stop-color:#966416;stop-opacity:1.0000000;"
  909 + offset="1.0000000"
  910 + id="stop4342" />
  911 + </linearGradient>
  912 + <linearGradient
  913 + id="linearGradient4344">
  914 + <stop
  915 + id="stop4346"
  916 + offset="0"
  917 + style="stop-color:#727e0a;stop-opacity:1;" />
  918 + <stop
  919 + id="stop4348"
  920 + offset="1.0000000"
  921 + style="stop-color:#5b6508;stop-opacity:1.0000000;" />
  922 + </linearGradient>
  923 + <linearGradient
  924 + id="linearGradient4356"
  925 + inkscape:collect="always">
  926 + <stop
  927 + id="stop4358"
  928 + offset="0"
  929 + style="stop-color:#000000;stop-opacity:1;" />
  930 + <stop
  931 + id="stop4360"
  932 + offset="1"
  933 + style="stop-color:#000000;stop-opacity:0;" />
  934 + </linearGradient>
  935 + <inkscape:perspective
  936 + id="perspective72"
  937 + inkscape:persp3d-origin="24 : 16 : 1"
  938 + inkscape:vp_z="48 : 24 : 1"
  939 + inkscape:vp_y="0 : 1000 : 0"
  940 + inkscape:vp_x="0 : 24 : 1"
  941 + sodipodi:type="inkscape:persp3d" />
  942 + <radialGradient
  943 + inkscape:collect="always"
  944 + xlink:href="#linearGradient3816"
  945 + id="radialGradient4243"
  946 + gradientUnits="userSpaceOnUse"
  947 + cx="31.112698"
  948 + cy="19.008621"
  949 + fx="31.112698"
  950 + fy="19.008621"
  951 + r="8.6620579" />
  952 + <radialGradient
  953 + inkscape:collect="always"
  954 + xlink:href="#linearGradient3800"
  955 + id="radialGradient4251"
  956 + gradientUnits="userSpaceOnUse"
  957 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  958 + cx="29.344931"
  959 + cy="17.064077"
  960 + fx="29.344931"
  961 + fy="17.064077"
  962 + r="9.1620579" />
  963 + <radialGradient
  964 + inkscape:collect="always"
  965 + xlink:href="#linearGradient3816"
  966 + id="radialGradient4257"
  967 + gradientUnits="userSpaceOnUse"
  968 + cx="31.112698"
  969 + cy="19.008621"
  970 + fx="31.112698"
  971 + fy="19.008621"
  972 + r="8.6620579" />
  973 + <radialGradient
  974 + inkscape:collect="always"
  975 + xlink:href="#linearGradient3816"
  976 + id="radialGradient4261"
  977 + gradientUnits="userSpaceOnUse"
  978 + cx="31.112698"
  979 + cy="19.008621"
  980 + fx="31.112698"
  981 + fy="19.008621"
  982 + r="8.6620579" />
  983 + <radialGradient
  984 + inkscape:collect="always"
  985 + xlink:href="#linearGradient4338"
  986 + id="radialGradient4263"
  987 + gradientUnits="userSpaceOnUse"
  988 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  989 + cx="29.344931"
  990 + cy="17.064077"
  991 + fx="29.344931"
  992 + fy="17.064077"
  993 + r="9.1620579" />
  994 + <linearGradient
  995 + inkscape:collect="always"
  996 + xlink:href="#linearGradient4356"
  997 + id="linearGradient4272"
  998 + gradientUnits="userSpaceOnUse"
  999 + gradientTransform="matrix(-0.977685,0.210075,0.210075,0.977685,40.096379,991.52674)"
  1000 + x1="22.686766"
  1001 + y1="36.390400"
  1002 + x2="21.408455"
  1003 + y2="35.739632" />
  1004 + <linearGradient
  1005 + inkscape:collect="always"
  1006 + xlink:href="#linearGradient4356"
  1007 + id="linearGradient4275"
  1008 + gradientUnits="userSpaceOnUse"
  1009 + gradientTransform="matrix(0.95236899,-0.304949,0.304949,0.95236899,-24.109161,1001.0625)"
  1010 + x1="20.661695"
  1011 + y1="35.817974"
  1012 + x2="22.626925"
  1013 + y2="36.217758" />
  1014 + <linearGradient
  1015 + inkscape:collect="always"
  1016 + xlink:href="#linearGradient3824"
  1017 + id="linearGradient4278"
  1018 + gradientUnits="userSpaceOnUse"
  1019 + gradientTransform="translate(-14.127271,995.6454)"
  1020 + x1="30.935921"
  1021 + y1="29.553486"
  1022 + x2="30.935921"
  1023 + y2="35.803486" />
  1024 + <radialGradient
  1025 + inkscape:collect="always"
  1026 + xlink:href="#linearGradient4344"
  1027 + id="radialGradient4287"
  1028 + gradientUnits="userSpaceOnUse"
  1029 + gradientTransform="matrix(1,0,0,0.681917,-1.7093807,1010.8792)"
  1030 + cx="16.214741"
  1031 + cy="19.836468"
  1032 + fx="16.214741"
  1033 + fy="19.836468"
  1034 + r="13.565360" />
  1035 + <radialGradient
  1036 + inkscape:collect="always"
  1037 + xlink:href="#linearGradient4338"
  1038 + id="radialGradient4300"
  1039 + gradientUnits="userSpaceOnUse"
  1040 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1041 + cx="29.344931"
  1042 + cy="17.064077"
  1043 + fx="29.344931"
  1044 + fy="17.064077"
  1045 + r="9.1620579" />
  1046 + <radialGradient
  1047 + inkscape:collect="always"
  1048 + xlink:href="#linearGradient4163"
  1049 + id="radialGradient4318"
  1050 + gradientUnits="userSpaceOnUse"
  1051 + gradientTransform="matrix(1.297564,0,0,0.884831,-4.2342547,1013.4195)"
  1052 + cx="28.089741"
  1053 + cy="27.203083"
  1054 + fx="28.089741"
  1055 + fy="27.203083"
  1056 + r="13.565360" />
  1057 + <radialGradient
  1058 + inkscape:collect="always"
  1059 + xlink:href="#linearGradient3816"
  1060 + id="radialGradient4321"
  1061 + gradientUnits="userSpaceOnUse"
  1062 + cx="31.112698"
  1063 + cy="19.008621"
  1064 + fx="31.112698"
  1065 + fy="19.008621"
  1066 + r="8.6620579" />
  1067 + <inkscape:perspective
  1068 + id="perspective4984"
  1069 + inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
  1070 + inkscape:vp_z="1 : 0.5 : 1"
  1071 + inkscape:vp_y="0 : 1000 : 0"
  1072 + inkscape:vp_x="0 : 0.5 : 1"
  1073 + sodipodi:type="inkscape:persp3d" />
  1074 + <linearGradient
  1075 + id="linearGradient5048">
  1076 + <stop
  1077 + style="stop-color:black;stop-opacity:0;"
  1078 + offset="0"
  1079 + id="stop5050" />
  1080 + <stop
  1081 + id="stop5056"
  1082 + offset="0.5"
  1083 + style="stop-color:black;stop-opacity:1;" />
  1084 + <stop
  1085 + style="stop-color:black;stop-opacity:0;"
  1086 + offset="1"
  1087 + id="stop5052" />
  1088 + </linearGradient>
  1089 + <linearGradient
  1090 + id="linearGradient259">
  1091 + <stop
  1092 + id="stop260"
  1093 + offset="0.0000000"
  1094 + style="stop-color:#fafafa;stop-opacity:1.0000000;" />
  1095 + <stop
  1096 + id="stop261"
  1097 + offset="1.0000000"
  1098 + style="stop-color:#bbbbbb;stop-opacity:1.0000000;" />
  1099 + </linearGradient>
  1100 + <linearGradient
  1101 + id="linearGradient269">
  1102 + <stop
  1103 + id="stop270"
  1104 + offset="0.0000000"
  1105 + style="stop-color:#a3a3a3;stop-opacity:1.0000000;" />
  1106 + <stop
  1107 + id="stop271"
  1108 + offset="1.0000000"
  1109 + style="stop-color:#4c4c4c;stop-opacity:1.0000000;" />
  1110 + </linearGradient>
  1111 + <linearGradient
  1112 + id="linearGradient15662">
  1113 + <stop
  1114 + id="stop15664"
  1115 + offset="0.0000000"
  1116 + style="stop-color:#ffffff;stop-opacity:1.0000000;" />
  1117 + <stop
  1118 + id="stop15666"
  1119 + offset="1.0000000"
  1120 + style="stop-color:#f8f8f8;stop-opacity:1.0000000;" />
  1121 + </linearGradient>
  1122 + <linearGradient
  1123 + inkscape:collect="always"
  1124 + id="linearGradient5866">
  1125 + <stop
  1126 + style="stop-color:#000000;stop-opacity:1;"
  1127 + offset="0"
  1128 + id="stop5868" />
  1129 + <stop
  1130 + style="stop-color:#000000;stop-opacity:0;"
  1131 + offset="1"
  1132 + id="stop5870" />
  1133 + </linearGradient>
  1134 + <linearGradient
  1135 + id="linearGradient2598">
  1136 + <stop
  1137 + style="stop-color:#859dbc;stop-opacity:1;"
  1138 + offset="0"
  1139 + id="stop2600" />
  1140 + <stop
  1141 + style="stop-color:#547299;stop-opacity:1;"
  1142 + offset="1"
  1143 + id="stop2602" />
  1144 + </linearGradient>
  1145 + <linearGradient
  1146 + inkscape:collect="always"
  1147 + id="linearGradient4404">
  1148 + <stop
  1149 + style="stop-color:#ffffff;stop-opacity:1;"
  1150 + offset="0"
  1151 + id="stop4406" />
  1152 + <stop
  1153 + style="stop-color:#ffffff;stop-opacity:0;"
  1154 + offset="1"
  1155 + id="stop4408" />
  1156 + </linearGradient>
  1157 + <linearGradient
  1158 + id="linearGradient5897">
  1159 + <stop
  1160 + style="stop-color:#000000;stop-opacity:0.0000000;"
  1161 + offset="0.0000000"
  1162 + id="stop5899" />
  1163 + <stop
  1164 + id="stop5905"
  1165 + offset="0.50000000"
  1166 + style="stop-color:#000000;stop-opacity:0.56701028;" />
  1167 + <stop
  1168 + style="stop-color:#000000;stop-opacity:0.0000000;"
  1169 + offset="1.0000000"
  1170 + id="stop5901" />
  1171 + </linearGradient>
  1172 + <linearGradient
  1173 + id="linearGradient2590">
  1174 + <stop
  1175 + style="stop-color:#ffffff;stop-opacity:1;"
  1176 + offset="0"
  1177 + id="stop2592" />
  1178 + <stop
  1179 + style="stop-color:#ffffff;stop-opacity:0;"
  1180 + offset="1"
  1181 + id="stop2594" />
  1182 + </linearGradient>
  1183 + <linearGradient
  1184 + inkscape:collect="always"
  1185 + id="linearGradient2906">
  1186 + <stop
  1187 + style="stop-color:#ffffff;stop-opacity:1;"
  1188 + offset="0"
  1189 + id="stop2908" />
  1190 + <stop
  1191 + style="stop-color:#ffffff;stop-opacity:0;"
  1192 + offset="1"
  1193 + id="stop2910" />
  1194 + </linearGradient>
  1195 + <linearGradient
  1196 + inkscape:collect="always"
  1197 + xlink:href="#linearGradient2896"
  1198 + id="linearGradient2902"
  1199 + x1="14.584077"
  1200 + y1="1.6392649"
  1201 + x2="14.552828"
  1202 + y2="2.4912448"
  1203 + gradientUnits="userSpaceOnUse"
  1204 + gradientTransform="matrix(1,0,0,1.594214,0,-0.790249)" />
  1205 + <linearGradient
  1206 + inkscape:collect="always"
  1207 + id="linearGradient2896">
  1208 + <stop
  1209 + style="stop-color:#000000;stop-opacity:1;"
  1210 + offset="0"
  1211 + id="stop2898" />
  1212 + <stop
  1213 + style="stop-color:#000000;stop-opacity:0;"
  1214 + offset="1"
  1215 + id="stop2900" />
  1216 + </linearGradient>
  1217 + <linearGradient
  1218 + inkscape:collect="always"
  1219 + xlink:href="#linearGradient2896"
  1220 + id="linearGradient5356"
  1221 + gradientUnits="userSpaceOnUse"
  1222 + gradientTransform="matrix(0.35541046,-0.09680687,0.16161446,0.54106498,4.9107799,1029.1036)"
  1223 + x1="14.584077"
  1224 + y1="1.6392649"
  1225 + x2="14.552828"
  1226 + y2="2.4912448" />
  1227 + <linearGradient
  1228 + inkscape:collect="always"
  1229 + xlink:href="#linearGradient2906"
  1230 + id="linearGradient5359"
  1231 + gradientUnits="userSpaceOnUse"
  1232 + gradientTransform="matrix(0.35541046,-0.09680687,0.12011148,0.40211819,4.9170956,1029.1248)"
  1233 + x1="13.354311"
  1234 + y1="1.4866425"
  1235 + x2="14.075844"
  1236 + y2="2.4017651" />
  1237 + <linearGradient
  1238 + inkscape:collect="always"
  1239 + xlink:href="#linearGradient2590"
  1240 + id="linearGradient5362"
  1241 + gradientUnits="userSpaceOnUse"
  1242 + gradientTransform="matrix(0.46898152,-0.12774141,0.12979691,0.43454379,2.9741905,1030.0125)"
  1243 + x1="19.970377"
  1244 + y1="6.1167107"
  1245 + x2="19.970377"
  1246 + y2="2.53125" />
  1247 + <linearGradient
  1248 + inkscape:collect="always"
  1249 + xlink:href="#linearGradient5897"
  1250 + id="linearGradient5365"
  1251 + gradientUnits="userSpaceOnUse"
  1252 + gradientTransform="matrix(0.48606741,0,0,0.75585752,-291.56104,989.60553)"
  1253 + x1="19"
  1254 + y1="9.7738247"
  1255 + x2="19"
  1256 + y2="15.635596" />
  1257 + <linearGradient
  1258 + inkscape:collect="always"
  1259 + xlink:href="#linearGradient4404"
  1260 + id="linearGradient5368"
  1261 + gradientUnits="userSpaceOnUse"
  1262 + gradientTransform="matrix(-0.46898152,0.12774141,0.1380797,0.46227355,19.308685,1025.339)"
  1263 + x1="16.8125"
  1264 + y1="1.875"
  1265 + x2="16.8125"
  1266 + y2="4.71875" />
  1267 + <linearGradient
  1268 + inkscape:collect="always"
  1269 + xlink:href="#linearGradient2598"
  1270 + id="linearGradient5371"
  1271 + gradientUnits="userSpaceOnUse"
  1272 + gradientTransform="matrix(0.46898152,-0.12774141,0.13168827,0.44087584,3.7514528,1029.219)"
  1273 + x1="18.431311"
  1274 + y1="19.119474"
  1275 + x2="18.402472"
  1276 + y2="4.2702327" />
  1277 + <linearGradient
  1278 + inkscape:collect="always"
  1279 + xlink:href="#linearGradient5866"
  1280 + id="linearGradient5374"
  1281 + gradientUnits="userSpaceOnUse"
  1282 + gradientTransform="matrix(0.43511303,-0.11851629,0.12998141,0.43516148,5.1497806,1029.4257)"
  1283 + x1="19.452349"
  1284 + y1="13.174174"
  1285 + x2="19.685436"
  1286 + y2="27.095339" />
  1287 + <radialGradient
  1288 + inkscape:collect="always"
  1289 + xlink:href="#linearGradient15662"
  1290 + id="radialGradient5394"
  1291 + gradientUnits="userSpaceOnUse"
  1292 + gradientTransform="matrix(0.35842521,0,0,0.36658484,-288.74498,994.99084)"
  1293 + cx="8.1435566"
  1294 + cy="7.2678967"
  1295 + fx="8.1435566"
  1296 + fy="7.2678967"
  1297 + r="38.158695" />
  1298 + <radialGradient
  1299 + inkscape:collect="always"
  1300 + xlink:href="#linearGradient259"
  1301 + id="radialGradient5397"
  1302 + gradientUnits="userSpaceOnUse"
  1303 + gradientTransform="matrix(0.353806,0,0,0.37006743,-289.95036,994.73776)"
  1304 + cx="33.966679"
  1305 + cy="35.736916"
  1306 + fx="33.966679"
  1307 + fy="35.736916"
  1308 + r="86.70845" />
  1309 + <radialGradient
  1310 + inkscape:collect="always"
  1311 + xlink:href="#linearGradient269"
  1312 + id="radialGradient5399"
  1313 + gradientUnits="userSpaceOnUse"
  1314 + gradientTransform="matrix(0.35667183,0,0,0.36709384,-288.71505,994.96754)"
  1315 + cx="8.824419"
  1316 + cy="3.7561285"
  1317 + fx="8.824419"
  1318 + fy="3.7561285"
  1319 + r="37.751713" />
  1320 + <radialGradient
  1321 + inkscape:collect="always"
  1322 + xlink:href="#linearGradient4163"
  1323 + id="radialGradient6182"
  1324 + gradientUnits="userSpaceOnUse"
  1325 + gradientTransform="matrix(1.297564,0,0,0.884831,-4.2342547,1013.4195)"
  1326 + cx="28.089741"
  1327 + cy="27.203083"
  1328 + fx="28.089741"
  1329 + fy="27.203083"
  1330 + r="13.565360" />
  1331 + <radialGradient
  1332 + inkscape:collect="always"
  1333 + xlink:href="#linearGradient3816"
  1334 + id="radialGradient6184"
  1335 + gradientUnits="userSpaceOnUse"
  1336 + cx="31.112698"
  1337 + cy="19.008621"
  1338 + fx="31.112698"
  1339 + fy="19.008621"
  1340 + r="8.6620579" />
  1341 + <radialGradient
  1342 + inkscape:collect="always"
  1343 + xlink:href="#linearGradient3800"
  1344 + id="radialGradient6186"
  1345 + gradientUnits="userSpaceOnUse"
  1346 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1347 + cx="29.344931"
  1348 + cy="17.064077"
  1349 + fx="29.344931"
  1350 + fy="17.064077"
  1351 + r="9.1620579" />
  1352 + <radialGradient
  1353 + inkscape:collect="always"
  1354 + xlink:href="#linearGradient3816"
  1355 + id="radialGradient6197"
  1356 + gradientUnits="userSpaceOnUse"
  1357 + cx="31.112698"
  1358 + cy="19.008621"
  1359 + fx="31.112698"
  1360 + fy="19.008621"
  1361 + r="8.6620579" />
  1362 + <radialGradient
  1363 + inkscape:collect="always"
  1364 + xlink:href="#linearGradient4163"
  1365 + id="radialGradient6199"
  1366 + gradientUnits="userSpaceOnUse"
  1367 + gradientTransform="matrix(1.297564,0,0,0.884831,-4.2342547,1013.4195)"
  1368 + cx="28.089741"
  1369 + cy="27.203083"
  1370 + fx="28.089741"
  1371 + fy="27.203083"
  1372 + r="13.565360" />
  1373 + <radialGradient
  1374 + inkscape:collect="always"
  1375 + xlink:href="#linearGradient3816"
  1376 + id="radialGradient6201"
  1377 + gradientUnits="userSpaceOnUse"
  1378 + cx="31.112698"
  1379 + cy="19.008621"
  1380 + fx="31.112698"
  1381 + fy="19.008621"
  1382 + r="8.6620579" />
  1383 + <radialGradient
  1384 + inkscape:collect="always"
  1385 + xlink:href="#linearGradient3800"
  1386 + id="radialGradient6203"
  1387 + gradientUnits="userSpaceOnUse"
  1388 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1389 + cx="29.344931"
  1390 + cy="17.064077"
  1391 + fx="29.344931"
  1392 + fy="17.064077"
  1393 + r="9.1620579" />
  1394 + <radialGradient
  1395 + inkscape:collect="always"
  1396 + xlink:href="#linearGradient3816"
  1397 + id="radialGradient6242"
  1398 + gradientUnits="userSpaceOnUse"
  1399 + cx="31.112698"
  1400 + cy="19.008621"
  1401 + fx="31.112698"
  1402 + fy="19.008621"
  1403 + r="8.6620579" />
  1404 + <radialGradient
  1405 + inkscape:collect="always"
  1406 + xlink:href="#linearGradient4344"
  1407 + id="radialGradient6244"
  1408 + gradientUnits="userSpaceOnUse"
  1409 + gradientTransform="matrix(1,0,0,0.681917,-1.7093807,1010.8792)"
  1410 + cx="16.214741"
  1411 + cy="19.836468"
  1412 + fx="16.214741"
  1413 + fy="19.836468"
  1414 + r="13.565360" />
  1415 + <radialGradient
  1416 + inkscape:collect="always"
  1417 + xlink:href="#linearGradient3816"
  1418 + id="radialGradient6246"
  1419 + gradientUnits="userSpaceOnUse"
  1420 + cx="31.112698"
  1421 + cy="19.008621"
  1422 + fx="31.112698"
  1423 + fy="19.008621"
  1424 + r="8.6620579" />
  1425 + <radialGradient
  1426 + inkscape:collect="always"
  1427 + xlink:href="#linearGradient4338"
  1428 + id="radialGradient6248"
  1429 + gradientUnits="userSpaceOnUse"
  1430 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1431 + cx="29.344931"
  1432 + cy="17.064077"
  1433 + fx="29.344931"
  1434 + fy="17.064077"
  1435 + r="9.1620579" />
  1436 + <linearGradient
  1437 + inkscape:collect="always"
  1438 + xlink:href="#linearGradient3824"
  1439 + id="linearGradient6250"
  1440 + gradientUnits="userSpaceOnUse"
  1441 + gradientTransform="translate(-14.127271,995.6454)"
  1442 + x1="30.935921"
  1443 + y1="29.553486"
  1444 + x2="30.935921"
  1445 + y2="35.803486" />
  1446 + <linearGradient
  1447 + inkscape:collect="always"
  1448 + xlink:href="#linearGradient4356"
  1449 + id="linearGradient6252"
  1450 + gradientUnits="userSpaceOnUse"
  1451 + gradientTransform="matrix(0.95236899,-0.304949,0.304949,0.95236899,-24.109161,1001.0625)"
  1452 + x1="20.661695"
  1453 + y1="35.817974"
  1454 + x2="22.626925"
  1455 + y2="36.217758" />
  1456 + <linearGradient
  1457 + inkscape:collect="always"
  1458 + xlink:href="#linearGradient4356"
  1459 + id="linearGradient6254"
  1460 + gradientUnits="userSpaceOnUse"
  1461 + gradientTransform="matrix(-0.977685,0.210075,0.210075,0.977685,40.096379,991.52674)"
  1462 + x1="22.686766"
  1463 + y1="36.390400"
  1464 + x2="21.408455"
  1465 + y2="35.739632" />
  1466 + <radialGradient
  1467 + inkscape:collect="always"
  1468 + xlink:href="#linearGradient259"
  1469 + id="radialGradient6256"
  1470 + gradientUnits="userSpaceOnUse"
  1471 + gradientTransform="matrix(0.353806,0,0,0.37006743,-289.95036,994.73776)"
  1472 + cx="33.966679"
  1473 + cy="35.736916"
  1474 + fx="33.966679"
  1475 + fy="35.736916"
  1476 + r="86.70845" />
  1477 + <radialGradient
  1478 + inkscape:collect="always"
  1479 + xlink:href="#linearGradient269"
  1480 + id="radialGradient6258"
  1481 + gradientUnits="userSpaceOnUse"
  1482 + gradientTransform="matrix(0.35667183,0,0,0.36709384,-288.71505,994.96754)"
  1483 + cx="8.824419"
  1484 + cy="3.7561285"
  1485 + fx="8.824419"
  1486 + fy="3.7561285"
  1487 + r="37.751713" />
  1488 + <radialGradient
  1489 + inkscape:collect="always"
  1490 + xlink:href="#linearGradient15662"
  1491 + id="radialGradient6260"
  1492 + gradientUnits="userSpaceOnUse"
  1493 + gradientTransform="matrix(0.35842521,0,0,0.36658484,-288.74498,994.99084)"
  1494 + cx="8.1435566"
  1495 + cy="7.2678967"
  1496 + fx="8.1435566"
  1497 + fy="7.2678967"
  1498 + r="38.158695" />
  1499 + <linearGradient
  1500 + inkscape:collect="always"
  1501 + xlink:href="#linearGradient2906"
  1502 + id="linearGradient6262"
  1503 + gradientUnits="userSpaceOnUse"
  1504 + gradientTransform="matrix(0.35541046,-0.09680687,0.12011148,0.40211819,4.9170956,1029.1248)"
  1505 + x1="13.354311"
  1506 + y1="1.4866425"
  1507 + x2="14.075844"
  1508 + y2="2.4017651" />
  1509 + <linearGradient
  1510 + inkscape:collect="always"
  1511 + xlink:href="#linearGradient2896"
  1512 + id="linearGradient6264"
  1513 + gradientUnits="userSpaceOnUse"
  1514 + gradientTransform="matrix(0.35541046,-0.09680687,0.16161446,0.54106498,4.9107799,1029.1036)"
  1515 + x1="14.584077"
  1516 + y1="1.6392649"
  1517 + x2="14.552828"
  1518 + y2="2.4912448" />
  1519 + <radialGradient
  1520 + inkscape:collect="always"
  1521 + xlink:href="#linearGradient4338"
  1522 + id="radialGradient6266"
  1523 + gradientUnits="userSpaceOnUse"
  1524 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1525 + cx="29.344931"
  1526 + cy="17.064077"
  1527 + fx="29.344931"
  1528 + fy="17.064077"
  1529 + r="9.1620579" />
  1530 + <radialGradient
  1531 + inkscape:collect="always"
  1532 + xlink:href="#linearGradient4163"
  1533 + id="radialGradient6275"
  1534 + gradientUnits="userSpaceOnUse"
  1535 + gradientTransform="matrix(1.297564,0,0,0.884831,-4.2342547,1013.4195)"
  1536 + cx="28.089741"
  1537 + cy="27.203083"
  1538 + fx="28.089741"
  1539 + fy="27.203083"
  1540 + r="13.565360" />
  1541 + <radialGradient
  1542 + inkscape:collect="always"
  1543 + xlink:href="#linearGradient3816"
  1544 + id="radialGradient6277"
  1545 + gradientUnits="userSpaceOnUse"
  1546 + cx="31.112698"
  1547 + cy="19.008621"
  1548 + fx="31.112698"
  1549 + fy="19.008621"
  1550 + r="8.6620579" />
  1551 + <radialGradient
  1552 + inkscape:collect="always"
  1553 + xlink:href="#linearGradient3800"
  1554 + id="radialGradient6279"
  1555 + gradientUnits="userSpaceOnUse"
  1556 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1557 + cx="29.344931"
  1558 + cy="17.064077"
  1559 + fx="29.344931"
  1560 + fy="17.064077"
  1561 + r="9.1620579" />
  1562 + <radialGradient
  1563 + inkscape:collect="always"
  1564 + xlink:href="#linearGradient4163"
  1565 + id="radialGradient6286"
  1566 + gradientUnits="userSpaceOnUse"
  1567 + gradientTransform="matrix(1.297564,0,0,0.884831,-6.8592547,1012.4195)"
  1568 + cx="28.089741"
  1569 + cy="27.203083"
  1570 + fx="28.089741"
  1571 + fy="27.203083"
  1572 + r="13.565360" />
  1573 + <radialGradient
  1574 + inkscape:collect="always"
  1575 + xlink:href="#linearGradient3800"
  1576 + id="radialGradient6300"
  1577 + gradientUnits="userSpaceOnUse"
  1578 + gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
  1579 + cx="29.344931"
  1580 + cy="17.064077"
  1581 + fx="29.344931"
  1582 + fy="17.064077"
  1583 + r="9.1620579" />
  1584 + </defs>
  1585 + <sodipodi:namedview
  1586 + id="base"
  1587 + pagecolor="#ffffff"
  1588 + bordercolor="#666666"
  1589 + borderopacity="1.0"
  1590 + inkscape:pageopacity="0.0"
  1591 + inkscape:pageshadow="2"
  1592 + inkscape:zoom="15.729167"
  1593 + inkscape:cx="24"
  1594 + inkscape:cy="23.872848"
  1595 + inkscape:document-units="px"
  1596 + inkscape:current-layer="layer1"
  1597 + showgrid="false"
  1598 + inkscape:window-width="1280"
  1599 + inkscape:window-height="975"
  1600 + inkscape:window-x="0"
  1601 + inkscape:window-y="0"
  1602 + inkscape:window-maximized="1" />
  1603 + <metadata
  1604 + id="metadata7">
  1605 + <rdf:RDF>
  1606 + <cc:Work
  1607 + rdf:about="">
  1608 + <dc:format>image/svg+xml</dc:format>
  1609 + <dc:type
  1610 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  1611 + <dc:title></dc:title>
  1612 + </cc:Work>
  1613 + </rdf:RDF>
  1614 + </metadata>
  1615 + <g
  1616 + inkscape:label="Camada 1"
  1617 + inkscape:groupmode="layer"
  1618 + id="layer1"
  1619 + transform="translate(0,-1004.3622)">
  1620 + <g
  1621 + id="g6205"
  1622 + transform="translate(-0.25,-0.625)">
  1623 + <path
  1624 + transform="matrix(1.77551,0,0,0.959183,-39.087601,1014.4169)"
  1625 + sodipodi:type="arc"
  1626 + style="color:#000000;fill:url(#radialGradient6242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1627 + id="path4177"
  1628 + sodipodi:cx="31.112698"
  1629 + sodipodi:cy="19.008621"
  1630 + sodipodi:rx="8.6620579"
  1631 + sodipodi:ry="8.6620579"
  1632 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z" />
  1633 + <path
  1634 + style="color:#000000;fill:url(#radialGradient6244);fill-opacity:1;fill-rule:evenodd;stroke:#404604;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1635 + d="m 11.151793,1037.2814 10.606602,0 c 3.005204,0 5.980484,-1.1019 7.071067,-4.2426 1.035639,-2.9825 0.176777,-8.6621 -6.540737,-13.2583 l -12.5511457,0 c -6.717514,4.2427 -7.556991,10.0449 -6.010407,13.4351 1.575595,3.4538 4.24264,4.0658 7.4246207,4.0658 z"
  1636 + id="path2329"
  1637 + sodipodi:nodetypes="cczcczc" />
  1638 + <path
  1639 + sodipodi:nodetypes="cccc"
  1640 + id="path3812"
  1641 + d="m 16.222986,1022.4322 c 0,0 -2.151323,1.6603 -1.965991,3.6605 -2.041226,-1.8008 -2.099873,-5.2515 -2.099873,-5.2515 l 4.065864,1.591 z"
  1642 + style="color:#000000;fill:#9db029;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible" />
  1643 + <path
  1644 + sodipodi:nodetypes="cczcczc"
  1645 + id="path3838"
  1646 + d="m 11.257258,1036.2172 10.31667,0 c 2.639723,0 5.253161,-0.9679 6.211112,-3.7266 0.909689,-2.6198 -0.09472,-7.6086 -5.995279,-11.6459 l -11.524725,0 c -5.9005567,3.7267 -6.8879397,8.8233 -5.5294457,11.8011 1.383978,3.0338 3.726667,3.5714 6.5216677,3.5714 z"
  1647 + style="opacity:0.21518986;color:#000000;fill:none;stroke:#ffffff;stroke-width:0.99999976px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
  1648 + <path
  1649 + style="color:#000000;fill:#9db029;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1650 + d="m 17.201414,1022.4322 c 0,0 2.151323,1.6603 1.965991,3.6605 2.041226,-1.8008 2.099873,-5.2515 2.099873,-5.2515 l -4.065864,1.591 z"
  1651 + id="path3810"
  1652 + sodipodi:nodetypes="cccc" />
  1653 + <path
  1654 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z"
  1655 + sodipodi:ry="8.6620579"
  1656 + sodipodi:rx="8.6620579"
  1657 + sodipodi:cy="19.008621"
  1658 + sodipodi:cx="31.112698"
  1659 + id="path3814"
  1660 + style="color:#000000;fill:url(#radialGradient6246);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1661 + sodipodi:type="arc"
  1662 + transform="translate(-14.959381,999.1454)" />
  1663 + <g
  1664 + id="g4290">
  1665 + <path
  1666 + transform="translate(-14.834381,995.6454)"
  1667 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z"
  1668 + sodipodi:ry="8.6620579"
  1669 + sodipodi:rx="8.6620579"
  1670 + sodipodi:cy="19.008621"
  1671 + sodipodi:cx="31.112698"
  1672 + id="path2327"
  1673 + style="color:#000000;fill:url(#radialGradient6248);fill-opacity:1;fill-rule:evenodd;stroke:#6f4709;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1674 + sodipodi:type="arc" />
  1675 + <path
  1676 + transform="matrix(0.877095,0,0,0.877095,-11.010454,997.98167)"
  1677 + sodipodi:type="arc"
  1678 + style="opacity:0.12658231;color:#000000;fill:none;stroke:#ffffff;stroke-width:1.14012825px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1679 + id="path3834"
  1680 + sodipodi:cx="31.112698"
  1681 + sodipodi:cy="19.008621"
  1682 + sodipodi:rx="8.6620579"
  1683 + sodipodi:ry="8.6620579"
  1684 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z" />
  1685 + </g>
  1686 + <path
  1687 + style="color:#000000;fill:url(#linearGradient6250);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1688 + d="m 20.874513,1029.68 4.24264,0 -2.474873,-2.2981 -0.53033,0.7072 -0.530331,-0.5304 -0.707106,2.1213 z"
  1689 + id="path4173" />
  1690 + <path
  1691 + style="opacity:0.22784807;color:#000000;fill:url(#linearGradient6252);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1692 + d="m 10.187502,1031.9428 c -1.3569797,0.1098 -2.4699596,-0.7834 -2.4699596,-0.7834 -1.1826865,-3.9834 -0.053718,-7.9677 -0.053718,-7.9677 0,0 1.0230233,6.7272 2.5236776,8.7511 z"
  1693 + id="path4368"
  1694 + sodipodi:nodetypes="cccc" />
  1695 + <path
  1696 + sodipodi:nodetypes="cccc"
  1697 + id="path4370"
  1698 + d="m 25.74428,1035.3888 c 1.231251,-0.581 1.80438,-2.0023 1.80438,-2.0023 -0.95912,-4.043 -3.976149,-6.8428 -3.976149,-6.8428 0,0 2.464593,6.3426 2.171769,8.8451 z"
  1699 + style="opacity:0.22784807;color:#000000;fill:url(#linearGradient6254);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible" />
  1700 + <rect
  1701 + transform="matrix(0.96484871,-0.26280595,0.28620224,0.95816923,0,0)"
  1702 + rx="0.42326209"
  1703 + ry="0.40700412"
  1704 + y="996.03387"
  1705 + x="-287.51788"
  1706 + height="14.54507"
  1707 + width="12.846511"
  1708 + id="rect15391"
  1709 + style="color:#000000;fill:url(#radialGradient6256);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient6258);stroke-width:1.00014806;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible" />
  1710 + <rect
  1711 + transform="matrix(0.96484871,-0.26280595,0.28620224,0.95816923,0,0)"
  1712 + rx="0.054903328"
  1713 + ry="0.052794438"
  1714 + y="996.38843"
  1715 + x="-287.14862"
  1716 + height="13.824175"
  1717 + width="12.132636"
  1718 + id="rect15660"
  1719 + style="color:#000000;fill:none;stroke:url(#radialGradient6260);stroke-width:0.36121485;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible" />
  1720 + <path
  1721 + sodipodi:nodetypes="cc"
  1722 + id="path15672"
  1723 + d="m 9.6371294,1030.1227 3.8428056,12.8652"
  1724 + style="fill:none;stroke:#000000;stroke-width:0.35702735;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.01754384;display:inline" />
  1725 + <path
  1726 + sodipodi:nodetypes="cc"
  1727 + id="path15674"
  1728 + d="m 9.9424741,1029.8656 3.8540689,12.903"
  1729 + style="fill:none;stroke:#ffffff;stroke-width:0.36116153;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.20467828;display:inline" />
  1730 + <g
  1731 + transform="matrix(0.35541046,-0.09680687,0.10137564,0.33939294,4.9908869,1029.3718)"
  1732 + id="g2188">
  1733 + <rect
  1734 + ry="0.065390877"
  1735 + rx="0.13778631"
  1736 + y="9"
  1737 + x="15.999994"
  1738 + height="1"
  1739 + width="20.000006"
  1740 + id="rect15686"
  1741 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1742 + <rect
  1743 + ry="0.065390877"
  1744 + rx="0.13778631"
  1745 + y="11"
  1746 + x="15.999994"
  1747 + height="1"
  1748 + width="20.000006"
  1749 + id="rect15688"
  1750 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1751 + <rect
  1752 + ry="0.065390877"
  1753 + rx="0.13778631"
  1754 + y="13"
  1755 + x="15.999994"
  1756 + height="1"
  1757 + width="20.000006"
  1758 + id="rect15690"
  1759 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1760 + <rect
  1761 + ry="0.065390877"
  1762 + rx="0.13778631"
  1763 + y="15"
  1764 + x="15.999994"
  1765 + height="1"
  1766 + width="20.000006"
  1767 + id="rect15692"
  1768 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1769 + <rect
  1770 + ry="0.065390877"
  1771 + rx="0.13778631"
  1772 + y="17"
  1773 + x="15.999994"
  1774 + height="1"
  1775 + width="20.000006"
  1776 + id="rect15694"
  1777 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1778 + <rect
  1779 + ry="0.065390877"
  1780 + rx="0.13778631"
  1781 + y="19"
  1782 + x="15.999994"
  1783 + height="1"
  1784 + width="20.000006"
  1785 + id="rect15696"
  1786 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1787 + <rect
  1788 + ry="0.065390877"
  1789 + rx="0.13778631"
  1790 + y="21"
  1791 + x="15.999994"
  1792 + height="1"
  1793 + width="20.000006"
  1794 + id="rect15698"
  1795 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1796 + <rect
  1797 + ry="0.065390877"
  1798 + rx="0.13778631"
  1799 + y="23"
  1800 + x="15.999994"
  1801 + height="1"
  1802 + width="20.000006"
  1803 + id="rect15700"
  1804 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1805 + <rect
  1806 + ry="0.065390877"
  1807 + rx="0.062003858"
  1808 + y="25"
  1809 + x="15.999986"
  1810 + height="1"
  1811 + width="9.0000057"
  1812 + id="rect15732"
  1813 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1814 + <rect
  1815 + ry="0.065390877"
  1816 + rx="0.13778631"
  1817 + y="29"
  1818 + x="15.999986"
  1819 + height="1"
  1820 + width="20.000006"
  1821 + id="rect15736"
  1822 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1823 + <rect
  1824 + ry="0.065390877"
  1825 + rx="0.13778631"
  1826 + y="31"
  1827 + x="15.999986"
  1828 + height="1"
  1829 + width="20.000006"
  1830 + id="rect15738"
  1831 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1832 + <rect
  1833 + ry="0.065390877"
  1834 + rx="0.13778631"
  1835 + y="33"
  1836 + x="15.999986"
  1837 + height="1"
  1838 + width="20.000006"
  1839 + id="rect15740"
  1840 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1841 + <rect
  1842 + ry="0.065390877"
  1843 + rx="0.13778631"
  1844 + y="35"
  1845 + x="15.999986"
  1846 + height="1"
  1847 + width="20.000006"
  1848 + id="rect15742"
  1849 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1850 + <rect
  1851 + ry="0.065390877"
  1852 + rx="0.096450485"
  1853 + y="37"
  1854 + x="15.999986"
  1855 + height="1"
  1856 + width="14.000014"
  1857 + id="rect15744"
  1858 + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
  1859 + </g>
  1860 + <path
  1861 + sodipodi:nodetypes="cccc"
  1862 + id="path2894"
  1863 + d="m 10.77767,1029.241 -0.310195,-0.9816 c -0.7572021,0.2062 -0.5578178,0.8619 -0.459453,1.1912 l 0.769647,-0.2096 z"
  1864 + style="opacity:0.48295456;color:#000000;fill:url(#linearGradient6262);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.10533953;marker:none;visibility:visible;display:inline;overflow:visible" />
  1865 + <path
  1866 + style="opacity:0.35795456;color:#000000;fill:url(#linearGradient6264);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.10533953;marker:none;visibility:visible;display:inline;overflow:visible"
  1867 + d="m 10.77767,1029.241 -0.229976,-0.7131 c -0.8149432,0.2286 -0.539672,0.9227 -0.539672,0.9227 l 0.769647,-0.2096 z"
  1868 + id="path2904"
  1869 + sodipodi:nodetypes="cccc" />
  1870 + <path
  1871 + transform="matrix(0.41151985,0,0,0.41151985,-5.6291758,1025.7467)"
  1872 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z"
  1873 + sodipodi:ry="8.6620579"
  1874 + sodipodi:rx="8.6620579"
  1875 + sodipodi:cy="19.008621"
  1876 + sodipodi:cx="31.112698"
  1877 + id="path4296"
  1878 + style="color:#000000;fill:url(#radialGradient6266);fill-opacity:1;fill-rule:evenodd;stroke:#6f4709;stroke-width:2.43001652;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1879 + sodipodi:type="arc" />
  1880 + <path
  1881 + transform="matrix(0.360942,0,0,0.360942,-4.0555539,1026.7081)"
  1882 + sodipodi:type="arc"
  1883 + style="opacity:0.12658231;color:#000000;fill:none;stroke:#ffffff;stroke-width:2.77052832;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1884 + id="path4298"
  1885 + sodipodi:cx="31.112698"
  1886 + sodipodi:cy="19.008621"
  1887 + sodipodi:rx="8.6620579"
  1888 + sodipodi:ry="8.6620579"
  1889 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z" />
  1890 + </g>
  1891 + <path
  1892 + transform="matrix(0.42405721,0,0,0.42405721,8.1654039,1030.3792)"
  1893 + sodipodi:type="arc"
  1894 + style="color:#000000;fill:url(#radialGradient6300);fill-opacity:1;fill-rule:evenodd;stroke:#c17d11;stroke-width:2.35817238;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1895 + id="path6296"
  1896 + sodipodi:cx="31.112698"
  1897 + sodipodi:cy="19.008621"
  1898 + sodipodi:rx="8.6620579"
  1899 + sodipodi:ry="8.6620579"
  1900 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z" />
  1901 + <path
  1902 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z"
  1903 + sodipodi:ry="8.6620579"
  1904 + sodipodi:rx="8.6620579"
  1905 + sodipodi:cy="19.008621"
  1906 + sodipodi:cx="31.112698"
  1907 + id="path6298"
  1908 + style="opacity:0.19620254000000001;color:#000000;fill:none;stroke:#ffffff;stroke-width:2.68861682;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1909 + sodipodi:type="arc"
  1910 + transform="matrix(0.37193846,0,0,0.37193846,9.7869677,1031.3699)" />
  1911 + <path
  1912 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z"
  1913 + sodipodi:ry="8.6620579"
  1914 + sodipodi:rx="8.6620579"
  1915 + sodipodi:cy="19.008621"
  1916 + sodipodi:cx="31.112698"
  1917 + id="path4306"
  1918 + style="color:#000000;fill:url(#radialGradient6197);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1919 + sodipodi:type="arc"
  1920 + transform="matrix(1.77551,0,0,0.583984,-23.12897,1035.6326)" />
  1921 + <path
  1922 + sodipodi:nodetypes="cczcczc"
  1923 + id="path4308"
  1924 + d="m 27.485424,1049.115 10.606602,0 c 3.005204,0 5.980484,-1.1019 7.071067,-4.2426 1.035639,-2.9825 0.176777,-8.6621 -6.540737,-13.2583 l -12.551146,0 c -6.717514,4.2427 -7.556991,10.0449 -6.010407,13.4351 1.575595,3.4538 4.24264,4.0658 7.424621,4.0658 z"
  1925 + style="color:#000000;fill:url(#radialGradient6286);fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
  1926 + <path
  1927 + style="opacity:0.21518986;color:#000000;fill:none;stroke:#ffffff;stroke-width:0.99999976px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1928 + d="m 27.414112,1048.0729 10.493447,-0.022 c 2.639723,0 5.253161,-0.9679 6.211112,-3.7266 0.909689,-2.6198 -0.09472,-7.6086 -5.995279,-11.6459 l -11.524725,-0.243 c -5.900557,3.7266 -7.04262,8.8232 -5.662029,12.0441 1.380592,3.221 3.395211,3.5714 6.477474,3.5935 z"
  1929 + id="path4314"
  1930 + sodipodi:nodetypes="cczcczc" />
  1931 + <path
  1932 + transform="translate(1.3742503,1010.979)"
  1933 + sodipodi:type="arc"
  1934 + style="color:#000000;fill:url(#radialGradient6277);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible"
  1935 + id="path4318"
  1936 + sodipodi:cx="31.112698"
  1937 + sodipodi:cy="19.008621"
  1938 + sodipodi:rx="8.6620579"
  1939 + sodipodi:ry="8.6620579"
  1940 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z" />
  1941 + <g
  1942 + id="g6288">
  1943 + <path
  1944 + transform="translate(1.4992503,1007.479)"
  1945 + sodipodi:type="arc"
  1946 + style="color:#000000;fill:url(#radialGradient6279);fill-opacity:1;fill-rule:evenodd;stroke:#c17d11;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1947 + id="path4320"
  1948 + sodipodi:cx="31.112698"
  1949 + sodipodi:cy="19.008621"
  1950 + sodipodi:rx="8.6620579"
  1951 + sodipodi:ry="8.6620579"
  1952 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z" />
  1953 + <path
  1954 + d="m 39.774755,19.008621 c 0,4.783923 -3.878135,8.662058 -8.662057,8.662058 -4.783923,0 -8.662058,-3.878135 -8.662058,-8.662058 0,-4.783922 3.878135,-8.662058 8.662058,-8.662058 4.783922,0 8.662057,3.878136 8.662057,8.662058 z"
  1955 + sodipodi:ry="8.6620579"
  1956 + sodipodi:rx="8.6620579"
  1957 + sodipodi:cy="19.008621"
  1958 + sodipodi:cx="31.112698"
  1959 + id="path4322"
  1960 + style="opacity:0.19620254;color:#000000;fill:none;stroke:#ffffff;stroke-width:1.14012825px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
  1961 + sodipodi:type="arc"
  1962 + transform="matrix(0.877095,0,0,0.877095,5.3231773,1009.8153)" />
  1963 + </g>
  1964 + </g>
  1965 +</svg>
... ...
plugins/bsc/public/style.css 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +.controller-profile_editor a.control-panel-bsc-enterprises {background-image: url(images/manage-bsc-enterprises.png)}
  2 +.controller-profile_editor .msie6 a.control-panel-bsc-enterprises {background-image: url(images/manage-bsc-enterprises.gif)}
  3 +
  4 +.controller-profile_editor a.control-panel-transfer-enterprise-ownership {background-image: url(images/transfer-ownership.png)}
  5 +
  6 +.icon-menu-bsc {
  7 + background-image: url(images/manage-bsc-enterprises-icon.png);
  8 +}
  9 +
  10 +#content .token-input-list {
  11 + margin-bottom: 30px;
  12 +}
... ...
plugins/bsc/test/functional/bsc_plugin_environment_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,82 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_environment_controller'
  3 +require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file'
  4 +
  5 +# Re-raise errors caught by the controller.
  6 +class BscPluginEnvironmentController; def rescue_action(e) raise e end; end
  7 +
  8 +class BscPluginEnvironmentControllerTest < Test::Unit::TestCase
  9 +
  10 + VALID_CNPJ = '94.132.024/0001-48'
  11 +
  12 + def setup
  13 + @controller = BscPluginEnvironmentController.new
  14 + @request = ActionController::TestRequest.new
  15 + @response = ActionController::TestResponse.new
  16 + user_login = create_admin_user(Environment.default)
  17 + login_as(user_login)
  18 + @admin = User[user_login].person
  19 + e = Environment.default
  20 + e.enabled_plugins = ['BscPlugin']
  21 + e.save!
  22 + end
  23 +
  24 + attr_accessor :admin
  25 +
  26 + should 'create a new bsc' do
  27 + assert_difference BscPlugin::Bsc, :count, 1 do
  28 + post :new, :profile_data => {:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ}
  29 + end
  30 +
  31 + assert_redirected_to :controller => 'profile_editor', :profile => 'sample-bsc'
  32 + end
  33 +
  34 + should 'not create an invalid bsc' do
  35 + assert_difference BscPlugin::Bsc, :count, 0 do
  36 + post :new, :profile_data => {:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => '29837492304'}
  37 + end
  38 +
  39 + assert_response 200
  40 + end
  41 +
  42 + should 'set the current user as the bsc admin' do
  43 + name = 'Sample Bsc'
  44 + post :new, :profile_data => {:business_name => name, :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ}
  45 + bsc = BscPlugin::Bsc.find_by_name(name)
  46 + assert_includes bsc.admins, admin
  47 + end
  48 +
  49 + should 'list correct enterprises on search' do
  50 + # Should list if: not validated AND (name matches OR identifier matches) AND not bsc
  51 + e1 = Enterprise.create!(:name => 'Sample Enterprise 1', :identifier => 'bli', :validated => false)
  52 + e2 = Enterprise.create!(:name => 'Bla', :identifier => 'sample-enterprise-6', :validated => false)
  53 + e3 = Enterprise.create!(:name => 'Blo', :identifier => 'blo', :validated => false)
  54 + e4 = BscPlugin::Bsc.create!(:business_name => "Sample Bsc", :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ, :validated => false)
  55 + e5 = Enterprise.create!(:name => 'Sample Enterprise 5', :identifier => 'sample-enterprise-5')
  56 + e5.validated = true
  57 + e5.save!
  58 +
  59 + get :search_enterprise, :q => 'sampl'
  60 +
  61 + assert_match /#{e1.name}/, @response.body
  62 + assert_match /#{e2.name}/, @response.body
  63 + assert_no_match /#{e3.name}/, @response.body
  64 + assert_no_match /#{e4.name}/, @response.body
  65 + assert_no_match /#{e5.name}/, @response.body
  66 + end
  67 +
  68 + should 'save validations' do
  69 + e1 = fast_create(Enterprise, :validated => false)
  70 + e2 = fast_create(Enterprise, :validated => false)
  71 + e3 = fast_create(Enterprise, :validated => false)
  72 +
  73 + post :save_validations, :q => "#{e1.id},#{e2.id}"
  74 + e1.reload
  75 + e2.reload
  76 + e3.reload
  77 +
  78 + assert e1.validated
  79 + assert e2.validated
  80 + assert !e3.validated
  81 + end
  82 +end
... ...
plugins/bsc/test/functional/bsc_plugin_myprofile_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,112 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_myprofile_controller'
  3 +require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file'
  4 +
  5 +# Re-raise errors caught by the controller.
  6 +class BscPluginMyprofileController; def rescue_action(e) raise e end; end
  7 +
  8 +class BscPluginMyprofileControllerTest < Test::Unit::TestCase
  9 +
  10 + VALID_CNPJ = '94.132.024/0001-48'
  11 +
  12 + def setup
  13 + @controller = BscPluginMyprofileController.new
  14 + @request = ActionController::TestRequest.new
  15 + @response = ActionController::TestResponse.new
  16 + @bsc = BscPlugin::Bsc.create!({:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ})
  17 + @admin = create_user('admin').person
  18 + @bsc.add_admin(@admin)
  19 + login_as(@admin.user.login)
  20 + e = Environment.default
  21 + e.enabled_plugins = ['BscPlugin']
  22 + e.save!
  23 + end
  24 +
  25 + attr_accessor :admin, :bsc
  26 +
  27 + should 'list enterprises on search' do
  28 + # Should list if match name
  29 + e1 = Enterprise.create!(:name => 'sample enterprise 1', :identifier => 'sample-enterprise-1')
  30 + # Should be case insensitive
  31 + e2 = Enterprise.create!(:name => 'SaMpLe eNtErPrIsE 2', :identifier => 'sample-enterprise-2')
  32 + # Should not list if don't match name
  33 + e3 = Enterprise.create!(:name => 'blo', :identifier => 'blo')
  34 + # Should not list if is has a bsc
  35 + e4 = Enterprise.create!(:name => 'sample enterprise 4', :identifier => 'sample-enterprise-4', :bsc => bsc)
  36 + # Should not list if is enabled
  37 + e5 = Enterprise.create!(:name => 'sample enterprise 5', :identifier => 'sample-enterprise-5', :enabled => true)
  38 + BscPlugin::AssociateEnterprise.create!(:requestor => admin, :target => e5, :bsc => bsc)
  39 + # Should search by identifier
  40 + e6 = Enterprise.create!(:name => 'Bla', :identifier => 'sample-enterprise-6')
  41 +
  42 + get :search_enterprise, :profile => bsc.identifier, :q => 'sampl'
  43 +
  44 + assert_match /#{e1.name}/, @response.body
  45 + assert_match /#{e2.name}/, @response.body
  46 + assert_no_match /#{e3.name}/, @response.body
  47 + assert_no_match /#{e4.name}/, @response.body
  48 + assert_no_match /#{e5.name}/, @response.body
  49 + assert_no_match /#{bsc.name}/, @response.body
  50 + assert_match /#{e6.name}/, @response.body
  51 + end
  52 +
  53 + should 'save associations' do
  54 + e1 = fast_create(Enterprise, :enabled => false)
  55 + e2 = fast_create(Enterprise, :enabled => false)
  56 +
  57 + post :save_associations, :profile => bsc.identifier, :q => "#{e1.id},#{e2.id}"
  58 + e1.reload
  59 + e2.reload
  60 + assert_equal e1.bsc, bsc
  61 + assert_equal e2.bsc, bsc
  62 +
  63 + post :save_associations, :profile => bsc.identifier, :q => "#{e1.id}"
  64 + e1.reload
  65 + e2.reload
  66 + assert_equal e1.bsc, bsc
  67 + assert_not_equal e2.bsc, bsc
  68 + end
  69 +
  70 + should 'create a task to the enabled enterprise instead of associating it' do
  71 + e = fast_create(Enterprise, :enabled => true)
  72 +
  73 + assert_difference BscPlugin::AssociateEnterprise, :count, 1 do
  74 + post :save_associations, :profile => bsc.identifier, :q => "#{e.id}"
  75 + bsc.reload
  76 + assert_not_includes bsc.enterprises, e
  77 + end
  78 + end
  79 +
  80 + should 'transfer ownership' do
  81 + p1 = create_user('p1').person
  82 + p2 = create_user('p2').person
  83 + p3 = create_user('p3').person
  84 +
  85 + role = Profile::Roles.admin(bsc.environment.id)
  86 +
  87 + bsc.add_admin(p1)
  88 + bsc.add_admin(p2)
  89 +
  90 + post :transfer_ownership, :profile => bsc.identifier, 'q_'+role.key => "#{p3.id}"
  91 +
  92 + assert_response :redirect
  93 +
  94 + assert_not_includes bsc.admins, p1
  95 + assert_not_includes bsc.admins, p2
  96 + assert_includes bsc.admins, p3
  97 + end
  98 +
  99 + should 'create enterprise' do
  100 + assert_difference Enterprise, :count, 1 do
  101 + post :create_enterprise, :profile => bsc.identifier, :create_enterprise => {:name => 'Test Bsc', :identifier => 'test-bsc'}
  102 + end
  103 +
  104 + enterprise = Enterprise.find_by_identifier('test-bsc')
  105 +
  106 + assert_equal true, enterprise.enabled
  107 + assert_equal false, enterprise.validated
  108 + assert_equal enterprise.bsc, bsc
  109 + end
  110 +
  111 +end
  112 +
... ...
plugins/bsc/test/unit/bsc_plugin/associate_enterprise_test.rb 0 → 100644
... ... @@ -0,0 +1,53 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'
  3 +
  4 +class BscPlugin::AssociateEnterpriseTest < Test::Unit::TestCase
  5 + VALID_CNPJ = '94.132.024/0001-48'
  6 +
  7 + def setup
  8 + @enterprise = fast_create(Enterprise)
  9 + @person = create_user('user').person
  10 + @bsc = BscPlugin::Bsc.create!(:business_name => 'Sample Bsc', :company_name => 'Sample Bsc Ltda.', :identifier => 'sample-bsc', :cnpj => VALID_CNPJ)
  11 + end
  12 +
  13 + attr_accessor :enterprise, :person, :bsc
  14 +
  15 + should 'associate enteprise with bsc after perform' do
  16 + task = BscPlugin::AssociateEnterprise.create!(:requestor => person, :target => enterprise, :bsc => bsc)
  17 + task.perform
  18 + bsc.reload
  19 +
  20 + assert_includes bsc.enterprises, enterprise
  21 + end
  22 +
  23 + should 'notify enterprise when some bsc create the request' do
  24 + enterprise.contact_email = 'enterprise@bsc.org'
  25 + enterprise.save!
  26 + assert_difference ActionMailer::Base.deliveries, :count, 1 do
  27 + BscPlugin::AssociateEnterprise.create!(:requestor => person, :target => enterprise, :bsc => bsc)
  28 + end
  29 + assert_includes ActionMailer::Base.deliveries.last.to, enterprise.contact_email
  30 + end
  31 +
  32 + should 'notify requestor when some enterprise reject the request' do
  33 + person.email = 'person@bsc.org'
  34 + person.save!
  35 + task = BscPlugin::AssociateEnterprise.create!(:requestor => person, :target => enterprise, :bsc => bsc)
  36 + assert_difference ActionMailer::Base.deliveries, :count, 1 do
  37 + task.cancel
  38 + end
  39 + assert_includes ActionMailer::Base.deliveries.last.to, person.contact_email
  40 + end
  41 +
  42 + should 'notify requestor when some enterprise accept the request' do
  43 + person.email = 'person@bsc.org'
  44 + person.save!
  45 + task = BscPlugin::AssociateEnterprise.create!(:requestor => person, :target => enterprise, :bsc => bsc)
  46 + assert_difference ActionMailer::Base.deliveries, :count, 1 do
  47 + task.finish
  48 + end
  49 + assert_includes ActionMailer::Base.deliveries.last.to, person.contact_email
  50 + end
  51 +
  52 +end
  53 +
... ...
plugins/bsc/test/unit/bsc_plugin/bsc_test.rb 0 → 100644
... ... @@ -0,0 +1,86 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'
  3 +require File.dirname(__FILE__) + '/../../../lib/ext/enterprise'
  4 +
  5 +class BscPlugin::BscTest < Test::Unit::TestCase
  6 + VALID_CNPJ = '94.132.024/0001-48'
  7 +
  8 + should 'validate presence of cnpj' do
  9 + bsc = BscPlugin::Bsc.new()
  10 + bsc.valid?
  11 +
  12 + assert bsc.errors.invalid?(:cnpj)
  13 + end
  14 +
  15 + should 'validate uniqueness of cnpj' do
  16 + bsc1 = BscPlugin::Bsc.create!({:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ})
  17 + bsc2 = BscPlugin::Bsc.new(:cnpj => VALID_CNPJ)
  18 + bsc2.valid?
  19 + assert bsc2.errors.invalid?(:cnpj)
  20 + end
  21 +
  22 + should 'have many enterprises' do
  23 + e1 = Enterprise.new(:name => 'Enterprise1', :identifier => 'enterprise1')
  24 + e2 = Enterprise.new(:name => 'Enterprise2', :identifier => 'enterprise2')
  25 + bsc = BscPlugin::Bsc.new(:business_name => 'Sample Bsc', :company_name => 'Sample Bsc Ltda.', :identifier => 'sample-bsc', :cnpj => VALID_CNPJ)
  26 + bsc.enterprises << e1
  27 + bsc.enterprises << e2
  28 + bsc.save!
  29 +
  30 + assert_equal e1.bsc, bsc
  31 + assert_equal e2.bsc, bsc
  32 + end
  33 +
  34 + should 'verify already requested enterprises' do
  35 + e1 = fast_create(Enterprise)
  36 + e2 = fast_create(Enterprise)
  37 + bsc = BscPlugin::Bsc.new()
  38 + task = BscPlugin::AssociateEnterprise.new(:target => e1, :bsc => bsc)
  39 + bsc.enterprise_requests.stubs(:pending).returns([task])
  40 +
  41 + assert bsc.already_requested?(e1)
  42 + assert !bsc.already_requested?(e2)
  43 + end
  44 +
  45 + should 'return associated enterprises products' do
  46 + e1 = fast_create(Enterprise)
  47 + e2 = fast_create(Enterprise)
  48 + category = fast_create(ProductCategory)
  49 + bsc = BscPlugin::Bsc.new()
  50 +
  51 + p1 = fast_create(Product, :product_category_id => category.id)
  52 + p2 = fast_create(Product, :product_category_id => category.id)
  53 + p3 = fast_create(Product, :product_category_id => category.id)
  54 +
  55 + e1.products << p1
  56 + e1.products << p2
  57 + e2.products << p3
  58 +
  59 + bsc.enterprises << e1
  60 + bsc.enterprises << e2
  61 +
  62 + assert_includes bsc.products, p1
  63 + assert_includes bsc.products, p2
  64 + assert_includes bsc.products, p3
  65 + end
  66 +
  67 + should 'reload products' do
  68 + e = fast_create(Enterprise)
  69 + category = fast_create(ProductCategory)
  70 + bsc = BscPlugin::Bsc.create!(:business_name => 'Sample Bsc', :company_name => 'Sample Bsc', :identifier => 'sample-bsc', :cnpj => VALID_CNPJ)
  71 + p = fast_create(Product, :product_category_id => category.id)
  72 +
  73 + e.bsc = bsc
  74 + e.save!
  75 + e.products << p
  76 +
  77 + assert_equal [], bsc.products
  78 + assert_equal [p], bsc.products(true)
  79 + end
  80 +
  81 + should 'not be able to create product' do
  82 + bsc = BscPlugin::Bsc.new
  83 + assert !bsc.create_product?
  84 + end
  85 +
  86 +end
... ...
plugins/bsc/test/unit/bsc_plugin/enterprise_test.rb 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'
  3 +require File.dirname(__FILE__) + '/../../../../../app/models/enterprise'
  4 +require File.dirname(__FILE__) + '/../../../lib/ext/enterprise'
  5 +
  6 +class ProductTest < Test::Unit::TestCase
  7 + VALID_CNPJ = '94.132.024/0001-48'
  8 +
  9 + should 'belongs to a bsc' do
  10 + bsc = BscPlugin::Bsc.create!({:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ})
  11 + enterprise = fast_create(Enterprise, :bsc_id => bsc.id)
  12 +
  13 + assert_equal bsc, enterprise.bsc
  14 + end
  15 +
  16 + should 'return correct enterprises on validated and not validated namedscopes' do
  17 + validated_enterprise = fast_create(Enterprise, :validated => true)
  18 + not_validated_enterprise = fast_create(Enterprise, :validated => false)
  19 +
  20 + assert_includes Enterprise.validated, validated_enterprise
  21 + assert_not_includes Enterprise.validated, not_validated_enterprise
  22 + assert_not_includes Enterprise.not_validated, validated_enterprise
  23 + assert_includes Enterprise.not_validated, not_validated_enterprise
  24 + end
  25 +end
  26 +
... ...
plugins/bsc/test/unit/bsc_plugin/product_test.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +require File.dirname(__FILE__) + '/../../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'
  3 +
  4 +class ProductTest < Test::Unit::TestCase
  5 + VALID_CNPJ = '94.132.024/0001-48'
  6 +
  7 + should 'return have bsc' do
  8 + bsc = BscPlugin::Bsc.create!({:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ})
  9 + enterprise = fast_create(Enterprise, :bsc_id => bsc.id)
  10 + product = fast_create(Product, :enterprise_id => enterprise.id)
  11 +
  12 + assert_equal bsc, product.bsc
  13 + end
  14 +end
... ...
plugins/bsc/test/unit/bsc_plugin_test.rb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file'
  3 +require File.dirname(__FILE__) + '/../../lib/ext/enterprise'
  4 +
  5 +class BscPluginTest < Test::Unit::TestCase
  6 +
  7 + VALID_CNPJ = '94.132.024/0001-48'
  8 +
  9 + should 'add profile controller filter correctly' do
  10 + bsc_plugin = BscPlugin.new
  11 + person = fast_create(Person)
  12 + context = mock()
  13 + context.stubs(:profile).returns(person)
  14 + context.stubs(:params).returns({:profile => person.identifier})
  15 + context.stubs(:user).returns(person)
  16 + context.stubs(:environment).returns(person.environment)
  17 + bsc_plugin.stubs(:context).returns(context)
  18 +
  19 + assert_nil bsc_plugin.profile_controller_filters.first[:block].call
  20 + assert_nil bsc_plugin.content_viewer_controller_filters.first[:block].call
  21 +
  22 + enterprise = fast_create(Enterprise, :validated => false)
  23 + enterprise.bsc = BscPlugin::Bsc.create!({:business_name => 'Sample Bsc', :identifier => 'sample-bsc', :company_name => 'Sample Bsc Ltda.', :cnpj => VALID_CNPJ})
  24 + enterprise.save!
  25 + context.stubs(:profile).returns(enterprise)
  26 + context.stubs(:params).returns({:profile => enterprise.identifier})
  27 + context.stubs(:environment).returns(enterprise.environment)
  28 +
  29 + assert_raise NameError do
  30 + bsc_plugin.profile_controller_filters.first[:block].call
  31 + end
  32 + assert_raise NameError do
  33 + bsc_plugin.content_viewer_controller_filters.first[:block].call
  34 + end
  35 + end
  36 +end
... ...
plugins/bsc/views/bsc_plugin/mailer/admin_notification.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= _('The management of %{bsc} was transferred to you.') % {:bsc => @bsc.name}%>
... ...
plugins/bsc/views/bsc_plugin_environment/new.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<%= error_messages_for :bsc %>
  2 +<h1><%= _('BSC registration') %></h1>
  3 +
  4 +<% labelled_form_for :profile_data, @bsc do |f| %>
  5 + <%= render :partial => 'shared/fields', :locals => {:f => f, :profile => @bsc} %>
  6 +
  7 + <% button_bar do %>
  8 + <%= submit_button('save', _('Save')) %>
  9 + <%= button('cancel', _('Cancel'), {:controller => 'admin_panel'}) %>
  10 + <% end %>
  11 +<% end %>
... ...
plugins/bsc/views/bsc_plugin_environment/validate_enterprises.html.erb 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +<h1><%= _('Validate enterprises') %></h1>
  2 +
  3 +<% form_tag :action => 'save_validations' do %>
  4 + <%= text_field_tag(:q, nil, :id => 'search-enterprises') %>
  5 +
  6 + <% button_bar do %>
  7 + <%= submit_button('save', _('Save'))%>
  8 + <%= button('cancel', _('Cancel'), {:controller => 'admin_panel'})%>
  9 + <% end %>
  10 +<% end %>
  11 +
  12 +<%= javascript_include_tag '/plugins/bsc/jquery_tokeninput/src/jquery.tokeninput.js' %>
  13 +<% stylesheet('/plugins/bsc/jquery_tokeninput/styles/token-input.css') %>
  14 +<% search_url = url_for(:action => 'search_enterprise') %>
  15 +<script type="text/javascript">
  16 + jQuery("#search-enterprises")
  17 + .tokenInput("<%= search_url %>", {
  18 + hintText: <%= _('Type in a search term for enterprise').to_json %>,
  19 + noResultsText: <%= _('No results').to_json %>,
  20 + searchingText: <%= _('Searching...').to_json %>,
  21 + searchDelay: 500,
  22 + backspaceDeleteItem: false
  23 + })
  24 + .focus();
  25 +
  26 + jQuery("#token-input-search-enterprises")
  27 + .live("keydown", function(event){
  28 + if(event.keyCode == "13") return false;
  29 + });
  30 +</script>
  31 +
... ...
plugins/bsc/views/bsc_plugin_myprofile/_similar_enterprises.html.erb 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +<div id="similar-enterprises" style="display: none; border-bottom: 2px solid #babdb6;">
  2 + <p><b><%= _('Existing enterprises:') %></b></p>
  3 + <p><i style="text-align: center;"><%= _('Were found %{count} enterprises with similar names on the same city, you can decide to associate one of them or create the new enterprise confirming the informations you typed in.') % {:count => content_tag('span', '', :id => 'similar-enterprises-count')} %></i></p>
  4 + <ul id="create-enterprise-similar-enterprises"></ul>
  5 + <% query = bsc.enterprises.map(&:id).join(',') %>
  6 + <script type="text/javascript">
  7 + update_enterprises = function(enterprises){
  8 + var association_url = <%= url_for({:controller => 'bsc_plugin_myprofile', :action => 'save_associations', :profile => bsc.identifier}).to_json %>;
  9 + jQuery('#create-enterprise-similar-enterprises').empty();
  10 + jQuery.each(enterprises, function(index, enterprise){
  11 + var id = enterprise[0]
  12 + var name = enterprise[1]
  13 + var query = <%= query.to_json %>;
  14 + if(query)
  15 + query += ',';
  16 + query += id.toString();
  17 + query = '?q='+query;
  18 + var url = association_url + query;
  19 + jQuery('#create-enterprise-similar-enterprises').append(
  20 + '<li>' + name + ' - <a href="' + url +'">' + <%= _('Associate').to_json %> + '</a></li>'
  21 + );
  22 + });
  23 + if (jQuery(enterprises).length > 0){
  24 + jQuery('#similar-enterprises-count').text(jQuery(enterprises).length.toString());
  25 + jQuery('#similar-enterprises').fadeIn();
  26 + }
  27 + else
  28 + jQuery('#similar-enterprises').fadeOut();
  29 + }
  30 + jQuery('#create_enterprise_name,#create_enterprise_city').change(function() {
  31 + var parameters = {};
  32 + if(jQuery('#create_enterprise_name').length)
  33 + parameters.name = jQuery('#create_enterprise_name').val();
  34 + if(jQuery('#create_enterprise_city').length)
  35 + parameters.city = jQuery('#create_enterprise_city').val();
  36 +
  37 + jQuery.ajax({
  38 + url: <%= url_for({:controller => 'bsc_plugin_myprofile', :action => 'similar_enterprises', :profile => bsc.identifier}).to_json %>,
  39 + dataType: 'json',
  40 + data: parameters,
  41 + success: function(data){ update_enterprises(data); },
  42 + error: function(ajax, stat, errorThrown) {
  43 + alert(stat+': '+errorThrown);
  44 + }
  45 + });
  46 + });
  47 + </script>
  48 +</div>
... ...
plugins/bsc/views/bsc_plugin_myprofile/create_enterprise.html.erb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +<%= error_messages_for 'create_enterprise' %>
  2 +
  3 +<h1><%= __('Enterprise registration') %></h1>
  4 +
  5 +<%= required_fields_message %>
  6 +
  7 +<% labelled_form_for(:create_enterprise, @create_enterprise) do |f| %>
  8 +
  9 + <%= required f.text_field 'name', :onchange => "updateUrlField(this, 'create_enterprise_identifier')", :size => 40 %>
  10 + <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => :create_enterprise, :profile => @create_enterprise } %>
  11 + <p style="border-bottom: 2px solid #babdb6"></p>
  12 + <%= required labelled_form_field(_('Address'), content_tag('code', environment.top_url + "/" + text_field(:create_enterprise, 'identifier', :size => 26))) %>
  13 + <p style="border-bottom: 2px solid #babdb6"></p>
  14 + <%= render :partial => 'similar_enterprises', :locals => {:bsc => profile}%>
  15 +
  16 + <% button_bar do %>
  17 + <%= submit_button('save', _('Save'), :cancel => {:controller => 'profile_editor', :profile => profile.identifier}) %>
  18 + <% end %>
  19 +<% end %>
  20 +
  21 +
... ...
plugins/bsc/views/bsc_plugin_myprofile/manage_associated_enterprises.html.erb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +<h1><%= _('Manage associated enterprises') %></h1>
  2 +
  3 +<% if !@pending_enterprises.blank? %>
  4 + <%= _('Associations awaiting approval:') %>
  5 + <ul>
  6 + <% @pending_enterprises.each do |enterprise| %>
  7 + <li><%= enterprise.name %></li>
  8 + <% end %>
  9 + </ul>
  10 +<% end %>
  11 +
  12 +<% form_tag :action => 'save_associations' do %>
  13 + <%= text_field_tag(:q, nil, :id => 'search-enterprises') %>
  14 +
  15 + <%= button('add', _('Add new enterprise'), {:action => 'create_enterprise'}) %>
  16 +
  17 + <% button_bar do %>
  18 + <%= submit_button('save', _('Save'))%>
  19 + <%= button('cancel', _('Cancel'), {:controller => 'profile_editor'})%>
  20 + <% end %>
  21 +
  22 +<% end %>
  23 +<%= javascript_include_tag '/plugins/bsc/jquery_tokeninput/src/jquery.tokeninput.js' %>
  24 +<% stylesheet('/plugins/bsc/jquery_tokeninput/styles/token-input.css') %>
  25 +<% search_url = url_for(:action => 'search_enterprise', :profile => profile.identifier) %>
  26 +<script type="text/javascript">
  27 + jQuery("#search-enterprises")
  28 + .tokenInput("<%= search_url %>", {
  29 + minChars: 3,
  30 + prePopulate: <%= profile.enterprises_to_json %>,
  31 + hintText: <%= _('Type in a search term for enterprise').to_json %>,
  32 + noResultsText: <%= _('No results').to_json %>,
  33 + searchingText: <%= _('Searching...').to_json %>,
  34 + searchDelay: 500,
  35 + backspaceDeleteItem: false
  36 + })
  37 + .focus();
  38 +
  39 + jQuery("#token-input-search-enterprises")
  40 + .live("keydown", function(event){
  41 + if(event.keyCode == "13") return false;
  42 + });
  43 +</script>
... ...
plugins/bsc/views/bsc_plugin_myprofile/transfer_ownership.html.erb 0 → 100644
... ... @@ -0,0 +1,54 @@
  1 +<h1><%= _('Transfer Ownership') %></h1>
  2 +
  3 +<div id='last-admin-message' style="margin-bottom: 20px;">
  4 + <%= _('This option allows you to transfer this enterprise\'s management to another user. This action will remove all the current administrators. Be careful when confirming this procedure.') %>
  5 +</div>
  6 +
  7 +<% if !profile.admins.blank? %>
  8 + <%= _('Current administrators:') %>
  9 + <ul style="list-style-type: none;">
  10 + <% profile.admins.each do |admin| %>
  11 + <li><%= link_to(profile_image(admin, :icon, :style => 'margin-right: 3px;'), admin.url) + link_to(admin.name, admin.url, :style => 'margin-top: -3px;') %></li>
  12 + <% end %>
  13 + </ul>
  14 +<% end %>
  15 +
  16 +<% form_tag do %>
  17 + <% @roles.each do |role|%>
  18 + <%= content_tag('b', _('Administrator:')) %>
  19 + <%= text_field_tag('q_'+role.key, nil, :id => 'search_'+role.key) %>
  20 + <% end %>
  21 +
  22 + <% button_bar do %>
  23 + <%= submit_button('save', _('Save'))%>
  24 + <%= button('cancel', _('Cancel'), {:controller => 'profile_editor'})%>
  25 + <% end %>
  26 +<% end %>
  27 +
  28 +<%= javascript_include_tag '/plugins/bsc/jquery_tokeninput/src/jquery.tokeninput.js' %>
  29 +<% stylesheet('/plugins/bsc/jquery_tokeninput/styles/token-input.css') %>
  30 +
  31 +<% @roles.each do |role| %>
  32 + <% search_url = url_for(:controller => 'profile_members', :action => 'search_user', :role => role.id, :profile => profile.identifier) %>
  33 + <script type="text/javascript">
  34 + jQuery(<%= ('#search_' + role.key).to_json %>)
  35 + .tokenInput("<%= search_url %>", {
  36 + hintText: <%= _('Type in a search term for the new administrator').to_json %>,
  37 + noResultsText: <%= _('No results').to_json %>,
  38 + searchingText: <%= _('Searching...').to_json %>,
  39 + searchDelay: 500,
  40 + backspaceDeleteItem: false,
  41 + preventDuplicates: true,
  42 + tokenLimit: 1,
  43 + queryParam: <%= ('q_'+role.key).to_json %>
  44 + })
  45 + .focus();
  46 +
  47 + jQuery("#token-input-search-enterprises")
  48 + .live("keydown", function(event){
  49 + if(event.keyCode == "13") return false;
  50 + });
  51 + </script>
  52 +<% end %>
  53 +
  54 +
... ...
plugins/bsc/views/profile/_profile_tab.html.erb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<ul>
  2 + <%= content_tag('li', content_tag('b', _('Contact phone: ')) + profile.contact_phone) if !profile.contact_phone.blank? %>
  3 + <%= content_tag('li', content_tag('b', _('Email: ')) + profile.contact_email) if !profile.contact_email.blank? %>
  4 + <%= content_tag('li', content_tag('b', _('Location: ')) + profile.state) if !profile.state.blank? %>
  5 + <%= content_tag('li', content_tag('b', _('Address: ')) + profile.address) if !profile.address.blank? %>
  6 +</ul>
... ...
plugins/bsc/views/profile_editor/bsc_plugin/_bsc.html.erb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<%= render :partial => 'shared/fields', :locals => {:f => f, :profile => profile} %>
  2 +<%= render :partial => 'moderation', :locals => { :profile => profile } %>
... ...
plugins/bsc/views/shared/_fields.html.erb 0 → 100644
... ... @@ -0,0 +1,66 @@
  1 +<!-- This line should be uncommented when bsc is merged with the former plugin -->
  2 +<%# extend FormerPlugin::FieldHelper %>
  3 +
  4 +<fieldset>
  5 + <legend><%= _('Basic information')%></legend>
  6 + <%= required f.text_field(:business_name, :onchange => "updateUrlField(this, 'profile_data_identifier')") %>
  7 + <%= required f.text_field(:company_name) %>
  8 + <%= required f.text_field(:cnpj) %>
  9 + <!-- This line should be uncommented when bsc is merged with the former plugin -->
  10 + <%#= widgets_for_form(f, :bsc_fields) %>
  11 +
  12 + <script type="text/javascript">
  13 + function submit_button(index) {
  14 + return $("profile_data_identifier").form.select("input.submit")[index];
  15 + }
  16 + function warn_value_change() {
  17 + show_warning('bsc-formitem', "identifier-change-confirmation");
  18 + disable_button(submit_button(0));
  19 + }
  20 + function confirm_change() {
  21 + enable_button(submit_button(0));
  22 + hide_warning('identifier-change-confirmation');
  23 + }
  24 + function no_change() {
  25 + $("profile_data_identifier").value = $("old_bsc_identifier").value;
  26 + enable_button(submit_button(0));
  27 + hide_warning('identifier-change-confirmation');
  28 + }
  29 + </script>
  30 +
  31 + <%= hidden_field_tag 'old_bsc_identifier', profile.identifier %>
  32 + <div id="bsc-formitem">
  33 + <%= content_tag('code',
  34 + environment.top_url + '/ ' +
  35 + text_field(:profile_data, :identifier, :onchange => "warn_value_change()", :size => 25)
  36 + ) +
  37 + content_tag('div',
  38 + content_tag('strong', _('WARNING!')) + '&nbsp;' +
  39 + _("You are about to change the address, and this will break external links to this bsc or to posts inside it. Do you really want to change?") +
  40 + content_tag('div',
  41 + button_to_function(:ok, _("Yes"), "confirm_change()") + ' ' +
  42 + button_to_function(:cancel, _('No'), 'no_change()')
  43 + ),
  44 + :id => 'identifier-change-confirmation',
  45 + :class => 'change-confirmation',
  46 + :style => 'display: none;'
  47 + )
  48 + %>
  49 + </div>
  50 +</fieldset>
  51 +
  52 +<fieldset>
  53 + <legend><%= _('Contact')%></legend>
  54 + <%= f.text_field(:contact_email) %>
  55 + <%= f.text_field(:organization_website) %>
  56 + <%= f.text_field(:contact_phone) %>
  57 +</fieldset>
  58 +
  59 +<fieldset>
  60 + <legend><%= _('Location')%></legend>
  61 + <%= f.text_field(:address) %>
  62 + <%= f.text_field(:zip_code) %>
  63 + <%= f.text_field(:city) %>
  64 + <%= f.text_field(:state) %>
  65 + <%= select_country(_('Country'), :profile_data, 'country', {:class => 'type-select'}) %>
  66 +</fieldset>
... ...
plugins/mezuro/lib/mezuro_plugin.rb
... ... @@ -19,7 +19,7 @@ class MezuroPlugin &lt; Noosfero::Plugin
19 19 MezuroPlugin::Project.by_profile(context.profile).with_tab.map do |project|
20 20 { :title => 'Mezuro ' + project.name,
21 21 :id => 'mezuro-project-'+project.identifier,
22   - :content => expanded_template("show.html.erb",{:current_project => project}) }
  22 + :content => lambda { render :partial => 'project_tab', :locals => {:current_project => project} } }
23 23 end
24 24 end
25 25 end
... ...
plugins/mezuro/views/profile/_project_tab.html.erb 0 → 100644
... ... @@ -0,0 +1,81 @@
  1 +<% @project = current_project %>
  2 +<% @total_metrics = @project.total_metrics if @project != nil %>
  3 +<% @statistical_metrics = @project.statistical_metrics if @project != nil %>
  4 +<% @svn_error = @project.svn_error if (@project != nil && @project.svn_error) %>
  5 +
  6 +<h1> <%= @project.name %>'s Info</h1>
  7 +<table id="show_info">
  8 + <tr>
  9 + <td><%= _("Name") %></td>
  10 + <td><%= @project.name %></td>
  11 + </tr>
  12 + <% if (@project.description != nil && @project.description != "" ) %>
  13 + <tr>
  14 + <td><%= _("Description") %></td>
  15 + <td><%= @project.description %></td>
  16 + </tr>
  17 + <% end %>
  18 + <tr>
  19 + <td><%= _("Repository address") %></td>
  20 + <td><%= @project.repository_url %></td>
  21 + </tr>
  22 +</table>
  23 +
  24 +<br />
  25 +
  26 +
  27 +<% if @svn_error %>
  28 + <h3>ERROR</h3>
  29 + <div id="svn_error">
  30 + <%= @svn_error %>
  31 + </div>
  32 + <br />
  33 + <br />
  34 + <p>
  35 + <%= _("Possible causes:") %>
  36 + <ul>
  37 + <li>
  38 + <%= _("Server is down") %>
  39 + </li>
  40 + <li>
  41 + <% _("URL invalid, in this case create another project with the correct URL
  42 + (Sorry for the incovenience, we're working for a better solution)") %>
  43 + </li>
  44 + </ul>
  45 + </p>
  46 +<%else%>
  47 + <h3> <%= _("Metric Results") %> </h3>
  48 + <% if @project.metrics_calculated? %>
  49 + <h3> <% _("Total Metrics") %> </h3>
  50 + <table id="total_metrics">
  51 + <% @total_metrics.each_with_index do |metric, index| %>
  52 + <tr id="tr_<%= metric.name %>" class="d<%= index % 2 %>">
  53 + <td class="metric_name"> <%= metric.name %> </td>
  54 + <td class="metric_value"> <%= metric.value %> </td>
  55 + </tr>
  56 + <% end %>
  57 + </table>
  58 +
  59 + <h3> <%= _("Statistical Metrics") %> </h3>
  60 + <% @statistical_metrics.each_key do |metric_name| %>
  61 + <div id="statistical_metrics">
  62 + <%= "#{metric_name}_average: #{@statistical_metrics[metric_name]["average"]}" %>
  63 + <ul>
  64 + <% @statistical_metrics[metric_name].each do |stat_name, stat_value| %>
  65 + <% if stat_name != "average" %>
  66 + <li>
  67 + <%= "#{metric_name}_#{stat_name}: #{stat_value}" %>
  68 + </li>
  69 + <% end %>
  70 + <% end %>
  71 + </ul>
  72 + </div>
  73 + <% end %>
  74 +
  75 + <% else %>
  76 + <div id="progress_message">
  77 + <%= _("Wait a moment while the metrics are calculated.<br/>
  78 + Reload the page manually in a few moment. ") %>
  79 + </div>
  80 + <% end %>
  81 +<% end %>
... ...
plugins/mezuro/views/show.html.erb
... ... @@ -1,81 +0,0 @@
1   -<% @project = locals[:current_project] %>
2   -<% @total_metrics = @project.total_metrics if @project != nil %>
3   -<% @statistical_metrics = @project.statistical_metrics if @project != nil %>
4   -<% @svn_error = @project.svn_error if (@project != nil && @project.svn_error) %>
5   -
6   -<h1> <%= @project.name %>'s Info</h1>
7   -<table id="show_info">
8   - <tr>
9   - <td><%= _("Name") %></td>
10   - <td><%= @project.name %></td>
11   - </tr>
12   - <% if (@project.description != nil && @project.description != "" ) %>
13   - <tr>
14   - <td><%= _("Description") %></td>
15   - <td><%= @project.description %></td>
16   - </tr>
17   - <% end %>
18   - <tr>
19   - <td><%= _("Repository address") %></td>
20   - <td><%= @project.repository_url %></td>
21   - </tr>
22   -</table>
23   -
24   -<br />
25   -
26   -
27   -<% if @svn_error %>
28   - <h3>ERROR</h3>
29   - <div id="svn_error">
30   - <%= @svn_error %>
31   - </div>
32   - <br />
33   - <br />
34   - <p>
35   - <%= _("Possible causes:") %>
36   - <ul>
37   - <li>
38   - <%= _("Server is down") %>
39   - </li>
40   - <li>
41   - <% _("URL invalid, in this case create another project with the correct URL
42   - (Sorry for the incovenience, we're working for a better solution)") %>
43   - </li>
44   - </ul>
45   - </p>
46   -<%else%>
47   - <h3> <%= _("Metric Results") %> </h3>
48   - <% if @project.metrics_calculated? %>
49   - <h3> <% _("Total Metrics") %> </h3>
50   - <table id="total_metrics">
51   - <% @total_metrics.each_with_index do |metric, index| %>
52   - <tr id="tr_<%= metric.name %>" class="d<%= index % 2 %>">
53   - <td class="metric_name"> <%= metric.name %> </td>
54   - <td class="metric_value"> <%= metric.value %> </td>
55   - </tr>
56   - <% end %>
57   - </table>
58   -
59   - <h3> <%= _("Statistical Metrics") %> </h3>
60   - <% @statistical_metrics.each_key do |metric_name| %>
61   - <div id="statistical_metrics">
62   - <%= "#{metric_name}_average: #{@statistical_metrics[metric_name]["average"]}" %>
63   - <ul>
64   - <% @statistical_metrics[metric_name].each do |stat_name, stat_value| %>
65   - <% if stat_name != "average" %>
66   - <li>
67   - <%= "#{metric_name}_#{stat_name}: #{stat_value}" %>
68   - </li>
69   - <% end %>
70   - <% end %>
71   - </ul>
72   - </div>
73   - <% end %>
74   -
75   - <% else %>
76   - <div id="progress_message">
77   - <%= _("Wait a moment while the metrics are calculated.<br/>
78   - Reload the page manually in a few moment. ") %>
79   - </div>
80   - <% end %>
81   -<% end %>
public/javascripts/jquery.tokeninput.js 0 → 100644
... ... @@ -0,0 +1,793 @@
  1 +/*
  2 + * jQuery Plugin: Tokenizing Autocomplete Text Entry
  3 + * Version 1.5.0
  4 + *
  5 + * Copyright (c) 2009 James Smith (http://loopj.com)
  6 + * Licensed jointly under the GPL and MIT licenses,
  7 + * choose which one suits your project best!
  8 + *
  9 + */
  10 +
  11 +(function ($) {
  12 +// Default settings
  13 +var DEFAULT_SETTINGS = {
  14 + hintText: "Type in a search term",
  15 + noResultsText: "No results",
  16 + searchingText: "Searching...",
  17 + deleteText: "&times;",
  18 + searchDelay: 300,
  19 + minChars: 1,
  20 + tokenLimit: null,
  21 + jsonContainer: null,
  22 + method: "GET",
  23 + contentType: "json",
  24 + queryParam: "q",
  25 + tokenDelimiter: ",",
  26 + preventDuplicates: false,
  27 + prePopulate: null,
  28 + processPrePopulate: false,
  29 + animateDropdown: true,
  30 + onResult: null,
  31 + onAdd: null,
  32 + onDelete: null,
  33 + idPrefix: "token-input-",
  34 + backspaceDeleteItem: true
  35 +};
  36 +
  37 +// Default classes to use when theming
  38 +var DEFAULT_CLASSES = {
  39 + tokenList: "token-input-list",
  40 + token: "token-input-token",
  41 + tokenDelete: "token-input-delete-token",
  42 + selectedToken: "token-input-selected-token",
  43 + highlightedToken: "token-input-highlighted-token",
  44 + dropdown: "token-input-dropdown",
  45 + dropdownItem: "token-input-dropdown-item",
  46 + dropdownItem2: "token-input-dropdown-item2",
  47 + selectedDropdownItem: "token-input-selected-dropdown-item",
  48 + inputToken: "token-input-input-token"
  49 +};
  50 +
  51 +// Input box position "enum"
  52 +var POSITION = {
  53 + BEFORE: 0,
  54 + AFTER: 1,
  55 + END: 2
  56 +};
  57 +
  58 +// Keys "enum"
  59 +var KEY = {
  60 + BACKSPACE: 8,
  61 + TAB: 9,
  62 + ENTER: 13,
  63 + ESCAPE: 27,
  64 + SPACE: 32,
  65 + PAGE_UP: 33,
  66 + PAGE_DOWN: 34,
  67 + END: 35,
  68 + HOME: 36,
  69 + LEFT: 37,
  70 + UP: 38,
  71 + RIGHT: 39,
  72 + DOWN: 40,
  73 + NUMPAD_ENTER: 108,
  74 + COMMA: 188
  75 +};
  76 +
  77 +// Additional public (exposed) methods
  78 +var methods = {
  79 + init: function(url_or_data_or_function, options) {
  80 + var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
  81 +
  82 + return this.each(function () {
  83 + $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
  84 + });
  85 + },
  86 + clear: function() {
  87 + this.data("tokenInputObject").clear();
  88 + return this;
  89 + },
  90 + add: function(item) {
  91 + this.data("tokenInputObject").add(item);
  92 + return this;
  93 + },
  94 + remove: function(item) {
  95 + this.data("tokenInputObject").remove(item);
  96 + return this;
  97 + }
  98 +}
  99 +
  100 +// Expose the .tokenInput function to jQuery as a plugin
  101 +$.fn.tokenInput = function (method) {
  102 + // Method calling and initialization logic
  103 + if(methods[method]) {
  104 + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  105 + } else {
  106 + return methods.init.apply(this, arguments);
  107 + }
  108 +};
  109 +
  110 +// TokenList class for each input
  111 +$.TokenList = function (input, url_or_data, settings) {
  112 + //
  113 + // Initialization
  114 + //
  115 +
  116 + // Configure the data source
  117 + if(typeof(url_or_data) === "string") {
  118 + // Set the url to query against
  119 + settings.url = url_or_data;
  120 +
  121 + // Make a smart guess about cross-domain if it wasn't explicitly specified
  122 + if(settings.crossDomain === undefined) {
  123 + if(settings.url.indexOf("://") === -1) {
  124 + settings.crossDomain = false;
  125 + } else {
  126 + settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
  127 + }
  128 + }
  129 + } else if(typeof(url_or_data) === "object") {
  130 + // Set the local data to search through
  131 + settings.local_data = url_or_data;
  132 + }
  133 +
  134 + // Build class names
  135 + if(settings.classes) {
  136 + // Use custom class names
  137 + settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
  138 + } else if(settings.theme) {
  139 + // Use theme-suffixed default class names
  140 + settings.classes = {};
  141 + $.each(DEFAULT_CLASSES, function(key, value) {
  142 + settings.classes[key] = value + "-" + settings.theme;
  143 + });
  144 + } else {
  145 + settings.classes = DEFAULT_CLASSES;
  146 + }
  147 +
  148 +
  149 + // Save the tokens
  150 + var saved_tokens = [];
  151 +
  152 + // Keep track of the number of tokens in the list
  153 + var token_count = 0;
  154 +
  155 + // Basic cache to save on db hits
  156 + var cache = new $.TokenList.Cache();
  157 +
  158 + // Keep track of the timeout, old vals
  159 + var timeout;
  160 + var input_val;
  161 +
  162 + // Create a new text input an attach keyup events
  163 + var input_box = $("<input type=\"text\" autocomplete=\"off\">")
  164 + .css({
  165 + outline: "none"
  166 + })
  167 + .attr("id", settings.idPrefix + input.id)
  168 + .focus(function () {
  169 + if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
  170 + show_dropdown_hint();
  171 + }
  172 + })
  173 + .blur(function () {
  174 + hide_dropdown();
  175 + $(this).val("");
  176 + })
  177 + .bind("keyup keydown blur update", resize_input)
  178 + .keydown(function (event) {
  179 + var previous_token;
  180 + var next_token;
  181 +
  182 + switch(event.keyCode) {
  183 + case KEY.LEFT:
  184 + case KEY.RIGHT:
  185 + case KEY.UP:
  186 + case KEY.DOWN:
  187 + if(!$(this).val()) {
  188 + previous_token = input_token.prev();
  189 + next_token = input_token.next();
  190 +
  191 + if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
  192 + // Check if there is a previous/next token and it is selected
  193 + if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
  194 + deselect_token($(selected_token), POSITION.BEFORE);
  195 + } else {
  196 + deselect_token($(selected_token), POSITION.AFTER);
  197 + }
  198 + } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
  199 + // We are moving left, select the previous token if it exists
  200 + select_token($(previous_token.get(0)));
  201 + } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
  202 + // We are moving right, select the next token if it exists
  203 + select_token($(next_token.get(0)));
  204 + }
  205 + } else {
  206 + var dropdown_item = null;
  207 +
  208 + if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
  209 + dropdown_item = $(selected_dropdown_item).next();
  210 + } else {
  211 + dropdown_item = $(selected_dropdown_item).prev();
  212 + }
  213 +
  214 + if(dropdown_item.length) {
  215 + select_dropdown_item(dropdown_item);
  216 + }
  217 + return false;
  218 + }
  219 + break;
  220 +
  221 + case KEY.BACKSPACE:
  222 + previous_token = input_token.prev();
  223 + if(!$(this).val().length && settings.backspaceDeleteItem) {
  224 + if(selected_token) {
  225 + delete_token($(selected_token));
  226 + } else if(previous_token.length) {
  227 + select_token($(previous_token.get(0)));
  228 + }
  229 +
  230 + return false;
  231 + } else if($(this).val().length === 1) {
  232 + hide_dropdown();
  233 + } else {
  234 + // set a timeout just long enough to let this function finish.
  235 + setTimeout(function(){do_search();}, 5);
  236 + }
  237 + break;
  238 +
  239 + case KEY.TAB:
  240 + case KEY.ENTER:
  241 + case KEY.NUMPAD_ENTER:
  242 + case KEY.COMMA:
  243 + if(selected_dropdown_item) {
  244 + add_token($(selected_dropdown_item).data("tokeninput"));
  245 + return false;
  246 + }
  247 + break;
  248 +
  249 + case KEY.ESCAPE:
  250 + hide_dropdown();
  251 + return true;
  252 +
  253 + default:
  254 + if(String.fromCharCode(event.which)) {
  255 + // set a timeout just long enough to let this function finish.
  256 + setTimeout(function(){do_search();}, 5);
  257 + }
  258 + break;
  259 + }
  260 + });
  261 +
  262 + // Keep a reference to the original input box
  263 + var hidden_input = $(input)
  264 + .hide()
  265 + .val("")
  266 + .focus(function () {
  267 + input_box.focus();
  268 + })
  269 + .blur(function () {
  270 + input_box.blur();
  271 + });
  272 +
  273 + // Keep a reference to the selected token and dropdown item
  274 + var selected_token = null;
  275 + var selected_token_index = 0;
  276 + var selected_dropdown_item = null;
  277 +
  278 + // The list to store the token items in
  279 + var token_list = $("<ul />")
  280 + .addClass(settings.classes.tokenList)
  281 + .click(function (event) {
  282 + var li = $(event.target).closest("li");
  283 + if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
  284 + toggle_select_token(li);
  285 + } else {
  286 + // Deselect selected token
  287 + if(selected_token) {
  288 + deselect_token($(selected_token), POSITION.END);
  289 + }
  290 +
  291 + // Focus input box
  292 + input_box.focus();
  293 + }
  294 + })
  295 + .mouseover(function (event) {
  296 + var li = $(event.target).closest("li");
  297 + if(li && selected_token !== this) {
  298 + li.addClass(settings.classes.highlightedToken);
  299 + }
  300 + })
  301 + .mouseout(function (event) {
  302 + var li = $(event.target).closest("li");
  303 + if(li && selected_token !== this) {
  304 + li.removeClass(settings.classes.highlightedToken);
  305 + }
  306 + })
  307 + .insertBefore(hidden_input);
  308 +
  309 + // The token holding the input box
  310 + var input_token = $("<li />")
  311 + .addClass(settings.classes.inputToken)
  312 + .appendTo(token_list)
  313 + .append(input_box);
  314 +
  315 + // The list to store the dropdown items in
  316 + var dropdown = $("<div>")
  317 + .addClass(settings.classes.dropdown)
  318 + .appendTo("body")
  319 + .hide();
  320 +
  321 + // Magic element to help us resize the text input
  322 + var input_resizer = $("<tester/>")
  323 + .insertAfter(input_box)
  324 + .css({
  325 + position: "absolute",
  326 + top: -9999,
  327 + left: -9999,
  328 + width: "auto",
  329 + fontSize: input_box.css("fontSize"),
  330 + fontFamily: input_box.css("fontFamily"),
  331 + fontWeight: input_box.css("fontWeight"),
  332 + letterSpacing: input_box.css("letterSpacing"),
  333 + whiteSpace: "nowrap"
  334 + });
  335 +
  336 + // Pre-populate list if items exist
  337 + hidden_input.val("");
  338 + var li_data = settings.prePopulate || hidden_input.data("pre");
  339 + if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
  340 + li_data = settings.onResult.call(hidden_input, li_data);
  341 + }
  342 + if(li_data && li_data.length) {
  343 + $.each(li_data, function (index, value) {
  344 + insert_token(value);
  345 + checkTokenLimit();
  346 + });
  347 + }
  348 +
  349 +
  350 + //
  351 + // Public functions
  352 + //
  353 +
  354 + this.clear = function() {
  355 + token_list.children("li").each(function() {
  356 + if ($(this).children("input").length === 0) {
  357 + delete_token($(this));
  358 + }
  359 + });
  360 + }
  361 +
  362 + this.add = function(item) {
  363 + add_token(item);
  364 + }
  365 +
  366 + this.remove = function(item) {
  367 + token_list.children("li").each(function() {
  368 + if ($(this).children("input").length === 0) {
  369 + var currToken = $(this).data("tokeninput");
  370 + var match = true;
  371 + for (var prop in item) {
  372 + if (item[prop] !== currToken[prop]) {
  373 + match = false;
  374 + break;
  375 + }
  376 + }
  377 + if (match) {
  378 + delete_token($(this));
  379 + }
  380 + }
  381 + });
  382 + }
  383 +
  384 + //
  385 + // Private functions
  386 + //
  387 +
  388 + function checkTokenLimit() {
  389 + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
  390 + input_box.hide();
  391 + hide_dropdown();
  392 + return;
  393 + } else {
  394 + input_box.focus();
  395 + }
  396 + }
  397 +
  398 + function resize_input() {
  399 + if(input_val === (input_val = input_box.val())) {return;}
  400 +
  401 + // Enter new content into resizer and resize input accordingly
  402 + var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  403 + input_resizer.html(escaped);
  404 + input_box.width(input_resizer.width() + 30);
  405 + }
  406 +
  407 + function is_printable_character(keycode) {
  408 + return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
  409 + (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
  410 + (keycode >= 186 && keycode <= 192) || // ; = , - . / ^
  411 + (keycode >= 219 && keycode <= 222)); // ( \ ) '
  412 + }
  413 +
  414 + // Inner function to a token to the list
  415 + function insert_token(item) {
  416 + var this_token = $("<li><p>"+ item.name +"</p></li>")
  417 + .addClass(settings.classes.token)
  418 + .insertBefore(input_token);
  419 +
  420 + // The 'delete token' button
  421 + $("<span>" + settings.deleteText + "</span>")
  422 + .addClass(settings.classes.tokenDelete)
  423 + .appendTo(this_token)
  424 + .click(function () {
  425 + delete_token($(this).parent());
  426 + return false;
  427 + });
  428 +
  429 + // Store data on the token
  430 + var token_data = {"id": item.id, "name": item.name};
  431 + $.data(this_token.get(0), "tokeninput", item);
  432 +
  433 + // Save this token for duplicate checking
  434 + saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
  435 + selected_token_index++;
  436 +
  437 + // Update the hidden input
  438 + var token_ids = $.map(saved_tokens, function (el) {
  439 + return el.id;
  440 + });
  441 + hidden_input.val(token_ids.join(settings.tokenDelimiter));
  442 +
  443 + token_count += 1;
  444 +
  445 + return this_token;
  446 + }
  447 +
  448 + // Add a token to the token list based on user input
  449 + function add_token (item) {
  450 + var callback = settings.onAdd;
  451 +
  452 + // See if the token already exists and select it if we don't want duplicates
  453 + if(token_count > 0 && settings.preventDuplicates) {
  454 + var found_existing_token = null;
  455 + token_list.children().each(function () {
  456 + var existing_token = $(this);
  457 + var existing_data = $.data(existing_token.get(0), "tokeninput");
  458 + if(existing_data && existing_data.id === item.id) {
  459 + found_existing_token = existing_token;
  460 + return false;
  461 + }
  462 + });
  463 +
  464 + if(found_existing_token) {
  465 + select_token(found_existing_token);
  466 + input_token.insertAfter(found_existing_token);
  467 + input_box.focus();
  468 + return;
  469 + }
  470 + }
  471 +
  472 + // Insert the new tokens
  473 + insert_token(item);
  474 + checkTokenLimit();
  475 +
  476 + // Clear input box
  477 + input_box.val("");
  478 +
  479 + // Don't show the help dropdown, they've got the idea
  480 + hide_dropdown();
  481 +
  482 + // Execute the onAdd callback if defined
  483 + if($.isFunction(callback)) {
  484 + callback.call(hidden_input,item);
  485 + }
  486 + }
  487 +
  488 + // Select a token in the token list
  489 + function select_token (token) {
  490 + token.addClass(settings.classes.selectedToken);
  491 + selected_token = token.get(0);
  492 +
  493 + // Hide input box
  494 + input_box.val("");
  495 +
  496 + // Hide dropdown if it is visible (eg if we clicked to select token)
  497 + hide_dropdown();
  498 + }
  499 +
  500 + // Deselect a token in the token list
  501 + function deselect_token (token, position) {
  502 + token.removeClass(settings.classes.selectedToken);
  503 + selected_token = null;
  504 +
  505 + if(position === POSITION.BEFORE) {
  506 + input_token.insertBefore(token);
  507 + selected_token_index--;
  508 + } else if(position === POSITION.AFTER) {
  509 + input_token.insertAfter(token);
  510 + selected_token_index++;
  511 + } else {
  512 + input_token.appendTo(token_list);
  513 + selected_token_index = token_count;
  514 + }
  515 +
  516 + // Show the input box and give it focus again
  517 + input_box.focus();
  518 + }
  519 +
  520 + // Toggle selection of a token in the token list
  521 + function toggle_select_token(token) {
  522 + var previous_selected_token = selected_token;
  523 +
  524 + if(selected_token) {
  525 + deselect_token($(selected_token), POSITION.END);
  526 + }
  527 +
  528 + if(previous_selected_token === token.get(0)) {
  529 + deselect_token(token, POSITION.END);
  530 + } else {
  531 + select_token(token);
  532 + }
  533 + }
  534 +
  535 + // Delete a token from the token list
  536 + function delete_token (token) {
  537 + // Remove the id from the saved list
  538 + var token_data = $.data(token.get(0), "tokeninput");
  539 + var callback = settings.onDelete;
  540 +
  541 + var index = token.prevAll().length;
  542 + if(index > selected_token_index) index--;
  543 +
  544 + // Delete the token
  545 + token.remove();
  546 + selected_token = null;
  547 +
  548 + // Show the input box and give it focus again
  549 + input_box.focus();
  550 +
  551 + // Remove this token from the saved list
  552 + saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
  553 + if(index < selected_token_index) selected_token_index--;
  554 +
  555 + // Update the hidden input
  556 + var token_ids = $.map(saved_tokens, function (el) {
  557 + return el.id;
  558 + });
  559 + hidden_input.val(token_ids.join(settings.tokenDelimiter));
  560 +
  561 + token_count -= 1;
  562 +
  563 + if(settings.tokenLimit !== null) {
  564 + input_box
  565 + .show()
  566 + .val("")
  567 + .focus();
  568 + }
  569 +
  570 + // Execute the onDelete callback if defined
  571 + if($.isFunction(callback)) {
  572 + callback.call(hidden_input,token_data);
  573 + }
  574 + }
  575 +
  576 + // Hide and clear the results dropdown
  577 + function hide_dropdown () {
  578 + dropdown.hide().empty();
  579 + selected_dropdown_item = null;
  580 + }
  581 +
  582 + function show_dropdown() {
  583 + dropdown
  584 + .css({
  585 + position: "absolute",
  586 + top: $(token_list).offset().top + $(token_list).outerHeight(),
  587 + left: $(token_list).offset().left,
  588 + zindex: 999
  589 + })
  590 + .show();
  591 + }
  592 +
  593 + function show_dropdown_searching () {
  594 + if(settings.searchingText) {
  595 + dropdown.html("<p>"+settings.searchingText+"</p>");
  596 + show_dropdown();
  597 + }
  598 + }
  599 +
  600 + function show_dropdown_hint () {
  601 + if(settings.hintText) {
  602 + dropdown.html("<p>"+settings.hintText+"</p>");
  603 + show_dropdown();
  604 + }
  605 + }
  606 +
  607 + // Highlight the query part of the search term
  608 + function highlight_term(value, term) {
  609 + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
  610 + }
  611 +
  612 + // Populate the results dropdown with some results
  613 + function populate_dropdown (query, results) {
  614 + if(results && results.length) {
  615 + dropdown.empty();
  616 + var dropdown_ul = $("<ul>")
  617 + .appendTo(dropdown)
  618 + .mouseover(function (event) {
  619 + select_dropdown_item($(event.target).closest("li"));
  620 + })
  621 + .mousedown(function (event) {
  622 + add_token($(event.target).closest("li").data("tokeninput"));
  623 + return false;
  624 + })
  625 + .hide();
  626 +
  627 + $.each(results, function(index, value) {
  628 + var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
  629 + .appendTo(dropdown_ul);
  630 +
  631 + if(index % 2) {
  632 + this_li.addClass(settings.classes.dropdownItem);
  633 + } else {
  634 + this_li.addClass(settings.classes.dropdownItem2);
  635 + }
  636 +
  637 + if(index === 0) {
  638 + select_dropdown_item(this_li);
  639 + }
  640 +
  641 + $.data(this_li.get(0), "tokeninput", value);
  642 + });
  643 +
  644 + show_dropdown();
  645 +
  646 + if(settings.animateDropdown) {
  647 + dropdown_ul.slideDown("fast");
  648 + } else {
  649 + dropdown_ul.show();
  650 + }
  651 + } else {
  652 + if(settings.noResultsText) {
  653 + dropdown.html("<p>"+settings.noResultsText+"</p>");
  654 + show_dropdown();
  655 + }
  656 + }
  657 + }
  658 +
  659 + // Highlight an item in the results dropdown
  660 + function select_dropdown_item (item) {
  661 + if(item) {
  662 + if(selected_dropdown_item) {
  663 + deselect_dropdown_item($(selected_dropdown_item));
  664 + }
  665 +
  666 + item.addClass(settings.classes.selectedDropdownItem);
  667 + selected_dropdown_item = item.get(0);
  668 + }
  669 + }
  670 +
  671 + // Remove highlighting from an item in the results dropdown
  672 + function deselect_dropdown_item (item) {
  673 + item.removeClass(settings.classes.selectedDropdownItem);
  674 + selected_dropdown_item = null;
  675 + }
  676 +
  677 + // Do a search and show the "searching" dropdown if the input is longer
  678 + // than settings.minChars
  679 + function do_search() {
  680 + var query = input_box.val().toLowerCase();
  681 +
  682 + if(query && query.length) {
  683 + if(selected_token) {
  684 + deselect_token($(selected_token), POSITION.AFTER);
  685 + }
  686 +
  687 + if(query.length >= settings.minChars) {
  688 + show_dropdown_searching();
  689 + clearTimeout(timeout);
  690 +
  691 + timeout = setTimeout(function(){
  692 + run_search(query);
  693 + }, settings.searchDelay);
  694 + } else {
  695 + hide_dropdown();
  696 + }
  697 + }
  698 + }
  699 +
  700 + // Do the actual search
  701 + function run_search(query) {
  702 + var cached_results = cache.get(query);
  703 + if(cached_results) {
  704 + populate_dropdown(query, cached_results);
  705 + } else {
  706 + // Are we doing an ajax search or local data search?
  707 + if(settings.url) {
  708 + // Extract exisiting get params
  709 + var ajax_params = {};
  710 + ajax_params.data = {};
  711 + if(settings.url.indexOf("?") > -1) {
  712 + var parts = settings.url.split("?");
  713 + ajax_params.url = parts[0];
  714 +
  715 + var param_array = parts[1].split("&");
  716 + $.each(param_array, function (index, value) {
  717 + var kv = value.split("=");
  718 + ajax_params.data[kv[0]] = kv[1];
  719 + });
  720 + } else {
  721 + ajax_params.url = settings.url;
  722 + }
  723 +
  724 + // Prepare the request
  725 + ajax_params.data[settings.queryParam] = query;
  726 + ajax_params.type = settings.method;
  727 + ajax_params.dataType = settings.contentType;
  728 + if(settings.crossDomain) {
  729 + ajax_params.dataType = "jsonp";
  730 + }
  731 +
  732 + // Attach the success callback
  733 + ajax_params.success = function(results) {
  734 + if($.isFunction(settings.onResult)) {
  735 + results = settings.onResult.call(hidden_input, results);
  736 + }
  737 + cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
  738 +
  739 + // only populate the dropdown if the results are associated with the active search query
  740 + if(input_box.val().toLowerCase() === query) {
  741 + populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
  742 + }
  743 + };
  744 +
  745 + // Make the request
  746 + $.ajax(ajax_params);
  747 + } else if(settings.local_data) {
  748 + // Do the search through local data
  749 + var results = $.grep(settings.local_data, function (row) {
  750 + return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
  751 + });
  752 +
  753 + if($.isFunction(settings.onResult)) {
  754 + results = settings.onResult.call(hidden_input, results);
  755 + }
  756 + cache.add(query, results);
  757 + populate_dropdown(query, results);
  758 + }
  759 + }
  760 + }
  761 +};
  762 +
  763 +// Really basic cache for the results
  764 +$.TokenList.Cache = function (options) {
  765 + var settings = $.extend({
  766 + max_size: 500
  767 + }, options);
  768 +
  769 + var data = {};
  770 + var size = 0;
  771 +
  772 + var flush = function () {
  773 + data = {};
  774 + size = 0;
  775 + };
  776 +
  777 + this.add = function (query, results) {
  778 + if(size > settings.max_size) {
  779 + flush();
  780 + }
  781 +
  782 + if(!data[query]) {
  783 + size += 1;
  784 + }
  785 +
  786 + data[query] = results;
  787 + };
  788 +
  789 + this.get = function (query) {
  790 + return data[query];
  791 + };
  792 +};
  793 +}(jQuery));
... ...
public/stylesheets/token-input-facebook.css 0 → 100644
... ... @@ -0,0 +1,122 @@
  1 +/* Example tokeninput style #2: Facebook style */
  2 +ul.token-input-list-facebook {
  3 + overflow: hidden;
  4 + height: auto !important;
  5 + height: 1%;
  6 + width: 400px;
  7 + border: 1px solid #8496ba;
  8 + cursor: text;
  9 + font-size: 12px;
  10 + font-family: Verdana;
  11 + min-height: 1px;
  12 + z-index: 999;
  13 + margin: 0;
  14 + padding: 0;
  15 + background-color: #fff;
  16 + list-style-type: none;
  17 + clear: left;
  18 +}
  19 +
  20 +ul.token-input-list-facebook li input {
  21 + border: 0;
  22 + width: 100px;
  23 + padding: 3px 8px;
  24 + background-color: white;
  25 + margin: 2px 0;
  26 + -webkit-appearance: caret;
  27 +}
  28 +
  29 +li.token-input-token-facebook {
  30 + overflow: hidden;
  31 + height: auto !important;
  32 + height: 15px;
  33 + margin: 3px;
  34 + padding: 1px 3px;
  35 + background-color: #eff2f7;
  36 + color: #000;
  37 + cursor: default;
  38 + border: 1px solid #ccd5e4;
  39 + font-size: 11px;
  40 + border-radius: 5px;
  41 + -moz-border-radius: 5px;
  42 + -webkit-border-radius: 5px;
  43 + float: left;
  44 + white-space: nowrap;
  45 +}
  46 +
  47 +li.token-input-token-facebook p {
  48 + display: inline;
  49 + padding: 0;
  50 + margin: 0;
  51 +}
  52 +
  53 +li.token-input-token-facebook span {
  54 + color: #a6b3cf;
  55 + margin-left: 5px;
  56 + font-weight: bold;
  57 + cursor: pointer;
  58 +}
  59 +
  60 +li.token-input-selected-token-facebook {
  61 + background-color: #5670a6;
  62 + border: 1px solid #3b5998;
  63 + color: #fff;
  64 +}
  65 +
  66 +li.token-input-input-token-facebook {
  67 + float: left;
  68 + margin: 0;
  69 + padding: 0;
  70 + list-style-type: none;
  71 +}
  72 +
  73 +div.token-input-dropdown-facebook {
  74 + position: absolute;
  75 + width: 400px;
  76 + background-color: #fff;
  77 + overflow: hidden;
  78 + border-left: 1px solid #ccc;
  79 + border-right: 1px solid #ccc;
  80 + border-bottom: 1px solid #ccc;
  81 + cursor: default;
  82 + font-size: 11px;
  83 + font-family: Verdana;
  84 + z-index: 1;
  85 +}
  86 +
  87 +div.token-input-dropdown-facebook p {
  88 + margin: 0;
  89 + padding: 5px;
  90 + font-weight: bold;
  91 + color: #777;
  92 +}
  93 +
  94 +div.token-input-dropdown-facebook ul {
  95 + margin: 0;
  96 + padding: 0;
  97 +}
  98 +
  99 +div.token-input-dropdown-facebook ul li {
  100 + background-color: #fff;
  101 + padding: 3px;
  102 + margin: 0;
  103 + list-style-type: none;
  104 +}
  105 +
  106 +div.token-input-dropdown-facebook ul li.token-input-dropdown-item-facebook {
  107 + background-color: #fff;
  108 +}
  109 +
  110 +div.token-input-dropdown-facebook ul li.token-input-dropdown-item2-facebook {
  111 + background-color: #fff;
  112 +}
  113 +
  114 +div.token-input-dropdown-facebook ul li em {
  115 + font-weight: bold;
  116 + font-style: normal;
  117 +}
  118 +
  119 +div.token-input-dropdown-facebook ul li.token-input-selected-dropdown-item-facebook {
  120 + background-color: #3b5998;
  121 + color: #fff;
  122 +}
0 123 \ No newline at end of file
... ...
public/stylesheets/token-input-mac.css 0 → 100644
... ... @@ -0,0 +1,204 @@
  1 +/* Example tokeninput style #2: Mac Style */
  2 +fieldset.token-input-mac {
  3 + position: relative;
  4 + padding: 0;
  5 + margin: 5px 0;
  6 + background: #fff;
  7 + width: 400px;
  8 + border: 1px solid #A4BDEC;
  9 + border-radius: 10px;
  10 + -moz-border-radius: 10px;
  11 + -webkit-border-radius: 10px;
  12 +}
  13 +
  14 +fieldset.token-input-mac.token-input-dropdown-mac {
  15 + border-radius: 10px 10px 0 0;
  16 + -moz-border-radius: 10px 10px 0 0;
  17 + -webkit-border-radius: 10px 10px 0 0;
  18 + box-shadow: 0 5px 20px 0 rgba(0,0,0,0.25);
  19 + -moz-box-shadow: 0 5px 20px 0 rgba(0,0,0,0.25);
  20 + -webkit-box-shadow: 0 5px 20px 0 rgba(0,0,0,0.25);
  21 +}
  22 +
  23 +ul.token-input-list-mac {
  24 + overflow: hidden;
  25 + height: auto !important;
  26 + height: 1%;
  27 + cursor: text;
  28 + font-size: 12px;
  29 + font-family: Verdana;
  30 + min-height: 1px;
  31 + z-index: 999;
  32 + margin: 0;
  33 + padding: 3px;
  34 + background: transparent;
  35 +}
  36 +
  37 +ul.token-input-list-mac.error {
  38 + border: 1px solid #C52020;
  39 +}
  40 +
  41 +ul.token-input-list-mac li {
  42 + list-style-type: none;
  43 +}
  44 +
  45 +li.token-input-token-mac p {
  46 + display: inline;
  47 + padding: 0;
  48 + margin: 0;
  49 +}
  50 +
  51 +li.token-input-token-mac span {
  52 + color: #a6b3cf;
  53 + margin-left: 5px;
  54 + font-weight: bold;
  55 + cursor: pointer;
  56 +}
  57 +
  58 +/* TOKENS */
  59 +
  60 +li.token-input-token-mac {
  61 + font-family: "Lucida Grande", Arial, serif;
  62 + font-size: 9pt;
  63 + line-height: 12pt;
  64 + overflow: hidden;
  65 + height: 16px;
  66 + margin: 3px;
  67 + padding: 0 10px;
  68 + background: none;
  69 + background-color: #dee7f8;
  70 + color: #000;
  71 + cursor: default;
  72 + border: 1px solid #a4bdec;
  73 + border-radius: 15px;
  74 + -moz-border-radius: 15px;
  75 + -webkit-border-radius: 15px;
  76 + float: left;
  77 +}
  78 +
  79 +li.token-input-highlighted-token-mac {
  80 + background-color: #bbcef1;
  81 + border: 1px solid #598bec;
  82 + color: #000;
  83 +}
  84 +
  85 +li.token-input-selected-token-mac {
  86 + background-color: #598bec;
  87 + border: 1px solid transparent;
  88 + color: #fff;
  89 +}
  90 +
  91 +li.token-input-highlighted-token-mac span.token-input-delete-token-mac {
  92 + color: #000;
  93 +}
  94 +
  95 +li.token-input-selected-token-mac span.token-input-delete-token-mac {
  96 + color: #fff;
  97 +}
  98 +
  99 +li.token-input-input-token-mac {
  100 + border: none;
  101 + background: transparent;
  102 + float: left;
  103 + padding: 0;
  104 + margin: 0;
  105 +}
  106 +
  107 +li.token-input-input-token-mac input {
  108 + border: 0;
  109 + width: 100px;
  110 + padding: 3px;
  111 + background-color: transparent;
  112 + margin: 0;
  113 +}
  114 +
  115 +div.token-input-dropdown-mac {
  116 + position: absolute;
  117 + border: 1px solid #A4BDEC;
  118 + border-top: none;
  119 + left: -1px;
  120 + right: -1px;
  121 + background-color: #fff;
  122 + overflow: hidden;
  123 + cursor: default;
  124 + font-size: 10pt;
  125 + font-family: "Lucida Grande", Arial, serif;
  126 + padding: 5px;
  127 + border-radius: 0 0 10px 10px;
  128 + -moz-border-radius: 0 0 10px 10px;
  129 + -webkit-border-radius: 0 0 10px 10px;
  130 + box-shadow: 0 5px 20px 0 rgba(0,0,0,0.25);
  131 + -moz-box-shadow: 0 5px 20px 0 rgba(0,0,0,0.25);
  132 + -webkit-box-shadow: 0 5px 20px 0 rgba(0,0,0,0.25);
  133 + clip:rect(0px, 1000px, 1000px, -10px);
  134 +}
  135 +
  136 +div.token-input-dropdown-mac p {
  137 + font-size: 8pt;
  138 + margin: 0;
  139 + padding: 0 5px;
  140 + font-style: italic;
  141 + color: #aaa;
  142 +}
  143 +
  144 +div.token-input-dropdown-mac h3.token-input-dropdown-category-mac {
  145 + font-family: "Lucida Grande", Arial, serif;
  146 + font-size: 10pt;
  147 + font-weight: bold;
  148 + border: none;
  149 + padding: 0 5px;
  150 + margin: 0;
  151 +}
  152 +
  153 +div.token-input-dropdown-mac ul {
  154 + margin: 0;
  155 + padding: 0;
  156 +}
  157 +
  158 +div.token-input-dropdown-mac ul li {
  159 + list-style-type: none;
  160 + cursor: pointer;
  161 + background: none;
  162 + background-color: #fff;
  163 + margin: 0;
  164 + padding: 0 0 0 25px;
  165 +}
  166 +
  167 +div.token-input-dropdown-mac ul li.token-input-dropdown-item-mac {
  168 + background-color: #fff;
  169 +}
  170 +
  171 +div.token-input-dropdown-mac ul li.token-input-dropdown-item-mac.odd {
  172 + background-color: #ECF4F9;
  173 + border-radius: 15px;
  174 + -moz-border-radius: 15px;
  175 + -webkit-border-radius: 15px;
  176 +}
  177 +
  178 +div.token-input-dropdown-mac ul li.token-input-dropdown-item-mac span.token-input-dropdown-item-description-mac {
  179 + float: right;
  180 + font-size: 8pt;
  181 + font-style: italic;
  182 + padding: 0 10px 0 0;
  183 + color: #999;
  184 +}
  185 +
  186 +div.token-input-dropdown-mac ul li strong {
  187 + font-weight: bold;
  188 + text-decoration: underline;
  189 + font-style: none;
  190 +}
  191 +
  192 +div.token-input-dropdown-mac ul li.token-input-selected-dropdown-item-mac,
  193 +div.token-input-dropdown-mac ul li.token-input-selected-dropdown-item-mac.odd {
  194 + background-color: #598bec;
  195 + color: #fff;
  196 + border-radius: 15px;
  197 + -moz-border-radius: 15px;
  198 + -webkit-border-radius: 15px;
  199 +}
  200 +
  201 +div.token-input-dropdown-mac ul li.token-input-selected-dropdown-item-mac span.token-input-dropdown-item-description-mac,
  202 +div.token-input-dropdown-mac ul li.token-input-selected-dropdown-item-mac.odd span.token-input-dropdown-item-description-mac {
  203 + color: #fff;
  204 +}
... ...
public/stylesheets/token-input.css 0 → 100644
... ... @@ -0,0 +1,113 @@
  1 +/* Example tokeninput style #1: Token vertical list*/
  2 +ul.token-input-list {
  3 + overflow: hidden;
  4 + height: auto !important;
  5 + height: 1%;
  6 + width: 497px;
  7 + border: 1px solid #999;
  8 + cursor: text;
  9 + font-size: 12px;
  10 + font-family: Verdana;
  11 + z-index: 999;
  12 + margin: 0;
  13 + padding: 0;
  14 + background-color: #fff;
  15 + list-style-type: none;
  16 + clear: left;
  17 +}
  18 +
  19 +ul.token-input-list li {
  20 + list-style-type: none;
  21 +}
  22 +
  23 +ul.token-input-list li input {
  24 + border: 0;
  25 + width: 350px;
  26 + padding: 3px 8px;
  27 + background-color: white;
  28 + -webkit-appearance: caret;
  29 +}
  30 +
  31 +li.token-input-token {
  32 + overflow: hidden;
  33 + height: auto !important;
  34 + height: 1%;
  35 + margin: 3px;
  36 + padding: 3px 5px;
  37 + background-color: #d0efa0;
  38 + color: #000;
  39 + font-weight: bold;
  40 + cursor: default;
  41 + display: block;
  42 +}
  43 +
  44 +li.token-input-token p {
  45 + float: left;
  46 + padding: 0;
  47 + margin: 0;
  48 +}
  49 +
  50 +li.token-input-token span {
  51 + float: right;
  52 + color: #777;
  53 + cursor: pointer;
  54 +}
  55 +
  56 +li.token-input-selected-token {
  57 + background-color: #08844e;
  58 + color: #fff;
  59 +}
  60 +
  61 +li.token-input-selected-token span {
  62 + color: #bbb;
  63 +}
  64 +
  65 +div.token-input-dropdown {
  66 + position: absolute;
  67 + width: 497px;
  68 + background-color: #fff;
  69 + overflow: hidden;
  70 + border-left: 1px solid #ccc;
  71 + border-right: 1px solid #ccc;
  72 + border-bottom: 1px solid #ccc;
  73 + cursor: default;
  74 + font-size: 12px;
  75 + font-family: Verdana;
  76 + z-index: 1;
  77 +}
  78 +
  79 +div.token-input-dropdown p {
  80 + margin: 0;
  81 + padding: 5px;
  82 + font-weight: bold;
  83 + color: #777;
  84 +}
  85 +
  86 +div.token-input-dropdown ul {
  87 + margin: 0;
  88 + padding: 0;
  89 +}
  90 +
  91 +div.token-input-dropdown ul li {
  92 + background-color: #fff;
  93 + padding: 3px;
  94 + list-style-type: none;
  95 +}
  96 +
  97 +div.token-input-dropdown ul li.token-input-dropdown-item {
  98 + background-color: #fafafa;
  99 +}
  100 +
  101 +div.token-input-dropdown ul li.token-input-dropdown-item2 {
  102 + background-color: #fff;
  103 +}
  104 +
  105 +div.token-input-dropdown ul li em {
  106 + font-weight: bold;
  107 + font-style: normal;
  108 +}
  109 +
  110 +div.token-input-dropdown ul li.token-input-selected-dropdown-item {
  111 + background-color: #d0efa0;
  112 +}
  113 +
... ...
script/sample-profiles
... ... @@ -95,6 +95,7 @@ save guest do
95 95 communities.rand.add_member(guest.person)
96 96 end
97 97 end
  98 +User.all(:conditions => ['login NOT LIKE "%%_template"']).each(&:activate)
98 99 done
99 100  
100 101 people = $environment.people
... ...
test/functional/account_controller_test.rb
... ... @@ -667,17 +667,21 @@ class AccountControllerTest &lt; Test::Unit::TestCase
667 667 end
668 668  
669 669 should 'merge user data with extra stuff from plugins' do
670   - plugin1 = mock()
671   - plugin1.stubs(:user_data_extras).returns({ :foo => 'bar' })
672   -
673   - plugin2 = mock()
674   - plugin2.stubs(:user_data_extras).returns({ :test => 5 })
  670 + class Plugin1 < Noosfero::Plugin
  671 + def user_data_extras
  672 + {:foo => 'bar'}
  673 + end
  674 + end
675 675  
676   - enabled_plugins = [plugin1, plugin2]
  676 + class Plugin2 < Noosfero::Plugin
  677 + def user_data_extras
  678 + {:test => 5}
  679 + end
  680 + end
677 681  
678   - plugins = mock()
679   - plugins.stubs(:enabled_plugins).returns(enabled_plugins)
680   - Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  682 + e = User.find_by_login('ze').environment
  683 + e.enable_plugin(Plugin1.name)
  684 + e.enable_plugin(Plugin2.name)
681 685  
682 686 login_as 'ze'
683 687  
... ...
test/functional/admin_panel_controller_test.rb
... ... @@ -342,4 +342,20 @@ class AdminPanelControllerTest &lt; Test::Unit::TestCase
342 342 assert_equal 3, Environment.default.news_amount_by_folder
343 343 end
344 344  
  345 + should 'display plugins links' do
  346 + plugin1_link = {:title => 'Plugin1 link', :url => 'plugin1.com'}
  347 + plugin2_link = {:title => 'Plugin2 link', :url => 'plugin2.com'}
  348 + links = [plugin1_link, plugin2_link]
  349 + plugins = mock()
  350 + plugins.stubs(:map).with(:admin_panel_links).returns(links)
  351 + plugins.stubs(:enabled_plugins).returns([])
  352 + plugins.stubs(:map).with(:body_beginning).returns([])
  353 + Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  354 +
  355 + get :index
  356 +
  357 + assert_tag :tag => 'a', :content => /#{plugin1_link[:title]}/, :attributes => {:href => /#{plugin1_link[:url]}/}
  358 + assert_tag :tag => 'a', :content => /#{plugin2_link[:title]}/, :attributes => {:href => /#{plugin2_link[:url]}/}
  359 + end
  360 +
345 361 end
... ...
test/functional/application_controller_test.rb
... ... @@ -442,28 +442,23 @@ class ApplicationControllerTest &lt; Test::Unit::TestCase
442 442 end
443 443  
444 444 should 'include stylesheets supplied by plugins' do
445   - plugin1 = mock()
446   - plugin1.stubs(:stylesheet?).returns(true)
447   - plugin1.stubs(:js_files).returns([])
448   - plugin1_class = mock()
  445 + class Plugin1 < Noosfero::Plugin
  446 + def stylesheet?
  447 + true
  448 + end
  449 + end
449 450 plugin1_path = '/plugin1/style.css'
450   - plugin1_class.stubs(:public_path).with('style.css').returns(plugin1_path)
451   - plugin1.stubs(:class).returns(plugin1_class)
452 451  
453   - plugin2 = mock()
454   - plugin2.stubs(:stylesheet?).returns(true)
455   - plugin2.stubs(:js_files).returns([])
456   - plugin2_class = mock()
  452 + class Plugin2 < Noosfero::Plugin
  453 + def stylesheet?
  454 + true
  455 + end
  456 + end
457 457 plugin2_path = '/plugin2/style.css'
458   - plugin2_class.stubs(:public_path).with('style.css').returns(plugin2_path)
459   - plugin2.stubs(:class).returns(plugin2_class)
460   -
461   - enabled_plugins = [plugin1, plugin2]
462 458  
463   - plugins = mock()
464   - plugins.stubs(:map).with(:body_beginning).returns([])
465   - plugins.stubs(:enabled_plugins).returns(enabled_plugins)
466   - Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  459 + environment = Environment.default
  460 + environment.enable_plugin(Plugin1.name)
  461 + environment.enable_plugin(Plugin2.name)
467 462  
468 463 get :index
469 464  
... ... @@ -472,33 +467,29 @@ class ApplicationControllerTest &lt; Test::Unit::TestCase
472 467 end
473 468  
474 469 should 'include javascripts supplied by plugins' do
  470 + class Plugin1 < Noosfero::Plugin
  471 + def js_files
  472 + ['js1.js']
  473 + end
  474 + end
  475 +
475 476 js1 = 'js1.js'
476   - plugin1 = mock()
477   - plugin1.stubs(:stylesheet?).returns(false)
478   - plugin1.stubs(:js_files).returns([js1])
479   - plugin1_class = mock()
480 477 plugin1_path = '/plugin1/'+js1
481   - plugin1_class.stubs(:public_path).with(js1).returns(plugin1_path)
482   - plugin1.stubs(:class).returns(plugin1_class)
  478 +
  479 + class Plugin2 < Noosfero::Plugin
  480 + def js_files
  481 + ['js2.js', 'js3.js']
  482 + end
  483 + end
483 484  
484 485 js2 = 'js2.js'
485 486 js3 = 'js3.js'
486   - plugin2 = mock()
487   - plugin2.stubs(:stylesheet?).returns(false)
488   - plugin2.stubs(:js_files).returns([js2, js3])
489   - plugin2_class = mock()
490 487 plugin2_path2 = '/plugin2/'+js2
491 488 plugin2_path3 = '/plugin2/'+js3
492   - plugin2_class.stubs(:public_path).with(js2).returns(plugin2_path2)
493   - plugin2_class.stubs(:public_path).with(js3).returns(plugin2_path3)
494   - plugin2.stubs(:class).returns(plugin2_class)
495 489  
496   - enabled_plugins = [plugin1, plugin2]
497   -
498   - plugins = mock()
499   - plugins.stubs(:map).with(:body_beginning).returns([])
500   - plugins.stubs(:enabled_plugins).returns(enabled_plugins)
501   - Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  490 + environment = Environment.default
  491 + environment.enable_plugin(Plugin1.name)
  492 + environment.enable_plugin(Plugin2.name)
502 493  
503 494 get :index
504 495  
... ...
test/functional/catalog_controller_test.rb
... ... @@ -94,23 +94,27 @@ class CatalogControllerTest &lt; Test::Unit::TestCase
94 94 end
95 95  
96 96 should 'include extra content supplied by plugins on catalog item extras' do
  97 + class Plugin1 < Noosfero::Plugin
  98 + def catalog_item_extras(product)
  99 + lambda {"<span id='plugin1'>This is Plugin1 speaking!</span>"}
  100 + end
  101 + end
  102 +
  103 + class Plugin2 < Noosfero::Plugin
  104 + def catalog_item_extras(product)
  105 + lambda {"<span id='plugin2'>This is Plugin2 speaking!</span>"}
  106 + end
  107 + end
  108 +
97 109 product = fast_create(Product, :enterprise_id => @enterprise.id)
98   - plugin1_local_variable = "Plugin1"
99   - plugin1_content = lambda {"<span id='plugin1'>This is #{plugin1_local_variable} speaking!</span>"}
100   - plugin2_local_variable = "Plugin2"
101   - plugin2_content = lambda {"<span id='plugin2'>This is #{plugin2_local_variable} speaking!</span>"}
102   - contents = [plugin1_content, plugin2_content]
103   -
104   - plugins = mock()
105   - plugins.stubs(:enabled_plugins).returns([])
106   - plugins.stubs(:map).with(:body_beginning).returns([])
107   - plugins.stubs(:map).with(:catalog_item_extras, product).returns(contents)
108   - Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  110 + environment = Environment.default
  111 + environment.enable_plugin(Plugin1.name)
  112 + environment.enable_plugin(Plugin2.name)
109 113  
110 114 get :index, :profile => @enterprise.identifier
111 115  
112   - assert_tag :tag => 'span', :content => 'This is ' + plugin1_local_variable + ' speaking!', :attributes => {:id => 'plugin1'}
113   - assert_tag :tag => 'span', :content => 'This is ' + plugin2_local_variable + ' speaking!', :attributes => {:id => 'plugin2'}
  116 + assert_tag :tag => 'span', :content => 'This is Plugin1 speaking!', :attributes => {:id => 'plugin1'}
  117 + assert_tag :tag => 'span', :content => 'This is Plugin2 speaking!', :attributes => {:id => 'plugin2'}
114 118 end
115 119  
116 120 end
... ...
test/functional/enterprise_registration_controller_test.rb
... ... @@ -179,5 +179,4 @@ all_fixtures
179 179 get :index
180 180 assert_equal assigns(:create_enterprise).target, environment
181 181 end
182   -
183 182 end
... ...
test/functional/manage_products_controller_test.rb
... ... @@ -455,4 +455,16 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase
455 455 assert_tag :tag => 'span', :content => 'This is ' + plugin1_local_variable + ' speaking!', :attributes => {:id => 'plugin1'}
456 456 assert_tag :tag => 'span', :content => 'This is ' + plugin2_local_variable + ' speaking!', :attributes => {:id => 'plugin2'}
457 457 end
  458 +
  459 + should 'not allow product creation for profiles that can\'t do it' do
  460 + class SpecialEnterprise < Enterprise
  461 + def create_product?
  462 + false
  463 + end
  464 + end
  465 + enterprise = SpecialEnterprise.create!(:identifier => 'special-enterprise', :name => 'Special Enterprise')
  466 + get 'new', :profile => enterprise.identifier
  467 + assert_response 403
  468 + end
  469 +
458 470 end
... ...
test/functional/profile_controller_test.rb
... ... @@ -1163,19 +1163,31 @@ class ProfileControllerTest &lt; Test::Unit::TestCase
1163 1163 end
1164 1164  
1165 1165 should 'display plugins tabs' do
1166   - plugin1_tab = {:title => 'Plugin1 tab', :id => 'plugin1_tab', :content => 'Content from plugin1.'}
1167   - plugin2_tab = {:title => 'Plugin2 tab', :id => 'plugin2_tab', :content => 'Content from plugin2.'}
1168   - tabs = [plugin1_tab, plugin2_tab]
1169   - plugins = mock()
1170   - plugins.stubs(:map).with(:profile_tabs).returns(tabs)
1171   - plugins.stubs(:enabled_plugins).returns([])
1172   - plugins.stubs(:map).with(:body_beginning).returns([])
1173   - Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  1166 + class Plugin1 < Noosfero::Plugin
  1167 + def profile_tabs
  1168 + {:title => 'Plugin1 tab', :id => 'plugin1_tab', :content => lambda { 'Content from plugin1.' }}
  1169 + end
  1170 + end
  1171 +
  1172 + class Plugin2 < Noosfero::Plugin
  1173 + def profile_tabs
  1174 + {:title => 'Plugin2 tab', :id => 'plugin2_tab', :content => lambda { 'Content from plugin2.' }}
  1175 + end
  1176 + end
  1177 +
  1178 + e = profile.environment
  1179 + e.enable_plugin(Plugin1.name)
  1180 + e.enable_plugin(Plugin2.name)
1174 1181  
1175 1182 get :index, :profile => profile.identifier
1176 1183  
1177   - assert_tag :tag => 'a', :content => /#{plugin1_tab[:title]}/, :attributes => {:href => /#{plugin1_tab[:id]}/}
1178   - assert_tag :tag => 'div', :content => /#{plugin1_tab[:content]}/, :attributes => {:id => /#{plugin1_tab[:id]}/}
  1184 + plugin1 = Plugin1.new
  1185 + plugin2 = Plugin2.new
  1186 +
  1187 + assert_tag :tag => 'a', :content => /#{plugin1.profile_tabs[:title]}/, :attributes => {:href => /#{plugin1.profile_tabs[:id]}/}
  1188 + assert_tag :tag => 'div', :content => /#{instance_eval(&plugin1.profile_tabs[:content])}/, :attributes => {:id => /#{plugin1.profile_tabs[:id]}/}
  1189 + assert_tag :tag => 'a', :content => /#{plugin2.profile_tabs[:title]}/, :attributes => {:href => /#{plugin2.profile_tabs[:id]}/}
  1190 + assert_tag :tag => 'div', :content => /#{instance_eval(&plugin2.profile_tabs[:content])}/, :attributes => {:id => /#{plugin2.profile_tabs[:id]}/}
1179 1191 end
1180 1192  
1181 1193 should 'redirect to profile page when try to request join_not_logged via GET method' do
... ...
test/functional/profile_members_controller_test.rb
... ... @@ -233,59 +233,87 @@ class ProfileMembersControllerTest &lt; Test::Unit::TestCase
233 233 assert_not_includes com.members, u
234 234 end
235 235  
236   - should 'find users' do
237   - ent = fast_create(Enterprise, :name => 'Test Ent', :identifier => 'test_ent')
238   - user = create_user_full('test_user').person
239   - person = create_user_with_permission('ent_user', 'manage_memberships', ent)
240   - login_as :ent_user
241   -
242   - get :find_users, :profile => ent.identifier, :query => 'test*', :scope => 'all_users'
243   -
244   - assert_includes assigns(:users_found), user
245   - end
246   -
247   - should 'not display members when finding users in all_users scope' do
248   - ent = fast_create(Enterprise, :name => 'Test Ent', :identifier => 'test_ent')
249   - user = create_user_full('test_user').person
250   -
251   - person = create_user_with_permission('ent_user', 'manage_memberships', ent)
252   - login_as :ent_user
253   -
254   - get :find_users, :profile => ent.identifier, :query => '*user', :scope => 'all_users'
255   -
256   - assert_tag :tag => 'a', :content => /#{user.name}/
257   - assert_no_tag :tag => 'a', :content => /#{person.name}/
258   - end
259   -
260   - should 'not display admins when finding users in new_admins scope' do
261   - ent = fast_create(Enterprise, :name => 'Test Ent', :identifier => 'test_ent')
262   -
263   - person = create_user('admin_user').person
264   - ent.add_admin(person)
265   -
266   - user = create_user_full('test_user').person
267   - ent.add_member(user).finish
268   -
269   - login_as :admin_user
270   -
271   - get :find_users, :profile => ent.identifier, :query => '*user', :scope => 'new_admins'
  236 + should 'list users on search by role' do
  237 + e = Enterprise.create!(:name => 'Sample Enterprise', :identifier => 'sample-enterprise')
  238 + user = create_user_with_permission('test_user', 'manage_memberships', e)
  239 + login_as :test_user
272 240  
273   - assert_tag :tag => 'a', :content => /#{user.name}/
274   - assert_no_tag :tag => 'a', :content => /#{person.name}/
  241 + # Should list if match name
  242 + p1 = create_user('person_1').person
  243 + p2 = create_user('person_2').person
  244 + # Should not list if don't match name
  245 + p3 = create_user('blo').person
  246 + r1 = Profile::Roles.organization_member_roles(e.environment.id).first
  247 + r2 = Profile::Roles.organization_member_roles(e.environment.id).last
  248 +
  249 + p4 = create_user('person_4').person
  250 + e.affiliate(p4, r1)
  251 + p5 = create_user('person_5').person
  252 + e.affiliate(p5, r2)
  253 +
  254 + # Should be case insensitive
  255 + p6 = create_user('PeRsOn_2').person
  256 + # Should list if match identifier
  257 + p7 = create_user('person_7').person
  258 + p7.name = 'Bli'
  259 + p7.save!
  260 +
  261 + get :search_user, :profile => e.identifier, 'q_'+r1.key => 'per', :role => r1.id
  262 + assert_match /#{p1.name}/, @response.body
  263 + assert_match /#{p2.name}/, @response.body
  264 + assert_no_match /#{p3.name}/, @response.body
  265 + assert_no_match /#{p4.name}/, @response.body
  266 + assert_match /#{p5.name}/, @response.body
  267 + assert_match /#{p6.name}/, @response.body
  268 + assert_match /#{p7.name}/, @response.body
  269 +
  270 + get :search_user, :profile => e.identifier, 'q_'+r2.key => 'per', :role => r2.id
  271 + assert_match /#{p1.name}/, @response.body
  272 + assert_match /#{p2.name}/, @response.body
  273 + assert_no_match /#{p3.name}/, @response.body
  274 + assert_match /#{p4.name}/, @response.body
  275 + assert_no_match /#{p5.name}/, @response.body
  276 + assert_match /#{p6.name}/, @response.body
  277 + assert_match /#{p7.name}/, @response.body
275 278 end
276 279  
277   - should 'return users with <query> as a prefix' do
278   - daniel = create_user_full('daniel').person
279   - daniela = create_user_full('daniela').person
280   -
281   - ent = fast_create(Enterprise, :name => 'Test Ent', :identifier => 'test_ent')
282   - person = create_user_with_permission('test_user', 'manage_memberships', ent)
  280 + should 'save associations' do
  281 + e = Enterprise.create!(:name => 'Sample Enterprise', :identifier => 'sample-enterprise')
  282 + user = create_user_with_permission('test_user', 'manage_memberships', e)
283 283 login_as :test_user
284 284  
285   - get :find_users, :profile => ent.identifier, :query => 'daniel', :scope => 'all_users'
286   -
287   - assert_includes assigns(:users_found), daniel
288   - assert_includes assigns(:users_found), daniela
  285 + p1 = create_user('person-1').person
  286 + p2 = create_user('person-2').person
  287 + p3 = create_user('person-3').person
  288 + roles = Profile::Roles.organization_member_roles(e.environment.id)
  289 + r1 = roles.first
  290 + r2 = roles.last
  291 + roles.delete(r1)
  292 + roles.delete(r2)
  293 +
  294 + roles_params = roles.inject({}) { |result, role| result.merge({'q_'+role.key => ''})}
  295 +
  296 + post :save_associations,
  297 + {:profile => e.identifier,
  298 + 'q_'+r1.key => "#{p1.id},#{p2.id},#{user.id}",
  299 + 'q_'+r2.key => "#{p2.id},#{p3.id}"}.merge(roles_params)
  300 + assert_includes e.members_by_role(r1), p1
  301 + assert_includes e.members_by_role(r1), p2
  302 + assert_not_includes e.members_by_role(r1), p3
  303 + assert_not_includes e.members_by_role(r2), p1
  304 + assert_includes e.members_by_role(r2), p2
  305 + assert_includes e.members_by_role(r2), p3
  306 +
  307 + post :save_associations,
  308 + {:profile => e.identifier,
  309 + 'q_'+r1.key => "#{p2.id},#{p3.id},#{user.id}",
  310 + 'q_'+r2.key => "#{p1.id},#{p2.id}"}.merge(roles_params)
  311 + assert_not_includes e.members_by_role(r1), p1
  312 + assert_includes e.members_by_role(r1), p2
  313 + assert_includes e.members_by_role(r1), p3
  314 + assert_includes e.members_by_role(r2), p1
  315 + assert_includes e.members_by_role(r2), p2
  316 + assert_not_includes e.members_by_role(r2), p3
289 317 end
290 318  
291 319 should 'ignore roles with id zero' do
... ...
test/functional/search_controller_test.rb
... ... @@ -290,24 +290,53 @@ class SearchControllerTest &lt; Test::Unit::TestCase
290 290 end
291 291  
292 292 should 'include extra content supplied by plugins on product asset' do
  293 + class Plugin1 < Noosfero::Plugin
  294 + def asset_product_extras(product, enterprise)
  295 + lambda {"<span id='plugin1'>This is Plugin1 speaking!</span>"}
  296 + end
  297 + end
  298 +
  299 + class Plugin2 < Noosfero::Plugin
  300 + def asset_product_extras(product, enterprise)
  301 + lambda {"<span id='plugin2'>This is Plugin2 speaking!</span>"}
  302 + end
  303 + end
  304 +
293 305 enterprise = fast_create(Enterprise)
294 306 product = fast_create(Product, :enterprise_id => enterprise.id)
295   - plugin1_local_variable = "Plugin1"
296   - plugin1_content = lambda {"<span id='plugin1'>This is #{plugin1_local_variable} speaking!</span>"}
297   - plugin2_local_variable = "Plugin2"
298   - plugin2_content = lambda {"<span id='plugin2'>This is #{plugin2_local_variable} speaking!</span>"}
299   - contents = [plugin1_content, plugin2_content]
300   -
301   - plugins = mock()
302   - plugins.stubs(:enabled_plugins).returns([])
303   - plugins.stubs(:map).with(:body_beginning).returns([])
304   - plugins.stubs(:map).with(:asset_product_extras, product, enterprise).returns(contents)
305   - Noosfero::Plugin::Manager.stubs(:new).returns(plugins)
  307 +
  308 + e = Environment.default
  309 + e.enable_plugin(Plugin1.name)
  310 + e.enable_plugin(Plugin2.name)
  311 +
  312 + get :assets, :asset => 'products'
  313 +
  314 + assert_tag :tag => 'span', :content => 'This is Plugin1 speaking!', :attributes => {:id => 'plugin1'}
  315 + assert_tag :tag => 'span', :content => 'This is Plugin2 speaking!', :attributes => {:id => 'plugin2'}
  316 + end
  317 +
  318 + should 'include extra properties of the product supplied by plugins' do
  319 + class Plugin1 < Noosfero::Plugin
  320 + def asset_product_properties(product)
  321 + return { :name => _('Property1'), :content => lambda { link_to(product.name, '/plugin1') } }
  322 + end
  323 + end
  324 + class Plugin2 < Noosfero::Plugin
  325 + def asset_product_properties(product)
  326 + return { :name => _('Property2'), :content => lambda { link_to(product.name, '/plugin2') } }
  327 + end
  328 + end
  329 + enterprise = fast_create(Enterprise)
  330 + product = fast_create(Product, :enterprise_id => enterprise.id)
  331 +
  332 + environment = Environment.default
  333 + environment.enable_plugin(Plugin1.name)
  334 + environment.enable_plugin(Plugin2.name)
306 335  
307 336 get :assets, :asset => 'products'
308 337  
309   - assert_tag :tag => 'span', :content => 'This is ' + plugin1_local_variable + ' speaking!', :attributes => {:id => 'plugin1'}
310   - assert_tag :tag => 'span', :content => 'This is ' + plugin2_local_variable + ' speaking!', :attributes => {:id => 'plugin2'}
  338 + assert_tag :tag => 'li', :content => /Property1/, :child => {:tag => 'a', :attributes => {:href => '/plugin1'}, :content => product.name}
  339 + assert_tag :tag => 'li', :content => /Property2/, :child => {:tag => 'a', :attributes => {:href => '/plugin2'}, :content => product.name}
311 340 end
312 341  
313 342 should 'paginate enterprise listing' do
... ...
test/unit/application_helper_test.rb
... ... @@ -18,18 +18,6 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
18 18  
19 19 File.expects(:exists?).with(p1+"test/_integer.rhtml").returns(true)
20 20  
21   - assert_equal 'integer', partial_for_class(Integer)
22   - end
23   -
24   -
25   - should 'calculate correctly partial for models recursively' do
26   - p1 = 'path1/'
27   - p2 = 'path2/'
28   - @controller = mock()
29   - @controller.stubs(:view_paths).returns([p1,p2])
30   -
31   - self.stubs(:params).returns({:controller => 'test'})
32   -
33 21 File.expects(:exists?).with(p1+"test/_float.rhtml").returns(false)
34 22 File.expects(:exists?).with(p1+"test/_float.html.erb").returns(false)
35 23 File.expects(:exists?).with(p2+"test/_float.rhtml").returns(false)
... ... @@ -40,22 +28,13 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
40 28 File.expects(:exists?).with(p1+"test/_numeric.html.erb").returns(false)
41 29 File.expects(:exists?).with(p2+"test/_numeric.rhtml").returns(true)
42 30  
43   - assert_equal 'numeric', partial_for_class(Float)
44   - end
45   -
46   - should 'raise error when partial is missing' do
47   - p1 = 'path1/'
48   - p2 = 'path2/'
49   - @controller = mock()
50   - @controller.stubs(:view_paths).returns([p1,p2])
51   -
52   - self.stubs(:params).returns({:controller => 'test'})
53   -
54 31 File.expects(:exists?).with(p1+"test/_object.rhtml").returns(false)
55 32 File.expects(:exists?).with(p1+"test/_object.html.erb").returns(false)
56 33 File.expects(:exists?).with(p2+"test/_object.rhtml").returns(false)
57 34 File.expects(:exists?).with(p2+"test/_object.html.erb").returns(false)
58 35  
  36 + assert_equal 'integer', partial_for_class(Integer)
  37 + assert_equal 'numeric', partial_for_class(Float)
59 38 assert_raises ArgumentError do
60 39 partial_for_class(Object)
61 40 end
... ... @@ -74,6 +53,15 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
74 53 assert_equal 'test/application_helper_test/school/project', partial_for_class(School::Project)
75 54 end
76 55  
  56 + should 'look for superclasses on view_for_profile actions' do
  57 + File.expects(:exists?).with("#{RAILS_ROOT}/app/views/blocks/profile_info_actions/float.rhtml").returns(false)
  58 + File.expects(:exists?).with("#{RAILS_ROOT}/app/views/blocks/profile_info_actions/float.html.erb").returns(false)
  59 + File.expects(:exists?).with("#{RAILS_ROOT}/app/views/blocks/profile_info_actions/numeric.rhtml").returns(false)
  60 + File.expects(:exists?).with("#{RAILS_ROOT}/app/views/blocks/profile_info_actions/numeric.html.erb").returns(true)
  61 +
  62 + assert_equal 'blocks/profile_info_actions/numeric.html.erb', view_for_profile_actions(Float)
  63 + end
  64 +
77 65 should 'give error when there is no partial for class' do
78 66 assert_raises ArgumentError do
79 67 partial_for_class(nil)
... ...
test/unit/enterprise_test.rb
... ... @@ -350,6 +350,20 @@ class EnterpriseTest &lt; Test::Unit::TestCase
350 350 assert_equal false, Enterprise['test_ent'].enabled?
351 351 end
352 352  
  353 + should 'enterprise is validated according to feature enterprises_are_validated_when_created' do
  354 + e = Environment.default
  355 +
  356 + e.enable('enterprises_are_validated_when_created')
  357 + e.save
  358 + enterprise = Enterprise.create(:name => 'test enteprise', :identifier => 'test_ent1')
  359 + assert enterprise.validated
  360 +
  361 + e.disable('enterprises_are_validated_when_created')
  362 + e.save
  363 + enterprise = Enterprise.create(:name => 'test enteprise', :identifier => 'test_ent2')
  364 + assert !enterprise.validated
  365 + end
  366 +
353 367 should 'have inactive_template when creating enterprise and feature is enabled' do
354 368 inactive_template = fast_create(Enterprise, :name => 'inactive enteprise template', :identifier => 'inactive_enterprise_template')
355 369 inactive_template.boxes.destroy_all
... ...
test/unit/environment_test.rb
... ... @@ -1183,4 +1183,17 @@ class EnvironmentTest &lt; Test::Unit::TestCase
1183 1183 assert_equal env.settings[:string_key], 'new value'
1184 1184 end
1185 1185  
  1186 + should 'be able to enable or disable a plugin' do
  1187 + environment = Environment.default
  1188 + plugin = 'Plugin'
  1189 +
  1190 + environment.enable_plugin(plugin)
  1191 + environment.reload
  1192 + assert_includes environment.enabled_plugins, plugin
  1193 +
  1194 + environment.disable_plugin(plugin)
  1195 + environment.reload
  1196 + assert_not_includes environment.enabled_plugins, plugin
  1197 + end
  1198 +
1186 1199 end
... ...
test/unit/organization_test.rb
... ... @@ -386,4 +386,26 @@ class OrganizationTest &lt; Test::Unit::TestCase
386 386 assert_includes Organization.more_active, profile
387 387 end
388 388  
  389 + should 'validates format of cnpj' do
  390 + organization = Organization.new(:cnpj => '239-234.234')
  391 + organization.valid?
  392 + assert organization.errors.invalid?(:cnpj)
  393 +
  394 + organization.cnpj = '94.132.024/0001-48'
  395 + organization.valid?
  396 + assert !organization.errors.invalid?(:cnpj)
  397 + end
  398 +
  399 + should 'return members by role in a json format' do
  400 + organization = fast_create(Organization)
  401 + p1 = create_user('person-1').person
  402 + p2 = create_user('person-2').person
  403 + role = Profile::Roles.organization_member_roles(organization.environment.id).last
  404 +
  405 + organization.affiliate(p1, role)
  406 + organization.affiliate(p2, role)
  407 +
  408 + assert_match [{:id => p1.id, :name => p1.name}, {:id => p2.id, :name => p2.name}].to_json, organization.members_by_role_to_json(role)
  409 + end
  410 +
389 411 end
... ...
test/unit/profile_test.rb
... ... @@ -1726,6 +1726,20 @@ class ProfileTest &lt; Test::Unit::TestCase
1726 1726 assert_not_includes env.profiles.without_image, with_image
1727 1727 end
1728 1728  
  1729 + should 'return enterprises subclasses too on namedscope enterprises' do
  1730 + class EnterpriseSubclass < Enterprise; end
  1731 + child = EnterpriseSubclass.create!(:identifier => 'child', :name => 'Child')
  1732 +
  1733 + assert_includes Profile.enterprises, child
  1734 + end
  1735 +
  1736 + should 'return communities subclasses too on namedscope communities' do
  1737 + class CommunitySubclass < Community; end
  1738 + child = CommunitySubclass.create!(:identifier => 'child', :name => 'Child')
  1739 +
  1740 + assert_includes Profile.communities, child
  1741 + end
  1742 +
1729 1743 private
1730 1744  
1731 1745 def assert_invalid_identifier(id)
... ...
vendor/plugins/validates_as_cnpj/LEIAME 0 → 100755
... ... @@ -0,0 +1,13 @@
  1 +Validacao de CNPJ
  2 +=================
  3 +
  4 +Plugin para validacao do formato e dos digitos verificadores de um CNPJ.
  5 +
  6 +Uso:
  7 + Adicione ao modelo:
  8 + validates_as_cnpj :nome_do_campo
  9 +
  10 +Exemplo:
  11 + class Empresa < ActiveRecord::Base
  12 + validates_as_cnpj :cnpj
  13 + end
... ...
vendor/plugins/validates_as_cnpj/init.rb 0 → 100755
... ... @@ -0,0 +1,17 @@
  1 +# init.rb - initialize the plugin
  2 +#
  3 +# Copyright (c) 2006 O.S. Systems
  4 +#
  5 +# Authors: André Ribeiro Camargo <andre@boaideia.inf.br>,
  6 +# Luis Gustavo S. Barreto <gustavo@ossystems.com.br>
  7 +#
  8 +# This program is free software; you can redistribute it and/or modify
  9 +# it under the terms of the GNU General Public License as published by
  10 +# the Free Software Foundation; either version 2 of the License, or
  11 +# (at your option) any later version.
  12 +#
  13 +# This program is distributed in the hope that it will be useful, but
  14 +# WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16 +# General Public License for more details.
  17 +require 'validates_as_cnpj'
... ...
vendor/plugins/validates_as_cnpj/lib/validates_as_cnpj.rb 0 → 100755
... ... @@ -0,0 +1,78 @@
  1 +# validates_as_cnpj.rb - implement the validation of cnpj
  2 +#
  3 +# Copyright (c) 2006 O.S. Systems
  4 +#
  5 +# Authors: André Ribeiro Camargo <andre@boaideia.inf.br>,
  6 +# Luis Gustavo S. Barreto <gustavo@ossystems.com.br>
  7 +#
  8 +# This program is free software; you can redistribute it and/or modify
  9 +# it under the terms of the GNU General Public License as published by
  10 +# the Free Software Foundation; either version 2 of the License, or
  11 +# (at your option) any later version.
  12 +#
  13 +# This program is distributed in the hope that it will be useful, but
  14 +# WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16 +# General Public License for more details.
  17 +module ValidaCNPJ
  18 + def self.valida_cnpj(cnpj = nil)
  19 + return nil if cnpj.nil?
  20 +
  21 + nulos = %w{11111111111111
  22 + 22222222222222
  23 + 33333333333333
  24 + 44444444444444
  25 + 55555555555555
  26 + 66666666666666
  27 + 77777777777777
  28 + 88888888888888
  29 + 99999999999999
  30 + 00000000000000}
  31 +
  32 + valor = cnpj.scan(/[0-9]/).collect{|x| x.to_i}
  33 +
  34 + fatores = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
  35 +
  36 + if valor.length == 14 and not nulos.member?(valor.to_s)
  37 + soma = 0
  38 + 0.upto(11) do |i|
  39 + soma += valor[i] * fatores[i]
  40 + end
  41 +
  42 + resto = soma % 11
  43 + dv1 = resto < 2 ? 0 : 11 - resto
  44 +
  45 + if dv1 == valor[12]
  46 + soma = 0
  47 + 0.upto(12) do |i|
  48 + soma += valor[i] * (i == 0 ? 6 : fatores[i-1])
  49 + end
  50 +
  51 + resto = soma % 11
  52 + dv2 = resto < 2 ? 0 : 11 - resto
  53 +
  54 + return true if dv2 == valor[13]
  55 + end
  56 + end
  57 + return nil
  58 + end
  59 +end
  60 +
  61 +module ActiveRecord
  62 + module Validations
  63 + module ClassMethods
  64 + def validates_as_cnpj(*attr_names)
  65 + configuration = { :message => "%{fn} is invalid" }
  66 + configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
  67 +
  68 + validates_each(attr_names, configuration) do |record, attr_name, value|
  69 + next if value.blank?
  70 +
  71 + unless ValidaCNPJ::valida_cnpj(value)
  72 + record.errors.add(attr_name, configuration[:message])
  73 + end
  74 + end
  75 + end
  76 + end
  77 + end
  78 +end
... ...
vendor/plugins/validates_as_cnpj/test/abstract_unit.rb 0 → 100755
... ... @@ -0,0 +1,7 @@
  1 +require '../../../../config/environment'
  2 +
  3 +$:.unshift(File.dirname(__FILE__) + '/../lib')
  4 +
  5 +require 'test/unit'
  6 +require 'active_record'
  7 +require 'connection'
... ...
vendor/plugins/validates_as_cnpj/test/cnpj_test.rb 0 → 100755
... ... @@ -0,0 +1,61 @@
  1 +
  2 +require File.dirname(__FILE__) + '/abstract_unit'
  3 +require '../lib/validates_as_cnpj'
  4 +
  5 +# Modelo
  6 +class CNPJData < ActiveRecord::Base
  7 + set_table_name "cnpjs"
  8 +
  9 + validates_as_cnpj :cnpj
  10 +end
  11 +
  12 +# Testes
  13 +class CNPJsTest < Test::Unit::TestCase
  14 + def test_aceita_cnpj_nulo_por_que_deve_ser_barrado_por_validates_presence_of
  15 + cnpj_valido = CNPJData.new(:id => 1, :cnpj => nil)
  16 +
  17 + assert cnpj_valido.save, "Nao salvou CNPJ nulo."
  18 + end
  19 +
  20 + def test_aceita_cnpj_vazio_por_que_deve_ser_barrado_por_validates_presence_of
  21 + cnpj_valido = CNPJData.new(:id => 1, :cnpj => "")
  22 +
  23 + assert cnpj_valido.save, "Nao salvou CNPJ vazio."
  24 + end
  25 +
  26 + def test_cnpj_incompleto
  27 + cnpj_invalido = CNPJData.new(:id => 1, :cnpj => "123")
  28 +
  29 + assert ( not cnpj_invalido.save ), "Salvou CNPJ incompleto."
  30 + end
  31 +
  32 + def test_cnpj_invalido_sem_pontuacao
  33 + cnpj_invalido = CNPJData.new(:id => 1, :cnpj => "00000000000000")
  34 +
  35 + assert ( not cnpj_invalido.save ), "Salvou CNPJ invalido."
  36 + end
  37 +
  38 + def test_cnpj_valido_sem_pontuacao
  39 + cnpj_valido = CNPJData.new(:id => 1, :cnpj => "04613251000100")
  40 +
  41 + assert cnpj_valido.save, "Nao salvou CNPJ valido."
  42 + end
  43 +
  44 + def test_cnpj_invalido_sem_pontuacao_com_digitos_verificadores_invertidos
  45 + cnpj_invalido = CNPJData.new(:id => 1, :cnpj => "10002574000125")
  46 +
  47 + assert ( not cnpj_invalido.save ), "Salvou CNPJ invalido."
  48 + end
  49 +
  50 + def test_cnpj_invalido_com_pontuacao
  51 + cnpj_invalido = CNPJData.new(:id => 1, :cnpj => "51.357.999/1110-98")
  52 +
  53 + assert ( not cnpj_invalido.save ), "CNPJ invalido foi salvo."
  54 + end
  55 +
  56 + def test_cnpj_valido_com_pontuacao
  57 + cnpj_valido = CNPJData.new(:id => 1, :cnpj => "94.132.024/0001-48")
  58 +
  59 + assert ( cnpj_valido.save ), "CNPJ valido nao foi salvo."
  60 + end
  61 +end
... ...
vendor/plugins/validates_as_cnpj/test/connection.rb 0 → 100755
... ... @@ -0,0 +1,6 @@
  1 +ActiveRecord::Base.establish_connection(
  2 + :adapter => "sqlite3",
  3 + :database => "db_test.db"
  4 +)
  5 +
  6 +load File.dirname(__FILE__) + "/fixtures/schema.rb"
... ...
vendor/plugins/validates_as_cnpj/test/fixtures/schema.rb 0 → 100755
... ... @@ -0,0 +1,6 @@
  1 +ActiveRecord::Schema.define do
  2 + create_table :cnpjs, :force => true do |t|
  3 + t.column :id, :string, :null => true
  4 + t.column :cnpj, :string, :null => true
  5 + end
  6 +end
... ...