2009-06-29 07:06:01 -05:00
|
|
|
'''This package contains stuff used at run-time for installing a generated
|
2011-12-01 13:53:13 -06:00
|
|
|
Zope product.'''
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2010-01-17 15:01:14 -06:00
|
|
|
import os, os.path, time
|
2009-10-18 07:52:27 -05:00
|
|
|
import appy
|
2011-09-26 14:19:34 -05:00
|
|
|
import appy.version
|
2011-12-05 08:11:29 -06:00
|
|
|
import appy.gen as gen
|
2011-01-14 02:06:25 -06:00
|
|
|
from appy.gen.po import PoParser
|
2011-12-01 13:53:13 -06:00
|
|
|
from appy.gen.utils import updateRolesForPermission, createObject
|
2012-09-26 16:13:02 -05:00
|
|
|
from appy.gen.indexer import defaultIndexes, updateIndexes
|
2012-02-02 10:30:54 -06:00
|
|
|
from appy.gen.migrator import Migrator
|
2011-01-14 02:06:25 -06:00
|
|
|
from appy.shared.data import languages
|
2010-08-05 11:23:17 -05:00
|
|
|
|
2011-11-25 11:01:20 -06:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
homePage = '''
|
2012-02-18 12:48:00 -06:00
|
|
|
<tal:hp define="tool python: context.config;
|
|
|
|
dummy python: request.RESPONSE.redirect(tool.getHomePage())">
|
|
|
|
</tal:hp>
|
2011-11-25 11:01:20 -06:00
|
|
|
'''
|
|
|
|
errorPage = '''
|
2012-01-04 11:03:46 -06:00
|
|
|
<tal:main define="tool python: context.config"
|
|
|
|
on-error="string: ServerError">
|
2011-11-25 11:01:20 -06:00
|
|
|
<html metal:use-macro="context/ui/template/macros/main">
|
|
|
|
<div metal:fill-slot="content" tal:define="o python:options">
|
|
|
|
<p tal:condition="o/error_message"
|
|
|
|
tal:content="structure o/error_message"></p>
|
|
|
|
<p>Error type: <b><span tal:replace="o/error_type"/></b></p>
|
|
|
|
<p>Error value: <b><span tal:replace="o/error_value"/></b></p>
|
|
|
|
<p tal:content="structure o/error_tb"></p>
|
|
|
|
</div>
|
|
|
|
</html>
|
|
|
|
</tal:main>
|
|
|
|
'''
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2010-01-17 15:01:14 -06:00
|
|
|
# Stuff for tracking user activity ---------------------------------------------
|
|
|
|
loggedUsers = {}
|
|
|
|
originalTraverse = None
|
|
|
|
doNotTrack = ('.jpg','.gif','.png','.js','.class','.css')
|
|
|
|
|
|
|
|
def traverseWrapper(self, path, response=None, validated_hook=None):
|
|
|
|
'''This function is called every time a users gets a URL, this is used for
|
|
|
|
tracking user activity. self is a BaseRequest'''
|
|
|
|
res = originalTraverse(self, path, response, validated_hook)
|
|
|
|
t = time.time()
|
|
|
|
if os.path.splitext(path)[-1].lower() not in doNotTrack:
|
|
|
|
# Do nothing when the user gets non-pages
|
|
|
|
userId = self['AUTHENTICATED_USER'].getId()
|
2011-06-10 10:20:09 -05:00
|
|
|
if userId:
|
|
|
|
loggedUsers[userId] = t
|
|
|
|
# "Touch" the SESSION object. Else, expiration won't occur.
|
|
|
|
session = self.SESSION
|
2010-01-17 15:01:14 -06:00
|
|
|
return res
|
|
|
|
|
2011-06-10 10:20:09 -05:00
|
|
|
def onDelSession(sessionObject, container):
|
|
|
|
'''This function is called when a session expires.'''
|
|
|
|
rq = container.REQUEST
|
|
|
|
if rq.cookies.has_key('__ac') and rq.cookies.has_key('_ZopeId') and \
|
|
|
|
(rq['_ZopeId'] == sessionObject.token):
|
|
|
|
# The request comes from a guy whose session has expired.
|
|
|
|
resp = rq.RESPONSE
|
|
|
|
resp.expireCookie('__ac', path='/')
|
2012-04-25 09:21:23 -05:00
|
|
|
resp.setHeader('Content-Type', 'text/html')
|
2011-06-10 10:20:09 -05:00
|
|
|
resp.write('<center>For security reasons, your session has ' \
|
|
|
|
'expired.</center>')
|
|
|
|
|
2009-06-29 07:06:01 -05:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class ZopeInstaller:
|
|
|
|
'''This Zope installer runs every time Zope starts and encounters this
|
|
|
|
generated Zope product.'''
|
2011-11-25 11:01:20 -06:00
|
|
|
def __init__(self, zopeContext, config, classes):
|
2009-06-29 07:06:01 -05:00
|
|
|
self.zopeContext = zopeContext
|
2011-11-25 11:01:20 -06:00
|
|
|
self.app = zopeContext._ProductContext__app # The root of the Zope tree
|
|
|
|
self.config = config
|
2010-08-05 11:23:17 -05:00
|
|
|
self.classes = classes
|
2010-09-02 09:16:08 -05:00
|
|
|
# Unwrap some useful config variables
|
2011-11-25 11:01:20 -06:00
|
|
|
self.productName = config.PROJECTNAME
|
|
|
|
self.languages = config.languages
|
|
|
|
self.logger = config.logger
|
|
|
|
self.addContentPermissions = config.ADD_CONTENT_PERMISSIONS
|
|
|
|
|
|
|
|
def installUi(self):
|
|
|
|
'''Installs the user interface.'''
|
|
|
|
# Some useful imports
|
2012-02-02 10:30:54 -06:00
|
|
|
from OFS.Folder import manage_addFolder
|
|
|
|
from OFS.Image import manage_addImage, manage_addFile
|
2011-11-25 11:01:20 -06:00
|
|
|
from Products.PythonScripts.PythonScript import PythonScript
|
|
|
|
from Products.PageTemplates.ZopePageTemplate import \
|
|
|
|
manage_addPageTemplate
|
2012-02-02 10:30:54 -06:00
|
|
|
# Delete the existing folder if it existed.
|
|
|
|
zopeContent = self.app.objectIds()
|
|
|
|
if 'ui' in zopeContent: self.app.manage_delObjects(['ui'])
|
|
|
|
manage_addFolder(self.app, 'ui')
|
2012-02-18 12:48:00 -06:00
|
|
|
# Browse the physical ui folders (the Appy one and an app-specific, if
|
|
|
|
# the app defines one) and create the corresponding objects in the Zope
|
|
|
|
# folder. In the case of files having the same name in both folders,
|
|
|
|
# the one from the app-specific folder is chosen.
|
2011-11-25 11:01:20 -06:00
|
|
|
j = os.path.join
|
2012-02-18 12:48:00 -06:00
|
|
|
uiFolders = [j(j(appy.getPath(), 'gen'), 'ui')]
|
|
|
|
appUi = j(self.config.diskFolder, 'ui')
|
|
|
|
if os.path.exists(appUi): uiFolders.insert(0, appUi)
|
|
|
|
for ui in uiFolders:
|
|
|
|
for root, dirs, files in os.walk(ui):
|
|
|
|
folderName = root[len(ui):]
|
|
|
|
# Get the Zope folder that corresponds to this name
|
|
|
|
zopeFolder = self.app.ui
|
|
|
|
if folderName:
|
|
|
|
for name in folderName.strip(os.sep).split(os.sep):
|
|
|
|
zopeFolder = zopeFolder._getOb(name)
|
|
|
|
# Create sub-folders at this level
|
|
|
|
for name in dirs:
|
|
|
|
if not hasattr(zopeFolder.aq_base, name):
|
|
|
|
manage_addFolder(zopeFolder, name)
|
|
|
|
# Create files at this level
|
|
|
|
for name in files:
|
|
|
|
zopeName, ext = os.path.splitext(name)
|
|
|
|
if ext not in ('.pt', '.py'):
|
|
|
|
# In the ZODB, pages and scripts have their name without
|
|
|
|
# their extension.
|
|
|
|
zopeName = name
|
|
|
|
if hasattr(zopeFolder.aq_base, zopeName): continue
|
|
|
|
f = file(j(root, name))
|
2012-09-20 02:37:33 -05:00
|
|
|
if zopeName == 'favicon.ico':
|
|
|
|
if not hasattr(self.app, zopeName):
|
|
|
|
# Copy it at the root. Else, IE won't notice it.
|
|
|
|
manage_addImage(self.app, zopeName, f)
|
|
|
|
elif ext in gen.File.imageExts:
|
2012-02-18 12:48:00 -06:00
|
|
|
manage_addImage(zopeFolder, zopeName, f)
|
|
|
|
elif ext == '.pt':
|
|
|
|
manage_addPageTemplate(zopeFolder,zopeName,'',f.read())
|
|
|
|
elif ext == '.py':
|
|
|
|
obj = PythonScript(zopeName)
|
|
|
|
zopeFolder._setObject(zopeName, obj)
|
|
|
|
zopeFolder._getOb(zopeName).write(f.read())
|
|
|
|
else:
|
|
|
|
manage_addFile(zopeFolder, zopeName, f)
|
|
|
|
f.close()
|
2011-11-25 11:01:20 -06:00
|
|
|
# Update the home page
|
|
|
|
if 'index_html' in zopeContent:
|
|
|
|
self.app.manage_delObjects(['index_html'])
|
|
|
|
manage_addPageTemplate(self.app, 'index_html', '', homePage)
|
|
|
|
# Update the error page
|
|
|
|
if 'standard_error_message' in zopeContent:
|
|
|
|
self.app.manage_delObjects(['standard_error_message'])
|
|
|
|
manage_addPageTemplate(self.app, 'standard_error_message', '',errorPage)
|
|
|
|
|
|
|
|
def installCatalog(self):
|
|
|
|
'''Create the catalog at the root of Zope if id does not exist.'''
|
|
|
|
if 'catalog' not in self.app.objectIds():
|
|
|
|
# Create the catalog
|
|
|
|
from Products.ZCatalog.ZCatalog import manage_addZCatalog
|
|
|
|
manage_addZCatalog(self.app, 'catalog', '')
|
|
|
|
self.logger.info('Appy catalog created.')
|
2011-12-01 13:53:13 -06:00
|
|
|
|
2012-09-26 16:13:02 -05:00
|
|
|
# Create lexicons for ZCTextIndexes
|
|
|
|
catalog = self.app.catalog
|
|
|
|
lexicons = catalog.objectIds()
|
|
|
|
from Products.ZCTextIndex.ZCTextIndex import manage_addLexicon
|
|
|
|
if 'xhtml_lexicon' not in lexicons:
|
|
|
|
lex = appy.Object(group='XHTML indexer', name='XHTML indexer')
|
|
|
|
manage_addLexicon(catalog, 'xhtml_lexicon', elements=[lex])
|
|
|
|
if 'text_lexicon' not in lexicons:
|
|
|
|
lex = appy.Object(group='Text indexer', name='Text indexer')
|
|
|
|
manage_addLexicon(catalog, 'text_lexicon', elements=[lex])
|
|
|
|
if 'list_lexicon' not in lexicons:
|
|
|
|
lex = appy.Object(group='List indexer', name='List indexer')
|
|
|
|
manage_addLexicon(catalog, 'list_lexicon', elements=[lex])
|
|
|
|
|
|
|
|
# Delete the deprecated one if it exists
|
|
|
|
if 'lexicon' in lexicons: catalog.manage_delObjects(['lexicon'])
|
2011-12-01 13:53:13 -06:00
|
|
|
|
2011-11-25 11:01:20 -06:00
|
|
|
# Create or update Appy-wide indexes and field-related indexes
|
2012-09-26 16:13:02 -05:00
|
|
|
indexInfo = defaultIndexes.copy()
|
2011-11-25 11:01:20 -06:00
|
|
|
tool = self.app.config
|
|
|
|
for className in self.config.attributes.iterkeys():
|
|
|
|
wrapperClass = tool.getAppyClass(className, wrapper=True)
|
2012-09-04 11:00:22 -05:00
|
|
|
indexInfo.update(wrapperClass.getIndexes(includeDefaults=False))
|
2012-09-26 16:13:02 -05:00
|
|
|
updateIndexes(self, indexInfo)
|
2011-11-25 11:01:20 -06:00
|
|
|
|
2011-11-28 15:50:01 -06:00
|
|
|
def getAddPermission(self, className):
|
|
|
|
'''What is the name of the permission allowing to create instances of
|
|
|
|
class whose name is p_className?'''
|
|
|
|
return self.productName + ': Add ' + className
|
|
|
|
|
2011-11-25 11:01:20 -06:00
|
|
|
def installBaseObjects(self):
|
|
|
|
'''Creates the tool and the root data folder if they do not exist.'''
|
|
|
|
# Create or update the base folder for storing data
|
|
|
|
zopeContent = self.app.objectIds()
|
2012-02-02 10:30:54 -06:00
|
|
|
from OFS.Folder import manage_addFolder
|
2011-11-28 15:50:01 -06:00
|
|
|
|
2012-06-02 07:36:49 -05:00
|
|
|
if 'config' not in zopeContent:
|
|
|
|
toolName = '%sTool' % self.productName
|
|
|
|
createObject(self.app, 'config', toolName, self.productName,
|
|
|
|
wf=False, noSecurity=True)
|
|
|
|
|
2011-11-28 15:50:01 -06:00
|
|
|
if 'data' not in zopeContent:
|
2012-02-02 10:30:54 -06:00
|
|
|
manage_addFolder(self.app, 'data')
|
2011-11-28 15:50:01 -06:00
|
|
|
data = self.app.data
|
2012-06-02 07:36:49 -05:00
|
|
|
tool = self.app.config
|
2011-11-28 15:50:01 -06:00
|
|
|
# Manager has been granted Add permissions for all root classes.
|
|
|
|
# This may not be desired, so remove this.
|
|
|
|
for className in self.config.rootClasses:
|
|
|
|
permission = self.getAddPermission(className)
|
|
|
|
data.manage_permission(permission, (), acquire=0)
|
|
|
|
# All roles defined as creators should be able to create the
|
|
|
|
# corresponding root classes in this folder.
|
|
|
|
i = -1
|
|
|
|
for klass in self.config.appClasses:
|
|
|
|
i += 1
|
|
|
|
if not klass.__dict__.has_key('root') or \
|
|
|
|
not klass.__dict__['root']:
|
|
|
|
continue # It is not a root class
|
|
|
|
className = self.config.appClassNames[i]
|
2012-06-02 07:36:49 -05:00
|
|
|
wrapperClass = tool.getAppyClass(className, wrapper=True)
|
|
|
|
creators = wrapperClass.getCreators(self.config)
|
2011-11-28 15:50:01 -06:00
|
|
|
permission = self.getAddPermission(className)
|
|
|
|
updateRolesForPermission(permission, tuple(creators), data)
|
|
|
|
|
|
|
|
# Remove some default objects created by Zope but not useful to Appy
|
2011-11-25 11:01:20 -06:00
|
|
|
for name in ('standard_html_footer', 'standard_html_header',\
|
|
|
|
'standard_template.pt'):
|
|
|
|
if name in zopeContent: self.app.manage_delObjects([name])
|
|
|
|
|
|
|
|
def installTool(self):
|
|
|
|
'''Updates the tool (now that the catalog is created) and updates its
|
2011-11-28 15:50:01 -06:00
|
|
|
inner objects (users, groups, translations, documents).'''
|
2011-11-25 11:01:20 -06:00
|
|
|
tool = self.app.config
|
|
|
|
tool.createOrUpdate(True, None)
|
|
|
|
tool.refreshSecurity()
|
|
|
|
appyTool = tool.appy()
|
2011-12-01 13:53:13 -06:00
|
|
|
appyTool.log('Appy version is "%s".' % appy.version.short)
|
2011-11-25 11:01:20 -06:00
|
|
|
|
2012-09-13 07:43:40 -05:00
|
|
|
# Create the admin user if it does not exist.
|
|
|
|
if not appyTool.count('User', noSecurity=True, login='admin'):
|
2012-06-02 07:36:49 -05:00
|
|
|
appyTool.create('users', noSecurity=True, login='admin',
|
|
|
|
password1='admin', password2='admin',
|
2012-02-16 11:13:51 -06:00
|
|
|
email='admin@appyframework.org', roles=['Manager'])
|
2011-11-25 11:01:20 -06:00
|
|
|
appyTool.log('Admin user "admin" created.')
|
2011-11-28 15:50:01 -06:00
|
|
|
|
|
|
|
# Create group "admins" if it does not exist
|
2012-05-05 10:04:19 -05:00
|
|
|
if not appyTool.count('Group', noSecurity=True, login='admins'):
|
2012-06-02 07:36:49 -05:00
|
|
|
appyTool.create('groups', noSecurity=True, login='admins',
|
|
|
|
title='Administrators', roles=['Manager'])
|
2011-11-28 15:50:01 -06:00
|
|
|
appyTool.log('Group "admins" created.')
|
|
|
|
|
|
|
|
# Create a group for every global role defined in the application
|
|
|
|
for role in self.config.applicationGlobalRoles:
|
|
|
|
relatedGroup = '%s_group' % role
|
2012-05-05 10:04:19 -05:00
|
|
|
if appyTool.count('Group', noSecurity=True, login=relatedGroup):
|
|
|
|
continue
|
2012-06-02 07:36:49 -05:00
|
|
|
appyTool.create('groups', noSecurity=True, login=relatedGroup,
|
|
|
|
title=relatedGroup, roles=[role])
|
2011-11-28 15:50:01 -06:00
|
|
|
appyTool.log('Group "%s", related to global role "%s", was ' \
|
|
|
|
'created.' % (relatedGroup, role))
|
|
|
|
|
2011-11-25 11:01:20 -06:00
|
|
|
# Create POD templates within the tool if required
|
|
|
|
for contentType in self.config.attributes.iterkeys():
|
|
|
|
appyClass = tool.getAppyClass(contentType)
|
|
|
|
if not appyClass: continue # May be an abstract class
|
|
|
|
wrapperClass = tool.getAppyClass(contentType, wrapper=True)
|
|
|
|
for appyType in wrapperClass.__fields__:
|
|
|
|
if appyType.type != 'Pod': continue
|
|
|
|
# Find the attribute that stores the template, and store on
|
|
|
|
# it the default one specified in the appyType if no
|
|
|
|
# template is stored yet.
|
|
|
|
attrName = appyTool.getAttributeName('podTemplate', appyClass,
|
|
|
|
appyType.name)
|
|
|
|
fileObject = getattr(appyTool, attrName)
|
|
|
|
if not fileObject or (fileObject.size == 0):
|
|
|
|
# There is no file. Put the one specified in the appyType.
|
|
|
|
fileName = os.path.join(appyTool.getDiskFolder(),
|
|
|
|
appyType.template)
|
|
|
|
if os.path.exists(fileName):
|
|
|
|
setattr(appyTool, attrName, fileName)
|
|
|
|
appyTool.log('Imported "%s" in the tool in ' \
|
|
|
|
'attribute "%s"'% (fileName, attrName))
|
|
|
|
else:
|
|
|
|
appyTool.log('Template "%s" was not found!' % \
|
|
|
|
fileName, type='error')
|
|
|
|
|
|
|
|
# Create or update Translation objects
|
|
|
|
translations = [t.o.id for t in appyTool.translations]
|
|
|
|
# We browse the languages supported by this application and check
|
|
|
|
# whether we need to create the corresponding Translation objects.
|
|
|
|
for language in self.languages:
|
|
|
|
if language in translations: continue
|
|
|
|
# We will create, in the tool, the translation object for this
|
|
|
|
# language. Determine first its title.
|
|
|
|
langId, langEn, langNat = languages.get(language)
|
|
|
|
if langEn != langNat:
|
|
|
|
title = '%s (%s)' % (langEn, langNat)
|
|
|
|
else:
|
|
|
|
title = langEn
|
2012-06-02 07:36:49 -05:00
|
|
|
appyTool.create('translations', noSecurity=True,
|
|
|
|
id=language, title=title)
|
2011-11-25 11:01:20 -06:00
|
|
|
appyTool.log('Translation object created for "%s".' % language)
|
|
|
|
|
|
|
|
# Execute custom installation code if any
|
2012-02-21 05:09:42 -06:00
|
|
|
if hasattr(appyTool, 'onInstall'): appyTool.onInstall()
|
2011-11-25 11:01:20 -06:00
|
|
|
|
2012-06-03 14:55:26 -05:00
|
|
|
# Now, if required, we synchronise every Translation object with the
|
|
|
|
# corresponding "po" file on disk.
|
|
|
|
if appyTool.loadTranslationsAtStartup:
|
|
|
|
appFolder = self.config.diskFolder
|
|
|
|
appName = self.config.PROJECTNAME
|
|
|
|
i18nFolder = os.path.join(appFolder, 'tr')
|
|
|
|
for translation in appyTool.translations:
|
|
|
|
# Get the "po" file
|
|
|
|
poName = '%s-%s.po' % (appName, translation.id)
|
|
|
|
poFile = PoParser(os.path.join(i18nFolder, poName)).parse()
|
|
|
|
for message in poFile.messages:
|
|
|
|
setattr(translation, message.id, message.getMessage())
|
|
|
|
appyTool.log('Translation "%s" updated from "%s".' % \
|
|
|
|
(translation.id, poName))
|
|
|
|
|
2011-11-25 11:01:20 -06:00
|
|
|
def configureSessions(self):
|
|
|
|
'''Configure the session machinery.'''
|
|
|
|
# Register a function warning us when a session object is deleted. When
|
2012-11-26 06:58:27 -06:00
|
|
|
# launching Zope in test mode, the temp folder does not exist.
|
2011-11-25 11:01:20 -06:00
|
|
|
if not hasattr(self.app, 'temp_folder'): return
|
|
|
|
self.app.temp_folder.session_data.setDelNotificationTarget(onDelSession)
|
|
|
|
|
|
|
|
def enableUserTracking(self):
|
|
|
|
'''Enables the machinery allowing to know who is currently logged in.
|
|
|
|
Information about logged users will be stored in RAM, in the variable
|
|
|
|
named loggedUsers defined above.'''
|
|
|
|
global originalTraverse
|
|
|
|
if not originalTraverse:
|
|
|
|
# User tracking is not enabled yet. Do it now.
|
|
|
|
BaseRequest = self.config.BaseRequest
|
|
|
|
originalTraverse = BaseRequest.traverse
|
|
|
|
BaseRequest.traverse = traverseWrapper
|
2010-08-05 11:23:17 -05:00
|
|
|
|
2011-11-25 11:01:20 -06:00
|
|
|
def installZopeClasses(self):
|
|
|
|
'''Zope-level class registration.'''
|
|
|
|
for klass in self.classes:
|
|
|
|
name = klass.__name__
|
|
|
|
module = klass.__module__
|
|
|
|
wrapper = klass.wrapperClass
|
|
|
|
exec 'from %s import manage_add%s as ctor' % (module, name)
|
|
|
|
self.zopeContext.registerClass(meta_type=name,
|
|
|
|
constructors = (ctor,),
|
|
|
|
permission = self.addContentPermissions[name])
|
|
|
|
# Create workflow prototypical instances in __instance__ attributes
|
2012-06-01 08:57:19 -05:00
|
|
|
wf = wrapper.getWorkflow()
|
|
|
|
if not hasattr(wf, '__instance__'): wf.__instance__ = wf()
|
2011-11-25 11:01:20 -06:00
|
|
|
|
|
|
|
def installAppyTypes(self):
|
2010-08-05 11:23:17 -05:00
|
|
|
'''We complete here the initialisation process of every Appy type of
|
|
|
|
every gen-class of the application.'''
|
2011-09-14 14:01:58 -05:00
|
|
|
appName = self.productName
|
2010-08-05 11:23:17 -05:00
|
|
|
for klass in self.classes:
|
2011-09-14 14:01:58 -05:00
|
|
|
# Store on wrapper class the ordered list of Appy types
|
|
|
|
wrapperClass = klass.wrapperClass
|
|
|
|
if not hasattr(wrapperClass, 'title'):
|
|
|
|
# Special field "type" is mandatory for every class.
|
[gen] Added param Search.default allowing to define a default Search. The default search, if present, will be triggered when clicking on the main link for a class, instead of the query that collects all instances of this class; appy.gen.Type: removed 3 obsolete params: 'index', 'editDefault' and 'optional'. For achieving the same result than using 'editDefault', one may define 'by hand' an attribute on the Tool for storing the editable default value, and define, on the appropriate field in param 'default', a method that returns the value of the tool attribute; Added Type.defaultForSearch, allowing, for some sub-types, to define a default value when displaying the corresponding widget on the search screen; added a default 'state' field allowing to include workflow state among search criteria in the search screens; removed obsolete test applications.
2012-10-31 07:20:25 -05:00
|
|
|
title = gen.String(multiplicity=(1,1),show='edit',indexed=True)
|
2011-09-14 14:01:58 -05:00
|
|
|
title.init('title', None, 'appy')
|
|
|
|
setattr(wrapperClass, 'title', title)
|
[gen] Added param Search.default allowing to define a default Search. The default search, if present, will be triggered when clicking on the main link for a class, instead of the query that collects all instances of this class; appy.gen.Type: removed 3 obsolete params: 'index', 'editDefault' and 'optional'. For achieving the same result than using 'editDefault', one may define 'by hand' an attribute on the Tool for storing the editable default value, and define, on the appropriate field in param 'default', a method that returns the value of the tool attribute; Added Type.defaultForSearch, allowing, for some sub-types, to define a default value when displaying the corresponding widget on the search screen; added a default 'state' field allowing to include workflow state among search criteria in the search screens; removed obsolete test applications.
2012-10-31 07:20:25 -05:00
|
|
|
# Special field "state" must be added for every class
|
|
|
|
state = gen.String(validator=gen.Selection('_appy_listStates'),
|
|
|
|
show='result')
|
|
|
|
state.init('state', None, 'workflow')
|
|
|
|
setattr(wrapperClass, 'state', state)
|
2011-09-14 14:01:58 -05:00
|
|
|
names = self.config.attributes[wrapperClass.__name__[:-8]]
|
|
|
|
wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names]
|
|
|
|
# Post-initialise every Appy type
|
2010-08-05 11:23:17 -05:00
|
|
|
for baseClass in klass.wrapperClass.__bases__:
|
2011-09-14 14:01:58 -05:00
|
|
|
if baseClass.__name__ == 'AbstractWrapper': continue
|
2010-08-05 11:23:17 -05:00
|
|
|
for name, appyType in baseClass.__dict__.iteritems():
|
2011-12-05 08:11:29 -06:00
|
|
|
if not isinstance(appyType, gen.Type) or \
|
|
|
|
(isinstance(appyType, gen.Ref) and appyType.isBack):
|
2011-09-14 14:01:58 -05:00
|
|
|
continue # Back refs are initialised within fw refs
|
|
|
|
appyType.init(name, baseClass, appName)
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2011-11-28 15:50:01 -06:00
|
|
|
def installRoles(self):
|
|
|
|
'''Installs the application-specific roles if not already done.'''
|
|
|
|
roles = list(self.app.__ac_roles__)
|
|
|
|
for role in self.config.applicationRoles:
|
|
|
|
if role not in roles: roles.append(role)
|
|
|
|
self.app.__ac_roles__ = tuple(roles)
|
|
|
|
|
2011-12-01 13:53:13 -06:00
|
|
|
def installDependencies(self):
|
|
|
|
'''Zope products are installed in alphabetical order. But here, we need
|
|
|
|
ZCTextIndex to be installed before our Appy application. So, we cheat
|
|
|
|
and force Zope to install it now.'''
|
|
|
|
from OFS.Application import install_product
|
|
|
|
import Products
|
|
|
|
install_product(self.app, Products.__path__[1], 'ZCTextIndex', [], {})
|
2012-02-02 10:30:54 -06:00
|
|
|
|
2009-06-29 07:06:01 -05:00
|
|
|
def install(self):
|
|
|
|
self.logger.info('is being installed...')
|
2011-12-01 13:53:13 -06:00
|
|
|
self.installDependencies()
|
2011-11-28 15:50:01 -06:00
|
|
|
self.installRoles()
|
2011-11-25 11:01:20 -06:00
|
|
|
self.installAppyTypes()
|
|
|
|
self.installZopeClasses()
|
2010-01-17 15:01:14 -06:00
|
|
|
self.enableUserTracking()
|
2011-11-25 11:01:20 -06:00
|
|
|
self.configureSessions()
|
|
|
|
self.installBaseObjects()
|
|
|
|
self.installCatalog()
|
2012-03-19 11:00:44 -05:00
|
|
|
# The following line cleans and rebuilds the catalog entirely.
|
|
|
|
#self.app.config.appy().refreshCatalog()
|
2011-11-25 11:01:20 -06:00
|
|
|
self.installTool()
|
|
|
|
self.installUi()
|
2012-02-02 10:30:54 -06:00
|
|
|
# Perform migrations if required
|
|
|
|
Migrator(self).run()
|
|
|
|
# Update Appy version in the database
|
|
|
|
self.app.config.appy().appyVersion = appy.version.short
|
2011-12-05 11:15:45 -06:00
|
|
|
# Empty the fake REQUEST object, only used at Zope startup.
|
|
|
|
del self.app.config.getProductConfig().fakeRequest.wrappers
|
2009-06-29 07:06:01 -05:00
|
|
|
# ------------------------------------------------------------------------------
|