Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrew Mleczko
2011-06-18 16:06:46 +02:00
11 changed files with 234 additions and 35 deletions
+3
View File
@@ -17,8 +17,11 @@ eggs =
Sphinx
unittest2
nose
Babel
lingua
scripts=
nosetests=tests
interpreter = python
[test]
recipe = zc.recipe.egg
+3
View File
@@ -4,12 +4,15 @@ from pyramid_formalchemy.resources import Models
def includeme(config):
"""include formalchemy's zcml"""
config.add_translation_dirs('formalchemy:i18n_resources/', 'pyramid_formalchemy:locale/')
config.add_static_view('fa_admin', 'pyramid_formalchemy:static')
config.add_directive('formalchemy_admin', 'pyramid_formalchemy.formalchemy_admin')
config.add_directive('formalchemy_model', 'pyramid_formalchemy.formalchemy_model')
config.add_directive('formalchemy_model_view', 'pyramid_formalchemy.formalchemy_model_view')
config.registry.pyramid_formalchemy_views = {}
config.add_route('set_language', '/set_language', 'pyramid_formalchemy.views.set_language')
def formalchemy_model_view(config, route_name,
model=None,
name='',
+90 -32
View File
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
from chameleon.zpt.template import PageTemplate
from pyramid.util import DottedNameResolver
from pyramid_formalchemy.i18n import TranslationString
from pyramid_formalchemy.i18n import get_localizer
from pyramid_formalchemy.i18n import _
import functools
def action(name=None):
@@ -29,16 +32,16 @@ class Action(object):
>>> request = Request.blank('/')
>>> class MyAction(Action):
... body = u'<a tal:attributes="%(attributes)s" tal:content="%(content)s"></a>'
... body = u'<a tal:attributes="%(attributes)s">${content}</a>'
>>> action = MyAction('myaction', content=repr('Click here'),
>>> action = MyAction('myaction', content=_('Click here'),
... attrs={'href': repr('#'), 'onclick': repr('$.click()')})
>>> action.render(request)
u'<a href="#" id="myaction" onclick="$.click()">Click here</a>'
"""
def __init__(self, id, action='', content="", attrs=None, **rcontext):
def __init__(self, id, content="", alt="", attrs=None, **rcontext):
self.id = id
self.attrs = attrs or {}
self.rcontext = rcontext
@@ -46,8 +49,9 @@ class Action(object):
self.attrs['id'] = repr(id)
self.update()
attributes = u';'.join([u'%s %s' % v for v in self.attrs.items()])
rcontext.update(attrs=self.attrs, attributes=attributes, id=id, content=content, action=action)
rcontext.update(attrs=self.attrs, attributes=attributes, id=id)
body = self.body % self.rcontext
rcontext.update(content=content, alt=alt)
self.template = PageTemplate(body)
def update(self):
@@ -56,6 +60,17 @@ class Action(object):
def render(self, request):
rcontext = {'action': self, 'request': request}
rcontext.update(self.rcontext)
localizer = get_localizer(request)
mapping = getattr(request, 'action_mapping', {})
if not mapping:
for k in ('model_name', 'model_id'):
mapping[k] = getattr(request, k, '')
request.action_mapping = mapping
for k in ('content', 'alt'):
v = rcontext[k]
if isinstance(v, TranslationString):
v = TranslationString(v, domain=v.domain, mapping=request.action_mapping)
rcontext[k] = localizer.translate(v)
return self.template.render(**rcontext)
def __repr__(self):
@@ -67,14 +82,30 @@ class Link(Action):
>>> from webob import Request
>>> request = Request.blank('/')
>>> action = Link('myaction', content='label',
>>> action = Link('myaction',
... attrs={'href': 'request.application_url'},
... label='Click here')
... content=_('Click here'))
>>> action.render(request)
u'<a href="http://localhost" id="myaction">Click here</a>'
"""
body = u'<a tal:attributes="%(attributes)s" tal:content="%(content)s"></a>'
body = u'<a tal:attributes="%(attributes)s">${content}</a>'
class ListItem(Action):
"""
An action rendered as a link::
>>> from webob import Request
>>> request = Request.blank('/')
>>> action = ListItem('myaction',
... attrs={'href': 'request.application_url'},
... content=_('Click here'))
>>> action.render(request)
u'<li><a href="http://localhost" id="myaction">Click here</a></li>'
"""
body = u'<li><a tal:attributes="%(attributes)s">${content}</a></li>'
class Input(Action):
"""An action rendered as an input::
@@ -82,7 +113,7 @@ class Input(Action):
>>> from webob import Request
>>> request = Request.blank('/')
>>> action = Input('myaction',
... value=repr('Click here'))
... value=_('Click here'))
>>> action.render(request)
u'<input type="submit" id="myaction" value="Myaction" />'
@@ -95,21 +126,21 @@ class Input(Action):
if 'type' not in self.attrs:
self.attrs['type'] = repr('submit')
class UILink(Action):
class UIButton(Action):
"""An action rendered as an jquery.ui aware link::
>>> from webob import Request
>>> request = Request.blank('/')
>>> action = UILink('myaction', icon='ui-icon-trash',
... label="string:Click here")
>>> action = UIButton('myaction', icon='ui-icon-trash',
... content=_("Click here"))
>>> print action.render(request)
<a class="ui-widget-header ui-widget-link ui-widget-button ui-corner-all " id="myaction">
<span class="ui-icon ui-icon-trash"></span>
Click here
</a>
>>> action = UILink('myaction', icon='ui-icon-trash',
... label="'Click here'", attrs={'onclick':'$(#link).click();'})
>>> action = UIButton('myaction', icon='ui-icon-trash',
... content=_("Click here"), attrs={'onclick':'$(#link).click();'})
>>> print action.render(request)
<a class="ui-widget-header ui-widget-link ui-widget-button ui-corner-all " href="#" id="myaction" onclick="$(#link).click();">
<span class="ui-icon ui-icon-trash"></span>
@@ -121,7 +152,7 @@ class UILink(Action):
<a class="ui-widget-header ui-widget-link ui-widget-button ui-corner-all ${state}"
tal:attributes="%(attributes)s">
<span class="ui-icon ${icon}"></span>
<span tal:replace="%(label)s"></span>
${content}
</a>'''
def update(self):
if 'state' not in self.rcontext:
@@ -135,17 +166,18 @@ class UILink(Action):
class Actions(list):
"""
>>> actions = Actions('pyramid_formalchemy.actions.delete',
... Link('link1', content=repr('A link'), attrs={'href':'request.application_url'}))
... Link('link1', content=_('A link'), attrs={'href':'request.application_url'}))
>>> actions
[<UILink delete>, <Link link1>]
[<UIButton delete>, <Link link1>]
>>> from webob import Request
>>> request = Request.blank('/')
>>> print actions.render(request) #doctest: +ELLIPSIS
>>> print actions.render(request) #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
<a class="ui-widget-header ...">
<span class="ui-icon ui-icon-trash"></span>
Delete
</a><a href="http://localhost" id="link1">A link</a>
</a>
<a href="http://localhost" id="link1">A link</a>
"""
@@ -154,44 +186,70 @@ class Actions(list):
list.__init__(self, [res.maybe_resolve(a) for a in args])
def render(self, request, **kwargs):
return u''.join([a.render(request, **kwargs) for a in self])
return u'\n'.join([a.render(request, **kwargs) for a in self])
class Languages(Actions):
"""
>>> langs = Languages('fr', 'en')
>>> langs
[<Link fr>, <Link en>]
>>> from webob import Request
>>> request = Request.blank('/')
>>> request.route_url = lambda name, _query: 'http://localhost/set_language?_LOCALE_=%(_LOCALE_)s' % _query
>>> print langs.render(request) #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
<a href="http://localhost/set_language?_LOCALE_=fr" id="fr">fr</a>
<a href="http://localhost/set_language?_LOCALE_=en" id="en">en</a>
"""
new = UILink(
def __init__(self, *args, **kwargs):
list.__init__(self)
klass=kwargs.get('class_', ListItem)
for l in args:
self.append(Link(id=l, content=_(l), attrs=dict(href="request.route_url('set_language', _query={'_LOCALE_': '%s'})" % l)))
new = UIButton(
id='new',
label='string:New ${request.model_name}',
content=_('New ${model_name}'),
icon='ui-icon-circle-plus',
attrs=dict(href="request.fa_url(request.model_name, 'new')"),
)
save = UILink(
save = UIButton(
id='save',
label='string:Save',
content=_('Save'),
icon='ui-icon-check',
attrs=dict(onclick="jQuery(this).parents('form').submit();"),
)
save_and_add_another = UILink(
save_and_add_another = UIButton(
id='save_and_add_another',
label='string:Save and add another',
content=_('Save and add another'),
icon='ui-icon-check',
attrs=dict(onclick=("var f = jQuery(this).parents('form');"
"jQuery('#next', f).val(window.location.href);"
"f.submit();")),
)
edit = UILink(
edit = UIButton(
id='edit',
label='string:Edit',
content=_('Edit'),
icon='ui-icon-check',
attrs=dict(href="request.fa_url(request.model_name, request.model_id, 'edit')"),
)
delete = UILink(
back = UIButton(
id='back',
content=_('Back'),
icon='ui-icon-circle-arrow-w',
attrs=dict(href="request.fa_url(request.model_name)"),
)
delete = UIButton(
id='delete',
views='edit',
label='string:Delete',
content=_('Delete'),
state='ui-state-error',
icon='ui-icon-trash',
attrs=dict(onclick=("string:var f = jQuery(this).parents('form');"
@@ -199,10 +257,10 @@ delete = UILink(
"f.submit();")),
)
cancel = UILink(
cancel = UIButton(
id='cancel',
views='edit',
label='string:Cancel',
content=_('Cancel'),
icon='ui-icon-circle-arrow-w',
attrs=dict(href="request.fa_url(request.model_name)"),
)
@@ -210,6 +268,6 @@ cancel = UILink(
defaults_actions = dict(
listing_buttons=Actions(new),
new_buttons=Actions(save, save_and_add_another, cancel),
show_buttons=Actions(edit, cancel),
show_buttons=Actions(edit, back),
edit_buttons=Actions(save, delete, cancel),
)
+7
View File
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from pyramid.i18n import TranslationStringFactory
from pyramid.i18n import TranslationString
from pyramid.i18n import get_localizer
_ = TranslationStringFactory('pyramid_formalchemy')
@@ -0,0 +1,56 @@
# French translations for pyramid_formalchemy.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the pyramid_formalchemy
# project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: pyramid_formalchemy 0.3\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-06-18 12:02+0200\n"
"PO-Revision-Date: 2011-06-18 13:18+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.5\n"
#: pyramid_formalchemy/actions.py:190
msgid "New ${model_name}"
msgstr "Nouveau ${model_name}"
#: pyramid_formalchemy/actions.py:198
msgid "Save"
msgstr "Sauver"
#: pyramid_formalchemy/actions.py:205
msgid "Save and add another"
msgstr "Sauver et ajouter un autre"
#: pyramid_formalchemy/actions.py:214
msgid "Edit"
msgstr "Editer"
#: pyramid_formalchemy/actions.py:221
msgid "Back"
msgstr "Retour"
#: pyramid_formalchemy/actions.py:229
msgid "Delete"
msgstr "Supprimer"
#: pyramid_formalchemy/actions.py:240
msgid "Cancel"
msgstr "Annuler"
#: pyramid_formalchemy/views.py:245
msgid "edit"
msgstr "Editer"
#: pyramid_formalchemy/views.py:252
msgid "delete"
msgstr "Supprimer"
@@ -0,0 +1,56 @@
# Translations template for pyramid_formalchemy.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the pyramid_formalchemy
# project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: pyramid_formalchemy 0.3\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-06-18 13:16+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.5\n"
#: pyramid_formalchemy/actions.py:190
msgid "New ${model_name}"
msgstr ""
#: pyramid_formalchemy/actions.py:198
msgid "Save"
msgstr ""
#: pyramid_formalchemy/actions.py:205
msgid "Save and add another"
msgstr ""
#: pyramid_formalchemy/actions.py:214
msgid "Edit"
msgstr ""
#: pyramid_formalchemy/actions.py:221
msgid "Back"
msgstr ""
#: pyramid_formalchemy/actions.py:229
msgid "Delete"
msgstr ""
#: pyramid_formalchemy/actions.py:240
msgid "Cancel"
msgstr ""
#: pyramid_formalchemy/views.py:245
msgid "edit"
msgstr ""
#: pyramid_formalchemy/views.py:252
msgid "delete"
msgstr ""
+7
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from pyramid.exceptions import NotFound
from pyramid_formalchemy import actions
from sqlalchemy import exceptions as sqlalchemy_exceptions
import logging
@@ -27,6 +28,12 @@ class Base(object):
if self.__model_class__:
request.model_class = self.__model_class__
request.model_name = self.__model_class__.__name__
langs = request.registry.settings.get('available_languages', '')
if langs:
if isinstance(langs, basestring):
langs = langs.split()
request.language_actions = actions.Languages(*langs)
def get_model(self):
request = self.request
+1 -1
View File
@@ -12,7 +12,7 @@
<a class="ui-widget-header ui-widget-link ui-corner-all"
tal:attributes="href request.fa_url(request.model_name)">
<span class="ui-icon ui-icon-circle-arrow-w"></span>
${F_('Cancel')}
${'Cancel'}
</a>
</p>
</form>
+6 -1
View File
@@ -38,6 +38,11 @@ class Session(object):
def commit(self):
"""commit transaction"""
def set_language(request):
resp = exc.HTTPFound(location=request.referer or request.application_url)
resp.set_cookie('_LOCALE_', request.GET.get('_LOCALE_', 'en'))
return resp
class ModelView(object):
"""A RESTful view bound to a model"""
@@ -132,7 +137,7 @@ class ModelView(object):
main = get_renderer('pyramid_formalchemy:templates/admin/master.pt').implementation(),
model_name=request.model_name,
breadcrumb=self.breadcrumb(**kwargs),
F_=get_translator().gettext)
F_=get_translator())
return kwargs
def render_grid(self, **kwargs):
+5 -1
View File
@@ -6,7 +6,7 @@ here = os.path.abspath(os.path.dirname(__file__))
README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = ['pyramid>=1.0', 'WebError', 'FormAlchemy>=1.3.8']
requires = ['pyramid>=1.0', 'WebError', 'FormAlchemy>=1.3.8', 'Babel']
setup(name='pyramid_formalchemy',
version='0.3',
@@ -23,6 +23,10 @@ setup(name='pyramid_formalchemy',
url='http://docs.formalchemy.org/pyramid_formalchemy/',
keywords='web pyramid pylons formalchemy',
packages=find_packages(exclude=['pyramidapp']),
message_extractors = { 'pyramid_formalchemy': [
('*.py', 'lingua_python', None ),
('templates/**.pt', 'lingua_xml', None ),
]},
include_package_data=True,
zip_safe=False,
install_requires=requires,