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 | 19 | * In the case the original author is a committer, he/she should feel free to |
20 | 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 | 42 | * Committers should feel free to push trivial (or urgent) changes directly. |
23 | 43 | There are no strict rule on what makes a change trivial or urgent; committers |
24 | 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 | 433 | end |
434 | 434 | |
435 | 435 | def theme_site_title |
436 | - theme_include('site_title') | |
436 | + @theme_site_title ||= theme_include 'site_title' | |
437 | 437 | end |
438 | 438 | |
439 | 439 | def theme_header |
440 | - theme_include('header') | |
440 | + @theme_header ||= theme_include 'header' | |
441 | 441 | end |
442 | 442 | |
443 | 443 | def theme_footer |
444 | - theme_include('footer') | |
444 | + @theme_footer ||= theme_include 'footer' | |
445 | 445 | end |
446 | 446 | |
447 | 447 | def theme_extra_navigation |
448 | - theme_include('navigation') | |
448 | + @theme_extra_navigation ||= theme_include 'navigation' | |
449 | 449 | end |
450 | 450 | |
451 | 451 | def is_testing_theme |
... | ... | @@ -674,13 +674,14 @@ module ApplicationHelper |
674 | 674 | html.join "\n" |
675 | 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 | 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 | 685 | end |
685 | 686 | |
686 | 687 | def file_field_or_thumbnail(label, image, i) | ... | ... |
app/helpers/layout_helper.rb
... | ... | @@ -18,6 +18,8 @@ module LayoutHelper |
18 | 18 | unless plugins_javascripts.empty? |
19 | 19 | output += javascript_include_tag plugins_javascripts, :cache => "cache/plugins-#{Digest::MD5.hexdigest plugins_javascripts.to_s}" |
20 | 20 | end |
21 | + output += theme_javascript_ng.to_s | |
22 | + | |
21 | 23 | output |
22 | 24 | end |
23 | 25 | |
... | ... | @@ -87,6 +89,10 @@ module LayoutHelper |
87 | 89 | theme_path + '/style.css' |
88 | 90 | end |
89 | 91 | |
92 | + def layout_template | |
93 | + if profile then profile.layout_template else environment.layout_template end | |
94 | + end | |
95 | + | |
90 | 96 | def addthis_javascript |
91 | 97 | if NOOSFERO_CONF['addthis_enabled'] |
92 | 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 | 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 | 30 | def has_permission_with_plugins?(permission, profile) |
25 | 31 | permissions = [has_permission_without_plugins?(permission, profile)] |
26 | 32 | permissions += plugins.map do |plugin| | ... | ... |
app/models/profile.rb
... | ... | @@ -146,8 +146,8 @@ class Profile < ActiveRecord::Base |
146 | 146 | alias_method_chain :count, :distinct |
147 | 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 | 151 | end |
152 | 152 | |
153 | 153 | acts_as_having_boxes | ... | ... |
app/views/layouts/application-ng.html.erb
... | ... | @@ -72,10 +72,7 @@ |
72 | 72 | <div id="navigation-end"></div> |
73 | 73 | </div><!-- end id="navigation" --> |
74 | 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 | 76 | </div><!-- end id="content" --> |
80 | 77 | </div><!-- end id="wrap-2" --> |
81 | 78 | </div><!-- end id="wrap-1" --> |
... | ... | @@ -84,7 +81,6 @@ |
84 | 81 | <%= theme_footer %> |
85 | 82 | </div><!-- end id="theme-footer" --> |
86 | 83 | <%= noosfero_layout_features %> |
87 | - <%= theme_javascript_ng %> | |
88 | 84 | <%= addthis_javascript %> |
89 | 85 | <%= |
90 | 86 | @plugins.dispatch(:body_ending).map do |content| | ... | ... |
config/application.rb
... | ... | @@ -111,9 +111,7 @@ module Noosfero |
111 | 111 | # Make sure the secret is at least 30 characters and all random, |
112 | 112 | # no regular words or you'll be exposed to dictionary attacks. |
113 | 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 | 116 | config.time_zone = File.read('/etc/timezone').split("\n").first |
119 | 117 | config.active_record.default_timezone = :local | ... | ... |
db/migrate/20140724134600_remove_environment_statistics_block_sooner.rb
0 → 100644
... | ... | @@ -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 | 60 | dbconfig-common, |
61 | 61 | adduser, |
62 | 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 | 68 | ${misc:Depends} |
64 | 69 | Recommends: |
65 | 70 | postgresql, | ... | ... |
etc/noosfero/varnish-noosfero.vcl
... | ... | @@ -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 | 20 | sub vcl_error { |
14 | 21 | set obj.http.Content-Type = "text/html; charset=utf-8"; |
15 | 22 | ... | ... |
lib/noosfero.rb
... | ... | @@ -70,16 +70,6 @@ module Noosfero |
70 | 70 | end |
71 | 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 | 73 | def self.url_options |
84 | 74 | case Rails.env |
85 | 75 | when 'development' | ... | ... |
lib/noosfero/plugin.rb
... | ... | @@ -88,18 +88,29 @@ class Noosfero::Plugin |
88 | 88 | # This is a generic method that initialize any possible filter defined by a |
89 | 89 | # plugin to a specific controller |
90 | 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 | 114 | end |
104 | 115 | end |
105 | 116 | end | ... | ... |
lib/tasks/release.rake
... | ... | @@ -137,7 +137,17 @@ EOF |
137 | 137 | new_version += '~rc1' |
138 | 138 | end |
139 | 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 | 151 | end |
142 | 152 | |
143 | 153 | puts "Current version: #{$version}" | ... | ... |
lib/unifreire_terminology.rb
... | ... | @@ -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 | -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 | 42 | \ No newline at end of file | ... | ... |
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 | 105 | <div id='pt' style='display: none' class='message'> |
106 | 106 | <h1>Problema temporário no sistema</h1> |
107 | 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 | 109 | </p> |
110 | 110 | <ul> |
111 | 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 | 113 | </ul> |
114 | 114 | </div> |
115 | 115 | ... | ... |
public/javascripts/application.js
... | ... | @@ -518,6 +518,21 @@ function new_qualifier_row(selector, select_qualifiers, delete_button) { |
518 | 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 | 536 | // controls the display of the login/logout stuff |
522 | 537 | jQuery(function($) { |
523 | 538 | $.ajaxSetup({ |
... | ... | @@ -528,18 +543,9 @@ jQuery(function($) { |
528 | 543 | }); |
529 | 544 | |
530 | 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 | 551 | // controls the display of contact list |
... | ... | @@ -582,12 +588,9 @@ function display_notice(message) { |
582 | 588 | } |
583 | 589 | |
584 | 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 | 594 | return false; |
592 | 595 | } |
593 | 596 | |
... | ... | @@ -1050,7 +1053,7 @@ jQuery(document).ready(function(){ |
1050 | 1053 | function apply_zoom_to_images(zoom_text) { |
1051 | 1054 | jQuery(function($) { |
1052 | 1055 | $(window).load( function() { |
1053 | - $('#article .article-body img:not(.disable-zoom)').each( function(index) { | |
1056 | + $('#article .article-body img').each( function(index) { | |
1054 | 1057 | var original = original_image_dimensions($(this).attr('src')); |
1055 | 1058 | if ($(this).width() < original['width'] || $(this).height() < original['height']) { |
1056 | 1059 | $(this).wrap('<div class="zoomable-image" />'); |
... | ... | @@ -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 | 383 | assert !organization.errors[:cnpj.to_s].present? |
384 | 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 | 413 | should 'return members by role in a json format' do |
387 | 414 | organization = fast_create(Organization) |
388 | 415 | p1 = create_user('person-1').person | ... | ... |
vendor/plugins/noosfero_caching/init.rb
... | ... | @@ -49,11 +49,11 @@ module NoosferoHttpCaching |
49 | 49 | |
50 | 50 | # filter off all cookies except for plugin-provided ones that are |
51 | 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 | 55 | c =~ /^_noosfero_plugin_\w+=/ && c =~ /path=\/\w+/ |
56 | - end | |
56 | + end.join(', ') | |
57 | 57 | end |
58 | 58 | |
59 | 59 | end |
... | ... | @@ -61,7 +61,7 @@ module NoosferoHttpCaching |
61 | 61 | end |
62 | 62 | |
63 | 63 | unless Rails.env.development? |
64 | - middleware = Rails.application.config.middleware | |
64 | + middleware = Noosfero::Application.config.middleware | |
65 | 65 | ActionController::Base.send(:include, NoosferoHttpCaching) |
66 | - middleware.use NoosferoHttpCaching::Middleware | |
66 | + middleware.insert_before ::ActionDispatch::Cookies, NoosferoHttpCaching::Middleware | |
67 | 67 | end | ... | ... |