def test_ignore_tag_with_fixed_xml_lang(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <p xml:lang="en">(c) 2007 Edgewall Software</p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(0, len(messages))
def test_ignore_attribute_with_expression(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <input type="submit" value="Reply" title="Reply to comment $num" /> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(0, len(messages))
def test_extract_included_attribute_text(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <span title="Foo"></span> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, None, u'Foo', []), messages[0])
def test_extract_non_included_attribute_interpolated(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <a href="#anchor_${num}">Foo</a> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, None, u'Foo', []), messages[0])
def test_extract_text_from_sub(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <py:if test="foo">Foo</py:if> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, None, u'Foo', []), messages[0])
def test_extract_attribute_expr(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <input type="submit" value="${_('Save')}" /> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, '_', u'Save', []), messages[0])
def test_extract_tag_with_variable_xml_lang(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <p xml:lang="${lang}">(c) 2007 Edgewall Software</p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, None, u'(c) 2007 Edgewall Software'), messages[0])
def test_extract_gettext_with_unicode_string(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> ${gettext("Grüße")} </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, 'gettext', u'Gr\xfc\xdfe', []), messages[0])
def test_extract_funky_plural_form(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> ${ngettext(len(items), *widget.display_names)} </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, 'ngettext', (None, None), []), messages[0])
def test_extract_i18n_msg_with_comment(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="" i18n:comment="As in foo bar">Foo</p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((3, None, u'Foo', ['As in foo bar']), messages[0])
def test_extract_plural_form(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> ${ngettext("Singular", "Plural", num)} </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((2, 'ngettext', (u'Singular', u'Plural', None), []), messages[0])
def test_extract_without_text(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> <p title="Bar">Foo</p> ${ngettext("Singular", "Plural", num)} </html>""") translator = Translator(extract_text=False) messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual((3, 'ngettext', (u'Singular', u'Plural', None), []), messages[0])
def test_extract_i18n_msg_multiple(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg=""> Please see <a href="help.html">Help</a> for <em>details</em>. </p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual('Please see [1:Help] for [2:details].', messages[0][2])
def test_extract_i18n_msg_with_param(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="name"> Hello, ${user.name}! </p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual('Hello, %(name)s!', messages[0][2])
def test_translate_with_translations_object(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="" i18n:comment="As in foo bar">Foo</p> </html>""") translator = Translator(DummyTranslations({'Foo': 'Voh'})) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p>Voh</p> </html>""", tmpl.generate().render())
def test_translate_included_attribute_text(self): """ Verify that translated attributes end up in a proper `Attrs` instance. """ html = HTML("""<html> <span title="Foo"></span> </html>""") translator = Translator(lambda s: u"Voh") stream = list(html.filter(translator)) kind, data, pos = stream[2] assert isinstance(data[1], Attrs)
def test_extract_i18n_msg_with_directive(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg=""> Show me <input type="text" name="num" py:attrs="{'value': x}" /> entries per page. </p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual('Show me [1:] entries per page.', messages[0][2])
def test_extract_i18n_msg_with_two_params(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="name, time"> Posted by ${post.author} at ${entry.time.strftime('%H:%m')} </p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual('Posted by %(name)s at %(time)s', messages[0][2])
def test_translate_i18n_msg_with_comment(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="" i18n:comment="As in foo bar">Foo</p> </html>""") gettext = lambda s: u"Voh" translator = Translator(gettext) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p>Voh</p> </html>""", tmpl.generate().render())
def test_extract_i18n_msg_multiple_empty(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg=""> Show me <input type="text" name="num" /> entries per page, starting at page <input type="text" name="num" />. </p> </html>""") translator = Translator() messages = list(translator.extract(tmpl.stream)) self.assertEqual(1, len(messages)) self.assertEqual('Show me [1:] entries per page, starting at page [2:].', messages[0][2])
def load_environment(global_conf, app_conf): """\ Configure the Pylons environment via the ``pylons.config`` object """ # Pylons paths root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) paths = dict(root=root, controllers=os.path.join(root, 'controllers'), static_files=os.path.join(root, 'public'), templates=[os.path.join(root, 'templates')]) # Initialize config with the basic options config.init_app(global_conf, app_conf, package='openspending.ui', paths=paths) config['routes.map'] = routing.make_map() config['pylons.app_globals'] = app_globals.Globals() config['pylons.h'] = helpers # set log level in markdown markdown.logger.setLevel(logging.WARN) # Establish celery loader: from openspending.command import celery # Translator (i18n) config['openspending.ui.translations'] = MultiDomainTranslator([config.get('lang', 'en')]) translator = Translator(config['openspending.ui.translations']) def template_loaded(template): translator.setup(template) template_paths = [paths['templates'][0]] extra_template_paths = config.get('extra_template_paths', '') if extra_template_paths: # must be first for them to override defaults template_paths = extra_template_paths.split(',') + template_paths # Create the Genshi TemplateLoader config['pylons.app_globals'].genshi_loader = TemplateLoader( search_path=template_paths, auto_reload=True, callback=template_loaded ) # SQLAlchemy engine = engine_from_config(config, 'openspending.db.') engine = construct_engine(engine) init_model(engine) # Configure Solr import openspending.lib.solr_util as solr solr.configure(config)
def test_directive_single_line_with_translator(self): tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/"> <py:for each="i in range(2)"><py:for each="j in range(1)"> <span py:content="i + j"></span> </py:for></py:for> </div>""") translator = Translator(lambda s: s) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual( """<div> <span>0</span> <span>1</span> </div>""", str(tmpl.generate()))
def test_translate_i18n_msg_with_attribute_param(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg=""> Hello, <a href="#${anchor}">dude</a>! </p> </html>""") gettext = lambda s: u"Sei gegrüßt, [1:Alter]!" translator = Translator(gettext) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p>Sei gegrüßt, <a href="#42">Alter</a>!</p> </html>""", tmpl.generate(anchor='42').render())
def test_translate_i18n_msg_multiple(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg=""> Please see <a href="help.html">Help</a> for <em>details</em>. </p> </html>""") gettext = lambda s: u"Für [2:Details] siehe bitte [1:Hilfe]." translator = Translator(gettext) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p>Für <em>Details</em> siehe bitte <a href="help.html">Hilfe</a>.</p> </html>""", tmpl.generate().render())
def test_translate_i18n_msg_multiple_empty(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg=""> Show me <input type="text" name="num" /> entries per page, starting at page <input type="text" name="num" />. </p> </html>""") gettext = lambda s: u"[1:] Einträge pro Seite, beginnend auf Seite [2:]." translator = Translator(gettext) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p><input type="text" name="num"/> Eintr\xc3\xa4ge pro Seite, beginnend auf Seite <input type="text" name="num"/>.</p> </html>""", tmpl.generate().render())
def test_translate_i18n_msg_with_param_reordered(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="name"> Hello, ${user.name}! </p> </html>""") gettext = lambda s: u"%(name)s, sei gegrüßt!" translator = Translator(gettext) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p>Jim, sei gegrüßt!</p> </html>""", tmpl.generate(user=dict(name='Jim')).render())
def test_translate_i18n_msg_with_attr(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="" title="Foo bar">Foo</p> </html>""") gettext = lambda s: u"Voh" translator = Translator(DummyTranslations({ 'Foo': u'Voh', 'Foo bar': u'Voh bär' })) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) self.assertEqual("""<html> <p title="Voh bär">Voh</p> </html>""", tmpl.generate().render())
def load_environment(global_conf, app_conf): """Configure the Pylons environment via the ``pylons.config`` object """ # Pylons paths root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) paths = dict(root=root, controllers=os.path.join(root, 'controllers'), static_files=os.path.join(root, 'public'), templates=[os.path.join(root, 'templates')]) # Initialize config with the basic options config.init_app(global_conf, app_conf, package='isitopen', template_engine='genshi', paths=paths) config['routes.map'] = make_map() config['pylons.g'] = app_globals.Globals() config['pylons.h'] = isitopen.lib.helpers # Customize templating options via this variable tmpl_options = config['buffet.template_options'] # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) engine = engine_from_config(config, 'sqlalchemy.') config['pylons.g'].sa_engine = engine # Translator (i18n) translator = Translator(ugettext) def template_loaded(template): template.filters.insert(0, translator) #translator.setup(template) # redo template setup to use genshi.search_path # This requires path notation in calls to render rather than dotted notation # e.g. render('index.html') not render('index') etc genshi = config['buffet.template_engines'].pop() # set None for template_root as not using dotted (python package) notation config.add_template_engine('genshi', None) tmpl_options = config['buffet.template_options'] tmpl_options['genshi.search_path'] = paths['templates'][0] tmpl_options["genshi.loader_callback"] = template_loaded
def test_translate_i18n_msg_with_two_params(self): tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" xmlns:i18n="http://genshi.edgewall.org/i18n"> <p i18n:msg="name, time"> Written by ${entry.author} at ${entry.time.strftime('%H:%M')} </p> </html>""") gettext = lambda s: u"%(name)s schrieb dies um %(time)s" translator = Translator(gettext) tmpl.filters.insert(0, translator) tmpl.add_directives(Translator.NAMESPACE, translator) entry = { 'author': 'Jim', 'time': datetime(2008, 4, 1, 14, 30) } self.assertEqual("""<html> <p>Jim schrieb dies um 14:30</p> </html>""", tmpl.generate(entry=entry).render())
def load_environment(global_conf, app_conf): """Configure the Pylons environment via the ``pylons.config`` object """ ###### Pylons monkey-patch # this must be run at a time when the env is semi-setup, thus inlined here. # Required by the deliverance plugin and iATI from pylons.wsgiapp import PylonsApp import pkg_resources find_controller_generic = PylonsApp.find_controller # This is from pylons 1.0 source, will monkey-patch into 0.9.7 def find_controller(self, controller): if controller in self.controller_classes: return self.controller_classes[controller] # Check to see if its a dotted name if '.' in controller or ':' in controller: mycontroller = pkg_resources \ .EntryPoint \ .parse('x=%s' % controller).load(False) self.controller_classes[controller] = mycontroller return mycontroller return find_controller_generic(self, controller) PylonsApp.find_controller = find_controller ###### END evil monkey-patch os.environ['CKAN_CONFIG'] = global_conf['__file__'] # Pylons paths root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) paths = dict(root=root, controllers=os.path.join(root, 'controllers'), static_files=os.path.join(root, 'public'), templates=[]) # Initialize config with the basic options config.init_app(global_conf, app_conf, package='ckan', paths=paths) # load all CKAN plugins p.load_all(config) # Load the synchronous search plugin, unless already loaded or # explicitly disabled if not 'synchronous_search' in config.get('ckan.plugins',[]) and \ asbool(config.get('ckan.search.automatic_indexing', True)): log.debug('Loading the synchronous search plugin') p.load('synchronous_search') for plugin in p.PluginImplementations(p.IConfigurer): # must do update in place as this does not work: # config = plugin.update_config(config) plugin.update_config(config) # This is set up before globals are initialized site_id = os.environ.get('CKAN_SITE_ID') if site_id: config['ckan.site_id'] = site_id site_url = config.get('ckan.site_url', '') ckan_host = config['ckan.host'] = urlparse(site_url).netloc if config.get('ckan.site_id') is None: if ':' in ckan_host: ckan_host, port = ckan_host.split(':') assert ckan_host, 'You need to configure ckan.site_url or ' \ 'ckan.site_id for SOLR search-index rebuild to work.' config['ckan.site_id'] = ckan_host # ensure that a favicon has been set favicon = config.get('ckan.favicon', '/images/icons/ckan.ico') config['ckan.favicon'] = favicon # Init SOLR settings and check if the schema is compatible #from ckan.lib.search import SolrSettings, check_solr_schema_version # lib.search is imported here as we need the config enabled and parsed import ckan.lib.search as search search.SolrSettings.init(config.get('solr_url'), config.get('solr_user'), config.get('solr_password')) search.check_solr_schema_version() config['routes.map'] = routing.make_map() config['routes.named_routes'] = routing.named_routes config['pylons.app_globals'] = app_globals.app_globals # initialise the globals config['pylons.app_globals']._init() # add helper functions helpers = _Helpers(h) config['pylons.h'] = helpers ## redo template setup to use genshi.search_path ## (so remove std template setup) legacy_templates_path = os.path.join(root, 'templates_legacy') jinja2_templates_path = os.path.join(root, 'templates') if asbool(config.get('ckan.legacy_templates', 'no')): # We want the new template path for extra snippets like the # dataviewer and also for some testing stuff template_paths = [legacy_templates_path, jinja2_templates_path] else: template_paths = [jinja2_templates_path, legacy_templates_path] extra_template_paths = config.get('extra_template_paths', '') if extra_template_paths: # must be first for them to override defaults template_paths = extra_template_paths.split(',') + template_paths config['pylons.app_globals'].template_paths = template_paths # Translator (i18n) translator = Translator(pylons.translator) def template_loaded(template): translator.setup(template) # Markdown ignores the logger config, so to get rid of excessive # markdown debug messages in the log, set it to the level of the # root logger. logging.getLogger("MARKDOWN").setLevel(logging.getLogger().level) # Create the Genshi TemplateLoader config['pylons.app_globals'].genshi_loader = TemplateLoader( template_paths, auto_reload=True, callback=template_loaded) ################################################################# # # # HORRIBLE GENSHI HACK # # # ################################################################# # # # Genshi does strange things to get stuff out of the template # # variables. This stops it from handling properties in the # # correct way as it returns the property rather than the actual # # value of the property. # # # # By overriding lookup_attr() in the LookupBase class we are # # able to get the required behaviour. Using @property allows # # us to move functionality out of templates whilst maintaining # # backwards compatability. # # # ################################################################# ''' This code is based on Genshi code Copyright © 2006-2012 Edgewall Software All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' from genshi.template.eval import LookupBase @classmethod def genshi_lookup_attr(cls, obj, key): __traceback_hide__ = True try: val = getattr(obj, key) except AttributeError: if hasattr(obj.__class__, key): raise else: try: val = obj[key] except (KeyError, TypeError): val = cls.undefined(key, owner=obj) if isinstance(val, property): val = val.fget() return val setattr(LookupBase, 'lookup_attr', genshi_lookup_attr) del genshi_lookup_attr del LookupBase ################################################################# # # # END OF GENSHI HACK # # # ################################################################# # Create Jinja2 environment env = lib.jinja_extensions.Environment( loader=lib.jinja_extensions.CkanFileSystemLoader(template_paths), autoescape=True, extensions=[ 'jinja2.ext.do', 'jinja2.ext.with_', lib.jinja_extensions.SnippetExtension, lib.jinja_extensions.CkanExtend, lib.jinja_extensions.CkanInternationalizationExtension, lib.jinja_extensions.LinkForExtension, lib.jinja_extensions.ResourceExtension, lib.jinja_extensions.UrlForStaticExtension, lib.jinja_extensions.UrlForExtension ]) env.install_gettext_callables(_, ungettext, newstyle=True) # custom filters env.filters['empty_and_escape'] = lib.jinja_extensions.empty_and_escape env.filters['truncate'] = lib.jinja_extensions.truncate config['pylons.app_globals'].jinja_env = env # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) # Setup the SQLAlchemy database engine # Suppress a couple of sqlalchemy warnings msgs = [ '^Unicode type received non-unicode bind param value', "^Did not recognize type 'BIGINT' of column 'size'", "^Did not recognize type 'tsvector' of column 'search_vector'" ] for msg in msgs: warnings.filterwarnings('ignore', msg, sqlalchemy.exc.SAWarning) ckan_db = os.environ.get('CKAN_DB') if ckan_db: config['sqlalchemy.url'] = ckan_db # for postgresql we want to enforce utf-8 sqlalchemy_url = config.get('sqlalchemy.url', '') if sqlalchemy_url.startswith('postgresql://'): extras = {'client_encoding': 'utf8'} else: extras = {} engine = sqlalchemy.engine_from_config(config, 'sqlalchemy.', **extras) if not model.meta.engine: model.init_model(engine) for plugin in p.PluginImplementations(p.IConfigurable): plugin.configure(config)