appy.gen: various bugfixes (creation of ZCTextIndexes and of the associated lexicon...).
This commit is contained in:
parent
a321257e55
commit
6733f4c7dc
|
@ -315,9 +315,6 @@ class Search:
|
||||||
# Indeed, for field 'title', Appy has a specific index
|
# Indeed, for field 'title', Appy has a specific index
|
||||||
# 'SortableTitle', because index 'Title' is a ZCTextIndex
|
# 'SortableTitle', because index 'Title' is a ZCTextIndex
|
||||||
# (for searchability) and can't be used for sorting.
|
# (for searchability) and can't be used for sorting.
|
||||||
elif fieldName == 'description':
|
|
||||||
if usage == 'search': return 'Description'
|
|
||||||
else: return None
|
|
||||||
else:
|
else:
|
||||||
return 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
|
return 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -142,7 +142,6 @@ class Generator(AbstractGenerator):
|
||||||
self.generateTool()
|
self.generateTool()
|
||||||
self.generateInit()
|
self.generateInit()
|
||||||
self.generateTests()
|
self.generateTests()
|
||||||
self.copyFile('Install.py', self.repls, destFolder='Extensions')
|
|
||||||
self.generateConfigureZcml()
|
self.generateConfigureZcml()
|
||||||
self.copyFile('import_steps.xml', self.repls,
|
self.copyFile('import_steps.xml', self.repls,
|
||||||
destFolder='profiles/default')
|
destFolder='profiles/default')
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
'''This package contains stuff used at run-time for installing a generated
|
'''This package contains stuff used at run-time for installing a generated
|
||||||
Plone product.'''
|
Zope product.'''
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, time
|
import os, os.path, time
|
||||||
from StringIO import StringIO
|
|
||||||
from sets import Set
|
|
||||||
import appy
|
import appy
|
||||||
import appy.version
|
import appy.version
|
||||||
from appy.gen import Type, Ref, String, File
|
from appy.gen import Type, Ref, String, File
|
||||||
from appy.gen.po import PoParser
|
from appy.gen.po import PoParser
|
||||||
from appy.gen.utils import produceNiceMessage, updateRolesForPermission, \
|
from appy.gen.utils import updateRolesForPermission, createObject
|
||||||
createObject
|
|
||||||
from appy.shared.data import languages
|
from appy.shared.data import languages
|
||||||
from migrator import Migrator
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
homePage = '''
|
homePage = '''
|
||||||
|
@ -38,99 +33,6 @@ errorPage = '''
|
||||||
</html>
|
</html>
|
||||||
</tal:main>
|
</tal:main>
|
||||||
'''
|
'''
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
class PloneInstaller:
|
|
||||||
'''This Plone installer runs every time the generated Plone product is
|
|
||||||
installed or uninstalled (in the Plone configuration interface).'''
|
|
||||||
def __init__(self, reinstall, ploneSite, config):
|
|
||||||
# p_cfg is the configuration module of the Plone product.
|
|
||||||
self.reinstall = reinstall # Is it a fresh install or a re-install?
|
|
||||||
self.ploneSite = ploneSite
|
|
||||||
self.config = cfg = config
|
|
||||||
# Unwrap some useful variables from config
|
|
||||||
self.productName = cfg.PROJECTNAME
|
|
||||||
self.appClasses = cfg.appClasses
|
|
||||||
self.appClassNames = cfg.appClassNames
|
|
||||||
self.allClassNames = cfg.allClassNames
|
|
||||||
self.applicationRoles = cfg.applicationRoles # Roles defined in the app
|
|
||||||
self.defaultAddRoles = cfg.defaultAddRoles
|
|
||||||
self.appFrontPage = cfg.appFrontPage
|
|
||||||
self.languages = cfg.languages
|
|
||||||
self.languageSelector = cfg.languageSelector
|
|
||||||
self.attributes = cfg.attributes
|
|
||||||
# A buffer for logging purposes
|
|
||||||
self.toLog = StringIO()
|
|
||||||
self.tool = None # The Plone version of the application tool
|
|
||||||
self.appyTool = None # The Appy version of the application tool
|
|
||||||
self.toolName = '%sTool' % self.productName
|
|
||||||
self.toolInstanceName = 'portal_%s' % self.productName.lower()
|
|
||||||
|
|
||||||
def installRootFolder(self):
|
|
||||||
'''Creates and/or configures, at the root of the Plone site and if
|
|
||||||
needed, the folder where the application will store instances of
|
|
||||||
root classes. Creates also the 'appy' folder (more precisely,
|
|
||||||
a Filesystem Directory View) at the root of the site, for storing
|
|
||||||
appy-wide ZPTs an images.'''
|
|
||||||
# Register first our own Appy folder type if needed.
|
|
||||||
site = self.ploneSite
|
|
||||||
if not hasattr(site.portal_types, self.appyFolderType):
|
|
||||||
self.registerAppyFolderType()
|
|
||||||
# Create the folder
|
|
||||||
if not hasattr(site.aq_base, self.productName):
|
|
||||||
# Temporarily allow me to create Appy large plone folders
|
|
||||||
getattr(site.portal_types, self.appyFolderType).global_allow = 1
|
|
||||||
# Allow to create Appy large folders in the plone site
|
|
||||||
getattr(site.portal_types,
|
|
||||||
'Plone Site').allowed_content_types += (self.appyFolderType,)
|
|
||||||
site.invokeFactory(self.appyFolderType, self.productName,
|
|
||||||
title=self.productName)
|
|
||||||
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
|
||||||
|
|
||||||
else:
|
|
||||||
appFolder = getattr(site, self.productName)
|
|
||||||
|
|
||||||
# Beyond content-type-specific "add" permissions, creators must also
|
|
||||||
# have the main permission "Add portal content".
|
|
||||||
permission = 'Add portal content'
|
|
||||||
updateRolesForPermission(permission, tuple(allCreators), appFolder)
|
|
||||||
|
|
||||||
def installRolesAndGroups(self):
|
|
||||||
'''Registers roles used by workflows and classes defined in this
|
|
||||||
application if they are not registered yet. Creates the corresponding
|
|
||||||
groups if needed.'''
|
|
||||||
site = self.ploneSite
|
|
||||||
data = list(site.__ac_roles__)
|
|
||||||
for role in self.config.applicationRoles:
|
|
||||||
if not role in data:
|
|
||||||
data.append(role)
|
|
||||||
# Add to portal_role_manager
|
|
||||||
prm = site.acl_users.portal_role_manager
|
|
||||||
try:
|
|
||||||
prm.addRole(role, role, 'Added by "%s"' % self.productName)
|
|
||||||
except KeyError: # Role already exists
|
|
||||||
pass
|
|
||||||
# If it is a global role, create a specific group and grant him
|
|
||||||
# this role
|
|
||||||
if role not in self.config.applicationGlobalRoles: continue
|
|
||||||
group = '%s_group' % role
|
|
||||||
if site.portal_groups.getGroupById(group): continue # Already there
|
|
||||||
site.portal_groups.addGroup(group, title=group)
|
|
||||||
site.portal_groups.setRolesForGroup(group, [role])
|
|
||||||
site.__ac_roles__ = tuple(data)
|
|
||||||
|
|
||||||
def finalizeInstallation(self):
|
|
||||||
'''Performs some final installation steps.'''
|
|
||||||
site = self.ploneSite
|
|
||||||
# Do not allow an anonymous user to register himself as new user
|
|
||||||
site.manage_permission('Add portal member', ('Manager',), acquire=0)
|
|
||||||
# Replace Plone front-page with an application-specific page if needed
|
|
||||||
if self.appFrontPage:
|
|
||||||
frontPageName = self.productName + 'FrontPage'
|
|
||||||
site.manage_changeProperties(default_page=frontPageName)
|
|
||||||
# Store the used Appy version (used for detecting new versions)
|
|
||||||
self.appyTool.appyVersion = appy.version.short
|
|
||||||
self.info('Appy version is %s.' % self.appyTool.appyVersion)
|
|
||||||
# Call custom installer if any
|
|
||||||
|
|
||||||
# Stuff for tracking user activity ---------------------------------------------
|
# Stuff for tracking user activity ---------------------------------------------
|
||||||
loggedUsers = {}
|
loggedUsers = {}
|
||||||
|
@ -162,6 +64,11 @@ def onDelSession(sessionObject, container):
|
||||||
resp.write('<center>For security reasons, your session has ' \
|
resp.write('<center>For security reasons, your session has ' \
|
||||||
'expired.</center>')
|
'expired.</center>')
|
||||||
|
|
||||||
|
class ZCTextIndexInfo:
|
||||||
|
'''Silly class used for storing information about a ZCTextIndex.'''
|
||||||
|
lexicon_id = "lexicon"
|
||||||
|
index_type = 'Okapi BM25 Rank'
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ZopeInstaller:
|
class ZopeInstaller:
|
||||||
'''This Zope installer runs every time Zope starts and encounters this
|
'''This Zope installer runs every time Zope starts and encounters this
|
||||||
|
@ -238,12 +145,20 @@ class ZopeInstaller:
|
||||||
(indexName, oldType, indexType))
|
(indexName, oldType, indexType))
|
||||||
if indexName not in catalog.indexes():
|
if indexName not in catalog.indexes():
|
||||||
# We need to create this index
|
# We need to create this index
|
||||||
type = indexType
|
if indexType != 'ZCTextIndex':
|
||||||
if type == 'ZCTextIndex': type = 'TextIndex'
|
catalog.addIndex(indexName, indexType)
|
||||||
catalog.addIndex(indexName, type)
|
else:
|
||||||
|
catalog.addIndex(indexName, indexType,extra=ZCTextIndexInfo)
|
||||||
|
catalog.reindexIndex(indexName, self.app.REQUEST)
|
||||||
logger.info('Created index "%s" of type "%s"...' % \
|
logger.info('Created index "%s" of type "%s"...' % \
|
||||||
(indexName, type))
|
(indexName, indexType))
|
||||||
|
# Indexing database content based on this index.
|
||||||
|
|
||||||
|
lexiconInfos = [
|
||||||
|
appy.Object(group='Case Normalizer', name='Case Normalizer'),
|
||||||
|
appy.Object(group='Stop Words', name=" Don't remove stop words"),
|
||||||
|
appy.Object(group='Word Splitter', name='Whitespace splitter')
|
||||||
|
]
|
||||||
def installCatalog(self):
|
def installCatalog(self):
|
||||||
'''Create the catalog at the root of Zope if id does not exist.'''
|
'''Create the catalog at the root of Zope if id does not exist.'''
|
||||||
if 'catalog' not in self.app.objectIds():
|
if 'catalog' not in self.app.objectIds():
|
||||||
|
@ -251,10 +166,17 @@ class ZopeInstaller:
|
||||||
from Products.ZCatalog.ZCatalog import manage_addZCatalog
|
from Products.ZCatalog.ZCatalog import manage_addZCatalog
|
||||||
manage_addZCatalog(self.app, 'catalog', '')
|
manage_addZCatalog(self.app, 'catalog', '')
|
||||||
self.logger.info('Appy catalog created.')
|
self.logger.info('Appy catalog created.')
|
||||||
|
|
||||||
|
# Create a lexicon for ZCTextIndexes
|
||||||
|
if 'lexicon' not in self.app.catalog.objectIds():
|
||||||
|
from Products.ZCTextIndex.ZCTextIndex import manage_addLexicon
|
||||||
|
manage_addLexicon(self.app.catalog, 'lexicon',
|
||||||
|
elements=self.lexiconInfos)
|
||||||
|
|
||||||
# Create or update Appy-wide indexes and field-related indexes
|
# Create or update Appy-wide indexes and field-related indexes
|
||||||
indexInfo = {'State': 'FieldIndex', 'UID': 'FieldIndex',
|
indexInfo = {'State': 'FieldIndex', 'UID': 'FieldIndex',
|
||||||
'Title': 'TextIndex', 'SortableTitle': 'FieldIndex',
|
'Title': 'ZCTextIndex', 'SortableTitle': 'FieldIndex',
|
||||||
'SearchableText': 'FieldIndex', 'Creator': 'FieldIndex',
|
'SearchableText': 'ZCTextIndex', 'Creator': 'FieldIndex',
|
||||||
'Created': 'DateIndex', 'ClassName': 'FieldIndex',
|
'Created': 'DateIndex', 'ClassName': 'FieldIndex',
|
||||||
'Allowed': 'KeywordIndex'}
|
'Allowed': 'KeywordIndex'}
|
||||||
tool = self.app.config
|
tool = self.app.config
|
||||||
|
@ -314,6 +236,7 @@ class ZopeInstaller:
|
||||||
tool.createOrUpdate(True, None)
|
tool.createOrUpdate(True, None)
|
||||||
tool.refreshSecurity()
|
tool.refreshSecurity()
|
||||||
appyTool = tool.appy()
|
appyTool = tool.appy()
|
||||||
|
appyTool.log('Appy version is "%s".' % appy.version.short)
|
||||||
|
|
||||||
# Create the admin user if no user exists.
|
# Create the admin user if no user exists.
|
||||||
if not self.app.acl_users.getUsers():
|
if not self.app.acl_users.getUsers():
|
||||||
|
@ -457,8 +380,17 @@ class ZopeInstaller:
|
||||||
if role not in roles: roles.append(role)
|
if role not in roles: roles.append(role)
|
||||||
self.app.__ac_roles__ = tuple(roles)
|
self.app.__ac_roles__ = tuple(roles)
|
||||||
|
|
||||||
|
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', [], {})
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
self.logger.info('is being installed...')
|
self.logger.info('is being installed...')
|
||||||
|
self.installDependencies()
|
||||||
self.installRoles()
|
self.installRoles()
|
||||||
self.installAppyTypes()
|
self.installAppyTypes()
|
||||||
self.installZopeClasses()
|
self.installZopeClasses()
|
||||||
|
|
|
@ -371,6 +371,7 @@ class ToolMixin(BaseMixin):
|
||||||
def truncateValue(self, value, appyType):
|
def truncateValue(self, value, appyType):
|
||||||
'''Truncates the p_value according to p_appyType width.'''
|
'''Truncates the p_value according to p_appyType width.'''
|
||||||
maxWidth = appyType['width']
|
maxWidth = appyType['width']
|
||||||
|
if isinstance(value, str): value = value.decode('utf-8')
|
||||||
if len(value) > maxWidth:
|
if len(value) > maxWidth:
|
||||||
return value[:maxWidth] + '...'
|
return value[:maxWidth] + '...'
|
||||||
return value
|
return value
|
||||||
|
@ -378,10 +379,9 @@ class ToolMixin(BaseMixin):
|
||||||
def truncateText(self, text, width=15):
|
def truncateText(self, text, width=15):
|
||||||
'''Truncates p_text to max p_width chars. If the text is longer than
|
'''Truncates p_text to max p_width chars. If the text is longer than
|
||||||
p_width, the truncated part is put in a "acronym" html tag.'''
|
p_width, the truncated part is put in a "acronym" html tag.'''
|
||||||
|
if isinstance(text, str): text = text.decode('utf-8')
|
||||||
if len(text) <= width: return text
|
if len(text) <= width: return text
|
||||||
else:
|
return '<acronym title="%s">%s</acronym>' % (text, text[:width] + '...')
|
||||||
return '<acronym title="%s">%s</acronym>' % \
|
|
||||||
(text, text[:width] + '...')
|
|
||||||
|
|
||||||
def getPublishedObject(self):
|
def getPublishedObject(self):
|
||||||
'''Gets the currently published object, if its meta_class is among
|
'''Gets the currently published object, if its meta_class is among
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
<!codeHeader!>
|
|
||||||
import appy.gen
|
|
||||||
from appy.gen.plone25.installer import PloneInstaller
|
|
||||||
import Products.<!applicationName!>.config as config
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def install(self, reinstall=False):
|
|
||||||
'''Installation procedure.'''
|
|
||||||
return PloneInstaller(reinstall, self, config).install()
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def uninstall(self, reinstall=False):
|
|
||||||
'''Uninstallation procedure.'''
|
|
||||||
return PloneInstaller(reinstall, self, config).uninstall()
|
|
||||||
# ------------------------------------------------------------------------------
|
|
|
@ -29,7 +29,7 @@ class TranslationWrapper(AbstractWrapper):
|
||||||
sourceMsg = sourceMsg.replace('<','<').replace('>','>')
|
sourceMsg = sourceMsg.replace('<','<').replace('>','>')
|
||||||
sourceMsg = sourceMsg.replace('\n', '<br/>')
|
sourceMsg = sourceMsg.replace('\n', '<br/>')
|
||||||
return '<div class="translationLabel"><acronym title="%s">' \
|
return '<div class="translationLabel"><acronym title="%s">' \
|
||||||
'<img src="help.png"/></acronym>%s</div>' % \
|
'<img src="ui/help.png"/></acronym>%s</div>' % \
|
||||||
(fieldName, sourceMsg)
|
(fieldName, sourceMsg)
|
||||||
|
|
||||||
def show(self, field):
|
def show(self, field):
|
||||||
|
|
Loading…
Reference in a new issue