wovnrb 3.11.0 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/wovnrb.rb CHANGED
@@ -1,145 +1,146 @@
1
- require 'rack'
2
- require 'wovnrb/api_translator'
3
- require 'wovnrb/headers'
4
- require 'wovnrb/store'
5
- require 'wovnrb/lang'
6
- require 'wovnrb/services/html_converter'
7
- require 'wovnrb/services/html_replace_marker'
8
- require 'wovnrb/url_language_switcher'
9
- require 'nokogiri'
10
- require 'active_support'
11
- require 'json'
12
- require 'wovnrb/helpers/nokogumbo_helper'
13
- require 'wovnrb/railtie' if defined?(Rails)
14
- require 'wovnrb/version'
15
-
16
- module Wovnrb
17
- class Interceptor
18
- def initialize(app, opts = {})
19
- @app = app
20
- @store = Store.instance
21
- opts = opts.transform_keys(&:to_s)
22
- @store.update_settings(opts)
23
- @url_lang_switcher = Wovnrb::UrlLanguageSwitcher.new(@store)
24
- end
25
-
26
- def call(env)
27
- # disabled by previous Rack middleware
28
- return @app.call(env) if Rack::Request.new(env).params['wovn_disable'] == true
29
-
30
- @store.settings.clear_dynamic_settings!
31
- return @app.call(env) unless Store.instance.valid_settings?
32
-
33
- @env = env
34
- headers = Headers.new(env, @store.settings, @url_lang_switcher)
35
- default_lang = @store.settings['default_lang']
36
- return @app.call(env) if @store.settings['test_mode'] && @store.settings['test_url'] != headers.url
37
-
38
- cookie_lang = Rack::Request.new(env).cookies['wovn_selected_lang']
39
- request_lang = headers.lang_code
40
- if @store.settings['use_cookie_lang'] && cookie_lang.present? && request_lang != cookie_lang && request_lang == @store.default_lang
41
- redirect_headers = headers.redirect(cookie_lang)
42
- return [302, redirect_headers, ['']]
43
- end
44
-
45
- # redirect if the path is set to the default language (for SEO purposes)
46
- if explicit_default_lang?(headers)
47
- redirect_headers = headers.redirect(default_lang)
48
- return [307, redirect_headers, ['']]
49
- end
50
-
51
- # if path containing language code is ignored, do nothing
52
- if headers.lang_code != default_lang && ignore_path?(headers.unmasked_pathname_without_trailing_slash)
53
- status, res_headers, body = @app.call(env)
54
-
55
- return output(headers, status, res_headers, body)
56
- end
57
- # pass to application
58
- status, res_headers, body = @app.call(headers.request_out)
59
-
60
- # disabled by next Rack middleware
61
- return output(headers, status, res_headers, body) unless res_headers['Content-Type']&.include?('html')
62
-
63
- request = Rack::Request.new(env)
64
-
65
- return output(headers, status, res_headers, body) if request.params['wovn_disable'] == true
66
-
67
- @store.settings.update_dynamic_settings!(request.params)
68
- return output(headers, status, res_headers, body) if ignore_path?(headers.pathname)
69
-
70
- body = switch_lang(headers, body) unless /^1|302/.match?(status.to_s)
71
-
72
- content_length = 0
73
- body.each { |b| content_length += b.respond_to?(:bytesize) ? b.bytesize : 0 }
74
- res_headers['Content-Length'] = content_length.to_s
75
-
76
- output(headers, status, res_headers, body)
77
- end
78
-
79
- def switch_lang(headers, body)
80
- translated_body = []
81
-
82
- # Must use `.each` for to support multiple-chunks in Sinatra
83
- string_body = ''
84
- body.each { |chunk| string_body += chunk }
85
- html_body = Helpers::NokogumboHelper.parse_html(string_body)
86
-
87
- if !wovn_ignored?(html_body) && !amp_page?(html_body)
88
-
89
- html_converter = HtmlConverter.new(html_body, @store, headers, @url_lang_switcher)
90
-
91
- if needs_api?(html_body, headers)
92
- converted_html, marker = html_converter.build_api_compatible_html
93
- translated_content = ApiTranslator.new(@store, headers, WovnLogger.uuid).translate(converted_html)
94
- translated_body.push(marker.revert(translated_content))
95
- else
96
- string_body = html_converter.build if html_body.html?
97
- translated_body.push(string_body)
98
- end
99
- else
100
- translated_body.push(string_body)
101
- end
102
-
103
- body.close if body.respond_to?(:close)
104
- translated_body
105
- end
106
-
107
- private
108
-
109
- def output(headers, status, res_headers, body)
110
- headers.out(res_headers)
111
- [status, res_headers, body]
112
- end
113
-
114
- def needs_api?(html_body, headers)
115
- headers.lang_code != @store.settings['default_lang'] &&
116
- (html_body.html? || @store.settings['translate_fragment'])
117
- end
118
-
119
- def wovn_ignored?(html_body)
120
- !html_body.xpath('//html[@wovn-ignore or @data-wovn-ignore]').empty?
121
- end
122
-
123
- def ignore_path?(path)
124
- @store.settings['ignore_globs'].ignore?(path)
125
- end
126
-
127
- # Checks if a given HTML body is an Accelerated Mobile Page (AMP).
128
- # To do so, it looks at the required attributes for the HTML tag:
129
- # https://www.ampproject.org/docs/tutorials/create/basic_markup.
130
- #
131
- # @param {Nokogiri::HTML5::Document} body The HTML body to check.
132
- #
133
- # @returns {Boolean} True is the HTML body is an AMP, false otherwise.
134
- def amp_page?(html_body)
135
- html_attributes = html_body.xpath('//html')[0].try(:attributes) || {}
136
-
137
- !!(html_attributes['amp'] || html_attributes["\u26A1"])
138
- end
139
-
140
- def explicit_default_lang?(headers)
141
- default_lang, url_pattern = @store.settings.values_at('default_lang', 'url_pattern')
142
- default_lang == headers.url_language && url_pattern != 'custom_domain'
143
- end
144
- end
145
- end
1
+ require 'rack'
2
+ require 'wovnrb/api_translator'
3
+ require 'wovnrb/headers'
4
+ require 'wovnrb/store'
5
+ require 'wovnrb/lang'
6
+ require 'wovnrb/services/html_converter'
7
+ require 'wovnrb/services/html_replace_marker'
8
+ require 'wovnrb/url_language_switcher'
9
+ require 'nokogiri'
10
+ require 'active_support'
11
+ require 'json'
12
+ require 'wovnrb/helpers/nokogumbo_helper'
13
+ require 'wovnrb/railtie' if defined?(Rails)
14
+ require 'wovnrb/version'
15
+
16
+ module Wovnrb
17
+ class Interceptor
18
+ def initialize(app, opts = {})
19
+ @app = app
20
+ @store = Store.instance
21
+ opts = opts.transform_keys(&:to_s)
22
+ @store.update_settings(opts)
23
+ @url_lang_switcher = Wovnrb::UrlLanguageSwitcher.new(@store)
24
+ end
25
+
26
+ def call(env)
27
+ # disabled by previous Rack middleware
28
+ request = Rack::Request.new(env)
29
+ return @app.call(env) if request.params['wovn_disable'] == true
30
+
31
+ @store.settings.clear_dynamic_settings!
32
+ return @app.call(env) unless Store.instance.valid_settings?
33
+
34
+ @env = env
35
+ headers = Headers.new(env, @store.settings, @url_lang_switcher)
36
+ default_lang = @store.settings['default_lang']
37
+ return @app.call(env) if @store.settings['test_mode'] && @store.settings['test_url'] != headers.url
38
+
39
+ cookie_lang = request.cookies['wovn_selected_lang']
40
+ request_lang = headers.lang_code
41
+ is_get_request = request.get?
42
+
43
+ if @store.settings['use_cookie_lang'] && cookie_lang.present? && request_lang != cookie_lang && request_lang == @store.default_lang && is_get_request
44
+ redirect_headers = headers.redirect(cookie_lang)
45
+ return [302, redirect_headers, ['']]
46
+ end
47
+
48
+ # redirect if the path is set to the default language (for SEO purposes)
49
+ if explicit_default_lang?(headers) && is_get_request
50
+ redirect_headers = headers.redirect(default_lang)
51
+ return [307, redirect_headers, ['']]
52
+ end
53
+
54
+ # if path containing language code is ignored, do nothing
55
+ if headers.lang_code != default_lang && ignore_path?(headers.unmasked_pathname_without_trailing_slash)
56
+ status, res_headers, body = @app.call(env)
57
+
58
+ return output(headers, status, res_headers, body)
59
+ end
60
+ # pass to application
61
+ status, res_headers, body = @app.call(headers.request_out)
62
+
63
+ # disabled by next Rack middleware
64
+ return output(headers, status, res_headers, body) unless res_headers['Content-Type']&.include?('html')
65
+
66
+ return output(headers, status, res_headers, body) if request.params['wovn_disable'] == true
67
+
68
+ @store.settings.update_dynamic_settings!(request.params)
69
+ return output(headers, status, res_headers, body) if ignore_path?(headers.pathname)
70
+
71
+ body = switch_lang(headers, body, status) unless /^1|302/.match?(status.to_s)
72
+
73
+ content_length = 0
74
+ body.each { |b| content_length += b.respond_to?(:bytesize) ? b.bytesize : 0 }
75
+ res_headers['Content-Length'] = content_length.to_s
76
+
77
+ output(headers, status, res_headers, body)
78
+ end
79
+
80
+ def switch_lang(headers, body, response_status_code)
81
+ translated_body = []
82
+
83
+ # Must use `.each` for to support multiple-chunks in Sinatra
84
+ string_body = ''
85
+ body.each { |chunk| string_body += chunk }
86
+ html_body = Helpers::NokogumboHelper.parse_html(string_body)
87
+
88
+ if !wovn_ignored?(html_body) && !amp_page?(html_body)
89
+
90
+ html_converter = HtmlConverter.new(html_body, @store, headers, @url_lang_switcher)
91
+
92
+ if needs_api?(html_body, headers)
93
+ converted_html, marker = html_converter.build_api_compatible_html
94
+ translated_content = ApiTranslator.new(@store, headers, WovnLogger.uuid, response_status_code).translate(converted_html)
95
+ translated_body.push(marker.revert(translated_content))
96
+ else
97
+ string_body = html_converter.build if html_body.html?
98
+ translated_body.push(string_body)
99
+ end
100
+ else
101
+ translated_body.push(string_body)
102
+ end
103
+
104
+ body.close if body.respond_to?(:close)
105
+ translated_body
106
+ end
107
+
108
+ private
109
+
110
+ def output(headers, status, res_headers, body)
111
+ headers.out(res_headers)
112
+ [status, res_headers, body]
113
+ end
114
+
115
+ def needs_api?(html_body, headers)
116
+ headers.lang_code != @store.settings['default_lang'] &&
117
+ (html_body.html? || @store.settings['translate_fragment'])
118
+ end
119
+
120
+ def wovn_ignored?(html_body)
121
+ !html_body.xpath('//html[@wovn-ignore or @data-wovn-ignore]').empty?
122
+ end
123
+
124
+ def ignore_path?(path)
125
+ @store.settings['ignore_globs'].ignore?(path)
126
+ end
127
+
128
+ # Checks if a given HTML body is an Accelerated Mobile Page (AMP).
129
+ # To do so, it looks at the required attributes for the HTML tag:
130
+ # https://www.ampproject.org/docs/tutorials/create/basic_markup.
131
+ #
132
+ # @param {Nokogiri::HTML5::Document} body The HTML body to check.
133
+ #
134
+ # @returns {Boolean} True is the HTML body is an AMP, false otherwise.
135
+ def amp_page?(html_body)
136
+ html_attributes = html_body.xpath('//html')[0].try(:attributes) || {}
137
+
138
+ !!(html_attributes['amp'] || html_attributes["\u26A1"])
139
+ end
140
+
141
+ def explicit_default_lang?(headers)
142
+ default_lang, url_pattern = @store.settings.values_at('default_lang', 'url_pattern')
143
+ default_lang == headers.url_language && url_pattern != 'custom_domain'
144
+ end
145
+ end
146
+ end