[gen] Changed the way to customize the Config in an app.

This commit is contained in:
Gaetan Delannay 2013-07-24 15:53:19 +02:00
parent 88bd5e5bce
commit 8833f7b0ae
12 changed files with 182 additions and 160 deletions

View file

@ -8,7 +8,7 @@ from appy.px import Px
class OgoneConfig: class OgoneConfig:
'''If you plan, in your app, to perform on-line payments via the Ogone (r) '''If you plan, in your app, to perform on-line payments via the Ogone (r)
system, create an instance of this class in your app and place it in the system, create an instance of this class in your app and place it in the
'ogone' attr of your appy.gen.Config instance.''' 'ogone' attr of your appy.gen.Config class.'''
def __init__(self): def __init__(self):
# self.env refers to the Ogone environment and can be "test" or "prod". # self.env refers to the Ogone environment and can be "test" or "prod".
self.env = 'test' self.env = 'test'
@ -103,7 +103,7 @@ class Ogone(Field):
necessary info for making the payment.''' necessary info for making the payment.'''
tool = obj.getTool() tool = obj.getTool()
# Basic Ogone parameters were generated in the app config module. # Basic Ogone parameters were generated in the app config module.
res = obj.getProductConfig().ogone.copy() res = obj.getProductConfig(True).ogone.copy()
shaKey = res['shaInKey'] shaKey = res['shaInKey']
# Remove elements from the Ogone config that we must not send in the # Remove elements from the Ogone config that we must not send in the
# payment request. # payment request.
@ -139,7 +139,7 @@ class Ogone(Field):
'''Returns True if the SHA-1 signature from Ogone matches retrieved '''Returns True if the SHA-1 signature from Ogone matches retrieved
params.''' params.'''
response = obj.REQUEST.form response = obj.REQUEST.form
shaKey = obj.getProductConfig().ogone['shaOutKey'] shaKey = obj.getProductConfig(True).ogone['shaOutKey']
digest = self.createShaDigest(response, shaKey, digest = self.createShaDigest(response, shaKey,
keysToIgnore=self.noShaOutKeys) keysToIgnore=self.noShaOutKeys)
return digest.lower() == response['SHASIGN'].lower() return digest.lower() == response['SHASIGN'].lower()

View file

