Commit c56dd40c authored by Hanno Schlichting's avatar Hanno Schlichting

Removed various persistent product related code and options. The...

Removed various persistent product related code and options. The `enable-product-installation` `zope.conf` setting is now a no-op.
parent 96d3f682
...@@ -33,6 +33,9 @@ Features Added ...@@ -33,6 +33,9 @@ Features Added
Restructuring Restructuring
+++++++++++++ +++++++++++++
- Removed various persistent product related code and options. The
`enable-product-installation` `zope.conf` setting is now a no-op.
- Changed the value for `default-zpublisher-encoding` and - Changed the value for `default-zpublisher-encoding` and
`management_page_charset` to `utf-8`. `management_page_charset` to `utf-8`.
......
...@@ -254,7 +254,7 @@ InitializeClass(DebugManager) ...@@ -254,7 +254,7 @@ InitializeClass(DebugManager)
class ApplicationManager(Folder,CacheManager): class ApplicationManager(Folder, CacheManager):
"""System management """System management
""" """
__roles__ = ('Manager',) __roles__ = ('Manager',)
......
...@@ -98,13 +98,7 @@ class ProductDispatcher(Implicit): ...@@ -98,13 +98,7 @@ class ProductDispatcher(Implicit):
'__FactoryDispatcher__', '__FactoryDispatcher__',
FactoryDispatcher) FactoryDispatcher)
productfolder = self.aq_acquire('_getProducts')()
try:
product = productfolder._product(name)
except AttributeError:
# If we do not have a persistent product entry, return
product = Product(name) product = Product(name)
dispatcher=dispatcher_class(product, self.aq_parent, REQUEST) dispatcher=dispatcher_class(product, self.aq_parent, REQUEST)
return dispatcher.__of__(self) return dispatcher.__of__(self)
......
...@@ -10,37 +10,9 @@ ...@@ -10,37 +10,9 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Product objects
"""
# The new Product model:
#
# Products may be defined in the Products folder or by placing directories
# in lib/python/Products.
#
# Products in lib/python/Products may have up to three sources of information:
#
# - Static information defined via Python. This information is
# described and made available via __init__.py.
#
# - Dynamic object data that gets copied into the Bobobase.
# This is contained in product.dat (which is obfuscated).
#
# - Static extensions supporting the dynamic data. These too
# are obfuscated.
#
# Products may be copied and pasted only within the products folder.
#
# If a product is deleted (or cut), it is automatically recreated
# on restart if there is still a product directory.
import os
from AccessControl.class_init import InitializeClass from AccessControl.class_init import InitializeClass
from AccessControl.owner import UnownableOwner from AccessControl.owner import UnownableOwner
from AccessControl.SecurityInfo import ClassSecurityInfo
from AccessControl.unauthorized import Unauthorized
from App.special_dtml import DTMLFile
from OFS.Folder import Folder from OFS.Folder import Folder
...@@ -65,225 +37,3 @@ class ProductFolder(Folder): ...@@ -65,225 +37,3 @@ class ProductFolder(Folder):
return 0 return 0
InitializeClass(ProductFolder) InitializeClass(ProductFolder)
class Product(Folder):
"""Model a product that can be created through the web.
"""
security = ClassSecurityInfo()
meta_type='Product'
icon='p_/Product_icon'
version=''
configurable_objects_=()
import_error_=None
manage_options = (
(Folder.manage_options[0],) +
tuple(Folder.manage_options[2:])
)
_properties = Folder._properties+(
{'id':'version', 'type': 'string'},
)
_reserved_names=('Help',)
def __init__(self, id, title):
from HelpSys.HelpSys import ProductHelp
self.id = id
self.title = title
self._setObject('Help', ProductHelp('Help', id))
security.declarePublic('Destination')
def Destination(self):
"Return the destination for factory output"
return self
security.declarePublic('DestinationURL')
def DestinationURL(self):
"Return the URL for the destination for factory output"
return self.REQUEST['BASE4']
manage_traceback = DTMLFile('dtml/traceback', globals())
manage_readme = DTMLFile('dtml/readme', globals())
def manage_get_product_readme__(self):
for name in ('README.txt', 'README.TXT', 'readme.txt'):
path = os.path.join(self.home, name)
if os.path.isfile(path):
return open(path).read()
return ''
def permissionMappingPossibleValues(self):
return self.possible_permissions()
def getProductHelp(self):
"""Returns the ProductHelp object associated with the Product.
"""
from HelpSys.HelpSys import ProductHelp
if not hasattr(self, 'Help'):
self._setObject('Help', ProductHelp('Help', self.id))
return self.Help
#
# Product refresh
#
_refresh_dtml = DTMLFile('dtml/refresh', globals())
def _readRefreshTxt(self, pid=None):
import Products
refresh_txt = None
if pid is None:
pid = self.id
for productDir in Products.__path__:
found = 0
for name in ('refresh.txt', 'REFRESH.txt', 'REFRESH.TXT'):
p = os.path.join(productDir, pid, name)
if os.path.exists(p):
found = 1
break
if found:
try:
file = open(p)
text = file.read()
file.close()
refresh_txt = text
break
except:
# Not found here.
pass
return refresh_txt
def manage_performRefresh(self, REQUEST=None):
""" Attempts to perform a refresh operation.
"""
from App.RefreshFuncs import performFullRefresh
if self._readRefreshTxt() is None:
raise Unauthorized, 'refresh.txt not found'
message = None
if performFullRefresh(self._p_jar, self.id):
from ZODB import Connection
Connection.resetCaches() # Clears cache in future connections.
message = 'Product refreshed.'
else:
message = 'An exception occurred.'
if REQUEST is not None:
return self.manage_refresh(REQUEST, manage_tabs_message=message)
def manage_enableAutoRefresh(self, enable=0, REQUEST=None):
""" Changes the auto refresh flag for this product.
"""
from App.RefreshFuncs import enableAutoRefresh
if self._readRefreshTxt() is None:
raise Unauthorized, 'refresh.txt not created'
enableAutoRefresh(self._p_jar, self.id, enable)
if enable:
message = 'Enabled auto refresh.'
else:
message = 'Disabled auto refresh.'
if REQUEST is not None:
return self.manage_refresh(REQUEST, manage_tabs_message=message)
def manage_selectDependentProducts(self, selections=(), REQUEST=None):
""" Selects which products to refresh simultaneously.
"""
from App.RefreshFuncs import setDependentProducts
if self._readRefreshTxt() is None:
raise Unauthorized, 'refresh.txt not created'
setDependentProducts(self._p_jar, self.id, selections)
if REQUEST is not None:
return self.manage_refresh(REQUEST)
InitializeClass(Product)
def initializeProduct(productp, name, home, app):
# Initialize a persistent product
assert doInstall()
fver = ''
if hasattr(productp, '__import_error__'):
ie = productp.__import_error__
else:
ie = None
# Retrieve version number from any suitable version.txt
for fname in ('version.txt', 'VERSION.txt', 'VERSION.TXT'):
try:
fpath = os.path.join(home, fname)
fhandle = open(fpath, 'r')
fver = fhandle.read().strip()
fhandle.close()
break
except IOError:
continue
old = None
products = app.Control_Panel.Products
try:
if ihasattr(products, name):
old=getattr(products, name)
if ihasattr(old,'version') and old.version==fver:
if hasattr(old, 'import_error_') and \
old.import_error_==ie:
# Version hasn't changed. Don't reinitialize.
return old
except:
pass
f = fver and (" (%s)" % fver)
product=Product(name, 'Installed product %s%s' % (name, f))
if old is not None:
app._manage_remove_product_meta_type(product)
products._delObject(name)
for id, v in old.objectItems():
try:
product._setObject(id, v)
except:
pass
products._setObject(name, product)
product.home = home
if ie:
product.import_error_=ie
product.title='Broken product %s' % name
product.icon='p_/BrokenProduct_icon'
product.manage_options=(
{'label':'Traceback', 'action':'manage_traceback'},
)
for name in ('README.txt', 'README.TXT', 'readme.txt'):
path = os.path.join(home, name)
if os.path.isfile(path):
product.manage_options=product.manage_options+(
{'label':'README', 'action':'manage_readme'},
)
break
# Ensure this product has a refresh tab.
found = 0
for option in product.manage_options:
if option.get('label') == 'Refresh':
found = 1
break
if not found:
product.manage_options = product.manage_options + (
{'label':'Refresh', 'action':'manage_refresh'},
)
return product
def ihasattr(o, name):
return hasattr(o, name) and o.__dict__.has_key(name)
def doInstall():
from App.config import getConfiguration
return getConfiguration().enable_product_installation
...@@ -190,9 +190,7 @@ class ProductContext: ...@@ -190,9 +190,7 @@ class ProductContext:
# currently also required by the _verifyObjectPaste # currently also required by the _verifyObjectPaste
# method of CopyContainers like Folders. # method of CopyContainers like Folders.
'action': ('manage_addProduct/%s/%s' % (pid, name)), 'action': ('manage_addProduct/%s/%s' % (pid, name)),
# 'product': Used by ProductRegistry for TTW products and by # 'product': No longer used
# OFS.Application for refreshing products.
# This key might not be available.
'product': pid, 'product': pid,
# 'permission': Guards the add action. # 'permission': Guards the add action.
'permission': permission, 'permission': permission,
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Product registry and new product factory model. There will be a new
# mechanism for defining actions for meta types. If an action is of
# the form:
#
# manage_addProduct-name-factoryid
#
# Then the machinery that invokes an add-product form
# will return:
# ....what?
class ProductRegistryMixin:
# This class implements a protocol for registering products that
# are defined through the web.
# This class is a mix-in class for the top-level application object.
def _manage_remove_product_meta_type(self, product,
id=None, meta_type=None):
r=[]
pid=product.id
for mt in self._getProductRegistryMetaTypes():
if 'product' in mt:
if mt['product']==pid and (
meta_type is None or meta_type==mt['name']):
continue
elif meta_type==mt['name']: continue
r.append(mt)
self._setProductRegistryMetaTypes(tuple(r))
def _constructor_prefix_string(self, pid):
return 'manage_addProduct/%s/' % pid
def _manage_add_product_meta_type(self, product, id, meta_type,
permission=''):
pid=product.id
meta_types=self._getProductRegistryMetaTypes()
for mt in meta_types:
if mt['name']==meta_type:
if 'product' not in mt: mt['product']=pid
if mt['product'] != pid:
raise ValueError, (
'The type <em>%s</em> is already defined.' % meta_type)
mt['action']='%s%s' % (
self._constructor_prefix_string(pid), id)
if permission: mt['permission']=permission
return
mt={
'name': meta_type,
'action': ('%s%s' % (
self._constructor_prefix_string(pid), id)),
'product': pid
}
if permission: mt['permission']=permission
self._setProductRegistryMetaTypes(meta_types+(mt,))
# HACK - sometimes an unwrapped App object seems to be passed as
# self to these methods, which means that they dont have an aq_aquire
# method. Until Jim has time to look into this, this aq_maybe method
# appears to be an effective work-around...
def aq_maybe(self, name):
if hasattr(self, name):
return getattr(self, name)
return self.aq_acquire(name)
def _manage_add_product_data(self, type, product, id, **data):
values=filter(
lambda d, product=product, id=id:
not (d['product']==product and d['id']==id),
list(self.aq_maybe('_getProductRegistryData')(type))
)
data['product']=product
data['id']=id
values.append(data)
self.aq_maybe('_setProductRegistryData')(type, tuple(values))
def _manage_remove_product_data(self, type, product, id):
values=filter(
lambda d, product=product, id=id:
not (d['product']==product and d['id']==id),
self.aq_maybe('_getProductRegistryData')(type)
)
self.aq_maybe('_setProductRegistryData')(type, tuple(values))
class ProductRegistry(ProductRegistryMixin):
# This class implements a protocol for registering products that
# are defined through the web. It also provides methods for
# getting hold of the Product Registry, Control_Panel.Products.
# This class is a mix-in class for the top-level application object.
def _getProducts(self): return self.Control_Panel.Products
_product_meta_types=()
def _getProductRegistryMetaTypes(self):
return self._product_meta_types
def _setProductRegistryMetaTypes(self, v):
self._product_meta_types=v
def _getProductRegistryData(self, name):
return getattr(self, '_product_%s' % name)
def _setProductRegistryData(self, name, v):
name='_product_%s' % name
if hasattr(self, name):
return setattr(self, name, v)
else:
raise AttributeError, name
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''
Functions for refreshing products.
'''
from logging import getLogger
import os
import sys
from time import time
from traceback import format_exception
from ExtensionClass import Base
from Persistence import PersistentMapping
LOG = getLogger('RefreshFuncs')
global_classes_timestamp = 0
products_mod_times = {}
_marker = [] # create a new marker object.
refresh_exc_info = {}
class dummyClass:
pass
class dummyClass2(Base):
pass
def dummyFunc():
pass
ClassTypes = (type(dummyClass), type(dummyClass2))
ModuleType = type(sys)
FuncType = type(dummyFunc)
next_auto_refresh_check = 0
AUTO_REFRESH_INTERVAL = 2 # 2 seconds.
# Functions for storing and retrieving the auto-refresh state for
# each product.
def _getCentralRefreshData(jar, create=0):
root = jar.root()
if root.has_key('RefreshData'):
rd = root['RefreshData']
else:
rd = PersistentMapping()
if create:
root['RefreshData'] = rd
return rd
def isAutoRefreshEnabled(jar, productid):
rd = _getCentralRefreshData(jar)
ids = rd.get('auto', None)
if ids:
return ids.get(productid, 0)
else:
return 0
def enableAutoRefresh(jar, productid, enable):
productid = str(productid)
rd = _getCentralRefreshData(jar, 1)
ids = rd.get('auto', None)
if ids is None:
if enable:
rd['auto'] = ids = PersistentMapping()
else:
return
if enable:
ids[productid] = 1
else:
if ids.has_key(productid):
del ids[productid]
def listAutoRefreshableProducts(jar):
rd = _getCentralRefreshData(jar)
auto = rd.get('auto', None)
if auto:
ids = []
for k, v in auto.items():
if v:
ids.append(k)
return ids
else:
return ()
def getDependentProducts(jar, productid):
rd = _getCentralRefreshData(jar)
products = rd.get('products', None)
if products is None:
return ()
product = products.get(productid, None)
if product is None:
return ()
return product.get('dependent_products', ())
def setDependentProducts(jar, productid, dep_ids):
productid = str(productid)
rd = _getCentralRefreshData(jar, 1)
products = rd.get('products', None)
if products is None:
rd['products'] = products = PersistentMapping()
product = products.get(productid, None)
if product is None:
products[productid] = product = PersistentMapping()
product['dependent_products'] = tuple(map(str, dep_ids))
# Functions for performing refresh.
def getReloadVar(module):
reload_var = getattr(module, '__refresh_module__', _marker)
if reload_var is _marker:
reload_var = getattr(module, '__reload_module__', _marker)
if reload_var is _marker:
reload_var = 1
return reload_var
def listRefreshableModules(productid):
prefix = "Products.%s" % productid
prefixdot = prefix + '.'
lpdot = len(prefixdot)
rval = []
for name, module in sys.modules.items():
if module and (name == prefix or name[:lpdot] == prefixdot):
reload_var = getReloadVar(module)
if reload_var:
rval.append((name, module))
return rval
def logBadRefresh(productid):
exc = sys.exc_info()
try:
LOG.error('Exception while refreshing %s' % productid, exc_info=exc)
if hasattr(exc[0], '__name__'):
error_type = exc[0].__name__
else:
error_type = str(exc[0])
error_value = str(exc[1])
info = ''.join(format_exception(exc[0], exc[1], exc[2], limit=200))
refresh_exc_info[productid] = (error_type, error_value, info)
finally:
exc = None
def performRefresh(jar, productid):
'''Attempts to perform a refresh operation.
'''
refresh_exc_info[productid] = None
setupModTimes(productid) # Refresh again only if changed again.
modlist = listRefreshableModules(productid)
former_modules = {}
try:
# Remove modules from sys.modules but keep a handle
# on the old modules in case there's a problem.
for name, module in modlist:
m = sys.modules.get(name, None)
if m is not None:
former_modules[name] = m
del sys.modules[name]
# Reimport and reinstall the product.
from OFS import Application
Application.reimport_product(productid)
app = jar.root()['Application']
Application.reinstall_product(app, productid)
return 1
except:
# Couldn't refresh. Reinstate removed modules.
for name, module in former_modules.items():
sys.modules[name] = module
raise
def performSafeRefresh(jar, productid):
try:
LOG.info('Refreshing product %s' % productid)
if not performRefresh(jar, productid):
return 0
except:
logBadRefresh(productid)
return 0
else:
return 1
def performFullRefresh(jar, productid):
# Refresh dependent products also.
if performSafeRefresh(jar, productid):
dep_ids = getDependentProducts(jar, productid)
for dep_id in dep_ids:
if isAutoRefreshEnabled(jar, dep_id):
if not performSafeRefresh(jar, dep_id):
return 0
else:
return 0
return 1
def getLastRefreshException(productid):
return refresh_exc_info.get(productid, None)
# Functions for quickly scanning the dates of product modules.
def tryFindProductDirectory(productid):
import Products
path_join = os.path.join
isdir = os.path.isdir
exists = os.path.exists
for products_dir in Products.__path__:
product_dir = path_join(products_dir, productid)
if not isdir(product_dir): continue
if not exists(path_join(product_dir, '__init__.py')):
if not exists(path_join(product_dir, '__init__.pyc')):
continue
return product_dir
return None
def tryFindModuleFilename(product_dir, filename):
# Try different variations of the filename of a module.
path_join = os.path.join
isdir = os.path.isdir
exists = os.path.exists
found = None
fn = path_join(product_dir, filename + '.py')
if exists(fn):
found = fn
if not found:
fn = fn + 'c'
if exists(fn):
found = fn
if not found:
fn = path_join(product_dir, filename)
if isdir(fn):
fn = path_join(fn, '__init__.py')
if exists(fn):
found = fn
else:
fn = fn + 'c'
if exists(fn):
found = fn
return found
def setupModTimes(productid):
mod_times = []
product_dir = tryFindProductDirectory(productid)
if product_dir is not None:
modlist = listRefreshableModules(productid)
path_join = os.path.join
exists = os.path.exists
for name, module in modlist:
splitname = name.split( '.')[2:]
if not splitname:
filename = '__init__'
else:
filename = apply(path_join, splitname)
found = tryFindModuleFilename(product_dir, filename)
if found:
try: mtime = os.stat(found)[8]
except: mtime = 0
mod_times.append((found, mtime))
products_mod_times[productid] = mod_times
def checkModTimes(productid):
# Returns 1 if there were changes.
mod_times = products_mod_times.get(productid, None)
if mod_times is None:
# Initialize the mod times.
setupModTimes(productid)
return 0
for filename, mod_time in mod_times:
try: mtime = os.stat(filename)[8]
except: mtime = 0
if mtime != mod_time:
# Something changed!
return 1
return 0
# Functions for performing auto-refresh.
def checkAutoRefresh(jar):
'''
Returns the IDs of products that need to be auto-refreshed.
'''
# Note: this function is NOT allowed to change the database!
global next_auto_refresh_check
now = time()
if next_auto_refresh_check and next_auto_refresh_check > now:
# Not enough time has passed.
return ()
next_auto_refresh_check = now + AUTO_REFRESH_INTERVAL
rd = _getCentralRefreshData(jar)
ids = rd.get('auto', None)
if not ids:
return ()
auto_refresh_ids = []
for productid in ids.keys():
if checkModTimes(productid):
auto_refresh_ids.append(productid)
return auto_refresh_ids
def finishAutoRefresh(jar, productids):
# This function is allowed to change the database.
for productid in productids:
performFullRefresh(jar, productid)
def autoRefresh(jar):
# Must be called before there are any changes made
# by the connection to the database!
import transaction
auto_refresh_ids = checkAutoRefresh(jar)
if auto_refresh_ids:
finishAutoRefresh(jar, auto_refresh_ids)
from ZODB import Connection
Connection.resetCaches()
transaction.commit()
jar._resetCache()
transaction.begin()
def setupAutoRefresh(jar):
# Install hook.
from App.ZApplication import connection_open_hooks
connection_open_hooks.append(autoRefresh)
# Init mod times.
checkAutoRefresh(jar)
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-var manage_get_product_readme__ fmt=structured-text>
<dtml-var manage_page_footer>
<dtml-comment>
Arguments for this method:
id, refresh_txt, error_type, error_value, error_tb, devel_mode,
auto_refresh_enabled, auto_refresh_other, dependent_products,
loaded_modules
</dtml-comment>
<dtml-let form_title="'Refresh product: ' + id">
<dtml-if manage_page_header>
<dtml-var manage_page_header>
<dtml-else>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html lang="en">
<head>
<title>&dtml-form_title;</title>
</head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555">
<h3>&dtml-form_title;</h3>
</dtml-if>
</dtml-let>
<dtml-var manage_tabs>
<dtml-if expr="refresh_txt == _.None">
<p>The refresh function, designed to ease the development of Zope
products, is not currently enabled for this product.
To make it available, put a file named "refresh.txt" in the &dtml-id;
product directory. Please note that not all products are
compatible with the refresh function.</p>
<dtml-else>
<dtml-if error_type>
<p><b>An exception occurred during the last refresh.</b><br />
Exception type: <b>&dtml-error_type;</b> <br />
Exception value: <b>&dtml-error_value;</b>
</p>
<pre>&dtml-error_tb;</pre>
<hr />
</dtml-if>
<form action="&dtml-absolute_url;" method="POST">
<table border="0">
<tr>
<td valign="top">
<dtml-if expr="_.string.strip(refresh_txt)">
<p>
<b>Important information about refreshing this product:</b><br />
<dtml-var refresh_txt fmt="structured-text">
</p>
</dtml-if>
<div align="center"><input type="submit"
name="manage_performRefresh:method" value="Refresh this product" />
</div>
<p>
<dtml-if auto_refresh_enabled>
<dtml-if devel_mode>
Auto refresh is enabled. Zope will repeatedly scan for
changes to the Python modules that make up this product and
execute a refresh when needed.
<dtml-else>
Although auto refresh is enabled, Zope is not in development
mode so auto refresh is not available. Use the "-D" argument
when starting Zope to enable development mode.
</dtml-if>
<dtml-else>
Auto refresh is disabled. Enable auto refresh
to cause Zope to frequently scan this product for changes.
Note that auto refresh can slow down Zope considerably
if enabled for more than a few products.
</dtml-if>
<br />
<dtml-let checked="auto_refresh_enabled and 'checked' or ' '">
<input type="checkbox" name="enable" value="1" &dtml-checked; />
Auto refresh mode &nbsp;
<input type="submit" name="manage_enableAutoRefresh:method"
value="Change" />
</dtml-let>
</p>
<dtml-if auto_refresh_other>
<p>Select dependent auto-refreshable products to be refreshed
simultaneously.<br />
<dtml-in auto_refresh_other sort>
<dtml-let checked="(_['sequence-item'] in dependent_products) and
'checked' or ' '">
<input type="checkbox" name="selections:list"
value="&dtml-sequence-item;" &dtml-checked; />
</dtml-let>
</dtml-in>
<input type="submit" name="manage_selectDependentProducts:method"
value="Change" />
</p>
</dtml-if>
</td>
<td valign="top" class="row-hilite">
<p><b>Refreshable product modules:</b></p>
<ul>
<dtml-in loaded_modules sort>
<li>&dtml-sequence-item;</li>
</dtml-in>
</ul>
</td>
</tr>
</table>
</form>
</dtml-if>
<dtml-if manage_page_footer>
<dtml-var manage_page_footer>
<dtml-else>
</body></html>
</dtml-if>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<h3>Import Traceback</h3>
<pre>&dtml-import_error_;</pre>
<dtml-var manage_page_footer>
...@@ -350,11 +350,6 @@ class ApplicationManagerTests(ConfigTestBase, ...@@ -350,11 +350,6 @@ class ApplicationManagerTests(ConfigTestBase,
am = self._makeOne() am = self._makeOne()
self.assertEqual(am.sys_platform(), sys.platform) self.assertEqual(am.sys_platform(), sys.platform)
def test_ctor_initializes_Products(self):
from App.Product import ProductFolder
am = self._makeOne()
self.assertTrue(isinstance(am.Products, ProductFolder))
def test__canCopy(self): def test__canCopy(self):
am = self._makeOne() am = self._makeOne()
self.assertFalse(am._canCopy()) self.assertFalse(am._canCopy())
......
...@@ -13,14 +13,11 @@ ...@@ -13,14 +13,11 @@
"""Application support """Application support
""" """
import os, sys, traceback import os, sys
from logging import getLogger from logging import getLogger
from cgi import escape from cgi import escape
from StringIO import StringIO
import Products import Products
import App.Product
import App.ProductRegistry
import transaction import transaction
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.class_init import InitializeClass from AccessControl.class_init import InitializeClass
...@@ -29,7 +26,6 @@ from Acquisition import aq_base ...@@ -29,7 +26,6 @@ from Acquisition import aq_base
from App.ApplicationManager import ApplicationManager from App.ApplicationManager import ApplicationManager
from App.config import getConfiguration from App.config import getConfiguration
from App import FactoryDispatcher from App import FactoryDispatcher
from App.Product import doInstall
from DateTime import DateTime from DateTime import DateTime
from HelpSys.HelpSys import HelpSys from HelpSys.HelpSys import HelpSys
from OFS.metaconfigure import get_packages_to_initialize from OFS.metaconfigure import get_packages_to_initialize
...@@ -56,7 +52,6 @@ APP_MANAGER = None ...@@ -56,7 +52,6 @@ APP_MANAGER = None
class Application(ApplicationDefaultPermissions, class Application(ApplicationDefaultPermissions,
ZDOM.Root, ZDOM.Root,
Folder.Folder, Folder.Folder,
App.ProductRegistry.ProductRegistry,
FindSupport, FindSupport,
): ):
"""Top-level system object""" """Top-level system object"""
...@@ -292,7 +287,6 @@ class AppInitializer: ...@@ -292,7 +287,6 @@ class AppInitializer:
global APP_MANAGER global APP_MANAGER
APP_MANAGER = ApplicationManager() APP_MANAGER = ApplicationManager()
APP_MANAGER._init() APP_MANAGER._init()
APP_MANAGER.Products=App.Product.ProductFolder()
app = self.getApp() app = self.getApp()
app._p_activate() app._p_activate()
...@@ -498,46 +492,33 @@ class AppInitializer: ...@@ -498,46 +492,33 @@ class AppInitializer:
self.commit('Added virtual_hosting') self.commit('Added virtual_hosting')
def install_products(self): def install_products(self):
app = self.getApp() return install_products()
# this defers to a function for b/c reasons
return install_products(app)
def install_standards(self): def install_standards(self):
app = self.getApp() app = self.getApp()
# this defers to a function for b/c reasons # this defers to a function for b/c reasons
return install_standards(app) return install_standards(app)
def install_products(app): def install_products(app=None):
# Install a list of products into the basic folder class, so
# that all folders know about top-level objects, aka products
folder_permissions = get_folder_permissions() folder_permissions = get_folder_permissions()
meta_types=[] meta_types = []
done={} done = {}
for priority, product_name, index, product_dir in get_products():
debug_mode = getConfiguration().debug_mode
transaction.get().note('Prior to product installs')
transaction.commit()
products = get_products()
for priority, product_name, index, product_dir in products:
# For each product, we will import it and try to call the # For each product, we will import it and try to call the
# intialize() method in the product __init__ module. If # intialize() method in the product __init__ module. If
# the method doesnt exist, we put the old-style information # the method doesnt exist, we put the old-style information
# together and do a default initialization. # together and do a default initialization.
if done.has_key(product_name): if product_name in done:
continue continue
done[product_name]=1 done[product_name] = 1
install_product(app, product_dir, product_name, meta_types, install_product(app, product_dir, product_name, meta_types,
folder_permissions, raise_exc=debug_mode) folder_permissions)
# Delayed install of packages-as-products # Delayed install of packages-as-products
for module, init_func in tuple(get_packages_to_initialize()): for module, init_func in tuple(get_packages_to_initialize()):
install_package(app, module, init_func, raise_exc=debug_mode) install_package(app, module, init_func)
Products.meta_types=Products.meta_types+tuple(meta_types) Products.meta_types = Products.meta_types + tuple(meta_types)
InitializeClass(Folder.Folder) InitializeClass(Folder.Folder)
def get_products(): def get_products():
...@@ -564,66 +545,45 @@ def get_products(): ...@@ -564,66 +545,45 @@ def get_products():
products.sort() products.sort()
return products return products
def import_products():
# Try to import each product, checking for and catching errors.
done={}
products = get_products()
debug_mode = getConfiguration().debug_mode
for priority, product_name, index, product_dir in products: def import_products():
if done.has_key(product_name): done = {}
for priority, product_name, index, product_dir in get_products():
if product_name in done:
LOG.warn('Duplicate Product name: ' LOG.warn('Duplicate Product name: '
'After loading Product %s from %s, ' 'After loading Product %s from %s, '
'I skipped the one in %s.' % ( 'I skipped the one in %s.' % (
`product_name`, `done[product_name]`, `product_dir`) ) `product_name`, `done[product_name]`, `product_dir`) )
continue continue
done[product_name]=product_dir done[product_name] = product_dir
import_product(product_dir, product_name, raise_exc=debug_mode) import_product(product_dir, product_name)
return done.keys() return done.keys()
def import_product(product_dir, product_name, raise_exc=0, log_exc=1):
def import_product(product_dir, product_name, raise_exc=None):
path_join=os.path.join path_join=os.path.join
isdir=os.path.isdir isdir=os.path.isdir
exists=os.path.exists exists=os.path.exists
_st=type('')
global_dict=globals() global_dict=globals()
silly=('__doc__',)
modules=sys.modules modules=sys.modules
have_module=modules.has_key
try: package_dir = path_join(product_dir, product_name)
package_dir=path_join(product_dir, product_name) if not isdir(package_dir):
if not isdir(package_dir): return return
if not exists(path_join(package_dir, '__init__.py')): if not exists(path_join(package_dir, '__init__.py')):
if not exists(path_join(package_dir, '__init__.pyc')): if not exists(path_join(package_dir, '__init__.pyc')):
if not exists(path_join(package_dir, '__init__.pyo')): if not exists(path_join(package_dir, '__init__.pyo')):
return return
pname="Products.%s" % product_name pname = "Products.%s" % product_name
try: product = __import__(pname, global_dict, global_dict, ('__doc__', ))
product=__import__(pname, global_dict, global_dict, silly)
if hasattr(product, '__module_aliases__'): if hasattr(product, '__module_aliases__'):
for k, v in product.__module_aliases__: for k, v in product.__module_aliases__:
if not have_module(k): if k not in modules:
if type(v) is _st and have_module(v): v=modules[v] if isinstance(v, str) and v in modules:
modules[k]=v v = modules[v]
except KeyboardInterrupt: modules[k] = v
raise
except:
exc = sys.exc_info()
if log_exc:
LOG.error('Could not import %s' % pname,
exc_info=exc)
f=StringIO()
traceback.print_exc(100,f)
f=f.getvalue()
try: modules[pname].__import_error__=f
except: pass
if raise_exc:
raise exc[0], exc[1], exc[2]
finally:
exc = None
def get_folder_permissions(): def get_folder_permissions():
...@@ -635,16 +595,14 @@ def get_folder_permissions(): ...@@ -635,16 +595,14 @@ def get_folder_permissions():
def install_product(app, product_dir, product_name, meta_types, def install_product(app, product_dir, product_name, meta_types,
folder_permissions, raise_exc=0, log_exc=1): folder_permissions, raise_exc=None):
from App.ProductContext import ProductContext from App.ProductContext import ProductContext
path_join=os.path.join path_join=os.path.join
isdir=os.path.isdir isdir=os.path.isdir
exists=os.path.exists exists=os.path.exists
global_dict=globals() global_dict=globals()
silly=('__doc__',)
if 1: # Preserve indentation for diff :-)
package_dir=path_join(product_dir, product_name) package_dir=path_join(product_dir, product_name)
__traceback_info__=product_name __traceback_info__=product_name
if not isdir(package_dir): return if not isdir(package_dir): return
...@@ -652,33 +610,19 @@ def install_product(app, product_dir, product_name, meta_types, ...@@ -652,33 +610,19 @@ def install_product(app, product_dir, product_name, meta_types,
if not exists(path_join(package_dir, '__init__.pyc')): if not exists(path_join(package_dir, '__init__.pyc')):
if not exists(path_join(package_dir, '__init__.pyo')): if not exists(path_join(package_dir, '__init__.pyo')):
return return
try:
product=__import__("Products.%s" % product_name, product=__import__("Products.%s" % product_name,
global_dict, global_dict, silly) global_dict, global_dict, ('__doc__', ))
# Install items into the misc_ namespace, used by products # Install items into the misc_ namespace, used by products
# and the framework itself to store common static resources # and the framework itself to store common static resources
# like icon images. # like icon images.
misc_=pgetattr(product, 'misc_', {}) misc_ = pgetattr(product, 'misc_', {})
if misc_: if misc_:
if isinstance(misc_, dict): if isinstance(misc_, dict):
misc_=Misc_(product_name, misc_) misc_=Misc_(product_name, misc_)
Application.misc_.__dict__[product_name]=misc_ Application.misc_.__dict__[product_name]=misc_
# Here we create a ProductContext object which contains
# information about the product and provides an interface
# for registering things like classes and help topics that
# should be associated with that product. Products are
# expected to implement a method named 'initialize' in
# their __init__.py that takes the ProductContext as an
# argument.
do_install = doInstall()
if do_install:
productObject = App.Product.initializeProduct(
product, product_name, package_dir, app)
context = ProductContext(productObject, app, product)
else:
# avoid any persistent connection
productObject = FactoryDispatcher.Product(product_name) productObject = FactoryDispatcher.Product(product_name)
context = ProductContext(productObject, None, product) context = ProductContext(productObject, None, product)
...@@ -687,53 +631,20 @@ def install_product(app, product_dir, product_name, meta_types, ...@@ -687,53 +631,20 @@ def install_product(app, product_dir, product_name, meta_types,
if initmethod is not None: if initmethod is not None:
initmethod(context) initmethod(context)
if do_install:
transaction.get().note('Installed product ' + product_name)
transaction.commit()
except Exception:
if log_exc:
LOG.error('Couldn\'t install %s' % product_name,
exc_info=sys.exc_info())
transaction.abort()
if raise_exc:
raise
def install_package(app, module, init_func, raise_exc=None):
def install_package(app, module, init_func, raise_exc=False, log_exc=True):
"""Installs a Python package like a product.""" """Installs a Python package like a product."""
from App.ProductContext import ProductContext from App.ProductContext import ProductContext
try:
do_install = doInstall()
name = module.__name__ name = module.__name__
if do_install:
product = App.Product.initializeProduct(module,
name,
module.__path__[0],
app)
else:
product = FactoryDispatcher.Product(name) product = FactoryDispatcher.Product(name)
app = None
product.package_name = name product.package_name = name
if init_func is not None: if init_func is not None:
newContext = ProductContext(product, app, module) newContext = ProductContext(product, None, module)
init_func(newContext) init_func(newContext)
package_initialized(module, init_func) package_initialized(module, init_func)
if do_install:
transaction.get().note('Installed package %s' % module.__name__)
transaction.commit()
except Exception:
if log_exc:
LOG.error("Couldn't install %s" % module.__name__,
exc_info=True)
transaction.abort()
if raise_exc:
raise
def install_standards(app): def install_standards(app):
# Check to see if we've already done this before # Check to see if we've already done this before
...@@ -774,58 +685,16 @@ def install_standards(app): ...@@ -774,58 +685,16 @@ def install_standards(app):
transaction.get().note('Installed standard objects') transaction.get().note('Installed standard objects')
transaction.commit() transaction.commit()
def reinstall_product(app, product_name):
folder_permissions = get_folder_permissions()
meta_types=[]
transaction.get().note('Prior to product reinstall')
transaction.commit()
for product_dir in Products.__path__:
product_names=os.listdir(product_dir)
product_names.sort()
if product_name in product_names:
removeProductMetaTypes(product_name)
install_product(app, product_dir, product_name, meta_types,
folder_permissions, raise_exc=1, log_exc=0)
break
Products.meta_types=Products.meta_types+tuple(meta_types)
InitializeClass(Folder.Folder)
def reimport_product(product_name):
for product_dir in Products.__path__:
product_names=os.listdir(product_dir)
product_names.sort()
if product_name in product_names:
import_product(product_dir, product_name,
raise_exc=1, log_exc=0)
break
def removeProductMetaTypes(pid):
"""Unregisters the meta types registered by a product.
"""
meta_types = Products.meta_types
new_mts = []
changed = 0
for meta_type in meta_types:
if meta_type.get('product', None) == pid:
# Remove this meta type.
changed = 1
else:
new_mts.append(meta_type)
if changed:
Products.meta_types = tuple(new_mts)
def pgetattr(product, name, default=install_products, __init__=0): def pgetattr(product, name, default=install_products, __init__=0):
if not __init__ and hasattr(product, name): return getattr(product, name) if not __init__ and hasattr(product, name):
return getattr(product, name)
if hasattr(product, '__init__'): if hasattr(product, '__init__'):
product=product.__init__ product=product.__init__
if hasattr(product, name): return getattr(product, name) if hasattr(product, name):
return getattr(product, name)
if default is not install_products: return default if default is not install_products:
return default
raise AttributeError, name raise AttributeError(name)
...@@ -210,14 +210,6 @@ class ObjectManager(CopyContainer, ...@@ -210,14 +210,6 @@ class ObjectManager(CopyContainer,
import Products import Products
external_candidates = [] external_candidates = []
# Look at _product_meta_types, if there is one
_pmt=()
if hasattr(self, '_product_meta_types'): _pmt=self._product_meta_types
elif hasattr(self, 'aq_acquire'):
try: _pmt=self.aq_acquire('_product_meta_types')
except: pass
external_candidates.extend(list(_pmt))
# Look at all globally visible meta types. # Look at all globally visible meta types.
for entry in getattr(Products, 'meta_types', ()): for entry in getattr(Products, 'meta_types', ()):
if ( (interfaces is not None) or (entry.get("visibility", None)=="Global") ): if ( (interfaces is not None) or (entry.get("visibility", None)=="Global") ):
......
...@@ -221,22 +221,10 @@ class TestProductInit( unittest.TestCase ): ...@@ -221,22 +221,10 @@ class TestProductInit( unittest.TestCase ):
self.configure(cfg) self.configure(cfg)
app = getApp() app = getApp()
from OFS.Application import install_products from OFS.Application import install_products
install_products(app) install_products()
obids = app.Control_Panel.Products.keys() obids = app.Control_Panel.Products.keys()
self.assertEquals(obids, []) self.assertEquals(obids, [])
def test_install_products_enabled(self):
self.makeFakeProducts()
cfg2 = cfg + '\nenable-product-installation on'
self.configure(cfg2)
app = getApp()
from OFS.Application import install_products
install_products(app)
obids = app.Control_Panel.Products.keys()
for name in FAKEPRODUCTS:
self.assert_(name in obids)
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
...@@ -51,9 +51,7 @@ def test_registerPackage(): ...@@ -51,9 +51,7 @@ def test_registerPackage():
>>> import Zope2 >>> import Zope2
>>> from OFS.Application import install_products >>> from OFS.Application import install_products
>>> install_products()
>>> app = Zope2.app()
>>> install_products(app)
pythonproduct2 initialized pythonproduct2 initialized
Make sure it is registered: Make sure it is registered:
......
...@@ -176,11 +176,10 @@ def _installProduct(name, quiet=0): ...@@ -176,11 +176,10 @@ def _installProduct(name, quiet=0):
if _patched and not _installedProducts.has_key(name): if _patched and not _installedProducts.has_key(name):
for priority, product_name, index, product_dir in get_products(): for priority, product_name, index, product_dir in get_products():
if product_name == name: if product_name == name:
if not quiet: _print('Installing %s ... ' % product_name) if not quiet:
# We want to fail immediately if a product throws an exception _print('Installing %s ... ' % product_name)
# during install, so we set the raise_exc flag.
install_product(_theApp, product_dir, product_name, meta_types, install_product(_theApp, product_dir, product_name, meta_types,
get_folder_permissions(), raise_exc=1) get_folder_permissions())
_installedProducts[product_name] = 1 _installedProducts[product_name] = 1
Products.meta_types = Products.meta_types + tuple(meta_types) Products.meta_types = Products.meta_types + tuple(meta_types)
InitializeClass(Folder) InitializeClass(Folder)
...@@ -207,10 +206,9 @@ def _installPackage(name, quiet=0): ...@@ -207,10 +206,9 @@ def _installPackage(name, quiet=0):
if _patched and not _installedPackages.has_key(name): if _patched and not _installedPackages.has_key(name):
for module, init_func in get_packages_to_initialize(): for module, init_func in get_packages_to_initialize():
if module.__name__ == name: if module.__name__ == name:
if not quiet: _print('Installing %s ... ' % module.__name__) if not quiet:
# We want to fail immediately if a package throws an exception _print('Installing %s ... ' % module.__name__)
# during install, so we set the raise_exc flag. install_package(_theApp, module, init_func)
install_package(_theApp, module, init_func, raise_exc=1)
_installedPackages[module.__name__] = 1 _installedPackages[module.__name__] = 1
if not quiet: if not quiet:
_print('done (%.3fs)\n' % (time.time() - start)) _print('done (%.3fs)\n' % (time.time() - start))
......
...@@ -127,10 +127,6 @@ def startup(): ...@@ -127,10 +127,6 @@ def startup():
# Initialize the app object # Initialize the app object
application = app() application = app()
OFS.Application.initialize(application) OFS.Application.initialize(application)
if getConfiguration().debug_mode:
# Set up auto-refresh.
from App.RefreshFuncs import setupAutoRefresh
setupAutoRefresh(application._p_jar)
application._p_jar.close() application._p_jar.close()
# "Log off" as system user # "Log off" as system user
......
...@@ -414,10 +414,7 @@ ...@@ -414,10 +414,7 @@
<key name="enable-product-installation" datatype="boolean" default="off"> <key name="enable-product-installation" datatype="boolean" default="off">
<description> <description>
If this directive is turned on, Zope performs 'product installation' BBB: This directive has no effect anymore.
(the registration of Python modules in various Products directories)
at startup. Only turn this on if your code relies on the Products section
in the Control_Panel to be populated.
</description> </description>
<metadefault>off</metadefault> <metadefault>off</metadefault>
</key> </key>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment