Commit 48c34b8c997297ae26c177aa1d744686deabbcb3
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'master' into stable
Conflicts: public/javascripts/application.js
Showing
49 changed files
with
1784 additions
and
239 deletions
Show diff stats
DEVELOPMENT.md
@@ -19,6 +19,26 @@ | @@ -19,6 +19,26 @@ | ||
19 | * In the case the original author is a committer, he/she should feel free to | 19 | * In the case the original author is a committer, he/she should feel free to |
20 | commit directly if after 1 week nobody has provided any kind of feedback. | 20 | commit directly if after 1 week nobody has provided any kind of feedback. |
21 | 21 | ||
22 | + * Developers who are not committers should feel free to ping committers if | ||
23 | + they do not get feedback on their contributions after 1 week. | ||
24 | + | ||
25 | + * On GitLab, one can just add a comment to the merge request; one can also | ||
26 | + @-mention specific committers or other developers who have expertise on | ||
27 | + the area of the contribution. | ||
28 | + | ||
29 | + * Committers should follow the activity of the project, and try to help | ||
30 | + reviewing contributions from others as much as possible. | ||
31 | + | ||
32 | + * On GitLab one can get emails for all activity on a project by setting the | ||
33 | + [notification settings](https://gitlab.com/profile/notifications) to | ||
34 | + "watch". | ||
35 | + | ||
36 | + * Anyone can help by reviewing contributions. Committers are the only ones | ||
37 | + who can give the final approval to a contribution, but everyone is welcome | ||
38 | + to help with code review, testing, etc. | ||
39 | + | ||
40 | + * See note above about setting up notification on GitLab. | ||
41 | + | ||
22 | * Committers should feel free to push trivial (or urgent) changes directly. | 42 | * Committers should feel free to push trivial (or urgent) changes directly. |
23 | There are no strict rule on what makes a change trivial or urgent; committers | 43 | There are no strict rule on what makes a change trivial or urgent; committers |
24 | are expected to exercise good judgement on a case by case basis. | 44 | are expected to exercise good judgement on a case by case basis. |
app/helpers/application_helper.rb
@@ -433,19 +433,19 @@ module ApplicationHelper | @@ -433,19 +433,19 @@ module ApplicationHelper | ||
433 | end | 433 | end |
434 | 434 | ||
435 | def theme_site_title | 435 | def theme_site_title |
436 | - theme_include('site_title') | 436 | + @theme_site_title ||= theme_include 'site_title' |
437 | end | 437 | end |
438 | 438 | ||
439 | def theme_header | 439 | def theme_header |
440 | - theme_include('header') | 440 | + @theme_header ||= theme_include 'header' |
441 | end | 441 | end |
442 | 442 | ||
443 | def theme_footer | 443 | def theme_footer |
444 | - theme_include('footer') | 444 | + @theme_footer ||= theme_include 'footer' |
445 | end | 445 | end |
446 | 446 | ||
447 | def theme_extra_navigation | 447 | def theme_extra_navigation |
448 | - theme_include('navigation') | 448 | + @theme_extra_navigation ||= theme_include 'navigation' |
449 | end | 449 | end |
450 | 450 | ||
451 | def is_testing_theme | 451 | def is_testing_theme |
@@ -674,13 +674,14 @@ module ApplicationHelper | @@ -674,13 +674,14 @@ module ApplicationHelper | ||
674 | html.join "\n" | 674 | html.join "\n" |
675 | end | 675 | end |
676 | 676 | ||
677 | + def theme_javascript_src | ||
678 | + script = File.join theme_path, 'theme.js' | ||
679 | + script if File.exists? File.join(Rails.root, 'public', script) | ||
680 | + end | ||
681 | + | ||
677 | def theme_javascript_ng | 682 | def theme_javascript_ng |
678 | - script = File.join(theme_path, 'theme.js') | ||
679 | - if File.exists?(File.join(Rails.root, 'public', script)) | ||
680 | - javascript_include_tag script | ||
681 | - else | ||
682 | - nil | ||
683 | - end | 683 | + script = theme_javascript_src |
684 | + javascript_include_tag script if script | ||
684 | end | 685 | end |
685 | 686 | ||
686 | def file_field_or_thumbnail(label, image, i) | 687 | def file_field_or_thumbnail(label, image, i) |
app/helpers/layout_helper.rb
@@ -18,6 +18,8 @@ module LayoutHelper | @@ -18,6 +18,8 @@ module LayoutHelper | ||
18 | unless plugins_javascripts.empty? | 18 | unless plugins_javascripts.empty? |
19 | output += javascript_include_tag plugins_javascripts, :cache => "cache/plugins-#{Digest::MD5.hexdigest plugins_javascripts.to_s}" | 19 | output += javascript_include_tag plugins_javascripts, :cache => "cache/plugins-#{Digest::MD5.hexdigest plugins_javascripts.to_s}" |
20 | end | 20 | end |
21 | + output += theme_javascript_ng.to_s | ||
22 | + | ||
21 | output | 23 | output |
22 | end | 24 | end |
23 | 25 | ||
@@ -87,6 +89,10 @@ module LayoutHelper | @@ -87,6 +89,10 @@ module LayoutHelper | ||
87 | theme_path + '/style.css' | 89 | theme_path + '/style.css' |
88 | end | 90 | end |
89 | 91 | ||
92 | + def layout_template | ||
93 | + if profile then profile.layout_template else environment.layout_template end | ||
94 | + end | ||
95 | + | ||
90 | def addthis_javascript | 96 | def addthis_javascript |
91 | if NOOSFERO_CONF['addthis_enabled'] | 97 | if NOOSFERO_CONF['addthis_enabled'] |
92 | '<script src="https://s7.addthis.com/js/152/addthis_widget.js"></script>' | 98 | '<script src="https://s7.addthis.com/js/152/addthis_widget.js"></script>' |
app/models/person.rb
@@ -21,6 +21,12 @@ class Person < Profile | @@ -21,6 +21,12 @@ class Person < Profile | ||
21 | { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] } | 21 | { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] } |
22 | } | 22 | } |
23 | 23 | ||
24 | + scope :by_role, lambda { |roles| | ||
25 | + roles = [roles] unless roles.kind_of?(Array) | ||
26 | + { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', | ||
27 | +roles] } | ||
28 | + } | ||
29 | + | ||
24 | def has_permission_with_plugins?(permission, profile) | 30 | def has_permission_with_plugins?(permission, profile) |
25 | permissions = [has_permission_without_plugins?(permission, profile)] | 31 | permissions = [has_permission_without_plugins?(permission, profile)] |
26 | permissions += plugins.map do |plugin| | 32 | permissions += plugins.map do |plugin| |
app/models/profile.rb
@@ -146,8 +146,8 @@ class Profile < ActiveRecord::Base | @@ -146,8 +146,8 @@ class Profile < ActiveRecord::Base | ||
146 | alias_method_chain :count, :distinct | 146 | alias_method_chain :count, :distinct |
147 | end | 147 | end |
148 | 148 | ||
149 | - def members_by_role(role) | ||
150 | - Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', role.id]) | 149 | + def members_by_role(roles) |
150 | + Person.members_of(self).by_role(roles) | ||
151 | end | 151 | end |
152 | 152 | ||
153 | acts_as_having_boxes | 153 | acts_as_having_boxes |
app/views/layouts/application-ng.html.erb
@@ -72,10 +72,7 @@ | @@ -72,10 +72,7 @@ | ||
72 | <div id="navigation-end"></div> | 72 | <div id="navigation-end"></div> |
73 | </div><!-- end id="navigation" --> | 73 | </div><!-- end id="navigation" --> |
74 | <div id="content"> | 74 | <div id="content"> |
75 | - <div id="content-inner"> | ||
76 | - <%= insert_boxes(yield) %> | ||
77 | - <br style='clear: both'/> | ||
78 | - </div><!-- end id="content-inner" --> | 75 | + <%= render 'layouts/content' %> |
79 | </div><!-- end id="content" --> | 76 | </div><!-- end id="content" --> |
80 | </div><!-- end id="wrap-2" --> | 77 | </div><!-- end id="wrap-2" --> |
81 | </div><!-- end id="wrap-1" --> | 78 | </div><!-- end id="wrap-1" --> |
@@ -84,7 +81,6 @@ | @@ -84,7 +81,6 @@ | ||
84 | <%= theme_footer %> | 81 | <%= theme_footer %> |
85 | </div><!-- end id="theme-footer" --> | 82 | </div><!-- end id="theme-footer" --> |
86 | <%= noosfero_layout_features %> | 83 | <%= noosfero_layout_features %> |
87 | - <%= theme_javascript_ng %> | ||
88 | <%= addthis_javascript %> | 84 | <%= addthis_javascript %> |
89 | <%= | 85 | <%= |
90 | @plugins.dispatch(:body_ending).map do |content| | 86 | @plugins.dispatch(:body_ending).map do |content| |
config/application.rb
@@ -111,9 +111,7 @@ module Noosfero | @@ -111,9 +111,7 @@ module Noosfero | ||
111 | # Make sure the secret is at least 30 characters and all random, | 111 | # Make sure the secret is at least 30 characters and all random, |
112 | # no regular words or you'll be exposed to dictionary attacks. | 112 | # no regular words or you'll be exposed to dictionary attacks. |
113 | config.secret_token = noosfero_session_secret | 113 | config.secret_token = noosfero_session_secret |
114 | - config.action_dispatch.session = { | ||
115 | - :key => '_noosfero_session', | ||
116 | - } | 114 | + config.session_store :cookie_store, :key => '_noosfero_session' |
117 | 115 | ||
118 | config.time_zone = File.read('/etc/timezone').split("\n").first | 116 | config.time_zone = File.read('/etc/timezone').split("\n").first |
119 | config.active_record.default_timezone = :local | 117 | config.active_record.default_timezone = :local |
db/migrate/20140724134600_remove_environment_statistics_block_sooner.rb
0 → 100644
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | +class RemoveEnvironmentStatisticsBlockSooner < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + update("UPDATE blocks SET type = 'StatisticsBlock' WHERE type = 'EnvironmentStatisticsBlock'") | ||
4 | + end | ||
5 | + | ||
6 | + def self.down | ||
7 | + say("Nothing to undo (cannot recover the data)") | ||
8 | + end | ||
9 | +end |
debian/control
@@ -60,6 +60,11 @@ Depends: | @@ -60,6 +60,11 @@ Depends: | ||
60 | dbconfig-common, | 60 | dbconfig-common, |
61 | adduser, | 61 | adduser, |
62 | exim4 | mail-transport-agent, | 62 | exim4 | mail-transport-agent, |
63 | +# to minimize upgrade issues: | ||
64 | + ruby-feedparser (>= 0.7-3~), | ||
65 | + ruby-eventmachine (>= 0.12.10-4~), | ||
66 | + ruby-rack (>= 1.4.5-2~), | ||
67 | + ruby-tzinfo (>= 1.1.0-2~), | ||
63 | ${misc:Depends} | 68 | ${misc:Depends} |
64 | Recommends: | 69 | Recommends: |
65 | postgresql, | 70 | postgresql, |
etc/noosfero/varnish-noosfero.vcl
@@ -10,6 +10,13 @@ sub vcl_recv { | @@ -10,6 +10,13 @@ sub vcl_recv { | ||
10 | } | 10 | } |
11 | } | 11 | } |
12 | 12 | ||
13 | +sub vcl_deliver { | ||
14 | + # Force clients to aways hit the server again for HTML pages | ||
15 | + if (resp.http.Content-Type ~ "^text/html") { | ||
16 | + set resp.http.Cache-Control = "no-cache"; | ||
17 | + } | ||
18 | +} | ||
19 | + | ||
13 | sub vcl_error { | 20 | sub vcl_error { |
14 | set obj.http.Content-Type = "text/html; charset=utf-8"; | 21 | set obj.http.Content-Type = "text/html; charset=utf-8"; |
15 | 22 |
lib/noosfero.rb
@@ -70,16 +70,6 @@ module Noosfero | @@ -70,16 +70,6 @@ module Noosfero | ||
70 | end | 70 | end |
71 | end | 71 | end |
72 | 72 | ||
73 | - def self.term(t) | ||
74 | - self.terminology.get(t) | ||
75 | - end | ||
76 | - def self.terminology | ||
77 | - @terminology ||= Noosfero::Terminology::Default.instance | ||
78 | - end | ||
79 | - def self.terminology=(term) | ||
80 | - @terminology = term | ||
81 | - end | ||
82 | - | ||
83 | def self.url_options | 73 | def self.url_options |
84 | case Rails.env | 74 | case Rails.env |
85 | when 'development' | 75 | when 'development' |
lib/noosfero/plugin.rb
@@ -88,18 +88,29 @@ class Noosfero::Plugin | @@ -88,18 +88,29 @@ class Noosfero::Plugin | ||
88 | # This is a generic method that initialize any possible filter defined by a | 88 | # This is a generic method that initialize any possible filter defined by a |
89 | # plugin to a specific controller | 89 | # plugin to a specific controller |
90 | def load_plugin_filters(plugin) | 90 | def load_plugin_filters(plugin) |
91 | - plugin_methods = plugin.instance_methods.select {|m| m.to_s.end_with?('_filters')} | ||
92 | - plugin_methods.each do |plugin_method| | ||
93 | - controller_class = plugin_method.to_s.gsub('_filters', '').camelize.constantize | ||
94 | - filters = plugin.new.send(plugin_method) | ||
95 | - filters = [filters] if !filters.kind_of?(Array) | ||
96 | - | ||
97 | - filters.each do |plugin_filter| | ||
98 | - filter_method = (plugin.name.underscore.gsub('/','_') + '_' + plugin_filter[:method_name]).to_sym | ||
99 | - controller_class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {})) | ||
100 | - controller_class.send(:define_method, filter_method) do | ||
101 | - instance_eval(&plugin_filter[:block]) if environment.plugin_enabled?(plugin) | ||
102 | - end | 91 | + Rails.configuration.to_prepare do |
92 | + filters = plugin.new.send 'application_controller_filters' rescue [] | ||
93 | + Noosfero::Plugin.add_controller_filters ApplicationController, plugin, filters | ||
94 | + | ||
95 | + plugin_methods = plugin.instance_methods.select {|m| m.to_s.end_with?('_filters')} | ||
96 | + plugin_methods.each do |plugin_method| | ||
97 | + controller_class = plugin_method.to_s.gsub('_filters', '').camelize.constantize | ||
98 | + | ||
99 | + filters = plugin.new.send(plugin_method) | ||
100 | + Noosfero::Plugin.add_controller_filters controller_class, plugin, filters | ||
101 | + end | ||
102 | + end | ||
103 | + end | ||
104 | + | ||
105 | + def add_controller_filters(controller_class, plugin, filters) | ||
106 | + unless filters.is_a?(Array) | ||
107 | + filters = [filters] | ||
108 | + end | ||
109 | + filters.each do |plugin_filter| | ||
110 | + filter_method = (plugin.name.underscore.gsub('/','_') + '_' + plugin_filter[:method_name]).to_sym | ||
111 | + controller_class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {})) | ||
112 | + controller_class.send(:define_method, filter_method) do | ||
113 | + instance_exec(&plugin_filter[:block]) if environment.plugin_enabled?(plugin) | ||
103 | end | 114 | end |
104 | end | 115 | end |
105 | end | 116 | end |
lib/tasks/release.rake
@@ -137,7 +137,17 @@ EOF | @@ -137,7 +137,17 @@ EOF | ||
137 | new_version += '~rc1' | 137 | new_version += '~rc1' |
138 | end | 138 | end |
139 | else | 139 | else |
140 | - new_version.sub!(/~rc[0-9]+/, '') | 140 | + if new_version =~ /~rc\d+/ |
141 | + new_version.sub!(/~rc[0-9]+/, '') | ||
142 | + else | ||
143 | + components = new_version.split('.').map(&:to_i) | ||
144 | + if components.size < 3 | ||
145 | + components << 1 | ||
146 | + else | ||
147 | + components[-1] += 1 | ||
148 | + end | ||
149 | + new_version = components.join('.') | ||
150 | + end | ||
141 | end | 151 | end |
142 | 152 | ||
143 | puts "Current version: #{$version}" | 153 | puts "Current version: #{$version}" |
lib/unifreire_terminology.rb
@@ -1,44 +0,0 @@ | @@ -1,44 +0,0 @@ | ||
1 | -require 'noosfero/terminology' | ||
2 | - | ||
3 | -class UnifreireTerminology < Noosfero::Terminology::Custom | ||
4 | - | ||
5 | - def initialize | ||
6 | - # NOTE: the hash values must be marked for translation!! | ||
7 | - super({ | ||
8 | - 'Enterprises' => N_('Institutions'), | ||
9 | - 'enterprises' => N_('institutions'), | ||
10 | - 'The enterprises where this user works.' => N_('The institution where this user belongs.'), | ||
11 | - 'A block that displays your enterprises' => N_('A block that displays your institutions.'), | ||
12 | - 'All enterprises' => N_('All institutions'), | ||
13 | - 'Disable search for enterprises' => N_('Disable search for institutions'), | ||
14 | - 'One enterprise' => N_('One institution'), | ||
15 | - '%{num} enterprises' => N_('%{num} institutions'), | ||
16 | - 'Favorite Enterprises' => N_('Favorite Institutions'), | ||
17 | - 'This user\'s favorite enterprises.' => N_('This user\'s favorite institutions'), | ||
18 | - 'A block that displays your favorite enterprises' => N_('A block that displays your favorite institutions'), | ||
19 | - 'All favorite enterprises' => N_('All favorite institutions'), | ||
20 | - 'A search for enterprises by products selled and local' => N_('A search for institutions by products selled and local'), | ||
21 | - 'Edit message for disabled enterprises' => N_('Edit message for disabled institutions'), | ||
22 | - 'Add favorite enterprise' => N_('Add favorite institution'), | ||
23 | - 'Validation info is the information the enterprises will see about how your organization processes the enterprises validations it receives: validation methodology, restrictions to the types of enterprises the organization validates etc.' => N_('Validation info is the information the institutions will see about how your organization processes the institutions validations it receives: validation methodology, restrictions to the types of institutions the organization validates etc.'), | ||
24 | - 'Here are all <b>%s</b>\'s enterprises.' => N_('Here are all <b>%s</b>\'s institutions.'), | ||
25 | - 'Here are all <b>%s</b>\'s favorite enterprises.' => N_('Here are all <b>%s</b>\'s favorite institutions.'), | ||
26 | - 'Favorite Enterprises' => N_('Favorite Institutions'), | ||
27 | - 'Enterprises in "%s"' => N_('Institutions in "%s"'), | ||
28 | - 'Register a new Enterprise' => N_('Register a new Institution'), | ||
29 | - 'Events' => N_('Schedule'), | ||
30 | - 'Manage enterprise fields' => N_('Manage institutions fields'), | ||
31 | - "%s's enterprises" => N_("%s's institutions"), | ||
32 | - 'Activate your enterprise' => N_('Activate your institution'), | ||
33 | - 'Enterprise activation code' => N_('Institution activation code'), | ||
34 | - 'Disable activation of enterprises' => N_('Disable activation of institutions'), | ||
35 | - "%s's favorite enterprises" => N_("%s's favorite institutions"), | ||
36 | - 'Disable Enterprise' => N_('Disable Institution'), | ||
37 | - 'Enable Enterprise' => N_('Enable Institution'), | ||
38 | - 'Enterprise Validation' => N_('Institution Validation'), | ||
39 | - 'Enterprise Info and settings' => N_('Institution Info and settings'), | ||
40 | - 'Enterprises are disabled when created' => N_('Institutions are disabled when created'), | ||
41 | - }) | ||
42 | - end | ||
43 | - | ||
44 | -end |
lib/zen3_terminology.rb
@@ -1,88 +0,0 @@ | @@ -1,88 +0,0 @@ | ||
1 | -require 'noosfero/terminology' | ||
2 | - | ||
3 | -class Zen3Terminology < Noosfero::Terminology::Custom | ||
4 | - | ||
5 | - def initialize | ||
6 | - # NOTE: the hash values must be marked for translation!! | ||
7 | - super({ | ||
8 | - 'My Home Page' => N_('My ePortfolio'), | ||
9 | - 'Homepage' => N_('ePortfolio'), | ||
10 | - 'Communities' => N_('Groups'), | ||
11 | - 'communities' => N_('groups'), | ||
12 | - 'A block that displays your communities' => N_('A block that displays your groups'), | ||
13 | - 'A block that displays your friends' => N_('A block that displays your contacts'), | ||
14 | - 'The communities in which the user is a member' => N_('The groups in which the user is a member'), | ||
15 | - 'All communities' => N_('All groups'), | ||
16 | - 'Community' => N_('Group'), | ||
17 | - 'One community' => N_('One group'), | ||
18 | - '%{num} communities' => N_('%{num} groups'), | ||
19 | - 'Disable search for communities' => N_('Disable search for groups'), | ||
20 | - 'Enterprises' => N_('Organizations'), | ||
21 | - 'enterprises' => N_('organizations'), | ||
22 | - 'The enterprises where this user works.' => N_('The organizations where this user works.'), | ||
23 | - 'A block that displays your enterprises' => N_('A block that displays your organizations.'), | ||
24 | - 'All enterprises' => N_('All organizations'), | ||
25 | - 'Disable search for enterprises' => N_('Disable search for organizations'), | ||
26 | - 'One enterprise' => N_('One organization'), | ||
27 | - '%{num} enterprises' => N_('%{num} organizations'), | ||
28 | - 'Favorite Enterprises' => N_('Favorite Organizations'), | ||
29 | - 'This user\'s favorite enterprises.' => N_('This user\'s favorite organizations'), | ||
30 | - 'A block that displays your favorite enterprises' => N_('A block that displays your favorite organizations'), | ||
31 | - 'All favorite enterprises' => N_('All favorite organizations'), | ||
32 | - 'A search for enterprises by products selled and local' => N_('A search for organizations by products selled and local'), | ||
33 | - 'Edit message for disabled enterprises' => N_('Edit message for disabled organizations'), | ||
34 | - 'Add enterprise as favorite' => N_('Add organization as favorite'), | ||
35 | - 'Validation info is the information the enterprises will see about how your organization processes the enterprises validations it receives: validation methodology, restrictions to the types of enterprises the organization validates etc.' => N_('Validation info is the information the organizations will see about how your organization processes the organizations validations it receives: validation methodology, restrictions to the types of organizations the organization validates etc.'), | ||
36 | - 'Here are all <b>%s</b>\'s enterprises.' => N_('Here all all <b>%s</b>\'s organizations.'), | ||
37 | - 'Here are all <b>%s</b>\'s favorite enterprises.' => N_('Here are all <b>%s</b>\'s favorite organizations.'), | ||
38 | - 'Favorite Enterprises' => N_('Favorite Organizations'), | ||
39 | - 'Enterprises in "%s"' => N_('Organizations in "%s"'), | ||
40 | - 'Register a new Enterprise' => N_('Register a new organization'), | ||
41 | - 'One friend' => N_('One contact'), | ||
42 | - '%s friends' => N_('%s contacts'), | ||
43 | - '%s communities' => N_('%s groups'), | ||
44 | - 'Are you sure you want to remove %s from your friends list?' => N_('Are you sure you want to remove %s from your contacts list?'), | ||
45 | - 'Note that %s will still have you as a friend, unless he/she also wants to remove you from his/her friend list.' => N_('Note that %s will still have you as a contact, unless he/she also wants to remove you from his/her contact list.'), | ||
46 | - 'Yes, I want to remove %s from my friend list' => N_('Yes, I want to remove %s from my contact list'), | ||
47 | - 'Adding %s as a friend' => N_('Adding %s as a contact'), | ||
48 | - 'Are you sure you want to add %s as your friend?' => N_('Are you sure you want to add %s as your contact?'), | ||
49 | - 'Note that %s will need to accept being added as your friend.' => N_('Note that %s will need to accept being added as your contact.'), | ||
50 | - 'Classify your new friend %s: ' => N_('Classify your new contact %s: '), | ||
51 | - 'Yes, I want to add %s as my friend' => N_('Yes, I want to add %s as my contact'), | ||
52 | - 'Manage friends' => N_('Manage contacts'), | ||
53 | - 'Add friend' => N_('Add contact'), | ||
54 | - 'Removing friend: %s' => N_('Removing friend: %s'), | ||
55 | - 'Clicking on this button will remove your friend relation with %s.' => N_('Clicking on this button will remove your contact relation with %s.'), | ||
56 | - 'You have no friends yet.' => N_('You have no contacts yet.'), | ||
57 | - '%s\'s friends' => N_('%s\'s contacts'), | ||
58 | - 'Here are all <b>%s</b>\'s friends.' => N_('Here are all <b>%s</b>\'s contacts.'), | ||
59 | - 'Friends' => N_('Contacts'), | ||
60 | - 'Creating new community' => N_('Creating new group'), | ||
61 | - 'Do you want to join this community?' => N_('Do you want to join this group?'), | ||
62 | - 'Activate your enterprise' => N_('Activate your organization'), | ||
63 | - 'Enterprise activation code' => N_('Organization activation code'), | ||
64 | - 'Disable activation of enterprises' => N_('Disable activation of organizations'), | ||
65 | - 'Manage community fields' => N_('Manage group fields'), | ||
66 | - 'Create a new community' => N_('Create a new group'), | ||
67 | - 'Preferred domain name:' => N_('Choose your host community:'), | ||
68 | - 'My communities' => N_('My groups'), | ||
69 | - 'Community Info and settings' => N_('Group Info and Settings'), | ||
70 | - '{#} community' => N_('{#} group'), | ||
71 | - '{#} communities' => N_('{#} groups'), | ||
72 | - '{#} enterprise' => N_('{#} organization'), | ||
73 | - '{#} enterprises' => N_('{#} organizations'), | ||
74 | - '{#} friend' => N_('{#} contact'), | ||
75 | - '{#} friends' => N_('{#} contacts'), | ||
76 | - "%s's favorite enterprises" => N_("%s's favorite organizations"), | ||
77 | - 'Disable Enterprise' => N_('Disable Organization'), | ||
78 | - 'Enable Enterprise' => N_('Enable Organization'), | ||
79 | - 'Enterprise Validation' => N_('Organization Validation'), | ||
80 | - 'Enterprise Info and settings' => N_('Organization Info and settings'), | ||
81 | - 'Choose the communities you want to join and/or create your own.' => N_('Choose the groups you want to join and/or create your own.'), | ||
82 | - 'New community' => N_('New group'), | ||
83 | - "Tags are important to new users, they'll be able to find your new community more easily." => N_("Tags are important to new users, they'll be able to find your new group more easily."), | ||
84 | - 'Enterprises are disabled when created' => N_('Organizations are disabled when created'), | ||
85 | - }) | ||
86 | - end | ||
87 | - | ||
88 | -end |
@@ -0,0 +1,52 @@ | @@ -0,0 +1,52 @@ | ||
1 | +class PjaxPlugin < Noosfero::Plugin | ||
2 | + | ||
3 | + def self.plugin_name | ||
4 | + I18n.t('pjax_plugin.lib.plugin.name') | ||
5 | + end | ||
6 | + | ||
7 | + def self.plugin_description | ||
8 | + I18n.t('pjax_plugin.lib.plugin.description') | ||
9 | + end | ||
10 | + | ||
11 | + def stylesheet? | ||
12 | + true | ||
13 | + end | ||
14 | + | ||
15 | + def js_files | ||
16 | + ['jquery.pjax.js', 'patchwork.js', 'loading-overlay', 'pjax', ].map{ |j| "javascripts/#{j}" } | ||
17 | + end | ||
18 | + | ||
19 | + def head_ending | ||
20 | + #TODO: add pjax meta | ||
21 | + end | ||
22 | + | ||
23 | + def body_beginning | ||
24 | + lambda{ render 'pjax_layouts/load_state_script' } | ||
25 | + end | ||
26 | + | ||
27 | + PjaxCheck = lambda do | ||
28 | + return unless request.headers['X-PJAX'] | ||
29 | + # raise makes pjax fallback to a regular request | ||
30 | + raise "Pjax can't be used here" if params[:controller] == 'account' | ||
31 | + | ||
32 | + @pjax = true | ||
33 | + @pjax_loaded_themes = request.headers['X-PJAX-Themes'].to_s.split(',') || [] | ||
34 | + | ||
35 | + unless self.respond_to? :get_layout_with_pjax | ||
36 | + self.class.send :define_method, :get_layout_with_pjax do | ||
37 | + if @pjax then 'pjax' else get_layout_without_pjax end | ||
38 | + end | ||
39 | + self.class.alias_method_chain :get_layout, :pjax | ||
40 | + end | ||
41 | + end | ||
42 | + | ||
43 | + def application_controller_filters | ||
44 | + [{ | ||
45 | + :type => 'before_filter', :method_name => 'pjax_check', | ||
46 | + :options => {}, :block => PjaxCheck, | ||
47 | + }] | ||
48 | + end | ||
49 | + | ||
50 | + protected | ||
51 | + | ||
52 | +end |
617 KB
3.4 KB
@@ -0,0 +1,838 @@ | @@ -0,0 +1,838 @@ | ||
1 | +// jquery.pjax.js | ||
2 | +// copyright chris wanstrath | ||
3 | +// https://github.com/defunkt/jquery-pjax | ||
4 | + | ||
5 | +(function($){ | ||
6 | + | ||
7 | +// When called on a container with a selector, fetches the href with | ||
8 | +// ajax into the container or with the data-pjax attribute on the link | ||
9 | +// itself. | ||
10 | +// | ||
11 | +// Tries to make sure the back button and ctrl+click work the way | ||
12 | +// you'd expect. | ||
13 | +// | ||
14 | +// Exported as $.fn.pjax | ||
15 | +// | ||
16 | +// Accepts a jQuery ajax options object that may include these | ||
17 | +// pjax specific options: | ||
18 | +// | ||
19 | +// | ||
20 | +// container - Where to stick the response body. Usually a String selector. | ||
21 | +// $(container).html(xhr.responseBody) | ||
22 | +// (default: current jquery context) | ||
23 | +// push - Whether to pushState the URL. Defaults to true (of course). | ||
24 | +// replace - Want to use replaceState instead? That's cool. | ||
25 | +// | ||
26 | +// For convenience the second parameter can be either the container or | ||
27 | +// the options object. | ||
28 | +// | ||
29 | +// Returns the jQuery object | ||
30 | +function fnPjax(selector, container, options) { | ||
31 | + var context = this | ||
32 | + return this.on('click.pjax', selector, function(event) { | ||
33 | + var opts = $.extend({}, optionsFor(container, options)) | ||
34 | + if (!opts.container) | ||
35 | + opts.container = $(this).attr('data-pjax') || context | ||
36 | + handleClick(event, opts) | ||
37 | + }) | ||
38 | +} | ||
39 | + | ||
40 | +// Public: pjax on click handler | ||
41 | +// | ||
42 | +// Exported as $.pjax.click. | ||
43 | +// | ||
44 | +// event - "click" jQuery.Event | ||
45 | +// options - pjax options | ||
46 | +// | ||
47 | +// Examples | ||
48 | +// | ||
49 | +// $(document).on('click', 'a', $.pjax.click) | ||
50 | +// // is the same as | ||
51 | +// $(document).pjax('a') | ||
52 | +// | ||
53 | +// $(document).on('click', 'a', function(event) { | ||
54 | +// var container = $(this).closest('[data-pjax-container]') | ||
55 | +// $.pjax.click(event, container) | ||
56 | +// }) | ||
57 | +// | ||
58 | +// Returns nothing. | ||
59 | +function handleClick(event, container, options) { | ||
60 | + options = optionsFor(container, options) | ||
61 | + | ||
62 | + var link = event.currentTarget | ||
63 | + | ||
64 | + if (link.tagName.toUpperCase() !== 'A') | ||
65 | + throw "$.fn.pjax or $.pjax.click requires an anchor element" | ||
66 | + | ||
67 | + // Middle click, cmd click, and ctrl click should open | ||
68 | + // links in a new tab as normal. | ||
69 | + if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) | ||
70 | + return | ||
71 | + | ||
72 | + // Ignore cross origin links | ||
73 | + if ( location.protocol !== link.protocol || location.hostname !== link.hostname ) | ||
74 | + return | ||
75 | + | ||
76 | + // Ignore anchors on the same page | ||
77 | + if (link.hash && link.href.replace(link.hash, '') === | ||
78 | + location.href.replace(location.hash, '')) | ||
79 | + return | ||
80 | + | ||
81 | + // Ignore empty anchor "foo.html#" | ||
82 | + if (link.href === location.href + '#') | ||
83 | + return | ||
84 | + | ||
85 | + var defaults = { | ||
86 | + url: link.href, | ||
87 | + container: $(link).attr('data-pjax'), | ||
88 | + target: link | ||
89 | + } | ||
90 | + | ||
91 | + var opts = $.extend({}, defaults, options) | ||
92 | + var clickEvent = $.Event('pjax:click') | ||
93 | + $(link).trigger(clickEvent, [opts]) | ||
94 | + | ||
95 | + if (!clickEvent.isDefaultPrevented()) { | ||
96 | + pjax(opts) | ||
97 | + event.preventDefault() | ||
98 | + } | ||
99 | +} | ||
100 | + | ||
101 | +// Public: pjax on form submit handler | ||
102 | +// | ||
103 | +// Exported as $.pjax.submit | ||
104 | +// | ||
105 | +// event - "click" jQuery.Event | ||
106 | +// options - pjax options | ||
107 | +// | ||
108 | +// Examples | ||
109 | +// | ||
110 | +// $(document).on('submit', 'form', function(event) { | ||
111 | +// var container = $(this).closest('[data-pjax-container]') | ||
112 | +// $.pjax.submit(event, container) | ||
113 | +// }) | ||
114 | +// | ||
115 | +// Returns nothing. | ||
116 | +function handleSubmit(event, container, options) { | ||
117 | + options = optionsFor(container, options) | ||
118 | + | ||
119 | + var form = event.currentTarget | ||
120 | + | ||
121 | + if (form.tagName.toUpperCase() !== 'FORM') | ||
122 | + throw "$.pjax.submit requires a form element" | ||
123 | + | ||
124 | + var defaults = { | ||
125 | + type: form.method.toUpperCase(), | ||
126 | + url: form.action, | ||
127 | + data: $(form).serializeArray(), | ||
128 | + container: $(form).attr('data-pjax'), | ||
129 | + target: form | ||
130 | + } | ||
131 | + | ||
132 | + pjax($.extend({}, defaults, options)) | ||
133 | + | ||
134 | + event.preventDefault() | ||
135 | +} | ||
136 | + | ||
137 | +// Loads a URL with ajax, puts the response body inside a container, | ||
138 | +// then pushState()'s the loaded URL. | ||
139 | +// | ||
140 | +// Works just like $.ajax in that it accepts a jQuery ajax | ||
141 | +// settings object (with keys like url, type, data, etc). | ||
142 | +// | ||
143 | +// Accepts these extra keys: | ||
144 | +// | ||
145 | +// container - Where to stick the response body. | ||
146 | +// $(container).html(xhr.responseBody) | ||
147 | +// push - Whether to pushState the URL. Defaults to true (of course). | ||
148 | +// replace - Want to use replaceState instead? That's cool. | ||
149 | +// | ||
150 | +// Use it just like $.ajax: | ||
151 | +// | ||
152 | +// var xhr = $.pjax({ url: this.href, container: '#main' }) | ||
153 | +// console.log( xhr.readyState ) | ||
154 | +// | ||
155 | +// Returns whatever $.ajax returns. | ||
156 | +function pjax(options) { | ||
157 | + options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options) | ||
158 | + | ||
159 | + if ($.isFunction(options.url)) { | ||
160 | + options.url = options.url() | ||
161 | + } | ||
162 | + | ||
163 | + var target = options.target | ||
164 | + | ||
165 | + var hash = parseURL(options.url).hash | ||
166 | + | ||
167 | + var context = options.context = findContainerFor(options.container) | ||
168 | + | ||
169 | + // We want the browser to maintain two separate internal caches: one | ||
170 | + // for pjax'd partial page loads and one for normal page loads. | ||
171 | + // Without adding this secret parameter, some browsers will often | ||
172 | + // confuse the two. | ||
173 | + if (!options.data) options.data = {} | ||
174 | + options.data._pjax = context.selector | ||
175 | + | ||
176 | + function fire(type, args) { | ||
177 | + var event = $.Event(type, { relatedTarget: target }) | ||
178 | + context.trigger(event, args) | ||
179 | + return !event.isDefaultPrevented() | ||
180 | + } | ||
181 | + | ||
182 | + var timeoutTimer | ||
183 | + | ||
184 | + options.beforeSend = function(xhr, settings) { | ||
185 | + // No timeout for non-GET requests | ||
186 | + // Its not safe to request the resource again with a fallback method. | ||
187 | + if (settings.type !== 'GET') { | ||
188 | + settings.timeout = 0 | ||
189 | + } | ||
190 | + | ||
191 | + xhr.setRequestHeader('X-PJAX', 'true') | ||
192 | + xhr.setRequestHeader('X-PJAX-Container', context.selector) | ||
193 | + | ||
194 | + if (!fire('pjax:beforeSend', [xhr, settings])) | ||
195 | + return false | ||
196 | + | ||
197 | + if (settings.timeout > 0) { | ||
198 | + timeoutTimer = setTimeout(function() { | ||
199 | + if (fire('pjax:timeout', [xhr, options])) | ||
200 | + xhr.abort('timeout') | ||
201 | + }, settings.timeout) | ||
202 | + | ||
203 | + // Clear timeout setting so jquerys internal timeout isn't invoked | ||
204 | + settings.timeout = 0 | ||
205 | + } | ||
206 | + | ||
207 | + options.requestUrl = parseURL(settings.url).href | ||
208 | + } | ||
209 | + | ||
210 | + options.complete = function(xhr, textStatus) { | ||
211 | + if (timeoutTimer) | ||
212 | + clearTimeout(timeoutTimer) | ||
213 | + | ||
214 | + fire('pjax:complete', [xhr, textStatus, options]) | ||
215 | + | ||
216 | + fire('pjax:end', [xhr, options]) | ||
217 | + } | ||
218 | + | ||
219 | + options.error = function(xhr, textStatus, errorThrown) { | ||
220 | + var container = extractContainer("", xhr, options) | ||
221 | + | ||
222 | + var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]) | ||
223 | + if (options.type == 'GET' && textStatus !== 'abort' && allowed) { | ||
224 | + locationReplace(container.url) | ||
225 | + } | ||
226 | + } | ||
227 | + | ||
228 | + options.success = function(data, status, xhr) { | ||
229 | + // If $.pjax.defaults.version is a function, invoke it first. | ||
230 | + // Otherwise it can be a static string. | ||
231 | + var currentVersion = (typeof $.pjax.defaults.version === 'function') ? | ||
232 | + $.pjax.defaults.version() : | ||
233 | + $.pjax.defaults.version | ||
234 | + | ||
235 | + var latestVersion = xhr.getResponseHeader('X-PJAX-Version') | ||
236 | + | ||
237 | + var container = extractContainer(data, xhr, options) | ||
238 | + | ||
239 | + // If there is a layout version mismatch, hard load the new url | ||
240 | + if (currentVersion && latestVersion && currentVersion !== latestVersion) { | ||
241 | + locationReplace(container.url) | ||
242 | + return | ||
243 | + } | ||
244 | + | ||
245 | + // If the new response is missing a body, hard load the page | ||
246 | + if (!container.contents) { | ||
247 | + locationReplace(container.url) | ||
248 | + return | ||
249 | + } | ||
250 | + | ||
251 | + pjax.state = { | ||
252 | + id: options.id || uniqueId(), | ||
253 | + url: container.url, | ||
254 | + title: container.title, | ||
255 | + container: context.selector, | ||
256 | + fragment: options.fragment, | ||
257 | + timeout: options.timeout | ||
258 | + } | ||
259 | + | ||
260 | + if (options.push || options.replace) { | ||
261 | + window.history.replaceState(pjax.state, container.title, container.url) | ||
262 | + } | ||
263 | + | ||
264 | + // Clear out any focused controls before inserting new page contents. | ||
265 | + document.activeElement.blur() | ||
266 | + | ||
267 | + if (container.title) document.title = container.title | ||
268 | + context.html(container.contents) | ||
269 | + | ||
270 | + // FF bug: Won't autofocus fields that are inserted via JS. | ||
271 | + // This behavior is incorrect. So if theres no current focus, autofocus | ||
272 | + // the last field. | ||
273 | + // | ||
274 | + // http://www.w3.org/html/wg/drafts/html/master/forms.html | ||
275 | + var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0] | ||
276 | + if (autofocusEl && document.activeElement !== autofocusEl) { | ||
277 | + autofocusEl.focus(); | ||
278 | + } | ||
279 | + | ||
280 | + executeScriptTags(container.scripts) | ||
281 | + | ||
282 | + // Scroll to top by default | ||
283 | + if (typeof options.scrollTo === 'number') | ||
284 | + $(window).scrollTop(options.scrollTo) | ||
285 | + | ||
286 | + // If the URL has a hash in it, make sure the browser | ||
287 | + // knows to navigate to the hash. | ||
288 | + if ( hash !== '' ) { | ||
289 | + // Avoid using simple hash set here. Will add another history | ||
290 | + // entry. Replace the url with replaceState and scroll to target | ||
291 | + // by hand. | ||
292 | + // | ||
293 | + // window.location.hash = hash | ||
294 | + var url = parseURL(container.url) | ||
295 | + url.hash = hash | ||
296 | + | ||
297 | + pjax.state.url = url.href | ||
298 | + window.history.replaceState(pjax.state, container.title, url.href) | ||
299 | + | ||
300 | + var target = $(url.hash) | ||
301 | + if (target.length) $(window).scrollTop(target.offset().top) | ||
302 | + } | ||
303 | + | ||
304 | + fire('pjax:success', [data, status, xhr, options]) | ||
305 | + } | ||
306 | + | ||
307 | + | ||
308 | + // Initialize pjax.state for the initial page load. Assume we're | ||
309 | + // using the container and options of the link we're loading for the | ||
310 | + // back button to the initial page. This ensures good back button | ||
311 | + // behavior. | ||
312 | + if (!pjax.state) { | ||
313 | + pjax.state = { | ||
314 | + id: uniqueId(), | ||
315 | + url: window.location.href, | ||
316 | + title: document.title, | ||
317 | + container: context.selector, | ||
318 | + fragment: options.fragment, | ||
319 | + timeout: options.timeout | ||
320 | + } | ||
321 | + window.history.replaceState(pjax.state, document.title) | ||
322 | + } | ||
323 | + | ||
324 | + // Cancel the current request if we're already pjaxing | ||
325 | + var xhr = pjax.xhr | ||
326 | + if ( xhr && xhr.readyState < 4) { | ||
327 | + xhr.onreadystatechange = $.noop | ||
328 | + xhr.abort() | ||
329 | + } | ||
330 | + | ||
331 | + pjax.options = options | ||
332 | + var xhr = pjax.xhr = $.ajax(options) | ||
333 | + | ||
334 | + if (xhr.readyState > 0) { | ||
335 | + if (options.push && !options.replace) { | ||
336 | + // Cache current container element before replacing it | ||
337 | + cachePush(pjax.state.id, context.clone().contents()) | ||
338 | + | ||
339 | + window.history.pushState(null, "", stripPjaxParam(options.requestUrl)) | ||
340 | + } | ||
341 | + | ||
342 | + fire('pjax:start', [xhr, options]) | ||
343 | + fire('pjax:send', [xhr, options]) | ||
344 | + } | ||
345 | + | ||
346 | + return pjax.xhr | ||
347 | +} | ||
348 | + | ||
349 | +// Public: Reload current page with pjax. | ||
350 | +// | ||
351 | +// Returns whatever $.pjax returns. | ||
352 | +function pjaxReload(container, options) { | ||
353 | + var defaults = { | ||
354 | + url: window.location.href, | ||
355 | + push: false, | ||
356 | + replace: true, | ||
357 | + scrollTo: false | ||
358 | + } | ||
359 | + | ||
360 | + return pjax($.extend(defaults, optionsFor(container, options))) | ||
361 | +} | ||
362 | + | ||
363 | +// Internal: Hard replace current state with url. | ||
364 | +// | ||
365 | +// Work for around WebKit | ||
366 | +// https://bugs.webkit.org/show_bug.cgi?id=93506 | ||
367 | +// | ||
368 | +// Returns nothing. | ||
369 | +function locationReplace(url) { | ||
370 | + window.history.replaceState(null, "", "#") | ||
371 | + window.location.replace(url) | ||
372 | +} | ||
373 | + | ||
374 | + | ||
375 | +var initialPop = true | ||
376 | +var initialURL = window.location.href | ||
377 | +var initialState = window.history.state | ||
378 | + | ||
379 | +// Initialize $.pjax.state if possible | ||
380 | +// Happens when reloading a page and coming forward from a different | ||
381 | +// session history. | ||
382 | +if (initialState && initialState.container) { | ||
383 | + pjax.state = initialState | ||
384 | +} | ||
385 | + | ||
386 | +// Non-webkit browsers don't fire an initial popstate event | ||
387 | +if ('state' in window.history) { | ||
388 | + initialPop = false | ||
389 | +} | ||
390 | + | ||
391 | +// popstate handler takes care of the back and forward buttons | ||
392 | +// | ||
393 | +// You probably shouldn't use pjax on pages with other pushState | ||
394 | +// stuff yet. | ||
395 | +function onPjaxPopstate(event) { | ||
396 | + var state = event.state | ||
397 | + | ||
398 | + if (state && state.container) { | ||
399 | + // When coming forward from a separate history session, will get an | ||
400 | + // initial pop with a state we are already at. Skip reloading the current | ||
401 | + // page. | ||
402 | + if (initialPop && initialURL == state.url) return | ||
403 | + | ||
404 | + // If popping back to the same state, just skip. | ||
405 | + // Could be clicking back from hashchange rather than a pushState. | ||
406 | + if (pjax.state.id === state.id) return | ||
407 | + | ||
408 | + var container = $(state.container) | ||
409 | + if (container.length) { | ||
410 | + var direction, contents = cacheMapping[state.id] | ||
411 | + | ||
412 | + if (pjax.state) { | ||
413 | + // Since state ids always increase, we can deduce the history | ||
414 | + // direction from the previous state. | ||
415 | + direction = pjax.state.id < state.id ? 'forward' : 'back' | ||
416 | + | ||
417 | + // Cache current container before replacement and inform the | ||
418 | + // cache which direction the history shifted. | ||
419 | + cachePop(direction, pjax.state.id, container.clone().contents()) | ||
420 | + } | ||
421 | + | ||
422 | + var popstateEvent = $.Event('pjax:popstate', { | ||
423 | + state: state, | ||
424 | + direction: direction | ||
425 | + }) | ||
426 | + container.trigger(popstateEvent) | ||
427 | + | ||
428 | + var options = { | ||
429 | + id: state.id, | ||
430 | + url: state.url, | ||
431 | + container: container, | ||
432 | + push: false, | ||
433 | + fragment: state.fragment, | ||
434 | + timeout: state.timeout, | ||
435 | + scrollTo: false | ||
436 | + } | ||
437 | + | ||
438 | + if (contents) { | ||
439 | + container.trigger('pjax:start', [null, options]) | ||
440 | + | ||
441 | + if (state.title) document.title = state.title | ||
442 | + container.html(contents) | ||
443 | + pjax.state = state | ||
444 | + | ||
445 | + container.trigger('pjax:end', [null, options]) | ||
446 | + } else { | ||
447 | + pjax(options) | ||
448 | + } | ||
449 | + | ||
450 | + // Force reflow/relayout before the browser tries to restore the | ||
451 | + // scroll position. | ||
452 | + container[0].offsetHeight | ||
453 | + } else { | ||
454 | + locationReplace(location.href) | ||
455 | + } | ||
456 | + } | ||
457 | + initialPop = false | ||
458 | +} | ||
459 | + | ||
460 | +// Fallback version of main pjax function for browsers that don't | ||
461 | +// support pushState. | ||
462 | +// | ||
463 | +// Returns nothing since it retriggers a hard form submission. | ||
464 | +function fallbackPjax(options) { | ||
465 | + var url = $.isFunction(options.url) ? options.url() : options.url, | ||
466 | + method = options.type ? options.type.toUpperCase() : 'GET' | ||
467 | + | ||
468 | + var form = $('<form>', { | ||
469 | + method: method === 'GET' ? 'GET' : 'POST', | ||
470 | + action: url, | ||
471 | + style: 'display:none' | ||
472 | + }) | ||
473 | + | ||
474 | + if (method !== 'GET' && method !== 'POST') { | ||
475 | + form.append($('<input>', { | ||
476 | + type: 'hidden', | ||
477 | + name: '_method', | ||
478 | + value: method.toLowerCase() | ||
479 | + })) | ||
480 | + } | ||
481 | + | ||
482 | + var data = options.data | ||
483 | + if (typeof data === 'string') { | ||
484 | + $.each(data.split('&'), function(index, value) { | ||
485 | + var pair = value.split('=') | ||
486 | + form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]})) | ||
487 | + }) | ||
488 | + } else if (typeof data === 'object') { | ||
489 | + for (key in data) | ||
490 | + form.append($('<input>', {type: 'hidden', name: key, value: data[key]})) | ||
491 | + } | ||
492 | + | ||
493 | + $(document.body).append(form) | ||
494 | + form.submit() | ||
495 | +} | ||
496 | + | ||
497 | +// Internal: Generate unique id for state object. | ||
498 | +// | ||
499 | +// Use a timestamp instead of a counter since ids should still be | ||
500 | +// unique across page loads. | ||
501 | +// | ||
502 | +// Returns Number. | ||
503 | +function uniqueId() { | ||
504 | + return (new Date).getTime() | ||
505 | +} | ||
506 | + | ||
507 | +// Internal: Strips _pjax param from url | ||
508 | +// | ||
509 | +// url - String | ||
510 | +// | ||
511 | +// Returns String. | ||
512 | +function stripPjaxParam(url) { | ||
513 | + return url | ||
514 | + .replace(/\?_pjax=[^&]+&?/, '?') | ||
515 | + .replace(/_pjax=[^&]+&?/, '') | ||
516 | + .replace(/[\?&]$/, '') | ||
517 | +} | ||
518 | + | ||
519 | +// Internal: Parse URL components and returns a Locationish object. | ||
520 | +// | ||
521 | +// url - String URL | ||
522 | +// | ||
523 | +// Returns HTMLAnchorElement that acts like Location. | ||
524 | +function parseURL(url) { | ||
525 | + var a = document.createElement('a') | ||
526 | + a.href = url | ||
527 | + return a | ||
528 | +} | ||
529 | + | ||
530 | +// Internal: Build options Object for arguments. | ||
531 | +// | ||
532 | +// For convenience the first parameter can be either the container or | ||
533 | +// the options object. | ||
534 | +// | ||
535 | +// Examples | ||
536 | +// | ||
537 | +// optionsFor('#container') | ||
538 | +// // => {container: '#container'} | ||
539 | +// | ||
540 | +// optionsFor('#container', {push: true}) | ||
541 | +// // => {container: '#container', push: true} | ||
542 | +// | ||
543 | +// optionsFor({container: '#container', push: true}) | ||
544 | +// // => {container: '#container', push: true} | ||
545 | +// | ||
546 | +// Returns options Object. | ||
547 | +function optionsFor(container, options) { | ||
548 | + // Both container and options | ||
549 | + if ( container && options ) | ||
550 | + options.container = container | ||
551 | + | ||
552 | + // First argument is options Object | ||
553 | + else if ( $.isPlainObject(container) ) | ||
554 | + options = container | ||
555 | + | ||
556 | + // Only container | ||
557 | + else | ||
558 | + options = {container: container} | ||
559 | + | ||
560 | + // Find and validate container | ||
561 | + if (options.container) | ||
562 | + options.container = findContainerFor(options.container) | ||
563 | + | ||
564 | + return options | ||
565 | +} | ||
566 | + | ||
567 | +// Internal: Find container element for a variety of inputs. | ||
568 | +// | ||
569 | +// Because we can't persist elements using the history API, we must be | ||
570 | +// able to find a String selector that will consistently find the Element. | ||
571 | +// | ||
572 | +// container - A selector String, jQuery object, or DOM Element. | ||
573 | +// | ||
574 | +// Returns a jQuery object whose context is `document` and has a selector. | ||
575 | +function findContainerFor(container) { | ||
576 | + container = $(container) | ||
577 | + | ||
578 | + if ( !container.length ) { | ||
579 | + throw "no pjax container for " + container.selector | ||
580 | + } else if ( container.selector !== '' && container.context === document ) { | ||
581 | + return container | ||
582 | + } else if ( container.attr('id') ) { | ||
583 | + return $('#' + container.attr('id')) | ||
584 | + } else { | ||
585 | + throw "cant get selector for pjax container!" | ||
586 | + } | ||
587 | +} | ||
588 | + | ||
589 | +// Internal: Filter and find all elements matching the selector. | ||
590 | +// | ||
591 | +// Where $.fn.find only matches descendants, findAll will test all the | ||
592 | +// top level elements in the jQuery object as well. | ||
593 | +// | ||
594 | +// elems - jQuery object of Elements | ||
595 | +// selector - String selector to match | ||
596 | +// | ||
597 | +// Returns a jQuery object. | ||
598 | +function findAll(elems, selector) { | ||
599 | + return elems.filter(selector).add(elems.find(selector)); | ||
600 | +} | ||
601 | + | ||
602 | +function parseHTML(html) { | ||
603 | + return $.parseHTML(html, document, true) | ||
604 | +} | ||
605 | + | ||
606 | +// Internal: Extracts container and metadata from response. | ||
607 | +// | ||
608 | +// 1. Extracts X-PJAX-URL header if set | ||
609 | +// 2. Extracts inline <title> tags | ||
610 | +// 3. Builds response Element and extracts fragment if set | ||
611 | +// | ||
612 | +// data - String response data | ||
613 | +// xhr - XHR response | ||
614 | +// options - pjax options Object | ||
615 | +// | ||
616 | +// Returns an Object with url, title, and contents keys. | ||
617 | +function extractContainer(data, xhr, options) { | ||
618 | + var obj = {} | ||
619 | + | ||
620 | + // Prefer X-PJAX-URL header if it was set, otherwise fallback to | ||
621 | + // using the original requested url. | ||
622 | + obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.requestUrl) | ||
623 | + | ||
624 | + // Attempt to parse response html into elements | ||
625 | + if (/<html/i.test(data)) { | ||
626 | + var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0])) | ||
627 | + var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0])) | ||
628 | + } else { | ||
629 | + var $head = $body = $(parseHTML(data)) | ||
630 | + } | ||
631 | + | ||
632 | + // If response data is empty, return fast | ||
633 | + if ($body.length === 0) | ||
634 | + return obj | ||
635 | + | ||
636 | + // If there's a <title> tag in the header, use it as | ||
637 | + // the page's title. | ||
638 | + obj.title = findAll($head, 'title').last().text() | ||
639 | + | ||
640 | + if (options.fragment) { | ||
641 | + // If they specified a fragment, look for it in the response | ||
642 | + // and pull it out. | ||
643 | + if (options.fragment === 'body') { | ||
644 | + var $fragment = $body | ||
645 | + } else { | ||
646 | + var $fragment = findAll($body, options.fragment).first() | ||
647 | + } | ||
648 | + | ||
649 | + if ($fragment.length) { | ||
650 | + obj.contents = $fragment.contents() | ||
651 | + | ||
652 | + // If there's no title, look for data-title and title attributes | ||
653 | + // on the fragment | ||
654 | + if (!obj.title) | ||
655 | + obj.title = $fragment.attr('title') || $fragment.data('title') | ||
656 | + } | ||
657 | + | ||
658 | + } else if (!/<html/i.test(data)) { | ||
659 | + obj.contents = $body | ||
660 | + } | ||
661 | + | ||
662 | + // Clean up any <title> tags | ||
663 | + if (obj.contents) { | ||
664 | + // Remove any parent title elements | ||
665 | + obj.contents = obj.contents.not(function() { return $(this).is('title') }) | ||
666 | + | ||
667 | + // Then scrub any titles from their descendants | ||
668 | + obj.contents.find('title').remove() | ||
669 | + | ||
670 | + // Gather all script[src] elements | ||
671 | + //obj.scripts = findAll(obj.contents, 'script[src]').remove() | ||
672 | + obj.contents = obj.contents.not(obj.scripts) | ||
673 | + } | ||
674 | + | ||
675 | + // Trim any whitespace off the title | ||
676 | + if (obj.title) obj.title = $.trim(obj.title) | ||
677 | + | ||
678 | + return obj | ||
679 | +} | ||
680 | + | ||
681 | +// Load an execute scripts using standard script request. | ||
682 | +// | ||
683 | +// Avoids jQuery's traditional $.getScript which does a XHR request and | ||
684 | +// globalEval. | ||
685 | +// | ||
686 | +// scripts - jQuery object of script Elements | ||
687 | +// | ||
688 | +// Returns nothing. | ||
689 | +function executeScriptTags(scripts) { | ||
690 | + if (!scripts) return | ||
691 | + | ||
692 | + var existingScripts = $('script[src]') | ||
693 | + | ||
694 | + scripts.each(function() { | ||
695 | + var src = this.src | ||
696 | + var matchedScripts = existingScripts.filter(function() { | ||
697 | + return this.src === src | ||
698 | + }) | ||
699 | + if (matchedScripts.length) return | ||
700 | + | ||
701 | + var script = document.createElement('script') | ||
702 | + script.type = $(this).attr('type') | ||
703 | + script.src = $(this).attr('src') | ||
704 | + document.head.appendChild(script) | ||
705 | + }) | ||
706 | +} | ||
707 | + | ||
708 | +// Internal: History DOM caching class. | ||
709 | +var cacheMapping = {} | ||
710 | +var cacheForwardStack = [] | ||
711 | +var cacheBackStack = [] | ||
712 | + | ||
713 | +// Push previous state id and container contents into the history | ||
714 | +// cache. Should be called in conjunction with `pushState` to save the | ||
715 | +// previous container contents. | ||
716 | +// | ||
717 | +// id - State ID Number | ||
718 | +// value - DOM Element to cache | ||
719 | +// | ||
720 | +// Returns nothing. | ||
721 | +function cachePush(id, value) { | ||
722 | + cacheMapping[id] = value | ||
723 | + cacheBackStack.push(id) | ||
724 | + | ||
725 | + // Remove all entires in forward history stack after pushing | ||
726 | + // a new page. | ||
727 | + while (cacheForwardStack.length) | ||
728 | + delete cacheMapping[cacheForwardStack.shift()] | ||
729 | + | ||
730 | + // Trim back history stack to max cache length. | ||
731 | + while (cacheBackStack.length > pjax.defaults.maxCacheLength) | ||
732 | + delete cacheMapping[cacheBackStack.shift()] | ||
733 | +} | ||
734 | + | ||
735 | +// Shifts cache from directional history cache. Should be | ||
736 | +// called on `popstate` with the previous state id and container | ||
737 | +// contents. | ||
738 | +// | ||
739 | +// direction - "forward" or "back" String | ||
740 | +// id - State ID Number | ||
741 | +// value - DOM Element to cache | ||
742 | +// | ||
743 | +// Returns nothing. | ||
744 | +function cachePop(direction, id, value) { | ||
745 | + var pushStack, popStack | ||
746 | + cacheMapping[id] = value | ||
747 | + | ||
748 | + if (direction === 'forward') { | ||
749 | + pushStack = cacheBackStack | ||
750 | + popStack = cacheForwardStack | ||
751 | + } else { | ||
752 | + pushStack = cacheForwardStack | ||
753 | + popStack = cacheBackStack | ||
754 | + } | ||
755 | + | ||
756 | + pushStack.push(id) | ||
757 | + if (id = popStack.pop()) | ||
758 | + delete cacheMapping[id] | ||
759 | +} | ||
760 | + | ||
761 | +// Public: Find version identifier for the initial page load. | ||
762 | +// | ||
763 | +// Returns String version or undefined. | ||
764 | +function findVersion() { | ||
765 | + return $('meta').filter(function() { | ||
766 | + var name = $(this).attr('http-equiv') | ||
767 | + return name && name.toUpperCase() === 'X-PJAX-VERSION' | ||
768 | + }).attr('content') | ||
769 | +} | ||
770 | + | ||
771 | +// Install pjax functions on $.pjax to enable pushState behavior. | ||
772 | +// | ||
773 | +// Does nothing if already enabled. | ||
774 | +// | ||
775 | +// Examples | ||
776 | +// | ||
777 | +// $.pjax.enable() | ||
778 | +// | ||
779 | +// Returns nothing. | ||
780 | +function enable() { | ||
781 | + $.fn.pjax = fnPjax | ||
782 | + $.pjax = pjax | ||
783 | + $.pjax.enable = $.noop | ||
784 | + $.pjax.disable = disable | ||
785 | + $.pjax.click = handleClick | ||
786 | + $.pjax.submit = handleSubmit | ||
787 | + $.pjax.reload = pjaxReload | ||
788 | + $.pjax.defaults = { | ||
789 | + timeout: 650, | ||
790 | + push: true, | ||
791 | + replace: false, | ||
792 | + type: 'GET', | ||
793 | + dataType: 'html', | ||
794 | + scrollTo: 0, | ||
795 | + maxCacheLength: 20, | ||
796 | + version: findVersion | ||
797 | + } | ||
798 | + $(window).on('popstate.pjax', onPjaxPopstate) | ||
799 | +} | ||
800 | + | ||
801 | +// Disable pushState behavior. | ||
802 | +// | ||
803 | +// This is the case when a browser doesn't support pushState. It is | ||
804 | +// sometimes useful to disable pushState for debugging on a modern | ||
805 | +// browser. | ||
806 | +// | ||
807 | +// Examples | ||
808 | +// | ||
809 | +// $.pjax.disable() | ||
810 | +// | ||
811 | +// Returns nothing. | ||
812 | +function disable() { | ||
813 | + $.fn.pjax = function() { return this } | ||
814 | + $.pjax = fallbackPjax | ||
815 | + $.pjax.enable = enable | ||
816 | + $.pjax.disable = $.noop | ||
817 | + $.pjax.click = $.noop | ||
818 | + $.pjax.submit = $.noop | ||
819 | + $.pjax.reload = function() { window.location.reload() } | ||
820 | + | ||
821 | + $(window).off('popstate.pjax', onPjaxPopstate) | ||
822 | +} | ||
823 | + | ||
824 | + | ||
825 | +// Add the state property to jQuery's event object so we can use it in | ||
826 | +// $(window).bind('popstate') | ||
827 | +if ( $.inArray('state', $.event.props) < 0 ) | ||
828 | + $.event.props.push('state') | ||
829 | + | ||
830 | +// Is pjax supported by this browser? | ||
831 | +$.support.pjax = | ||
832 | + window.history && window.history.pushState && window.history.replaceState && | ||
833 | + // pushState isn't reliable on iOS until 5. | ||
834 | + !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/) | ||
835 | + | ||
836 | +$.support.pjax ? enable() : disable() | ||
837 | + | ||
838 | +})(jQuery); |
@@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
1 | +if (typeof loading_overlay === 'undefined') { | ||
2 | + | ||
3 | +// block user actions while making a post. Also indicate the network transaction | ||
4 | +loading_overlay = { | ||
5 | + | ||
6 | + show: function (selector) { | ||
7 | + var element = jQuery(selector); | ||
8 | + var overlay = jQuery('<div>', { | ||
9 | + class: 'loading-overlay', | ||
10 | + css: { | ||
11 | + width: element.outerWidth(), | ||
12 | + height: element.outerHeight(), | ||
13 | + left: element.position().left, | ||
14 | + top: element.position().top, | ||
15 | + marginLeft: parseFloat(element.css('margin-left')), | ||
16 | + marginTop: parseFloat(element.css('margin-top')), | ||
17 | + marginRight: parseFloat(element.css('margin-right')), | ||
18 | + marginBottom: parseFloat(element.css('margin-bottom')), | ||
19 | + }, | ||
20 | + }).appendTo(element).get(0); | ||
21 | + | ||
22 | + overlay.dest = element; | ||
23 | + element.loading_overlay = overlay; | ||
24 | + }, | ||
25 | + | ||
26 | + hide: function (selector) { | ||
27 | + var element = jQuery(selector); | ||
28 | + var overlay = element.find('.loading-overlay'); | ||
29 | + overlay.remove(); | ||
30 | + }, | ||
31 | + | ||
32 | +}; | ||
33 | + | ||
34 | +} |
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
1 | +var patch = (function () { | ||
2 | + /*jshint evil: true */ | ||
3 | + | ||
4 | + "use strict"; | ||
5 | + | ||
6 | + var global = new Function("return this;")(), // Get a reference to the global object | ||
7 | + fnProps = Object.getOwnPropertyNames(Function); // Get the own ("static") properties of the Function constructor | ||
8 | + | ||
9 | + return function (original, originalRef, patches) { | ||
10 | + | ||
11 | + var ref = global[originalRef] = original, // Maintain a reference to the original constructor as a new property on the global object | ||
12 | + args = [], | ||
13 | + newRef, // This will be the new patched constructor | ||
14 | + i; | ||
15 | + | ||
16 | + patches.called = patches.called || originalRef; // If we are not patching static calls just pass them through to the original function | ||
17 | + | ||
18 | + for (i = 0; i < original.length; i++) { // Match the arity of the original constructor | ||
19 | + args[i] = "a" + i; // Give the arguments a name (native constructors don't care, but user-defined ones will break otherwise) | ||
20 | + } | ||
21 | + | ||
22 | + if (patches.constructed) { // This string is evaluated to create the patched constructor body in the case that we are patching newed calls | ||
23 | + args.push("'use strict'; return (!!this ? " + patches.constructed + " : " + patches.called + ").apply(null, arguments);"); | ||
24 | + } else { // This string is evaluated to create the patched constructor body in the case that we are only patching static calls | ||
25 | + args.push("'use strict'; return (!!this ? new (Function.prototype.bind.apply(" + originalRef + ", [{}].concat([].slice.call(arguments))))() : " + patches.called + ".apply(null, arguments));"); | ||
26 | + } | ||
27 | + | ||
28 | + newRef = new (Function.prototype.bind.apply(Function, [{}].concat(args)))(); // Create a new function to wrap the patched constructor | ||
29 | + newRef.prototype = original.prototype; // Keep a reference to the original prototype to ensure instances of the patch appear as instances of the original | ||
30 | + newRef.prototype.constructor = newRef; // Ensure the constructor of patched instances is the patched constructor | ||
31 | + | ||
32 | + Object.getOwnPropertyNames(ref).forEach(function (property) { // Add any "static" properties of the original constructor to the patched one | ||
33 | + if (fnProps.indexOf(property) < 0) { // Don't include static properties of Function since the patched constructor will already have them | ||
34 | + newRef[property] = ref[property]; | ||
35 | + } | ||
36 | + }); | ||
37 | + | ||
38 | + return newRef; // Return the patched constructor | ||
39 | + }; | ||
40 | + | ||
41 | +}()); | ||
0 | \ No newline at end of file | 42 | \ No newline at end of file |
@@ -0,0 +1,179 @@ | @@ -0,0 +1,179 @@ | ||
1 | + | ||
2 | +pjax = { | ||
3 | + | ||
4 | + states: {}, | ||
5 | + current_state: null, | ||
6 | + initial_state: null, | ||
7 | + | ||
8 | + themes: {}, | ||
9 | + | ||
10 | + load: function() { | ||
11 | + var target = jQuery('#content'); | ||
12 | + var content = jQuery('#content-inner'); | ||
13 | + var loadingTarget = jQuery('#content'); | ||
14 | + | ||
15 | + var container = '.pjax-container'; | ||
16 | + target.addClass('pjax-container'); | ||
17 | + | ||
18 | + jQuery(document).pjax('a', container); | ||
19 | + | ||
20 | + jQuery(document).on('pjax:beforeSend', function(event, xhr, settings) { | ||
21 | + var themes = jQuery.map(pjax.themes, function(theme) { return theme.id }).join(','); | ||
22 | + xhr.setRequestHeader('X-PJAX-Themes', themes); | ||
23 | + }); | ||
24 | + | ||
25 | + jQuery(document).on('pjax:send', function(event) { | ||
26 | + /* initial state is only initialized after the first navigation, | ||
27 | + * so we do associate it here */ | ||
28 | + if (!pjax.states[jQuery.pjax.state.id]) | ||
29 | + pjax.states[jQuery.pjax.state.id] = pjax.initial_state; | ||
30 | + | ||
31 | + loading_overlay.show(loadingTarget); | ||
32 | + }); | ||
33 | + jQuery(document).on('pjax:complete', function(event) { | ||
34 | + loading_overlay.hide(loadingTarget); | ||
35 | + }); | ||
36 | + | ||
37 | + jQuery(document).on('pjax:popstate', function(event) { | ||
38 | + pjax.popstate(event.state, event.direction); | ||
39 | + }); | ||
40 | + | ||
41 | + jQuery(document).on('pjax:timeout', function(event) { | ||
42 | + // Prevent default timeout redirection behavior | ||
43 | + event.preventDefault(); | ||
44 | + }); | ||
45 | + | ||
46 | + pjax.patch.document_write(); | ||
47 | + //pjax.patch.xhr(); | ||
48 | + }, | ||
49 | + | ||
50 | + update: function(state, from_state) { | ||
51 | + if (!from_state) | ||
52 | + from_state = this.current_state || this.initial_state; | ||
53 | + | ||
54 | + if (state.layout_template != from_state.layout_template) { | ||
55 | + var lt_css = jQuery('head link[href^="/designs/templates"]'); | ||
56 | + lt_css.attr('href', lt_css.attr('href').replace(/templates\/.+\/stylesheets/, 'templates/'+state.layout_template+'/stylesheets')); | ||
57 | + } | ||
58 | + | ||
59 | + if (state.theme.id != from_state.theme.id) | ||
60 | + this.update_theme(state, from_state); | ||
61 | + | ||
62 | + document.body.className = state.body_classes; | ||
63 | + | ||
64 | + render_all_jquery_ui_widgets(); | ||
65 | + | ||
66 | + userDataCallback(noosfero.user_data); | ||
67 | + | ||
68 | + // theme's update dependent on content. must be last thing to run | ||
69 | + if (state.theme_update_js) | ||
70 | + jQuery.globalEval(state.theme_update_js); | ||
71 | + | ||
72 | + pjax.current_state = state; | ||
73 | + }, | ||
74 | + | ||
75 | + update_theme: function(state, from_state) { | ||
76 | + // wait for the new theme css load | ||
77 | + this.loading.show(function() { | ||
78 | + return !pjax.css_loaded('/designs/themes/'+state.theme.id+'/style.css'); | ||
79 | + }); | ||
80 | + | ||
81 | + var css = jQuery('head link[href^="/designs/themes/'+from_state.theme.id+'/style"]'); | ||
82 | + css.attr('href', css.attr('href').replace(/themes\/.+\/style/, 'themes/'+state.theme.id+'/style')); | ||
83 | + | ||
84 | + jQuery('head link[rel="shortcut icon"]').attr('href', state.theme.favicon); | ||
85 | + | ||
86 | + jQuery('#theme-header').html(state.theme.header); | ||
87 | + jQuery('#site-title').html(state.theme.site_title); | ||
88 | + jQuery('#navigation ul').html(state.theme.extra_navigation); | ||
89 | + jQuery('#theme-footer').html(state.theme.footer); | ||
90 | + | ||
91 | + jQuery('head script[src^="/designs/themes/'+from_state.theme.id+'/theme.js"]').remove(); | ||
92 | + if (state.theme.js_src) { | ||
93 | + var script = document.createElement('script'); | ||
94 | + script.type = 'text/javascript', script.src = state.theme.js_src; | ||
95 | + document.head.appendChild(script); | ||
96 | + } | ||
97 | + }, | ||
98 | + | ||
99 | + popstate: function(state, direction) { | ||
100 | + state = pjax.states[state.id]; | ||
101 | + var from_state = pjax.states[jQuery.pjax.state.id]; | ||
102 | + pjax.update(state, from_state); | ||
103 | + }, | ||
104 | + | ||
105 | + loading: { | ||
106 | + repeatCallback: null, | ||
107 | + | ||
108 | + show: function(repeatCallback) { | ||
109 | + this.repeatCallback = repeatCallback; | ||
110 | + this.gears().show(); | ||
111 | + this.pool(); | ||
112 | + }, | ||
113 | + | ||
114 | + pool: function() { | ||
115 | + setTimeout(this.timeout, 50); | ||
116 | + }, | ||
117 | + | ||
118 | + timeout: function() { | ||
119 | + var repeat = pjax.loading.repeatCallback(); | ||
120 | + if (repeat) | ||
121 | + pjax.loading.pool(); | ||
122 | + else | ||
123 | + pjax.loading.gears().hide(); | ||
124 | + }, | ||
125 | + | ||
126 | + gears: function() { | ||
127 | + var gears = jQuery('#pjax-loading-gears'); | ||
128 | + if (!gears.length) { | ||
129 | + gears = jQuery('<div>', { | ||
130 | + id: 'pjax-loading-gears', | ||
131 | + }); | ||
132 | + gears.appendTo(document.body); | ||
133 | + } | ||
134 | + | ||
135 | + return gears; | ||
136 | + }, | ||
137 | + }, | ||
138 | + | ||
139 | + css_loaded: function(path) { | ||
140 | + var found = false; | ||
141 | + for (index in document.styleSheets) { | ||
142 | + var stylesheet = document.styleSheets[index]; | ||
143 | + if (!stylesheet.href) | ||
144 | + continue; | ||
145 | + | ||
146 | + found = stylesheet.href.indexOf(path) != -1; | ||
147 | + if (found) | ||
148 | + break; | ||
149 | + } | ||
150 | + return found; | ||
151 | + }, | ||
152 | + | ||
153 | + patch: { | ||
154 | + | ||
155 | + document_write: function () { | ||
156 | + // document.write doesn't work after ready state | ||
157 | + document._write = document.write; | ||
158 | + document.write = function (data) { | ||
159 | + if (document.readyState != 'loading') | ||
160 | + content.append(data); | ||
161 | + else | ||
162 | + document._write(data); | ||
163 | + }; | ||
164 | + }, | ||
165 | + | ||
166 | + xhr: function () { | ||
167 | + XMLHttpRequest = patch(XMLHttpRequest, '_XMLHttpRequest', { | ||
168 | + constructed: function() { | ||
169 | + console.log('here') | ||
170 | + | ||
171 | + var args = [].slice.call(arguments); | ||
172 | + return new (Function.prototype.bind.apply(_XMLHttpRequest, [{}].concat(args))); | ||
173 | + }, | ||
174 | + }); | ||
175 | + }, | ||
176 | + | ||
177 | + }, | ||
178 | +}; | ||
179 | + |
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +.loading-overlay { | ||
2 | + position: absolute; | ||
3 | + background-image: url(/plugins/pjax/images/loading-overlay.gif); | ||
4 | + opacity: 0.1; | ||
5 | + z-index: 10; } | ||
6 | + | ||
7 | +#pjax-loading-gears { | ||
8 | + display: none; | ||
9 | + position: fixed; | ||
10 | + top: 0; | ||
11 | + left: 0; | ||
12 | + right: 0; | ||
13 | + bottom: 0; | ||
14 | + z-index: 9999; | ||
15 | + background: white url(/plugins/pjax/images/loading-gears.gif) no-repeat center center; | ||
16 | + background-size: 5%; } |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +@import 'loading-overlay'; | ||
2 | + | ||
3 | +#pjax-loading-gears { | ||
4 | + display: none; //default | ||
5 | + position: fixed; | ||
6 | + top: 0; | ||
7 | + left: 0; | ||
8 | + right: 0; | ||
9 | + bottom: 0; | ||
10 | + z-index: 9999; | ||
11 | + background: white url(/plugins/pjax/images/loading-gears.gif) no-repeat center center; | ||
12 | + background-size: 5%; | ||
13 | +} | ||
14 | + |
plugins/pjax/vendor/plugins/xhr_status_except_pjax/init.rb
0 → 100644
@@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
1 | +class ActionDispatch::Request | ||
2 | + | ||
3 | + def xml_http_request_with_pjax? | ||
4 | + xml_http_request_without_pjax? and @env['HTTP_X_PJAX'].blank? | ||
5 | + end | ||
6 | + | ||
7 | +end | ||
8 | + | ||
9 | + ActionDispatch::Request.send :alias_method_chain, :xml_http_request?, :pjax | ||
10 | + ActionDispatch::Request.send :alias_method, :xhr?, :xml_http_request? | ||
11 | + |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +<% | ||
2 | + update_js = render(:file => "#{Rails.root}/public/designs/themes/#{current_theme}/pjax_update.js.erb").to_json rescue nil | ||
3 | +%> | ||
4 | + | ||
5 | +<title><%= h page_title %></title> | ||
6 | + | ||
7 | +<%= render :file => "#{Rails.root}/public/designs/themes/#{current_theme}/layouts/_content.html.erb" rescue | ||
8 | + render "layouts/content" %> | ||
9 | + | ||
10 | +<%= javascript_tag do %> | ||
11 | + <%= render 'pjax_shared/load_state.js' %> | ||
12 | + | ||
13 | + <% if update_js %> | ||
14 | + state.theme_update_js = <%= update_js %>; | ||
15 | + <% end %> | ||
16 | + | ||
17 | + pjax.update(state); | ||
18 | +<% end %> | ||
19 | + |
plugins/pjax/views/pjax_layouts/_load_state_script.html.erb
0 → 100644
@@ -0,0 +1,26 @@ | @@ -0,0 +1,26 @@ | ||
1 | +var theme_id = <%= current_theme.to_json %>; | ||
2 | + | ||
3 | +<% if @pjax_loaded_themes.blank? or not @pjax_loaded_themes.include? current_theme %> | ||
4 | + pjax.themes[theme_id] = { | ||
5 | + id: theme_id, | ||
6 | + favicon: '<%= image_path theme_favicon %>', | ||
7 | + header: '<%= escape_javascript theme_header %>', | ||
8 | + site_title: '<%= escape_javascript theme_site_title %>', | ||
9 | + extra_navigation: '<%= escape_javascript theme_extra_navigation %>', | ||
10 | + footer: '<%= escape_javascript theme_footer %>', | ||
11 | + js_src: <%= theme_javascript_src.to_json %>, | ||
12 | + }; | ||
13 | +<% end %> | ||
14 | + | ||
15 | +var state = { | ||
16 | + body_classes: <%= body_classes.to_json %>, | ||
17 | + layout_template: <%= layout_template.to_json %>, | ||
18 | + theme: pjax.themes[theme_id], | ||
19 | +}; | ||
20 | + | ||
21 | +if (jQuery.pjax.state) | ||
22 | + pjax.states[jQuery.pjax.state.id] = state; | ||
23 | + | ||
24 | +if (!pjax.initial_state) | ||
25 | + pjax.initial_state = state; | ||
26 | + |
@@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
1 | +README - Profile Members Headlines (ProfileMembersHeadlines Plugin) | ||
2 | +=================================================================== | ||
3 | + | ||
4 | +ProfileMembersHeadlines is a plugin that allow users to add a block that | ||
5 | +displays the most recent post from members with the defined roles. | ||
6 | + | ||
7 | +The user also can configure the limit of headlines. | ||
8 | + | ||
9 | +This block will be available for all layout columns of communities and enterprises. | ||
10 | + | ||
11 | +INSTALL | ||
12 | +======= | ||
13 | + | ||
14 | +Enable Plugin | ||
15 | +------------- | ||
16 | + | ||
17 | +Also, you need to enable ProfileMembersHeadlines Plugin at you Noosfero: | ||
18 | + | ||
19 | +cd <your_noosfero_dir> | ||
20 | +./script/noosfero-plugins enable profile_members_headlines | ||
21 | + | ||
22 | +Active Plugin | ||
23 | +------------- | ||
24 | + | ||
25 | +As a Noosfero administrator user, go to administrator panel: | ||
26 | + | ||
27 | +- Click on "Enable/disable plugins" option | ||
28 | +- Click on "Profile Members Headlines Plugin" checkbox | ||
29 | + | ||
30 | +Running DisplayContent tests | ||
31 | +-------------------- | ||
32 | + | ||
33 | +$ rake test:noosfero_plugins:profile_members_headlines_plugin | ||
34 | + | ||
35 | +LICENSE | ||
36 | +======= | ||
37 | + | ||
38 | +See Noosfero license. |
plugins/profile_members_headlines/lib/profile_members_headlines_block.rb
0 → 100644
@@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
1 | +class ProfileMembersHeadlinesBlock < Block | ||
2 | + | ||
3 | + settings_items :interval, :type => 'integer', :default => 10 | ||
4 | + settings_items :limit, :type => 'integer', :default => 10 | ||
5 | + settings_items :navigation, :type => 'boolean', :default => true | ||
6 | + settings_items :filtered_roles, :type => Array, :default => [] | ||
7 | + | ||
8 | + attr_accessible :interval, :limit, :navigation, :filtered_roles | ||
9 | + | ||
10 | + def self.description | ||
11 | + _('Display headlines from members of a community') | ||
12 | + end | ||
13 | + | ||
14 | + def help | ||
15 | + _('This block displays one post from members of a community.') | ||
16 | + end | ||
17 | + | ||
18 | + include Noosfero::Plugin::HotSpot | ||
19 | + | ||
20 | + def default_title | ||
21 | + _('Profile members headlines') | ||
22 | + end | ||
23 | + | ||
24 | + def filtered_roles=(array) | ||
25 | + self.settings[:filtered_roles] = array.map(&:to_i).select { |r| !r.to_i.zero? } | ||
26 | + end | ||
27 | + | ||
28 | + def authors_list | ||
29 | + result = owner.members_by_role(filtered_roles).public.includes([:image,:domains,:preferred_domain,:environment]).order('updated_at DESC') | ||
30 | + | ||
31 | + result.all(:limit => limit * 5).select { |p| p.has_headline? | ||
32 | +}.slice(0..limit-1) | ||
33 | + end | ||
34 | + | ||
35 | + def content(args={}) | ||
36 | + block = self | ||
37 | + members = authors_list | ||
38 | + proc do | ||
39 | + render :file => 'blocks/headlines', :locals => { :block => block, :members => members } | ||
40 | + end | ||
41 | + end | ||
42 | + | ||
43 | +end |
plugins/profile_members_headlines/lib/profile_members_headlines_plugin.rb
0 → 100644
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +require_dependency File.dirname(__FILE__) + '/profile_members_headlines_block' | ||
2 | +require 'ext/person' | ||
3 | + | ||
4 | +class ProfileMembersHeadlinesPlugin < Noosfero::Plugin | ||
5 | + | ||
6 | + def self.plugin_name | ||
7 | + "Profile Members Headlines Plugin" | ||
8 | + end | ||
9 | + | ||
10 | + def self.plugin_description | ||
11 | + _("A plugin that adds a block where you can display posts from members.") | ||
12 | + end | ||
13 | + | ||
14 | + def self.extra_blocks | ||
15 | + { ProfileMembersHeadlinesBlock => { :type => [Community], :position => | ||
16 | +['1'] }} | ||
17 | + end | ||
18 | + | ||
19 | + def stylesheet? | ||
20 | + true | ||
21 | + end | ||
22 | + | ||
23 | +end |
379 Bytes
@@ -0,0 +1,59 @@ | @@ -0,0 +1,59 @@ | ||
1 | +.profile-members-headlines-block { | ||
2 | + width: 100%; | ||
3 | +} | ||
4 | + | ||
5 | +.profile-members-headlines-block .headlines-container { | ||
6 | + height: 180px; | ||
7 | +} | ||
8 | + | ||
9 | +.profile-members-headlines-block .headlines-container .author, | ||
10 | +.profile-members-headlines-block .headlines-container .post { | ||
11 | + vertical-align: top; | ||
12 | + min-width: 150px; | ||
13 | +} | ||
14 | + | ||
15 | +.profile-members-headlines-block .headlines-container .author { | ||
16 | + position: absolute; | ||
17 | +} | ||
18 | + | ||
19 | +.profile-members-headlines-block .headlines-container .author p { | ||
20 | + text-align: center; | ||
21 | +} | ||
22 | + | ||
23 | +.profile-members-headlines-block .headlines-container .post { | ||
24 | + margin-left: 160px; | ||
25 | +} | ||
26 | + | ||
27 | +.profile-members-headlines-block #content .headlines-container h4 { | ||
28 | + margin-top: 15px; | ||
29 | + font-weight: bold; | ||
30 | +} | ||
31 | + | ||
32 | +.profile-members-headlines-block .headlines-container a { | ||
33 | + text-decoration: none; | ||
34 | +} | ||
35 | + | ||
36 | +.profile-members-headlines-block .headlines-container a:hover { | ||
37 | + text-decoration: underline; | ||
38 | +} | ||
39 | + | ||
40 | +.profile-members-headlines-block .headlines-container .post .date { | ||
41 | + margin-top: 15px; | ||
42 | +} | ||
43 | + | ||
44 | +.profile-members-headlines-block .headlines-block-pager { | ||
45 | + text-align: center; | ||
46 | +} | ||
47 | + | ||
48 | +.profile-members-headlines-block .headlines-block-pager a { | ||
49 | + padding: 0px 5px; | ||
50 | +} | ||
51 | + | ||
52 | +.profile-members-headlines-block .headlines-block-pager a.activeSlide { | ||
53 | + text-decoration: none; | ||
54 | +} | ||
55 | + | ||
56 | +.profile-members-headlines-block .headlines-block-pager a.activeSlide:visited, | ||
57 | +.profile-members-headlines-block .headlines-block-pager a.activeSlide { | ||
58 | + color: #000; | ||
59 | +} |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../test/test_helper' |
plugins/profile_members_headlines/test/unit/profile_members_headlines_block_test.rb
0 → 100644
@@ -0,0 +1,81 @@ | @@ -0,0 +1,81 @@ | ||
1 | +require 'test_helper' | ||
2 | + | ||
3 | +class ProfileMembersHeadlinesBlockTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + include Noosfero::Plugin::HotSpot | ||
6 | + | ||
7 | + def setup | ||
8 | + @environment = fast_create(Environment) | ||
9 | + @environment.enable_plugin(ProfileMembersHeadlinesPlugin) | ||
10 | + | ||
11 | + @member1 = fast_create(Person) | ||
12 | + @member2 = fast_create(Person) | ||
13 | + @community = fast_create(Community) | ||
14 | + community.add_member member1 | ||
15 | + community.add_member member2 | ||
16 | + end | ||
17 | + attr_accessor :environment, :community, :member1, :member2 | ||
18 | + | ||
19 | + should 'inherit from Block' do | ||
20 | + assert_kind_of Block, ProfileMembersHeadlinesBlock.new | ||
21 | + end | ||
22 | + | ||
23 | + should 'describe itself' do | ||
24 | + assert_not_equal Block.description, ProfileMembersHeadlinesBlock.description | ||
25 | + end | ||
26 | + | ||
27 | + should 'provide a default title' do | ||
28 | + assert_not_equal Block.new.default_title, ProfileMembersHeadlinesBlock.new.default_title | ||
29 | + end | ||
30 | + | ||
31 | + should 'not have authors if they have no blog' do | ||
32 | + block = ProfileMembersHeadlinesBlock.create | ||
33 | + block.stubs(:owner).returns(community) | ||
34 | + | ||
35 | + self.expects(:render).with(:file => 'blocks/headlines', :locals => { :block => block, :members => []}).returns('file-without-authors-and-headlines') | ||
36 | + assert_equal 'file-without-authors-and-headlines', instance_eval(&block.content) | ||
37 | + end | ||
38 | + | ||
39 | + should 'display headlines file' do | ||
40 | + block = ProfileMembersHeadlinesBlock.create | ||
41 | + block.stubs(:owner).returns(community) | ||
42 | + blog = fast_create(Blog, :profile_id => member1.id) | ||
43 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => member1.id, :parent_id => blog.id) | ||
44 | + self.expects(:render).with(:file => 'blocks/headlines', :locals => { :block => block, :members => []}).returns('file-with-authors-and-headlines') | ||
45 | + assert_equal 'file-with-authors-and-headlines', instance_eval(&block.content) | ||
46 | + end | ||
47 | + | ||
48 | + should 'select only authors with articles and selected roles to display' do | ||
49 | + role = Role.create!(:name => 'role1') | ||
50 | + community.affiliate(member1, role) | ||
51 | + block = ProfileMembersHeadlinesBlock.new(:limit => 1, :filtered_roles => [role.id]) | ||
52 | + block.expects(:owner).returns(community) | ||
53 | + blog = fast_create(Blog, :profile_id => member1.id) | ||
54 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => member1.id, :parent_id => blog.id) | ||
55 | + assert_equal [member1], block.authors_list | ||
56 | + end | ||
57 | + | ||
58 | + should 'not select private authors to display' do | ||
59 | + block = ProfileMembersHeadlinesBlock.new(:limit => 1) | ||
60 | + block.expects(:owner).returns(community) | ||
61 | + private_author = fast_create(Person, :public_profile => false) | ||
62 | + blog = fast_create(Blog, :profile_id => private_author.id) | ||
63 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => private_author.id, :parent_id => blog.id) | ||
64 | + assert_equal [], block.authors_list | ||
65 | + end | ||
66 | + | ||
67 | + should 'filter authors by roles to display' do | ||
68 | + role = Role.create!(:name => 'role1') | ||
69 | + author = fast_create(Person) | ||
70 | + community.affiliate(author, role) | ||
71 | + | ||
72 | + block = ProfileMembersHeadlinesBlock.new(:limit => 3, :filtered_roles => | ||
73 | +[role.id]) | ||
74 | + block.stubs(:owner).returns(community) | ||
75 | + community.members.each do |member| | ||
76 | + blog = fast_create(Blog, :profile_id => member.id) | ||
77 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => member.id, :parent_id => blog.id) | ||
78 | + end | ||
79 | + assert_equal [author], block.authors_list | ||
80 | + end | ||
81 | +end |
plugins/profile_members_headlines/test/unit/profile_members_headlines_plugin_test.rb
0 → 100644
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
1 | +require 'test_helper' | ||
2 | + | ||
3 | +class ProfileMembersHeadlinesPluginTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + include Noosfero::Plugin::HotSpot | ||
6 | + | ||
7 | + def setup | ||
8 | + @environment = fast_create(Environment) | ||
9 | + @environment.enable_plugin(ProfileMembersHeadlinesPlugin) | ||
10 | + end | ||
11 | + attr_accessor :environment | ||
12 | + | ||
13 | + should 'has a name' do | ||
14 | + assert_not_equal Noosfero::Plugin.plugin_name, ProfileMembersHeadlinesPlugin.plugin_name | ||
15 | + end | ||
16 | + | ||
17 | + should 'describe itself' do | ||
18 | + assert_not_equal Noosfero::Plugin.plugin_description, ProfileMembersHeadlinesPlugin.plugin_description | ||
19 | + end | ||
20 | + | ||
21 | + should 'return ProfileMembersHeadlinesBlock in extra_blocks class method' do | ||
22 | + assert ProfileMembersHeadlinesPlugin.extra_blocks.keys.include?(ProfileMembersHeadlinesBlock) | ||
23 | + end | ||
24 | + | ||
25 | + should 'ProfileMembersHeadlinesBlock not available for environment' do | ||
26 | + assert_not_includes plugins.dispatch(:extra_blocks, :type => Environment), ProfileMembersHeadlinesBlock | ||
27 | + end | ||
28 | + | ||
29 | + should 'ProfileMembersHeadlinesBlock not available for people' do | ||
30 | + assert_not_includes plugins.dispatch(:extra_blocks, :type => Person), ProfileMembersHeadlinesBlock | ||
31 | + end | ||
32 | + | ||
33 | + should "ProfileMembersHeadlinesBlock be available for community" do | ||
34 | + assert_includes plugins.dispatch(:extra_blocks, :type => Community), ProfileMembersHeadlinesBlock | ||
35 | + end | ||
36 | + | ||
37 | + should 'has stylesheet' do | ||
38 | + assert ProfileMembersHeadlinesPlugin.new.stylesheet? | ||
39 | + end | ||
40 | + | ||
41 | +end |
plugins/profile_members_headlines/views/blocks/headlines.html.erb
0 → 100644
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | +<%= block_title(block.title) %> | ||
2 | + | ||
3 | +<% unless members.empty? %> | ||
4 | + <div class='headlines-container'> | ||
5 | + <% members.each do |member| %> | ||
6 | + <div> | ||
7 | + <% headline = member.headline %> | ||
8 | + <%= link_to_profile(profile_image(member, :big) + content_tag(:p, member.short_name), member.identifier, {:class => 'author'}) %> | ||
9 | + <div class='post'> | ||
10 | + <h4><%= link_to(headline.title, headline.url, :class => 'title') %></h4> | ||
11 | + <div class='lead'> | ||
12 | + <%= headline.short_lead %> | ||
13 | + </div> | ||
14 | + <div class='date'> | ||
15 | + <%= show_date(headline.published_at) %> | ||
16 | + </div> | ||
17 | + <div class='tags'> | ||
18 | + <%= headline.tags.map { |t| link_to(t, :controller => 'profile', :profile => member.identifier, :action => 'tags', :id => t.name ) }.join("\n") %> | ||
19 | + </div> | ||
20 | + </div> | ||
21 | + </div> | ||
22 | + <% end %> | ||
23 | + </div> | ||
24 | + <% if block.navigation %> | ||
25 | + <div class='headlines-block-pager'> | ||
26 | + </div> | ||
27 | + <% end %> | ||
28 | + | ||
29 | + <script> | ||
30 | + (function($) { | ||
31 | + var options = {fx: 'fade', pause: 1, fastOnEvent: 1, timeout: <%= block.interval * 1000 %>}; | ||
32 | + options.pager = '#block-<%= block.id %> .headlines-block-pager'; | ||
33 | + $('#block-<%= block.id %> .headlines-container').cycle(options); | ||
34 | + })(jQuery); | ||
35 | + </script> | ||
36 | +<% else %> | ||
37 | + <em><%= _('No headlines to be shown.') %></em> | ||
38 | +<% end %> | ||
39 | + |
plugins/profile_members_headlines/views/box_organizer/_profile_members_headlines_block.html.erb
0 → 100644
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +<div id="profile_members_headlines_plugin"> | ||
2 | + | ||
3 | + <%= labelled_form_field _('Headlines transition:'), select('block', 'interval', [[_('No automatic transition'), 0]] + [1, 2, 3, 4, 5, 10, 20, 30, 60].map {|item| [n_('Every 1 second', 'Every %d seconds', item) % item, item]}) %> | ||
4 | + | ||
5 | + <%= labelled_form_field check_box(:block, :navigation) + _('Display navigation buttons'), '' %> | ||
6 | + <%= labelled_form_field _('Limit of headlines'), text_field(:block, :limit, :size => 3) %> | ||
7 | + | ||
8 | + <%= label :block, :filtered_roles, _('Choose which roles should be displayed:'), :class => 'formlabel' %> | ||
9 | + | ||
10 | + <% Profile::Roles.organization_member_roles(environment).each do |role| %> | ||
11 | + <%= labelled_check_box(role.name, 'block[filtered_roles][]', role.id, @block.filtered_roles.include?(role.id)) %> | ||
12 | + <% end %> | ||
13 | +</div> |
public/500.html
@@ -105,11 +105,11 @@ | @@ -105,11 +105,11 @@ | ||
105 | <div id='pt' style='display: none' class='message'> | 105 | <div id='pt' style='display: none' class='message'> |
106 | <h1>Problema temporário no sistema</h1> | 106 | <h1>Problema temporário no sistema</h1> |
107 | <p> | 107 | <p> |
108 | - Nossa equipe técnica está trabalhando nele, por favor tente mais tarde. Perdoe o incoveniente. | 108 | + Nossa equipe técnica está trabalhando nele, por favor tente mais tarde. Perdoe o inconveniente. |
109 | </p> | 109 | </p> |
110 | <ul> | 110 | <ul> |
111 | <li><a href='javascript: history.back()'>Voltar</a></li> | 111 | <li><a href='javascript: history.back()'>Voltar</a></li> |
112 | - <li><a href='/'>Ir para a página inicial do site.</a></li> | 112 | + <li><a href='/'>Ir para a página inicial do site</a></li> |
113 | </ul> | 113 | </ul> |
114 | </div> | 114 | </div> |
115 | 115 |
public/javascripts/application.js
@@ -518,6 +518,21 @@ function new_qualifier_row(selector, select_qualifiers, delete_button) { | @@ -518,6 +518,21 @@ function new_qualifier_row(selector, select_qualifiers, delete_button) { | ||
518 | jQuery(selector).append("<tr><td>" + select_qualifiers + "</td><td id='certifier-area-" + index + "'><select></select>" + delete_button + "</td></tr>"); | 518 | jQuery(selector).append("<tr><td>" + select_qualifiers + "</td><td id='certifier-area-" + index + "'><select></select>" + delete_button + "</td></tr>"); |
519 | } | 519 | } |
520 | 520 | ||
521 | +function userDataCallback(data) { | ||
522 | + noosfero.user_data = data; | ||
523 | + if (data.login) { | ||
524 | + jQuery('head').append('<meta content="authenticity_token" name="csrf-param" />'); | ||
525 | + jQuery('head').append('<meta content="'+jQuery.cookie("_noosfero_.XSRF-TOKEN")+'" name="csrf-token" />'); | ||
526 | + } | ||
527 | + if (data.notice) { | ||
528 | + display_notice(data.notice); | ||
529 | + // clear notice so that it is not display again in the case this function is called again. | ||
530 | + data.notice = null; | ||
531 | + } | ||
532 | + // Bind this event to do more actions with the user data (for example, inside plugins) | ||
533 | + jQuery(window).trigger("userDataLoaded", data); | ||
534 | +}; | ||
535 | + | ||
521 | // controls the display of the login/logout stuff | 536 | // controls the display of the login/logout stuff |
522 | jQuery(function($) { | 537 | jQuery(function($) { |
523 | $.ajaxSetup({ | 538 | $.ajaxSetup({ |
@@ -528,18 +543,9 @@ jQuery(function($) { | @@ -528,18 +543,9 @@ jQuery(function($) { | ||
528 | }); | 543 | }); |
529 | 544 | ||
530 | var user_data = noosfero_root() + '/account/user_data'; | 545 | var user_data = noosfero_root() + '/account/user_data'; |
531 | - $.getJSON(user_data, function userDataCallBack(data) { | ||
532 | - if (data.login) { | ||
533 | - $('head').append('<meta content="authenticity_token" name="csrf-param" />'); | ||
534 | - $('head').append('<meta content="'+$.cookie("_noosfero_.XSRF-TOKEN")+'" name="csrf-token" />'); | ||
535 | - } | ||
536 | - if (data.notice) { | ||
537 | - display_notice(data.notice); | ||
538 | - } | ||
539 | - // Bind this event to do more actions with the user data (for example, inside plugins) | ||
540 | - $(window).trigger("userDataLoaded", data); | ||
541 | - }); | 546 | + $.getJSON(user_data, userDataCallback) |
542 | 547 | ||
548 | + $.ajaxSetup({ cache: false }); | ||
543 | }); | 549 | }); |
544 | 550 | ||
545 | // controls the display of contact list | 551 | // controls the display of contact list |
@@ -582,12 +588,9 @@ function display_notice(message) { | @@ -582,12 +588,9 @@ function display_notice(message) { | ||
582 | } | 588 | } |
583 | 589 | ||
584 | function open_chat_window(self_link, anchor) { | 590 | function open_chat_window(self_link, anchor) { |
585 | - if(anchor) { | ||
586 | - jQuery('#chat').show('fast'); | ||
587 | - jQuery("#chat" ).trigger('opengroup', anchor); | ||
588 | - } else { | ||
589 | - jQuery('#chat').toggle('fast'); | ||
590 | - } | 591 | + anchor = anchor || '#'; |
592 | + var noosfero_chat_window = window.open(noosfero_root() + '/chat' + anchor,'noosfero_chat','width=900,height=500'); | ||
593 | + noosfero_chat_window.focus(); | ||
591 | return false; | 594 | return false; |
592 | } | 595 | } |
593 | 596 | ||
@@ -1050,7 +1053,7 @@ jQuery(document).ready(function(){ | @@ -1050,7 +1053,7 @@ jQuery(document).ready(function(){ | ||
1050 | function apply_zoom_to_images(zoom_text) { | 1053 | function apply_zoom_to_images(zoom_text) { |
1051 | jQuery(function($) { | 1054 | jQuery(function($) { |
1052 | $(window).load( function() { | 1055 | $(window).load( function() { |
1053 | - $('#article .article-body img:not(.disable-zoom)').each( function(index) { | 1056 | + $('#article .article-body img').each( function(index) { |
1054 | var original = original_image_dimensions($(this).attr('src')); | 1057 | var original = original_image_dimensions($(this).attr('src')); |
1055 | if ($(this).width() < original['width'] || $(this).height() < original['height']) { | 1058 | if ($(this).width() < original['width'] || $(this).height() < original['height']) { |
1056 | $(this).wrap('<div class="zoomable-image" />'); | 1059 | $(this).wrap('<div class="zoomable-image" />'); |
@@ -1069,41 +1072,3 @@ function apply_zoom_to_images(zoom_text) { | @@ -1069,41 +1072,3 @@ function apply_zoom_to_images(zoom_text) { | ||
1069 | }); | 1072 | }); |
1070 | }); | 1073 | }); |
1071 | } | 1074 | } |
1072 | - | ||
1073 | -function getQueryParams(qs) { | ||
1074 | - qs = qs.split("+").join(" "); | ||
1075 | - var params = {}, | ||
1076 | - tokens, | ||
1077 | - re = /[?&]?([^=]+)=([^&]*)/g; | ||
1078 | - while (tokens = re.exec(qs)) { | ||
1079 | - params[decodeURIComponent(tokens[1])] | ||
1080 | - = decodeURIComponent(tokens[2]); | ||
1081 | - } | ||
1082 | - return params; | ||
1083 | -} | ||
1084 | - | ||
1085 | -var fullwidth=false; | ||
1086 | -function toggle_fullwidth(itemId){ | ||
1087 | - if(fullwidth){ | ||
1088 | - jQuery(itemId).removeClass("fullwidth"); | ||
1089 | - jQuery("#fullscreen-btn").show() | ||
1090 | - jQuery("#exit-fullscreen-btn").hide() | ||
1091 | - fullwidth = false; | ||
1092 | - } | ||
1093 | - else{ | ||
1094 | - jQuery(itemId).addClass("fullwidth"); | ||
1095 | - jQuery("#exit-fullscreen-btn").show() | ||
1096 | - jQuery("#fullscreen-btn").hide() | ||
1097 | - fullwidth = true; | ||
1098 | - } | ||
1099 | - jQuery(window).trigger("toggleFullwidth", fullwidth); | ||
1100 | -} | ||
1101 | - | ||
1102 | -function fullscreenPageLoad(itemId){ | ||
1103 | - jQuery(document).ready(function(){ | ||
1104 | - var $_GET = getQueryParams(document.location.search); | ||
1105 | - if ($_GET['fullscreen']==1){ | ||
1106 | - toggle_fullwidth(itemId); | ||
1107 | - } | ||
1108 | - }); | ||
1109 | -} |
test/unit/organization_test.rb
@@ -383,6 +383,33 @@ class OrganizationTest < ActiveSupport::TestCase | @@ -383,6 +383,33 @@ class OrganizationTest < ActiveSupport::TestCase | ||
383 | assert !organization.errors[:cnpj.to_s].present? | 383 | assert !organization.errors[:cnpj.to_s].present? |
384 | end | 384 | end |
385 | 385 | ||
386 | + should 'get members by role' do | ||
387 | + community = fast_create(Community) | ||
388 | + role1 = Role.create!(:name => 'role1') | ||
389 | + person1 = fast_create(Person) | ||
390 | + community.affiliate(person1, role1) | ||
391 | + role2 = Role.create!(:name => 'role2') | ||
392 | + person2 = fast_create(Person) | ||
393 | + community.affiliate(person2, role2) | ||
394 | + | ||
395 | + assert_equal [person1], community.members_by_role([role1]) | ||
396 | + end | ||
397 | + | ||
398 | + should 'get members by more than one role' do | ||
399 | + community = fast_create(Community) | ||
400 | + role1 = Role.create!(:name => 'role1') | ||
401 | + person1 = fast_create(Person) | ||
402 | + community.affiliate(person1, role1) | ||
403 | + role2 = Role.create!(:name => 'role2') | ||
404 | + person2 = fast_create(Person) | ||
405 | + community.affiliate(person2, role2) | ||
406 | + role3 = Role.create!(:name => 'role3') | ||
407 | + person3 = fast_create(Person) | ||
408 | + community.affiliate(person3, role3) | ||
409 | + | ||
410 | + assert_equal [person2, person3], community.members_by_role([role2, role3]) | ||
411 | + end | ||
412 | + | ||
386 | should 'return members by role in a json format' do | 413 | should 'return members by role in a json format' do |
387 | organization = fast_create(Organization) | 414 | organization = fast_create(Organization) |
388 | p1 = create_user('person-1').person | 415 | p1 = create_user('person-1').person |
vendor/plugins/noosfero_caching/init.rb
@@ -49,11 +49,11 @@ module NoosferoHttpCaching | @@ -49,11 +49,11 @@ module NoosferoHttpCaching | ||
49 | 49 | ||
50 | # filter off all cookies except for plugin-provided ones that are | 50 | # filter off all cookies except for plugin-provided ones that are |
51 | # path-specific (i.e path != "/"). | 51 | # path-specific (i.e path != "/"). |
52 | - def remove_unwanted_cookies(cookie_list) | ||
53 | - return nil if cookie_list.nil? | ||
54 | - cookie_list.select do |c| | 52 | + def remove_unwanted_cookies(set_cookie) |
53 | + return nil if set_cookie.nil? | ||
54 | + set_cookie.split(/\s*,\s*/).select do |c| | ||
55 | c =~ /^_noosfero_plugin_\w+=/ && c =~ /path=\/\w+/ | 55 | c =~ /^_noosfero_plugin_\w+=/ && c =~ /path=\/\w+/ |
56 | - end | 56 | + end.join(', ') |
57 | end | 57 | end |
58 | 58 | ||
59 | end | 59 | end |
@@ -61,7 +61,7 @@ module NoosferoHttpCaching | @@ -61,7 +61,7 @@ module NoosferoHttpCaching | ||
61 | end | 61 | end |
62 | 62 | ||
63 | unless Rails.env.development? | 63 | unless Rails.env.development? |
64 | - middleware = Rails.application.config.middleware | 64 | + middleware = Noosfero::Application.config.middleware |
65 | ActionController::Base.send(:include, NoosferoHttpCaching) | 65 | ActionController::Base.send(:include, NoosferoHttpCaching) |
66 | - middleware.use NoosferoHttpCaching::Middleware | 66 | + middleware.insert_before ::ActionDispatch::Cookies, NoosferoHttpCaching::Middleware |
67 | end | 67 | end |