@ -554,51 +554,63 @@ class Tool(Model):
class User(Model): class User(Model):
'''If you want to extend or modify the User class, subclass me.''' '''If you want to extend or modify the User class, subclass me.'''
# ------------------------------------------------------------------------------
class LdapConfig:
'''Parameters for authenticating users to an external LDAP.'''
server = '' # Name of the LDAP server
port = None # Port for this server.
# Login and password of the technical power user that the Appy application
# will use to connect to the LDAP.
adminLogin = ''
adminPassword = ''
# LDAP attribute to use as login for authenticating users.
loginAttribute = 'dn' # Can also be "mail", "sAMAccountName", "cn"
baseDn = '' # Base distinguished name where to find users in the LDAP.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class Config: class Config:
'''If you want to specify some configuration parameters for appy.gen and '''If you want to specify some configuration parameters for appy.gen and
your application, please create an instance of this class and modify its your application, please create a class named "Config" in the __init__.py
attributes. You may put your instance anywhere in your application file of your application and override some of the attributes defined
(main package, sub-package, etc).''' here, ie:
# The default Config instance, used if the application does not give one. import appy.gen
defaultConfig = None class Config(appy.gen.Config):
def getDefault(): langages = ('en', 'fr')
if not Config.defaultConfig: '''
Config.defaultConfig = Config()
return Config.defaultConfig
getDefault = staticmethod(getDefault)
def __init__(self): # For every language code that you specify in this list, appy.gen will
# For every language code that you specify in this list, appy.gen will # produce and maintain translation files.
# produce and maintain translation files. languages = ['en']
self.languages = ['en'] # If languageSelector is True, on every page, a language selector will
# If languageSelector is True, on every page, a language selector will # allow to switch between languages defined in self.languages. Else,
# allow to switch between languages defined in self.languages. Else, # the browser-defined language will be used for choosing the language
# the browser-defined language will be used for choosing the language # of returned pages.
# of returned pages. languageSelector = False
self.languageSelector = False # People having one of these roles will be able to create instances
# People having one of these roles will be able to create instances # of classes defined in your application.
# of classes defined in your application. defaultCreators = ['Manager', 'Owner']
self.defaultCreators = ['Manager', 'Owner'] # Number of translations for every page on a Translation object
# Number of translations for every page on a Translation object translationsPerPage = 30
self.translationsPerPage = 30 # Language that will be used as a basis for translating to other
# Language that will be used as a basis for translating to other # languages.
# languages. sourceLanguage = 'en'
self.sourceLanguage = 'en' # Activate or not the button on home page for asking a new password
# Activate or not the button on home page for asking a new password activateForgotPassword = True
self.activateForgotPassword = True # Enable session timeout?
# Enable session timeout? enableSessionTimeout = False
self.enableSessionTimeout = False # If the following field is True, the login/password widget will be
# If the following field is True, the login/password widget will be # discreet. This is for sites where authentication is not foreseen for
# discreet. This is for sites where authentication is not foreseen for # the majority of visitors (just for some administrators).
# the majority of visitors (just for some administrators). discreetLogin = False
self.discreetLogin = False # When using Ogone, place an instance of appy.gen.ogone.OgoneConfig in
# When using Ogone, place an instance of appy.gen.ogone.OgoneConfig in # the field below.
# the field below. ogone = None
self.ogone = None # When using Google analytics, specify here the Analytics ID
# When using Google analytics, specify here the Analytics ID googleAnalyticsId = None
self.googleAnalyticsId = None # Create a group for every global role?
# Create a group for every global role? groupsForGlobalRoles = False
self.groupsForGlobalRoles = False # When using a LDAP for authenticating users, place an instance of class
# LdapConfig above in the field below.
ldap = None
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -146,29 +146,23 @@ class Generator:
self.user = None self.user = None
self.workflows = [] self.workflows = []
self.initialize() self.initialize()
self.config = gen.Config.getDefault() self.config = gen.Config
self.modulesWithTests = set() self.modulesWithTests = set()
self.totalNumberOfTests = 0 self.totalNumberOfTests = 0
def determineAppyType(self, klass): def determineGenType(self, klass):
'''Is p_klass an Appy class ? An Appy workflow? None of this ? '''If p_klass is:
If it (or a parent) declares at least one appy type definition, * a gen-class, this method returns "class";
it will be considered an Appy class. If it (or a parent) declares at * a gen-workflow, this method it "workflow";
least one state definition, it will be considered an Appy * none of it, this method returns None.
workflow.'''
res = 'none' If p_klass declares at least one static attribute that is a
for attrValue in klass.__dict__.itervalues(): appy.fields.Field, it will be considered a gen-class. If p_klass
if isinstance(attrValue, gen.Field): declares at least one static attribute that is a appy.gen.State,
res = 'class' it will be considered a gen-workflow.'''
elif isinstance(attrValue, gen.State): for attr in klass.__dict__.itervalues():
res = 'workflow' if isinstance(attr, gen.Field): return 'class'
if not res: elif isinstance(attr, gen.State): return 'workflow'
for baseClass in klass.__bases__:
baseClassType = self.determineAppyType(baseClass)
if baseClassType != 'none':
res = baseClassType
break
return res
def containsTests(self, moduleOrClass): def containsTests(self, moduleOrClass):
'''Returns True if p_moduleOrClass contains doctests. This method also '''Returns True if p_moduleOrClass contains doctests. This method also
@ -206,53 +200,53 @@ class Generator:
# Find all classes in this module # Find all classes in this module
for name in module.__dict__.keys(): for name in module.__dict__.keys():
exec 'moduleElem = module.%s' % name exec 'moduleElem = module.%s' % name
if (type(moduleElem) == classType) and \ # Ignore non-classes module elements or classes that were imported
(moduleElem.__module__ == module.__name__): # from other modules.
# We have found a Python class definition in this module. if (type(moduleElem) != classType) or \
appyType = self.determineAppyType(moduleElem) (moduleElem.__module__ != module.__name__): continue
if appyType == 'none': continue # Ignore classes that are not gen-classes or gen-workflows.
# Produce a list of static class attributes (in the order genType = self.determineGenType(moduleElem)
# of their definition). if not genType: continue
attrs = astClasses[moduleElem.__name__].attributes # Produce a list of static class attributes (in the order
# Collect non-parsable attrs = back references added # of their definition).
# programmatically attrs = astClasses[moduleElem.__name__].attributes
moreAttrs = [] # Collect non-parsable attrs = back references added
for eName, eValue in moduleElem.__dict__.iteritems(): # programmatically
if isinstance(eValue, gen.Field) and (eName not in attrs): moreAttrs = []
moreAttrs.append(eName) for eName, eValue in moduleElem.__dict__.iteritems():
# Sort them in alphabetical order: else, order would be random if isinstance(eValue, gen.Field) and (eName not in attrs):
moreAttrs.sort() moreAttrs.append(eName)
if moreAttrs: attrs += moreAttrs # Sort them in alphabetical order: else, order would be random
# Add attributes added as back references moreAttrs.sort()
if appyType == 'class': if moreAttrs: attrs += moreAttrs
# Determine the class type (standard, tool, user...) # Add attributes added as back references
if issubclass(moduleElem, gen.Tool): if genType == 'class':
if not self.tool: # Determine the class type (standard, tool, user...)
klass = self.descriptorClasses['tool'] if issubclass(moduleElem, gen.Tool):
self.tool = klass(moduleElem, attrs, self) if not self.tool:
else: klass = self.descriptorClasses['tool']
self.tool.update(moduleElem, attrs) self.tool = klass(moduleElem, attrs, self)
elif issubclass(moduleElem, gen.User):
if not self.user:
klass = self.descriptorClasses['user']
self.user = klass(moduleElem, attrs, self)
else:
self.user.update(moduleElem, attrs)
else: else:
descriptorClass = self.descriptorClasses['class'] self.tool.update(moduleElem, attrs)
descriptor = descriptorClass(moduleElem,attrs, self) elif issubclass(moduleElem, gen.User):
self.classes.append(descriptor) if not self.user:
# Manage classes containing tests klass = self.descriptorClasses['user']
if self.containsTests(moduleElem): self.user = klass(moduleElem, attrs, self)
self.modulesWithTests.add(module.__name__) else:
elif appyType == 'workflow': self.user.update(moduleElem, attrs)
descriptorClass = self.descriptorClasses['workflow'] else:
descriptor = descriptorClass(moduleElem, attrs, self) descriptorClass = self.descriptorClasses['class']
self.workflows.append(descriptor) descriptor = descriptorClass(moduleElem,attrs, self)
if self.containsTests(moduleElem): self.classes.append(descriptor)
self.modulesWithTests.add(module.__name__) # Manage classes containing tests
elif isinstance(moduleElem, gen.Config): if self.containsTests(moduleElem):
self.config = moduleElem self.modulesWithTests.add(module.__name__)
elif genType == 'workflow':
descriptorClass = self.descriptorClasses['workflow']
descriptor = descriptorClass(moduleElem, attrs, self)
self.workflows.append(descriptor)
if self.containsTests(moduleElem):
self.modulesWithTests.add(module.__name__)
def walkApplication(self): def walkApplication(self):
'''This method walks into the application and creates the corresponding '''This method walks into the application and creates the corresponding
@ -262,6 +256,13 @@ class Generator:
sys.path.append(containingFolder) sys.path.append(containingFolder)
# What is the name of the application ? # What is the name of the application ?
appName = os.path.basename(self.application) appName = os.path.basename(self.application)
# Get the app-specific config if any
exec 'import %s as appModule' % appName
if hasattr (appModule, 'Config'):
self.config = appModule.Config
if not issubclass(self.config, gen.Config):
raise Exception('Your Config class must subclass ' \
'appy.gen.Config.')
# Collect modules (only a the first level) in this application. Import # Collect modules (only a the first level) in this application. Import
# them all, to be sure class definitions are complete (ie, back # them all, to be sure class definitions are complete (ie, back
# references are set from one class to the other). Moreover, potential # references are set from one class to the other). Moreover, potential
@ -556,9 +557,6 @@ class ZopeGenerator(Generator):
if theImport not in imports: if theImport not in imports:
imports.append(theImport) imports.append(theImport)
repls['imports'] = '\n'.join(imports) repls['imports'] = '\n'.join(imports)
# Compute default add roles
repls['defaultAddRoles'] = ','.join(
['"%s"' % r for r in self.config.defaultCreators])
# Compute list of add permissions # Compute list of add permissions
addPermissions = '' addPermissions = ''
for classDescr in classesAll: for classDescr in classesAll:
@ -599,16 +597,6 @@ class ZopeGenerator(Generator):
repls['gRoles'] = ','.join(['"%s"' % r.name for r in globalRoles]) repls['gRoles'] = ','.join(['"%s"' % r.name for r in globalRoles])
grantableRoles = self.getAllUsedRoles(local=False, grantable=True) grantableRoles = self.getAllUsedRoles(local=False, grantable=True)
repls['grRoles'] = ','.join(['"%s"' % r.name for r in grantableRoles]) repls['grRoles'] = ','.join(['"%s"' % r.name for r in grantableRoles])
# Generate configuration options
repls['languages'] = ','.join('"%s"' % l for l in self.config.languages)
repls['languageSelector'] = self.config.languageSelector
repls['sourceLanguage'] = self.config.sourceLanguage
repls['enableSessionTimeout'] = self.config.enableSessionTimeout
repls['discreetLogin'] = self.config.discreetLogin
repls['ogone'] = repr(self.config.ogone)
repls['googleAnalyticsId'] = repr(self.config.googleAnalyticsId)
repls['activateForgotPassword'] = self.config.activateForgotPassword
repls['groupsForGlobalRoles'] = self.config.groupsForGlobalRoles
self.copyFile('config.pyt', repls, destName='config.py') self.copyFile('config.pyt', repls, destName='config.py')
def generateInit(self): def generateInit(self):

