[gen] Bugfix while managing languages, ui improvements.

This commit is contained in:
Gaetan Delannay 2012-05-29 20:50:18 +02:00
parent aaaccb0669
commit ede29fb6c1
14 changed files with 140 additions and 96 deletions

View file

@ -261,8 +261,8 @@ class ZopeInstaller:
# may still be in the way for migration purposes.
users = ('admin',) # We suppose there is at least a user.
if not users:
appyTool.create('users', login='admin', firstName='admin',
name='admin', password1='admin', password2='admin',
appyTool.create('users', login='admin', password1='admin',
password2='admin',
email='admin@appyframework.org', roles=['Manager'])
appyTool.log('Admin user "admin" created.')

View file

@ -121,6 +121,13 @@ class ToolMixin(BaseMixin):
p_code.'''
return languages.get(code)[2]
def changeLanguage(self):
'''Sets the language cookie with the new desired language code that is
in request["language"].'''
rq = self.REQUEST
rq.RESPONSE.setCookie('_ZopeLg', rq['language'], path='/')
return self.goto(rq['HTTP_REFERER'])
def getGlobalCssJs(self):
'''Returns the list of CSS and JS files to include in the main template.
The method ensures that appy.css and appy.js come first.'''
@ -231,6 +238,12 @@ class ToolMixin(BaseMixin):
res = appyObj.showPortlet()
except AttributeError:
res = True
else:
appyObj = self.appy()
try:
res = appyObj.showPortletAt(context)
except AttributeError:
res = True
return res
def getObject(self, uid, appy=False, brain=False):
@ -979,7 +992,7 @@ class ToolMixin(BaseMixin):
elem is the one-line user info as shown on every page; second line is
the URL to edit user info.'''
appyUser = self.appy().appyUser
res = [appyUser.title, appyUser.login]
res = [appyUser.title]
rolesToShow = [r for r in appyUser.roles \
if r not in ('Authenticated', 'Member')]
if rolesToShow:

View file

@ -1345,10 +1345,16 @@ class BaseMixin:
def getUserLanguage(self):
'''Gets the language (code) of the current user.'''
if not hasattr(self, 'REQUEST'): return 'en'
# Try first the "LANGUAGE" key from the request
# Try the value which comes from the cookie. Indeed, if such a cookie is
# present, it means that the user has explicitly chosen this language
# via the language selector.
rq = self.REQUEST
if '_ZopeLg' in rq.cookies: return rq.cookies['_ZopeLg']
# Try the LANGUAGE key from the request: it corresponds to the language
# as configured in the user's browser.
res = self.REQUEST.get('LANGUAGE', None)
if res: return res
# Try then the HTTP_ACCEPT_LANGUAGE key from the request, which stores
# Try the HTTP_ACCEPT_LANGUAGE key from the request, which stores
# language preferences as defined in the user's browser. Several
# languages can be listed, from most to less wanted.
res = self.REQUEST.get('HTTP_ACCEPT_LANGUAGE', None)

View file

@ -93,7 +93,7 @@ class ModelClass:
elif callable(value):
className = wrapperName
if (appyType.type == 'Ref') and appyType.isBack:
className = appyType.back.klass.__name__
className = value.im_class.__name__
value = '%s.%s' % (className, value.__name__)
typeArgs += '%s=%s,' % (name, value)
return '%s(%s)' % (appyType.__class__.__name__, typeArgs)
@ -143,10 +143,13 @@ class User(ModelClass):
'password2', 'email', 'roles']
# All methods defined below are fake. Real versions are in the wrapper.
title = gen.String(show=False, indexed=True)
gm = {'group': 'main', 'multiplicity': (1,1), 'width': 25}
gm = {'group': 'main', 'width': 25}
def showName(self): pass
name = gen.String(show=showName, **gm)
firstName = gen.String(show=showName, **gm)
def showEmail(self): pass
email = gen.String(show=showEmail)
gm['multiplicity'] = (1,1)
def showLogin(self): pass
def validateLogin(self): pass
login = gen.String(show=showLogin, validator=validateLogin,
@ -156,8 +159,6 @@ class User(ModelClass):
password1 = gen.String(format=gen.String.PASSWORD, show=showPassword,
validator=validatePassword, **gm)
password2 = gen.String(format=gen.String.PASSWORD, show=showPassword, **gm)
def showEmail(self): pass
email = gen.String(show=showEmail, group='main', width=25)
gm['multiplicity'] = (0, None)
def showRoles(self): pass
roles = gen.String(show=showRoles, indexed=True,

View file

@ -1,16 +1,16 @@
body { font: 75% Helvetica,Arial,sans-serif; background-color: #EAEAEA;
margin-top: 18px}
pre { font: 100% Helvetica,Arial,sans-serif; margin: 0}
h1 { font-size: 14pt; margin:0;}
h2 { font-size: 13pt; margin:0; font-style: italic; font-weight: normal;
background-color: #d7dee4}
h3 { font-size: 12pt; margin:0; font-weight: bold;}
h4 { font-size: 11pt; margin:0;}
h1 { font-size: 14pt; margin-bottom:4px;}
h2 { font-size: 13pt; margin-bottom:4px; font-style: italic;
font-weight: normal; background-color: #d7dee4}
h3 { font-size: 12pt; margin-bottom:4px; font-weight: bold;}
h4 { font-size: 11pt; margin-bottom:4px;}
h5 { font-size: 10pt; margin:0; font-style: italic; font-weight: normal;
background-color: #d7dee4}
h6 { font-size: 9pt; margin:0; font-weight: bold;}
a { text-decoration: none; color: #503737;}
a:visited { color: #503737;}
a { text-decoration: none; color: #436976;}
a:visited { color: #436976;}
table { font-size: 100%; border-spacing: 0px; border-collapse:collapse;}
form { margin: 0; padding: 0;}
p { margin: 0;}
@ -40,7 +40,7 @@ ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0;
list-style: none outside none;}
ul li { margin: 0; background-image: url("ui/li.gif"); padding-left: 10px;
background-repeat: no-repeat; background-position: 0 4px;}
img {border: 0}
img { border: 0; vertical-align: middle}
/* Styles that apply when viewing content of XHTML fields, that mimic styles
that ckeditor uses for displaying XHTML content in the edit view. */
@ -51,9 +51,10 @@ img {border: 0}
.main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9;
border-style: solid; border-width: 1px; border-color: grey}
.top { height: 75px; margin-left: 3em; vertical-align: top;}
.lang { margin-right: 3px; }
.userStrip { background-color: #d7dee4; height: 35px;
border-top: 1px solid #5F7983; border-bottom: 1px solid #5F7983; }
.lang { margin-right: 6px; }
.userStrip { background-color: #6282B3; height: 35px;
border-top: 3px solid #034984; border-bottom: 2px solid #034984; }
.userStripText { font-size: 110%; padding: 0 0.3em 0 0.3em; color: white }
.login { margin-top: 2px; margin-bottom: 2px; color: black;}
.buttons { margin-left: 4px;}
.fakeButton { border: 1px solid #D7DEE4; background-color: #fde8e0;
@ -117,3 +118,6 @@ img {border: 0}
.topSpace { margin-top: 15px;}
.discreet { color: grey}
.pageLink { padding-left: 6px; font-style: italic}
.footer { font-size: 95% }
.footer td { background-color: #CBCBC9; border-top: 1px solid grey;
padding: 0.4em 1em 0.5em }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 B

After

Width:  |  Height:  |  Size: 476 B

4
gen/ui/footer.pt Normal file
View file

@ -0,0 +1,4 @@
<table metal:define-macro="footer"
cellpadding="0" cellspacing="0" width="100%" class="footer">
<tr><td align="right">Made with <a href="http://appyframework.org" target="_blank">Appy</a></td></tr>
</table>

View file

@ -289,7 +289,7 @@
</tal:edit>
<tal:refresh condition="contextObj/isDebug">
<img title="Refresh" style="cursor:pointer"
<img title="Refresh" style="cursor:pointer; vertical-align:top"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode=layoutType, page=page, refresh='yes');
src string: $appUrl/ui/refresh.png"/>
</tal:refresh>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

After

Width:  |  Height:  |  Size: 191 B

View file

@ -39,12 +39,12 @@
<td align="right" tal:condition="tool/showLanguageSelector">
<tal:lgs define="languages tool/getLanguages;
defaultLanguage python: languages[0];
asLinks python: len(languages) &lt;= 5">
asLinks python: len(languages) &lt;= 8">
<table tal:condition="asLinks">
<tr>
<td tal:repeat="lang languages">
<a class="lang"
tal:attributes="href python: req.get('ACTUAL_URL')+'/switchLanguage?set_language=%s' % lang;
tal:attributes="href string: $appUrl/config/changeLanguage?language=$lang;
title python: tool.getLanguageName(lang)"
tal:content="python: lang"></a>
</td>
@ -94,7 +94,7 @@
</div>
</td>
</tr>
<tal:comment replace="nothing">The user data strip</tal:comment>
<tal:comment replace="nothing">The user strip</tal:comment>
<tr>
<td>
<table class="userStrip" width="100%">
@ -103,7 +103,7 @@
<tal:comment replace="nothing">The user login form for anonymous users</tal:comment>
<table align="center" tal:condition="python: isAnon and ('/temp_folder/' not in req['ACTUAL_URL'])"
class="login">
<tr><td>
<tr><td class="userStripText">
<form name="loginform" method="post"
tal:attributes="action python: tool.absolute_url() + '/performLogin'">
@ -141,7 +141,7 @@
<img tal:attributes="src string: $appUrl/ui/logout.gif"/>
</a>
</td>
<td align="right" tal:define="userInfo tool/getUserLine">
<td align="right" class="userStripText" tal:define="userInfo tool/getUserLine">
<span tal:content="python: userInfo[0]"></span>
<a tal:attributes="href python: userInfo[1]">
<img tal:attributes="src string: $appUrl/ui/edit.gif"/>
@ -168,6 +168,9 @@
</table>
</td>
</tr>
<tr><tal:comment replace="nothing">Footer</tal:comment>
<td><metal:call use-macro="app/ui/footer/macros/footer"/></td>
</tr>
</table>
</body>
</html>

View file

@ -156,76 +156,72 @@
</tr></table>
</tal:atMostOneReference>
<tal:comment replace="nothing">Display a fieldset in all other cases.</tal:comment>
<tal:comment replace="nothing">Display a table in all other cases.</tal:comment>
<tal:anyNumberOfReferences condition="not: atMostOneRef">
<fieldset tal:attributes="class python:test(innerRef, 'innerAppyFieldset', '')">
<legend tal:condition="python: not innerRef or showPlusIcon">
(<span tal:replace="totalNumber"/>)
<metal:plusIcon use-macro="app/ui/widgets/ref/macros/plusIcon"/>
<tal:comment replace="nothing">The search icon if field is queryable</tal:comment>
<a tal:condition="python: objs and appyType['queryable']"
tal:attributes="href python: '%s/ui/search?className=%s&ref=%s:%s' % (tool.absolute_url(), linkedPortalType, contextObj.UID(), appyType['name'])">
<img src="search.gif" tal:attributes="title python: _('search_objects')"/></a>
</legend>
<div tal:condition="python: not innerRef or showPlusIcon">
(<span tal:replace="totalNumber"/>)
<metal:plusIcon use-macro="app/ui/widgets/ref/macros/plusIcon"/>
<tal:comment replace="nothing">The search icon if field is queryable</tal:comment>
<a tal:condition="python: objs and appyType['queryable']"
tal:attributes="href python: '%s/ui/search?className=%s&ref=%s:%s' % (tool.absolute_url(), linkedPortalType, contextObj.UID(), appyType['name'])">
<img src="search.gif" tal:attributes="title python: _('search_objects')"/></a>
</div>
<tal:comment replace="nothing">Appy (top) navigation</tal:comment>
<metal:nav use-macro="here/ui/navigate/macros/appyNavigate"/>
<tal:comment replace="nothing">Appy (top) navigation</tal:comment>
<metal:nav use-macro="here/ui/navigate/macros/appyNavigate"/>
<tal:comment replace="nothing">No object is present</tal:comment>
<p tal:condition="not:objs" tal:content="python: _('no_ref')"></p>
<tal:comment replace="nothing">No object is present</tal:comment>
<p tal:condition="not:objs" tal:content="python: _('no_ref')"></p>
<table width="100%" tal:condition="python: objs"
tal:attributes="class python:test(innerRef, 'innerAppyTable', '')">
<tr valign="bottom"><td>
<table width="100%" tal:condition="python: objs"
tal:attributes="class python:test(innerRef, 'innerAppyTable', '')">
<tr valign="bottom"><td>
<tal:comment replace="nothing">Show forward or backward reference(s)</tal:comment>
<table tal:attributes="class python:test(innerRef, '', 'list');
width python:test(innerRef, '100%', appyType['layouts']['view']['width']);">
<tal:widgets define="widgets python: objs[0].getAppyTypesFromNames(appyType['shownInfo'])">
<tr tal:condition="appyType/showHeaders">
<th tal:repeat="widget widgets">
<span tal:content="python: _(widget['labelId'])"></span>
<metal:sortIcons use-macro="app/ui/widgets/ref/macros/sortIcons" />
</th>
<th tal:content="python: _('ref_actions')"></th>
</tr>
<tal:row repeat="obj objs">
<tr valign="middle" tal:define="odd repeat/obj/odd"
tal:attributes="class python:test(odd, 'even', 'odd')">
<td tal:repeat="widget widgets"
tal:attributes="width python: appyType['shownInfoWidths'][repeat['widget'].index]">
<tal:title condition="python: widget['name'] == 'title'">
<metal:showObjectTitle use-macro="app/ui/widgets/ref/macros/objectTitle"/>
</tal:title>
<tal:state condition="python: widget['name'] == 'state'"
content="python: _(obj.getWorkflowLabel())">
</tal:state>
<tal:other condition="python: widget['name'] not in ('title', 'state')">
<tal:field define="contextObj python:obj;
layoutType python: 'cell';
innerRef python:True">
<metal:field use-macro="app/ui/widgets/show/macros/field" />
</tal:field>
</tal:other>
</td>
<tal:comment replace="nothing">Actions</tal:comment>
<td align="right">
<tal:show condition="obj/mayAct">
<metal:showObjectActions use-macro="app/ui/widgets/ref/macros/objectActions" />
</tal:show>
</td>
</tr>
</tal:row>
</tal:widgets>
</table>
</td></tr>
</table>
<tal:comment replace="nothing">Show forward or backward reference(s)</tal:comment>
<table tal:attributes="class python:test(innerRef, '', 'list');
width python:test(innerRef, '100%', appyType['layouts']['view']['width']);">
<tal:widgets define="widgets python: objs[0].getAppyTypesFromNames(appyType['shownInfo'])">
<tr tal:condition="appyType/showHeaders">
<th tal:repeat="widget widgets">
<span tal:content="python: _(widget['labelId'])"></span>
<metal:sortIcons use-macro="app/ui/widgets/ref/macros/sortIcons" />
</th>
<th tal:content="python: _('ref_actions')"></th>
</tr>
<tal:row repeat="obj objs">
<tr valign="middle" tal:define="odd repeat/obj/odd"
tal:attributes="class python:test(odd, 'even', 'odd')">
<td tal:repeat="widget widgets"
tal:attributes="width python: appyType['shownInfoWidths'][repeat['widget'].index]">
<tal:title condition="python: widget['name'] == 'title'">
<metal:showObjectTitle use-macro="app/ui/widgets/ref/macros/objectTitle"/>
</tal:title>
<tal:state condition="python: widget['name'] == 'state'"
content="python: _(obj.getWorkflowLabel())">
</tal:state>
<tal:other condition="python: widget['name'] not in ('title', 'state')">
<tal:field define="contextObj python:obj;
layoutType python: 'cell';
innerRef python:True">
<metal:field use-macro="app/ui/widgets/show/macros/field" />
</tal:field>
</tal:other>
</td>
<tal:comment replace="nothing">Actions</tal:comment>
<td align="right">
<tal:show condition="obj/mayAct">
<metal:showObjectActions use-macro="app/ui/widgets/ref/macros/objectActions" />
</tal:show>
</td>
</tr>
</tal:row>
</tal:widgets>
</table>
</td></tr>
</table>
<tal:comment replace="nothing">Appy (bottom) navigation</tal:comment>
<metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/>
</fieldset>
<tal:comment replace="nothing">A carriage return needed in some cases.</tal:comment>
<tal:comment replace="nothing">Appy (bottom) navigation</tal:comment>
<metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/>
</tal:anyNumberOfReferences>
</div>

View file

@ -51,6 +51,27 @@ class UserWrapper(AbstractWrapper):
# wants to edit information about himself.
if self.user.has_role('Owner', self): return 'edit'
def setPassword(self, newPassword=None):
'''Sets a p_newPassword for self. If p_newPassword is not given, we
generate one. This method returns the generated password (or simply
p_newPassword if no generation occurred).'''
if newPassword:
msgPart = 'changed'
else:
newPassword = self.getField('password1').generatePassword()
msgPart = 'generated'
login = self.login
zopeUser = self.o.acl_users.getUserById(login)
tool = self.tool.o
zopeUser.__ = tool._encryptPassword(newPassword)
if self.user.getId() == login:
# The user for which we change the password is the currently logged
# user. So update the authentication cookie, too.
tool._updateCookie(login, newPassword)
self.log('Password %s by "%s" for "%s".' % \
(msgPart, self.user.getId(), login))
return newPassword
def getGrantableRoles(self):
'''Returns the list of roles that the admin can grant to a user.'''
res = []
@ -70,7 +91,7 @@ class UserWrapper(AbstractWrapper):
return self._callCustom('validate', new, errors)
def onEdit(self, created):
self.title = self.firstName + ' ' + self.name
self.title = self.login
aclUsers = self.o.acl_users
login = self.login
if created:
@ -89,11 +110,7 @@ class UserWrapper(AbstractWrapper):
zopeUser.roles = self.roles
# Update the password if the user has entered new ones.
rq = self.request
if rq.has_key('password1'):
tool = self.tool.o
zopeUser.__ = tool._encryptPassword(rq['password1'])
# Update the cookie value
tool._updateCookie(login, rq['password1'])
if rq.has_key('password1'): self.setPassword(rq['password1'])
self.password1 = self.password2 = ''
# "self" must be owned by its Zope user.
if 'Owner' not in self.o.get_local_roles_for_userid(login):