| OLD | NEW |
| 1 # This file is part of the Adblock Plus web scripts, | 1 # This file is part of the Adblock Plus web scripts, |
| 2 # Copyright (C) 2006-2016 Eyeo GmbH | 2 # Copyright (C) 2006-2016 Eyeo GmbH |
| 3 # | 3 # |
| 4 # Adblock Plus is free software: you can redistribute it and/or modify | 4 # Adblock Plus is free software: you can redistribute it and/or modify |
| 5 # it under the terms of the GNU General Public License version 3 as | 5 # it under the terms of the GNU General Public License version 3 as |
| 6 # published by the Free Software Foundation. | 6 # published by the Free Software Foundation. |
| 7 # | 7 # |
| 8 # Adblock Plus is distributed in the hope that it will be useful, | 8 # Adblock Plus is distributed in the hope that it will be useful, |
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 # GNU General Public License for more details. | 11 # GNU General Public License for more details. |
| 12 # | 12 # |
| 13 # You should have received a copy of the GNU General Public License | 13 # You should have received a copy of the GNU General Public License |
| 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| 15 | 15 |
| 16 from __future__ import unicode_literals |
| 17 |
| 16 import os | 18 import os |
| 17 import HTMLParser | 19 import HTMLParser |
| 18 import re | 20 import re |
| 19 | 21 |
| 20 import jinja2 | 22 import jinja2 |
| 21 import markdown | 23 import markdown |
| 22 | 24 |
| 23 | 25 |
| 24 # Monkey-patch Markdown's isBlockLevel function to ensure that no paragraphs | 26 # Monkey-patch Markdown's isBlockLevel function to ensure that no paragraphs |
| 25 # are inserted into the <head> tag | 27 # are inserted into the <head> tag |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 70 finally: | 72 finally: |
| 71 self._string = None | 73 self._string = None |
| 72 self._attrs = None | 74 self._attrs = None |
| 73 self._pagename = None | 75 self._pagename = None |
| 74 self._inside_fixed = False | 76 self._inside_fixed = False |
| 75 self._fixed_strings = None | 77 self._fixed_strings = None |
| 76 | 78 |
| 77 def handle_starttag(self, tag, attrs): | 79 def handle_starttag(self, tag, attrs): |
| 78 if self._inside_fixed: | 80 if self._inside_fixed: |
| 79 raise Exception("Unexpected HTML tag '{}' inside a fixed string" | 81 raise Exception("Unexpected HTML tag '{}' inside a fixed string" |
| 80 'on page {}'.format(tag, self._pagename)) | 82 ' on page {}'.format(tag, self._pagename)) |
| 81 if tag == 'fix': | 83 elif tag == 'fix': |
| 82 self._inside_fixed = True | 84 self._inside_fixed = True |
| 83 self._fixed_strings.append([]) | 85 self._fixed_strings.append([]) |
| 84 if tag in self._whitelist: | 86 elif tag in self._whitelist: |
| 85 self._attrs.setdefault(tag, []).append(attrs) | 87 self._attrs.setdefault(tag, []).append(attrs) |
| 86 self._string.append('<{}>'.format(tag)) | 88 self._string.append('<{}>'.format(tag)) |
| 87 else: | 89 else: |
| 88 raise Exception("Unexpected HTML tag '{}' inside a fixed string" | 90 raise Exception("Unexpected HTML tag '{}' inside a fixed string" |
| 89 'on page {}'.format(tag, self._pagename)) | 91 ' on page {}'.format(tag, self._pagename)) |
| 90 | 92 |
| 91 def handle_endtag(self, tag): | 93 def handle_endtag(self, tag): |
| 92 if tag == 'fix': | 94 if tag == 'fix': |
| 93 self._string.append('{{{}}}'.format(self._fixed_strings)) | 95 self._string.append('{{{}}}'.format(len(self._fixed_strings))) |
| 94 self._inside_fixed = False | 96 self._inside_fixed = False |
| 95 else: | 97 else: |
| 96 self._string.append('</{}>'.format(tag)) | 98 self._string.append('</{}>'.format(tag)) |
| 97 | 99 |
| 98 def _append_text(self, s): | 100 def _append_text(self, s): |
| 99 if self._inside_fixed: | 101 if self._inside_fixed: |
| 100 self._fixed_strings[-1].append(s) | 102 self._fixed_strings[-1].append(s) |
| 101 else: | 103 else: |
| 102 self._string.append(s) | 104 self._string.append(s) |
| 103 | 105 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 149 return re.escape(escape(s)) | 151 return re.escape(escape(s)) |
| 150 | 152 |
| 151 # Handle duplicated strings | 153 # Handle duplicated strings |
| 152 if default: | 154 if default: |
| 153 self._seen_defaults[(page, name)] = (default, comment) | 155 self._seen_defaults[(page, name)] = (default, comment) |
| 154 else: | 156 else: |
| 155 try: | 157 try: |
| 156 default, comment = self._seen_defaults[(page, name)] | 158 default, comment = self._seen_defaults[(page, name)] |
| 157 except KeyError: | 159 except KeyError: |
| 158 raise Exception('Text not yet defined for string {} on page' | 160 raise Exception('Text not yet defined for string {} on page' |
| 159 '{}'.format(name, page)) | 161 ' {}'.format(name, page)) |
| 160 | 162 |
| 161 # Extract tag attributes from default string | 163 # Extract tag attributes from default string |
| 162 default, saved_attributes, fixed_strings = ( | 164 default, saved_attributes, fixed_strings = ( |
| 163 self._attribute_parser.parse(default, self._params['page'])) | 165 self._attribute_parser.parse(default, self._params['page'])) |
| 164 | 166 |
| 165 # Get translation | 167 # Get translation |
| 166 locale = self._params['locale'] | 168 locale = self._params['locale'] |
| 167 if locale == self._params['defaultlocale']: | 169 if locale == self._params['defaultlocale']: |
| 168 result = default | 170 result = default |
| 169 elif name in localedata: | 171 elif name in localedata: |
| 170 result = localedata[name].strip() | 172 result = localedata[name].strip() |
| 171 else: | 173 else: |
| 172 result = default | 174 result = default |
| 173 self.missing_translations += 1 | 175 self.missing_translations += 1 |
| 174 self.total_translations += 1 | 176 self.total_translations += 1 |
| 175 | 177 |
| 176 # Perform callback with the string if required, e.g. for the | 178 # Perform callback with the string if required, e.g. for the |
| 177 # translations script | 179 # translations script |
| 178 callback = self._params['localized_string_callback'] | 180 callback = self._params['localized_string_callback'] |
| 179 if callback: | 181 if callback: |
| 180 callback(page, locale, name, result, comment, fixed_strings) | 182 callback(page, locale, name, result, comment, fixed_strings) |
| 181 | 183 |
| 182 # Insert fixed strings | 184 # Insert fixed strings |
| 183 for i, fixed_string in enumerate(fixed_strings, 1): | 185 for i, fixed_string in enumerate(fixed_strings, 1): |
| 184 result = result.replace('{{{%d}}}'.format(i), fixed_string) | 186 result = result.replace('{{{}}}'.format(i), fixed_string) |
| 185 | 187 |
| 186 # Insert attributes | 188 # Insert attributes |
| 187 result = escape(result) | 189 result = escape(result) |
| 188 | 190 |
| 189 def stringify_attribute((name, value)): | 191 def stringify_attribute((name, value)): |
| 190 return '{}="{}"'.format( | 192 return '{}="{}"'.format( |
| 191 escape(name), | 193 escape(name), |
| 192 escape(self.insert_localized_strings(value, {})) | 194 escape(self.insert_localized_strings(value, {})) |
| 193 ) | 195 ) |
| 194 | 196 |
| 195 for tag in self.whitelist: | 197 for tag in self.whitelist: |
| 196 allowed_contents = '(?:[^<>]|{})'.format('|').join(( | 198 allowed_contents = '(?:[^<>]|{})'.format('|'.join(( |
| 197 '<(?:{}[^<>]*?|/{})>'.format(t, t) | 199 '<(?:{}[^<>]*?|/{})>'.format(t, t) |
| 198 for t in map(re.escape, self.whitelist - {tag}) | 200 for t in map(re.escape, self.whitelist - {tag}) |
| 199 )) | 201 ))) |
| 200 saved = saved_attributes.get(tag, []) | 202 saved = saved_attributes.get(tag, []) |
| 201 for attrs in saved: | 203 for attrs in saved: |
| 202 attrs = map(stringify_attribute, attrs) | 204 attrs = map(stringify_attribute, attrs) |
| 203 result = re.sub( | 205 result = re.sub( |
| 204 r'{}({}*?){}'.format(re_escape('<{}>'.format(tag)), | 206 r'{}({}*?){}'.format(re_escape('<{}>'.format(tag)), |
| 205 allowed_contents, | 207 allowed_contents, |
| 206 re_escape('</{}>'.format(tag))), | 208 re_escape('</{}>'.format(tag))), |
| 207 lambda match: r'<{}{}>{}</{}>'.format( | 209 lambda match: r'<{}{}>{}</{}>'.format( |
| 208 tag, | 210 tag, |
| 209 ' ' + ' '.join(attrs) if attrs else '', | 211 ' ' + ' '.join(attrs) if attrs else '', |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 279 self._params['includedata'] = ( | 281 self._params['includedata'] = ( |
| 280 self._params['source'].read_include(name, format_)) | 282 self._params['source'].read_include(name, format_)) |
| 281 | 283 |
| 282 converter = converter_class(self._params, | 284 converter = converter_class(self._params, |
| 283 key='includedata') | 285 key='includedata') |
| 284 result = converter() | 286 result = converter() |
| 285 self.missing_translations += converter.missing_translations | 287 self.missing_translations += converter.missing_translations |
| 286 self.total_translations += converter.total_translations | 288 self.total_translations += converter.total_translations |
| 287 return result | 289 return result |
| 288 raise Exception('Failed to resolve include {}' | 290 raise Exception('Failed to resolve include {}' |
| 289 'on page {}'.format(name, self._params['page'])) | 291 ' on page {}'.format(name, self._params['page'])) |
| 290 | 292 |
| 291 return re.sub( | 293 return re.sub( |
| 292 r'{}\?\s*include\s+([^\s<>"]+)\s*\?{}'.format( | 294 r'{}\?\s*include\s+([^\s<>"]+)\s*\?{}'.format( |
| 293 self.include_start_regex, | 295 self.include_start_regex, |
| 294 self.include_end_regex | 296 self.include_end_regex |
| 295 ), | 297 ), |
| 296 resolve_include, | 298 resolve_include, |
| 297 text | 299 text |
| 298 ) | 300 ) |
| 299 | 301 |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 continue | 392 continue |
| 391 | 393 |
| 392 path = os.path.join(dirname, filename) | 394 path = os.path.join(dirname, filename) |
| 393 namespace = self._params['source'].exec_file(path) | 395 namespace = self._params['source'].exec_file(path) |
| 394 | 396 |
| 395 name = os.path.basename(root) | 397 name = os.path.basename(root) |
| 396 try: | 398 try: |
| 397 dictionary[name] = namespace[name] | 399 dictionary[name] = namespace[name] |
| 398 except KeyError: | 400 except KeyError: |
| 399 raise Exception('Expected symbol {} not found' | 401 raise Exception('Expected symbol {} not found' |
| 400 'in {}'.format(name, path)) | 402 ' in {}'.format(name, path)) |
| 401 | 403 |
| 402 self._env = jinja2.Environment( | 404 self._env = jinja2.Environment( |
| 403 loader=SourceTemplateLoader(self._params['source']), | 405 loader=SourceTemplateLoader(self._params['source']), |
| 404 autoescape=True) | 406 autoescape=True) |
| 405 self._env.filters.update(filters) | 407 self._env.filters.update(filters) |
| 406 self._env.globals.update(globals) | 408 self._env.globals.update(globals) |
| 407 | 409 |
| 408 def get_html(self, source, filename): | 410 def get_html(self, source, filename): |
| 409 env = self._env | 411 env = self._env |
| 410 code = env.compile(source, None, filename) | 412 code = env.compile(source, None, filename) |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 477 stack.pop() | 479 stack.pop() |
| 478 stack[-1]['subitems'].append(item) | 480 stack[-1]['subitems'].append(item) |
| 479 stack.append(item) | 481 stack.append(item) |
| 480 return structured | 482 return structured |
| 481 | 483 |
| 482 converters = { | 484 converters = { |
| 483 'html': RawConverter, | 485 'html': RawConverter, |
| 484 'md': MarkdownConverter, | 486 'md': MarkdownConverter, |
| 485 'tmpl': TemplateConverter, | 487 'tmpl': TemplateConverter, |
| 486 } | 488 } |
| OLD | NEW |