View file

@ -75,7 +75,7 @@ class ZopeInstaller:
self.classes = classes self.classes = classes
# Unwrap some useful config variables # Unwrap some useful config variables
self.productName = config.PROJECTNAME self.productName = config.PROJECTNAME
self.languages = config.languages self.languages = config.appConfig.languages
self.logger = config.logger self.logger = config.logger
self.addContentPermissions = config.ADD_CONTENT_PERMISSIONS self.addContentPermissions = config.ADD_CONTENT_PERMISSIONS
@ -251,7 +251,7 @@ class ZopeInstaller:
# Create a group for every global role defined in the application # Create a group for every global role defined in the application
# (if required). # (if required).
if self.app.config.getProductConfig().groupsForGlobalRoles: if self.config.appConfig.groupsForGlobalRoles:
for role in self.config.applicationGlobalRoles: for role in self.config.applicationGlobalRoles:
groupId = role.lower() groupId = role.lower()
if appyTool.count('Group', noSecurity=True, login=groupId): if appyTool.count('Group', noSecurity=True, login=groupId):
@ -333,7 +333,7 @@ class ZopeInstaller:
# launching Zope in test mode, the temp folder does not exist. # launching Zope in test mode, the temp folder does not exist.
if not hasattr(self.app, 'temp_folder'): return if not hasattr(self.app, 'temp_folder'): return
sessionData = self.app.temp_folder.session_data sessionData = self.app.temp_folder.session_data
if self.config.enableSessionTimeout: if self.config.appConfig.enableSessionTimeout:
sessionData.setDelNotificationTarget(onDelSession) sessionData.setDelNotificationTarget(onDelSession)
else: else:
sessionData.setDelNotificationTarget(None) sessionData.setDelNotificationTarget(None)

5
gen/ldap.py Normal file
View file

@ -0,0 +1,5 @@
# ------------------------------------------------------------------------------
def authenticate(login, password, ldapConfig, tool):
'''Tries to authenticate user p_login in the LDAP.'''
return
# ------------------------------------------------------------------------------

View file

@ -2,7 +2,7 @@
import os, os.path, sys, re, time, random, types, base64, urllib import os, os.path, sys, re, time, random, types, base64, urllib
from appy import Object from appy import Object
import appy.gen import appy.gen
from appy.gen import Search, String, Page from appy.gen import Search, String, Page, ldap
from appy.gen.utils import SomeObjects, getClassName, GroupDescr, SearchDescr from appy.gen.utils import SomeObjects, getClassName, GroupDescr, SearchDescr
from appy.gen.mixins import BaseMixin from appy.gen.mixins import BaseMixin
from appy.gen.wrappers import AbstractWrapper from appy.gen.wrappers import AbstractWrapper
@ -139,6 +139,8 @@ class ToolMixin(BaseMixin):
'''Gets attribute named p_name.''' '''Gets attribute named p_name.'''
if source == 'config': if source == 'config':
obj = self.getProductConfig() obj = self.getProductConfig()
elif source == 'app':
obj = self.getProductConfig(True)
else: else:
obj = self.appy() obj = self.appy()
return getattr(obj, name, None) return getattr(obj, name, None)
@ -160,7 +162,7 @@ class ToolMixin(BaseMixin):
'''We must show the language selector if the app config requires it and '''We must show the language selector if the app config requires it and
it there is more than 2 supported languages. Moreover, on some pages, it there is more than 2 supported languages. Moreover, on some pages,
switching the language is not allowed.''' switching the language is not allowed.'''
cfg = self.getProductConfig() cfg = self.getProductConfig(True)
if not cfg.languageSelector: return if not cfg.languageSelector: return
if len(cfg.languages) < 2: return if len(cfg.languages) < 2: return
page = self.REQUEST.get('ACTUAL_URL').split('/')[-1] page = self.REQUEST.get('ACTUAL_URL').split('/')[-1]
@ -168,11 +170,11 @@ class ToolMixin(BaseMixin):
def showForgotPassword(self): def showForgotPassword(self):
'''We must show link "forgot password?" when the app requires it.''' '''We must show link "forgot password?" when the app requires it.'''
return self.getProductConfig().activateForgotPassword return self.getProductConfig(True).activateForgotPassword
def getLanguages(self): def getLanguages(self):
'''Returns the supported languages. First one is the default.''' '''Returns the supported languages. First one is the default.'''
return self.getProductConfig().languages return self.getProductConfig(True).languages
def getLanguageName(self, code): def getLanguageName(self, code):
'''Gets the language name (in this language) from a 2-chars language '''Gets the language name (in this language) from a 2-chars language
@ -1005,27 +1007,43 @@ class ToolMixin(BaseMixin):
'''Returns the encrypted version of clear p_password.''' '''Returns the encrypted version of clear p_password.'''
return self.acl_users._encryptPassword(password) return self.acl_users._encryptPassword(password)
def _zopeAuthenticate(self, request):
'''Performs the Zope-level authentication. Returns True if
authentication succeeds.'''
user = self.acl_users.validate(rq)
return not self.userIsAnon()
def _ldapAuthenticate(self, login, password):
'''Performs a LDAP-based authentication. Returns True if authentication
succeeds.'''
# Check if LDAP is configured.
ldapConfig = self.getProductConfig(True).ldap
if not ldapConfig: return
user = ldap.authenticate(login, password, ldapConfig, self)
if not user: return
return True
def performLogin(self): def performLogin(self):
'''Logs the user in.''' '''Logs the user in.'''
rq = self.REQUEST rq = self.REQUEST
jsEnabled = rq.get('js_enabled', False) in ('1', 1) jsEnabled = rq.get('js_enabled', False) in ('1', 1)
cookiesEnabled = rq.get('cookies_enabled', False) in ('1', 1) cookiesEnabled = rq.get('cookies_enabled', False) in ('1', 1)
urlBack = rq['HTTP_REFERER'] urlBack = rq['HTTP_REFERER']
if jsEnabled and not cookiesEnabled: if jsEnabled and not cookiesEnabled:
msg = self.translate('enable_cookies') msg = self.translate('enable_cookies')
return self.goto(urlBack, msg) return self.goto(urlBack, msg)
# Perform the Zope-level authentication # Extract the login and password
login = rq.get('__ac_name', '') login = rq.get('__ac_name', '')
self._updateCookie(login, rq.get('__ac_password', '')) password = rq.get('__ac_password', '')
user = self.acl_users.validate(rq) # Perform the Zope-level authentication
if self.userIsAnon(): self._updateCookie(login, password)
rq.RESPONSE.expireCookie('__ac', path='/') if self._zopeAuthenticate(rq) or self._ldapAuthenticate(login,password):
msg = self.translate('login_ko')
logMsg = 'Authentication failed (tried with login "%s").' % login
else:
msg = self.translate('login_ok') msg = self.translate('login_ok')
logMsg = 'User "%s" logged in.' % login logMsg = 'User "%s" logged in.' % login
else:
rq.RESPONSE.expireCookie('__ac', path='/')
msg = self.translate('login_ko')
logMsg = 'Authentication failed with login "%s".' % login
self.log(logMsg) self.log(logMsg)
return self.goto(self.getApp().absolute_url(), msg) return self.goto(self.getApp().absolute_url(), msg)
@ -1303,7 +1321,7 @@ class ToolMixin(BaseMixin):
# Disable Google Analytics when we are in debug mode. # Disable Google Analytics when we are in debug mode.
if self.isDebug(): return if self.isDebug(): return
# Disable Google Analytics if no ID is found in the config. # Disable Google Analytics if no ID is found in the config.
gaId = self.getProductConfig().googleAnalyticsId gaId = self.getProductConfig(True).googleAnalyticsId
if not gaId: return if not gaId: return
# Google Analytics must be enabled: return the chunk of Javascript # Google Analytics must be enabled: return the chunk of Javascript
# code specified by Google. # code specified by Google.

