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
|
||||
# 'SortableTitle', because index 'Title' is a ZCTextIndex
|
||||
# (for searchability) and can't be used for sorting.
|
||||
elif fieldName == 'description':
|
||||
if usage == 'search': return 'Description'
|
||||
else: return None
|
||||
else:
|
||||
return 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
|
||||
@staticmethod
|
||||
|
|
|
@ -142,7 +142,6 @@ class Generator(AbstractGenerator):
|
|||
self.generateTool()
|
||||
self.generateInit()
|
||||
self.generateTests()
|
||||
self.copyFile('Install.py', self.repls, destFolder='Extensions')
|
||||
self.generateConfigureZcml()
|
||||
self.copyFile('import_steps.xml', self.repls,
|
||||
destFolder='profiles/default')
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
'''This package contains stuff used at run-time for installing a generated
|
||||
Plone product.'''
|
||||
Zope product.'''
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
import os, os.path, time
|
||||
from StringIO import StringIO
|
||||
from sets import Set
|
||||
import appy
|
||||
import appy.version
|
||||
from appy.gen import Type, Ref, String, File
|
||||
from appy.gen.po import PoParser
|
||||
from appy.gen.utils import produceNiceMessage, updateRolesForPermission, \
|
||||
createObject
|
||||
from appy.gen.utils import updateRolesForPermission, createObject
|
||||
from appy.shared.data import languages
|
||||
from migrator import Migrator
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
homePage = '''
|
||||
|
@ -38,99 +33,6 @@ errorPage = '''
|
|||
</html>
|
||||
</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 ---------------------------------------------
|
||||
loggedUsers = {}
|
||||
|
@ -162,6 +64,11 @@ def onDelSession(sessionObject, container):
|
|||
resp.write('<center>For security reasons, your session has ' \
|
||||
'expired.</center>')
|
||||
|
||||
class ZCTextIndexInfo:
|
||||
'''Silly class used for storing information about a ZCTextIndex.'''
|
||||
lexicon_id = "lexicon"
|
||||
index_type = 'Okapi BM25 Rank'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class ZopeInstaller:
|
||||
'''This Zope installer runs every time Zope starts and encounters this
|
||||
|
@ -238,12 +145,20 @@ class ZopeInstaller:
|
|||
(indexName, oldType, indexType))
|
||||
if indexName not in catalog.indexes():
|
||||
# We need to create this index
|
||||
type = indexType
|
||||
if type == 'ZCTextIndex': type = 'TextIndex'
|
||||
catalog.addIndex(indexName, type)
|
||||
if indexType != 'ZCTextIndex':
|
||||
catalog.addIndex(indexName, indexType)
|
||||
else:
|
||||
catalog.addIndex(indexName, indexType,extra=ZCTextIndexInfo)
|
||||
catalog.reindexIndex(indexName, self.app.REQUEST)
|
||||
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):
|
||||
'''Create the catalog at the root of Zope if id does not exist.'''
|
||||
if 'catalog' not in self.app.objectIds():
|
||||
|
@ -251,10 +166,17 @@ class ZopeInstaller:
|
|||
from Products.ZCatalog.ZCatalog import manage_addZCatalog
|
||||
manage_addZCatalog(self.app, 'catalog', '')
|
||||
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
|
||||
indexInfo = {'State': 'FieldIndex', 'UID': 'FieldIndex',
|
||||
'Title': 'TextIndex', 'SortableTitle': 'FieldIndex',
|
||||
'SearchableText': 'FieldIndex', 'Creator': 'FieldIndex',
|
||||
'Title': 'ZCTextIndex', 'SortableTitle': 'FieldIndex',
|
||||
'SearchableText': 'ZCTextIndex', 'Creator': 'FieldIndex',
|
||||
'Created': 'DateIndex', 'ClassName': 'FieldIndex',
|
||||
'Allowed': 'KeywordIndex'}
|
||||
tool = self.app.config
|
||||
|
@ -314,6 +236,7 @@ class ZopeInstaller:
|
|||
tool.createOrUpdate(True, None)
|
||||
tool.refreshSecurity()
|
||||
appyTool = tool.appy()
|
||||
appyTool.log('Appy version is "%s".' % appy.version.short)
|
||||
|
||||
# Create the admin user if no user exists.
|
||||
if not self.app.acl_users.getUsers():
|
||||
|
@ -457,8 +380,17 @@ class ZopeInstaller:
|
|||
if role not in roles: roles.append(role)
|
||||
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):
|
||||
self.logger.info('is being installed...')
|
||||
self.installDependencies()
|
||||
self.installRoles()
|
||||
self.installAppyTypes()
|
||||
self.installZopeClasses()
|
||||
|
|
|
@ -371,6 +371,7 @@ class ToolMixin(BaseMixin):
|
|||
def truncateValue(self, value, appyType):
|
||||
'''Truncates the p_value according to p_appyType width.'''
|
||||
maxWidth = appyType['width']
|
||||
if isinstance(value, str): value = value.decode('utf-8')
|
||||
if len(value) > maxWidth:
|
||||
return value[:maxWidth] + '...'
|
||||
return value
|
||||
|
@ -378,10 +379,9 @@ class ToolMixin(BaseMixin):
|
|||
def truncateText(self, text, width=15):
|
||||
'''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.'''
|
||||
if isinstance(text, str): text = text.decode('utf-8')
|
||||
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):
|
||||
'''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('\n', '<br/>')
|
||||
return '<div class="translationLabel"><acronym title="%s">' \
|
||||
'<img src="help.png"/></acronym>%s</div>' % \
|
||||
'<img src="ui/help.png"/></acronym>%s</div>' % \
|
||||
(fieldName, sourceMsg)
|
||||
|
||||
def show(self, field):
|
||||
|
|
Loading…
Reference in a new issue