[gen] Bugfix while managing languages, ui improvements.
|
@ -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.')
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
|
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
BIN
gen/ui/edit.gif
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 476 B |
4
gen/ui/footer.pt
Normal 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>
|
|
@ -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>
|
||||
|
|
BIN
gen/ui/plus.png
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 251 B |
Before Width: | Height: | Size: 189 B After Width: | Height: | Size: 191 B |
|
@ -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) <= 5">
|
||||
asLinks python: len(languages) <= 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>
|
||||
|
|
|
@ -156,17 +156,16 @@
|
|||
</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">
|
||||
<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>
|
||||
</legend>
|
||||
</div>
|
||||
|
||||
<tal:comment replace="nothing">Appy (top) navigation</tal:comment>
|
||||
<metal:nav use-macro="here/ui/navigate/macros/appyNavigate"/>
|
||||
|
@ -223,9 +222,6 @@
|
|||
|
||||
<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:anyNumberOfReferences>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|