View file

@ -1646,9 +1646,12 @@ class BaseMixin:
'''Returns the application tool.''' '''Returns the application tool.'''
return self.getPhysicalRoot().config return self.getPhysicalRoot().config
def getProductConfig(self): def getProductConfig(self, app=False):
'''Returns a reference to the config module.''' '''Returns a reference to the config module. If p_app is True, it
return self.__class__.config returns the application config.'''
res = self.__class__.config
if app: res = res.appConfig
return res
def getParent(self): def getParent(self):
'''If this object is stored within another one, this method returns it. '''If this object is stored within another one, this method returns it.

View file

@ -164,6 +164,9 @@ class User(ModelClass):
def showRoles(self): pass def showRoles(self): pass
roles = gen.String(show=showRoles, indexed=True, roles = gen.String(show=showRoles, indexed=True,
validator=gen.Selection('getGrantableRoles'), **gm) validator=gen.Selection('getGrantableRoles'), **gm)
# Where is this user stored? By default, in the ZODB. But the user can be
# stored in an external LDAP.
source = gen.String(show=False, default='zodb', layouts='f', **gm)
# The Group class -------------------------------------------------------------- # The Group class --------------------------------------------------------------
class Group(ModelClass): class Group(ModelClass):

View file

@ -24,7 +24,6 @@ logger = logging.getLogger('<!applicationName!>')
# Some global variables -------------------------------------------------------- # Some global variables --------------------------------------------------------
PROJECTNAME = '<!applicationName!>' PROJECTNAME = '<!applicationName!>'
diskFolder = os.path.dirname(<!applicationName!>.__file__) diskFolder = os.path.dirname(<!applicationName!>.__file__)
defaultAddRoles = [<!defaultAddRoles!>]
ADD_CONTENT_PERMISSIONS = { ADD_CONTENT_PERMISSIONS = {
<!addPermissions!>} <!addPermissions!>}
@ -43,16 +42,10 @@ applicationRoles = [<!roles!>]
applicationGlobalRoles = [<!gRoles!>] applicationGlobalRoles = [<!gRoles!>]
grantableRoles = [<!grRoles!>] grantableRoles = [<!grRoles!>]
# Configuration options try:
languages = [<!languages!>] appConfig = <!applicationName!>.Config
languageSelector = <!languageSelector!> except AttributeError:
sourceLanguage = '<!sourceLanguage!>' appConfig = appy.gen.Config
activateForgotPassword = <!activateForgotPassword!>
enableSessionTimeout = <!enableSessionTimeout!>
discreetLogin = <!discreetLogin!>
ogone = <!ogone!>
googleAnalyticsId = <!googleAnalyticsId!>
groupsForGlobalRoles = <!groupsForGlobalRoles!>
# When Zope is starting or runs in test mode, there is no request object. We # When Zope is starting or runs in test mode, there is no request object. We
# create here a fake one for storing Appy wrappers. # create here a fake one for storing Appy wrappers.

View file

@ -16,7 +16,7 @@
contextObj python: tool.getPublishedObject(layoutType) or tool.getHomeObject(); contextObj python: tool.getPublishedObject(layoutType) or tool.getHomeObject();
showPortlet python: tool.showPortlet(context, layoutType); showPortlet python: tool.showPortlet(context, layoutType);
dir python: tool.getLanguageDirection(lang); dir python: tool.getLanguageDirection(lang);
discreetLogin python: tool.getAttr('discreetLogin', source='config'); discreetLogin python: tool.getAttr('discreetLogin', source='app');
dleft python: (dir == 'ltr') and 'left' or 'right'; dleft python: (dir == 'ltr') and 'left' or 'right';
dright python: (dir == 'ltr') and 'right' or 'left'; dright python: (dir == 'ltr') and 'right' or 'left';
x python: resp.setHeader('Content-type', 'text/html;;charset=UTF-8'); x python: resp.setHeader('Content-type', 'text/html;;charset=UTF-8');

View file

@ -14,7 +14,7 @@ class TranslationWrapper(AbstractWrapper):
# either from the config object. # either from the config object.
sourceLanguage = self.sourceLanguage sourceLanguage = self.sourceLanguage
if not sourceLanguage: if not sourceLanguage:
sourceLanguage = self.o.getProductConfig().sourceLanguage sourceLanguage = self.o.getProductConfig(True).sourceLanguage
sourceTranslation = getattr(tool.o, sourceLanguage).appy() sourceTranslation = getattr(tool.o, sourceLanguage).appy()
# p_field is the Computed field. We need to get the name of the # p_field is the Computed field. We need to get the name of the
# corresponding field holding the translation message. # corresponding field holding the translation message.
@ -43,7 +43,7 @@ class TranslationWrapper(AbstractWrapper):
if field.type == 'Computed': name = field.name[:-6] if field.type == 'Computed': name = field.name[:-6]
else: name = field.name else: name = field.name
# Get the source message # Get the source message
sourceLanguage = self.o.getProductConfig().sourceLanguage sourceLanguage = self.o.getProductConfig(True).sourceLanguage
sourceTranslation = getattr(tool.o, sourceLanguage).appy() sourceTranslation = getattr(tool.o, sourceLanguage).appy()
sourceMsg = getattr(sourceTranslation, name) sourceMsg = getattr(sourceTranslation, name)
if field.isEmptyValue(sourceMsg): return False if field.isEmptyValue(sourceMsg): return False

View file

@ -360,7 +360,7 @@ class AbstractWrapper(object):
obj = zobj and zobj.appy() or None; obj = zobj and zobj.appy() or None;
showPortlet=ztool.showPortlet(zobj, layoutType); showPortlet=ztool.showPortlet(zobj, layoutType);
dir=ztool.getLanguageDirection(lang); dir=ztool.getLanguageDirection(lang);
discreetLogin=ztool.getAttr('discreetLogin', source='config'); discreetLogin=ztool.getProductConfig(True).discreetLogin;
dleft=(dir == 'ltr') and 'left' or 'right'; dleft=(dir == 'ltr') and 'left' or 'right';
dright=(dir == 'ltr') and 'right' or 'left'; dright=(dir == 'ltr') and 'right' or 'left';
x=resp.setHeader('Content-type', ztool.xhtmlEncoding); x=resp.setHeader('Content-type', ztool.xhtmlEncoding);
@ -879,7 +879,7 @@ class AbstractWrapper(object):
p_cfg is the product config that holds the default value.''' p_cfg is the product config that holds the default value.'''
res = klass._getParentAttr('creators') res = klass._getParentAttr('creators')
# Return default creators if no creators was found. # Return default creators if no creators was found.
if not res: res = cfg.defaultAddRoles if not res: res = cfg.appConfig.defaultCreators
return res return res
@classmethod @classmethod