Commit 437d5131 authored by Hanno Schlichting's avatar Hanno Schlichting

Factored out PythonScripts

parent f44f4ddd
......@@ -50,6 +50,7 @@ eggs =
MultiMapping
Persistence
Products.ExternalMethod
Products.PythonScripts
Products.ZCTextIndex
Record
RestrictedPython
......
......@@ -25,8 +25,10 @@ Restructuring
- Avoid using the ``Products.PythonScripts.standard`` module inside the
database manager ZMI.
- Factored out the `Products.MIMETools` and `Products.ExternalMethod` packages
into their own distributions.
- Factored out the `Products.MIMETools`, `Products.ExternalMethod` and
`Products.PythonScripts` packages into their own distributions. They will
no longer be included by default in Zope 2.14 but live on as independent
add-ons.
- Factored out the `Products.ZSQLMethods` into its own distribution. The
distribution also includes the `Shared.DC.ZRDB` code. The Zope2 distribution
......
......@@ -47,8 +47,6 @@ setup(name='Zope2',
'Missing',
'MultiMapping',
'Persistence',
'Products.ExternalMethod',
'Products.MIMETools',
'Products.ZCTextIndex',
'Record',
'RestrictedPython',
......@@ -99,6 +97,10 @@ setup(name='Zope2',
'zope.testing',
'zope.traversing',
'zope.viewlet',
# BBB optional dependencies to be removed in Zope 2.14
'Products.ExternalMethod',
'Products.MIMETools',
'Products.PythonScripts',
],
include_package_data=True,
......
......@@ -9,7 +9,9 @@ Missing = svn svn://svn.zope.org/repos/main/Missing/trunk
MultiMapping = svn svn://svn.zope.org/repos/main/MultiMapping/trunk
nt_svcutils = svn svn://svn.zope.org/repos/main/nt_svcutils/trunk
Persistence = svn svn://svn.zope.org/repos/main/Persistence/trunk
Products.ExternalMethod = svn svn://svn.zope.org/repos/main/Products.ExternalMethod/trunk
Products.MIMETools = svn svn://svn.zope.org/repos/main/Products.MIMETools/trunk
Products.PythonScripts = svn svn://svn.zope.org/repos/main/Products.PythonScripts/trunk
Products.ZCTextIndex = svn svn://svn.zope.org/repos/main/Products.ZCTextIndex/trunk
Record = svn svn://svn.zope.org/repos/main/Record/trunk
tempstorage = svn svn://svn.zope.org/repos/main/tempstorage/trunk
......
2001-04-26 Evan Simpson <evan@digicool.com>
* Version 2.0.0
* Totally replaced zbytecodhacks engine with Zope's new
RestrictedPython package, which it shares with DTML.
1999-12-13 Evan Simpson <evan@4-am.com>
* Version 0.1.7
* Nested functions and lambdas are now supported, with full safety.
* You can access all of the dtml-var format functions through a builtin
dictionary called special_formats (eg: special_formats['html-quote']).
* Handing off to Digital Creations for inclusion in CVS.
* Packaged with packProduct script, which excludes parent directories
and .pyc files. Makes for a smaller package, and doesn't step on
ownership/permissions of lib/python/Products path elements.
1999-12-01 Evan Simpson <evan@4-am.com>
* Added COPYRIGHT.txt, making Wide Open Source licence (BSD-style)
explicit. (Mike Goldman provided the text, I provided the silly name).
* Jeff Rush donated a PrincipiaSearchSource method, so that
PythonMethod objects can be zcataloged to the same degree
as DTML Methods.
* Also from Jeff Rush, a document_src method, so that the source of
PythonMethods can be viewed via a "View Source" link if desired.
* If a PM has a 'traverse_subpath' parameter, you can now directly
traverse it. The elements of the subpath will then be put into a list
in 'traverse_subpath'. (thanks to Anthony Baxter)
1999-11-11 Evan Simpson <evan@4-am.com>
* Version 0.1.6
* Fix to builtins messed up DTML Methods, so I re-fixed it.
1999-11-05 Evan Simpson <evan@4-am.com>
* Version 0.1.5
* Killed *%#&$@ weird bug in which having 'add' documents in 'www'
subdirectory prevented rename, paste, or import of existing
PythonMethods! See use of '_www'.
* Range, test, and several other Zope 'builtins' had an unbound 'self'
argument unless called on _, but that's fixed.
* Safe multiplication was utterly broken (thanks to the guard); now
it works. Is anyone using the safe version??
1999-10-18 Evan Simpson <evan@4-am.com>
* Eliminated bug which delayed stringification of printed values.
1999-10-08 Evan Simpson <evan@4-am.com>
* Version 0.1.4
* Fixed mis-design noticed by Michel Pelletier, and refactored
MakeFunction. Now both kinds of Python Method have the bugfix
from 0.1.3, and shouldn't provoke a transaction when called.
1999-10-07 Evan Simpson <evan@4-am.com>
* Version 0.1.3
* Fixed parameter bug with 'self' and no defaults
1999-09-24 Evan Simpson <evan@4-am.com>
* Version 0.1.2
* Added WebDAV/FTP access code donated by Michel Pelletier
* Made parameters part of WebDAV/FTP text
* Eliminated initialization of globals to None
* Added 'global_exists' global function instead
* Killed bug with unused parameters
* Put switch in Guarded.py to allow both regular and
dangerous (XXX) PythonMethods to live side-by-side.
This means that people who patched version 0.1.1
will have to re-create any unsafe PMs they use (Sorry).
1999-09-10 Evan Simpson <evan@4-am.com>
* Version 0.1.1
* Incorporated DT_Util builtins and guards
* Fixed direct access via URL
* Fixed methodAdd.dtml
* rstrip function body
* Major changes to zbytecodehacks
''' RemotePS.py
External Method that allows you to remotely (via XML-RPC, for instance)
execute restricted Python code.
For example, create an External Method 'restricted_exec' in your Zope
root, and you can remotely call:
foobarsize = s.foo.bar.restricted_exec('len(context.objectIds())')
'''
from Products.PythonScripts.PythonScript import PythonScript
from string import join
def restricted_exec(self, body, varmap=None):
ps = PythonScript('temp')
if varmap is None:
varmap = {}
ps.ZPythonScript_edit(join(varmap.keys(), ','), body)
return apply(ps.__of__(self), varmap.values())
This diff is collapsed.
Python Scripts
The Python Scripts product provides support for restricted execution of
Python scripts, exposing them as callable objects within the Zope
environment.
Providing access to extra modules
Python script objects have a limited number of "safe" modules
available to them by default. In the course of working with Zope,
you will probably wish to make other modules available to script
objects.
The Utility.py module in the PythonScripts products provides a
simple way to make modules available for use by script objects
on a site-wide basis. Before making a module available to Python
scripts, you should carefully consider the potential for abuse
or misuse of the module, since all users with permission to
create and edit Python scripts will be able to use any functions
and classes defined in the module. In some cases, you may want to
create a custom module that just imports a subset of names from
another module and make that custom module available to reduce
the risk of abuse.
The easiest way to make modules available to Python scripts on
your site is to create a new directory in your Products directory
containing an "__init__.py" file. At Zope startup time, this
"product" will be imported, and any module assertions you make
in the __init__.py will take effect. Here's how to do it:
o In your Products directory (either in lib/python of your
Zope installation or in the root of your Zope install,
depending on your deployment model), create a new directory
with a name like "GlobalModules".
o In the new directory, create a file named "__init__.py".
o Edit the __init__.py file, and add calls to the 'allow_module'
function (located in the Products.PythonScripts.Utility module),
passing the names of modules to be enabled for use by scripts.
For example:
# Global module assertions for Python scripts
from Products.PythonScripts.Utility import allow_module
allow_module('base64')
allow_module('re')
allow_module('DateTime.DateTime')
This example adds the modules 'base64', 're' and the 'DateTime'
module in the 'DateTime' package for use by Python scripts. Note
that for packages (dotted names), each module in the package path
will become available to script objects.
o Restart your Zope server. After restarting, the modules you enabled
in your custom product will be available to Python scripts.
NB -- Placing security assestions within the package/module you are trying
to import will not work unless that package/module is located in
your Products directory.
This is because that package/module would have to be imported for its
included security assertions to take effect, but to do
that would require importing a module without any security
declarations, which defeats the point of the restricted
python environment.
Products work differently as they are imported at Zope startup.
By placing a package/module in your Products directory, you are
asserting, among other things, that it is safe for Zope to check
that package/module for security assertions. As a result, please
be careful when place packages or modules that are not Zope Products
in the Products directory.
##############################################################################
#
# 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
#
##############################################################################
"""Utility module for making simple security assertions for
Python scripts."""
__version__='$Revision: 1.6 $'[11:-2]
# These have been relocated, and should be imported from AccessControl
from AccessControl import allow_module, allow_class
##############################################################################
#
# 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
#
##############################################################################
__doc__='''Python Scripts Product Initialization
$Id$'''
import PythonScript
import standard
# Temporary
from Shared.DC import Scripts
__module_aliases__ = (
('Products.PythonScripts.Script', Scripts.Script),
('Products.PythonScripts.Bindings', Scripts.Bindings),
('Products.PythonScripts.BindingsUI', Scripts.BindingsUI),)
__roles__ = None
__allow_access_to_unprotected_subobjects__ = 1
def initialize(context):
context.registerClass(
PythonScript.PythonScript,
permission='Add Python Scripts',
constructors=(PythonScript.manage_addPythonScriptForm,
PythonScript.manage_addPythonScript),
icon='www/pyscript.gif'
)
context.registerHelp()
context.registerHelpTitle('Script (Python)')
global _m
_m['recompile'] = recompile
_m['recompile__roles__'] = ('Manager',)
# utility stuff
def recompile(self):
'''Recompile all Python Scripts'''
base = self.this()
scripts = base.ZopeFind(base, obj_metatypes=('Script (Python)',),
search_sub=1)
names = []
for name, ob in scripts:
if ob._v_change:
names.append(name)
ob._compile()
ob._p_changed = 1
if names:
return 'The following Scripts were recompiled:\n' + '\n'.join(names)
return 'No Scripts were found that required recompilation.'
import patches
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<five:deprecatedManageAddDelete
class="Products.PythonScripts.PythonScript.PythonScript"/>
</configure>
Allowing Import of Modules
Scripts are able to import a small number of Python modules for
which there are security declarations. These include 'string',
'math', and 'random'. The only way to make other Python modules
available for import is to add security declarations to them in the
filesystem.
MyScriptModules
The simplest way to allow import of a module is to create your own
simple custom Product. To make this Product:
1. Create a subdirectory of your Zope installation's "Products"
directory. The name of the directory doesn't really matter; Let's
call it 'MyScriptModules'.
2. Create a file in this subdirectory called '__init__.py'.
3. Add the following lines to your '__init__.py'::
from Products.PythonScripts.Utility import allow_module, allow_class
from AccessControl import ModuleSecurityInfo, ClassSecurityInfo
from AccessControl.class_init import InitializeClass
4. For each module to which you want to allow access, add
security declarations in '__init__.py'.
Security Declarations
You will need to write different security declarations depending
on how much of a module you want to expose. You should import the
module at the Python command line, and use 'dir(<module_name>)' to
examine its contents. Names starting with underscore ('_') may be
safely ignored. Be wary of dangerous modules, such as 'sys' and
'os', which may be exposed by the module.
You can handle a module, such as 'base64', that contains only safe
functions by writing 'allow_module("module_name")'.
To allow access to only some names, in a module with dangerous
contents, you can write::
ModuleSecurityInfo('module_name').declarePublic('name1',
'name2', ...)
If the module contains a class that you want to use, you will need
to add the following::
from <module_name> import <class>
allow_class(<class>)
Certain modules, such as 'sha', provide extension types instead of
classes. Security declarations typically cannot be added to
extension types, so the only way to use this sort of module is to
write a Python wrapper class, or use External Methods.
##############################################################################
#
# 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
#
##############################################################################
def manage_addPythonScript(id, REQUEST=None):
"""Add a Python script to a folder.
"""
class PythonScript:
"""
Python Scripts contain python code that gets executed when you call the
script by:
o Calling the script through the web by going to its location with a
web browser.
o Calling the script from another script object.
o Calling the script from a method object, such as a DTML Method.
Python Scripts can contain a "safe" subset of the python language.
Python Scripts must be safe because they can be potentially edited by
many different users through an insecure medium like the web. The
following safety issues drive the need for secure Python Scripts:
o Because many users can use Zope, a Python Script must make sure it
does not allow a user to do something they are not allowed to do,
like deleting an object they do not have permission to delete.
Because of this requirement, Python Scripts do many security checks
in the course of their execution.
o Because Python Scripts can be edited through the insecure medium of
the web, they are not allowed access to the Zope server's
file-system. Normal Python builtins like 'open' are, therefore,
not allowed.
o Because many standard Python modules break the above two security
restrictions, only a small subset of Python modules may be imported
into a Python Scripts with the "import" statement unless they have
been validated by Zope's security policy. Currently, the following
standard python modules have been validated:
o string
o math
o random
o Products.PythonScripts.standard
o Because it allows you to execute arbitrary python code, the python
"exec" statement is not allowed in Python methods.
o Because they may represent or cause security violations, some
Python builtin functions are not allowed. The following
Python builtins are not allowed:
o open
o input
o raw_input
o eval
o execfile
o compile
o type
o coerce
o intern
o dir
o globals
o locals
o vars
o buffer
o reduce
o Other builtins are restricted in nature. The following builtins
are restricted:
range -- Due to possible memory denial of service attacks, the
range builtin is restricted to creating ranges less than 10,000
elements long.
filter, map, tuple, list -- For the same reason, builtins
that construct lists from sequences do not operate on strings.
getattr, setattr, delattr -- Because these may enable Python
code to circumvent Zope's security system, they are replaced with
custom, security constrained versions.
o In order to be consistent with the Python expressions
available to DTML, the builtin functions are augmented with a
small number of functions and a class:
o test
o namespace
o render
o same_type
o DateTime
o Because the "print" statement cannot operate normally in Zope,
its effect has been changed. Rather than sending text to
stdout, "print" appends to an internal variable. The special
builtin name "printed" evaluates to the concatenation of all
text printed so far during the current execution of the
script.
"""
__constructor__ = manage_addPythonScript
__extends__=(
'PythonScripts.Script.Script',
)
def ZPythonScriptHTML_editAction(REQUEST, title, params, body):
"""
Change the script's main parameters. This method accepts the
following arguments:
REQUEST -- The current request.
title -- The new value of the Python Script's title. This must
be a string.
params -- The new value of the Python Script's parameters. Must
be a comma seperated list of values in valid python function
signature syntax. If it does not contain a valid signature
string, a SyntaxError is raised.
body -- The new value of the Python Script's body. Must contain
valid Python syntax. If it does not contain valid Python syntax,
a SyntaxError is raised.
"""
def ZPythonScript_setTitle(title):
"""
Change the script's title. This method accepts one argument,
'title' which is the new value for the script's title and must be a
string.
"""
def ZPythonScript_edit(params, body):
"""
Change the parameters and body of the script. This method accepts
two arguments:
params -- The new value of the Python Script's parameters. Must
be a comma seperated list of values in valid python function
signature syntax. If it does not contain a valid signature
string, a SyntaxError is raised.
body -- The new value of the Python Script's body. Must contain
valid Python syntax. If it does not contain valid Python syntax,
a SyntaxError is raised.
"""
def ZPythonScriptHTML_upload(REQUEST, file=''):
"""
Pass the text in file to the 'write' method.
"""
def ZScriptHTML_tryParams():
"""
Return a list of the required parameters with which to
test the script.
"""
def read():
"""
Return the body of the Python Script, with a special comment
block prepended. This block contains meta-data in the form of
comment lines as expected by the 'write' method.
"""
def write(text):
"""
Change the script by parsing the text argument into parts.
Leading lines that begin with '##' are stripped off, and if
they are of the form '##name=value', they are used to set
meta-data such as the title and parameters. The remainder of
the text is set as the body of the Python Script.
"""
def document_src(REQUEST=None, RESPONSE=None):
"""
Return the text of the 'read' method, with content type
'text/plain' set on the RESPONSE.
"""
Edit View: Edit A Script (Python)
Description
This view allows you to edit the logic which composes a script
in Python. Script instances execute in a restricted
context, bounded by your user's privilege level in Zope, and
certain global restrictions of all through-the-web code. For
information about what you "can" and "cannot" do in a Script
instance as opposed to non-through-the-web Python,
see the API Reference documentation for "Script (Python)" in
this help system.
Controls
'Title' -- Allows you to specify the Zope title of the script.
'Id' -- Allows you to specify the id of the script.
'Parameter List' -- Enter function parameters for this script
separated by commas. For example: *foo, bar, baz*
Status Elements
'Bound Names' -- the names used by this script for bindings.
You may use these names in the body of the script to refer to
bound elements. The defaults are::
context -- the script's "parent" respective to acquisition.
container -- the script's "parent" respective to containment.
script -- the script object itself.
traverse_subpath -- if the script is called directly from a URL,
this is the portion of the URL path after the script's name,
split at slash separators, into a list of strings. If the script
was not called directly from a URL, this will be an empty list.
Another possible name binding, to the "namespace" object, is
not set by default. If this was set, if the Script was
called from DTML, it would represent the namespace of the
calling DTML object.
More information about bindings can be found by visiting the
help screens of the "Bindings" tab of a Script (Python)
instance.
Buttons and Other Form Elements
'Save Changes' -- saves changes you make to the body, title, or
parameter list.
'Taller'/'Shorter'/'Wider'/'Narrower' -- make the body textarea
taller, shorter, wider, or narrower.
'File' -- upload a file into this Script (Python) instance.
File Upload Details
Files uploaded into a Script (Python) instance may either
consist only of the actual body of the function, or the file
containing the function body may contain at its head a set of
lines starting with "##" which describe bindings, parameters,
and the title. For example, a file uploaded into a Script
(Python) instance might be simply::
return "Hello"
If you upload this file into a Script (Python) instance, the
existing settings (or default settings) for bindings,
parameters, and title will remain.
However, if you wished to, you could develop a Script (Python)
on disk which looked like::
## Script (Python) "foo"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=goop, fudge
##title=
##
return "Fudge was %s, goop was %s" % (fudge, goop)
The lines preceded by "##" are metadata about the Script
(Python) instance which can survive a round trip via FTP or
through the web interface. When these lines are encountered
by the parser after an upload (or webform save), they serve to
*modify* the settings of the Script (Python) instance with the
metadata contained within the blocked area.
Lines beginning with "##" without any spaces after the "##"
are contextually meaningful to the file upload parser. There
are three keywords which can directly follow a "##": "bind",
"parameters", and "title".
The "bind" keyword following a "##" binds a name to a object
in the context this Script (Python) instance's body. For
example, the line "##bind container=goober" binds the name
"goober" to the acquisition parent of the script, allowing you
to refer to "goober" in the script body. Legal objects to
which to bind are: container, context, namespace, script, and
subpath. See the help available from the "bindings" tab of
Script (Python) instances for more details about what bindings
mean.
The "title" keyword following a "##" provides a title to the
script. E.g. "title=A Really Neat Script"
The "parameters" keyword following a "##" provides parameters
to the Script (Python) instance. E.g. "parameters=foo,bar,baz".
Test View: Test a Script (Python)
Description
This view allows you to test a Script (Python) instance.
Controls
If a Script has no parameters, when the "Test" tab is
visited the return value of the script will be presented in
the manage_main frame.
However, if a Script instance has parameters, a form
will be presented with fields for "Parameter" and "Value",
changeable on a per-parameter basis. These accept string
values. The 'Run Script' button runs the script after the
'Parameter' and 'Value' fields have been filled in, and
returns the results in the manage_main frame.
##############################################################################
#
# 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
#
##############################################################################
class Script:
"""
Web-callable script base interface.
"""
def ZScriptHTML_tryAction(REQUEST, argvars):
"""
Apply the test parameters provided by the dictionary 'argvars'.
This will call the current script with the given arguments and
return the result.
"""
"""
Products.PythonScripts.standard: Utility functions and classes
The functions and classes in this module are available from
Python-based scripts, DTML, and Page Templates.
"""
def whole_dollars(number):
"""
Show a numeric value with a dollar symbol.
"""
def dollars_and_cents(number):
"""
Show a numeric value with a dollar symbol and two decimal places.
"""
def structured_text(s):
"""
Convert a string in structured-text format to HTML.
See Also
"Structured-Text Rules":http://dev.zope.org/Members/jim/StructuredTextWiki/StructuredTextNGRules
"""
def sql_quote(s):
"""
Convert single quotes to pairs of single quotes. This is needed to
safely include values in Standard Query Language (SQL) strings.
"""
def html_quote(s):
"""
Convert characters that have special meaning in HTML to HTML
character entities.
See Also
"Python 'cgi' module":http://www.python.org/doc/current/lib/Functions_in_cgi_module.html
'escape' function.
"""
def url_quote(s):
"""
Convert characters that have special meaning in URLS to HTML
character entities using decimal values.
See Also
"Python 'urllib' module":http://www.python.org/doc/current/lib/module-urllib.html
'quote' function.
"""
def url_quote_plus(s):
"""
Like url_quote but also replace blank space characters with
'+'. This is needed for building query strings in some cases.
See Also
"Python 'urllib' module":http://www.python.org/doc/current/lib/module-urllib.html
'quote_plus' function.
"""
def url_unquote(s):
"""
Convert HTML %xx character entities into the characters they
represent. (Undoes the affects of url_quote).
See Also
"Python 'urllib' module":http://www.python.org/doc/current/lib/module-urllib.html
'unquote' function.
"""
def url_unquote_plus(s):
"""
Like url_unquote but also replace '+' characters with blank spaces.
See Also
"Python 'urllib' module":http://www.python.org/doc/current/lib/module-urllib.html
'unquote_plus' function.
"""
def urlencode(query, doseq=0):
"""
Convert a mapping object (such as a dictionary) or a sequence of
two-element tuples to a URL encoded query string. Useful for generating
query strings programmatically.
See Also
"Python 'urllib' module":http://www.python.org/doc/current/lib/module-urllib.html
'urlencode' function.
"""
def newline_to_br(s):
"""
Convert newlines and carriage-return and newline combinations to
break tags.
"""
def thousand_commas(number):
"""
Insert commas every three digits to the left of a decimal point in
values containing numbers. For example, the value, "12000
widgets" becomes "12,000 widgets".
"""
class DTML:
"""
DTML - temporary, security-restricted DTML objects
"""
def __init__(source, **kw):
"""
Create a DTML object with source text and keyword
variables. The source text defines the DTML source
content. The optinal keyword arguments define variables.
"""
def call(client=None, REQUEST={}, **kw):
"""
Render the DTML.
To accomplish its task, DTML often needs to resolve various
names into objects. For example, when the code &lt;dtml-var
spam&gt; is executed, the DTML engine tries to resolve the
name 'spam'.
In order to resolve names, you must be pass a namespace to the
DTML. This can be done several ways:
* By passing a 'client' object - If the argument 'client' is
passed, then names are looked up as attributes on the
argument.
* By passing a 'REQUEST' mapping - If the argument 'REQUEST'
is passed, then names are looked up as items on the
argument. If the object is not a mapping, an TypeError
will be raised when a name lookup is attempted.
* By passing keyword arguments -- names and their values can
be passed as keyword arguments to the Method.
The namespace given to a DTML object is the composite of these
three methods. You can pass any number of them or none at
all. Names will be looked up first in the keyword argument,
next in the client and finally in the mapping.
"""
'''Examples for enabling Script import
This file contains example code that can be used to make various
standard Python modules available to Scripts.
In order to use the example code, create a directory called
"MyScriptModules", or something equally descriptive, in your
Zope's "Products" directory. Copy this file to a file called
"__init__.py" in the new directory. Edit the new file,
uncommenting the block of code for each module that you want to
make available for import by Scripts.
You can, of course, add your own code to your "__init__.py" for
modules that are not listed below. The list is not comprehensive,
but is provided as a decent cross-section of modules.
NB: Placing security assestions within the package/module you are trying
to import will not work unless that package/module is located in
your Products directory.
This is because that package/module would have to be imported for its
included security assertions to take effect, but to do
that would require importing a module without any security
declarations, which defeats the point of the restricted
python environment.
Products work differently as they are imported at Zope startup.
By placing a package/module in your Products directory, you are
asserting, among other things, that it is safe for Zope to check
that package/module for security assertions. As a result, please
be careful when place packages or modules that are not Zope Products
in the Products directory.
'''
from AccessControl import allow_module, allow_class, allow_type
from AccessControl import ModuleSecurityInfo
# These modules are pretty safe
# allow_module('base64')
# allow_module('binascii')
# allow_module('bisect')
# allow_module('colorsys')
# allow_module('crypt')
# Only parts of these modules should be exposed
# ModuleSecurityInfo('fnmatch').declarePublic('fnmatch', 'fnmatchcase')
# ModuleSecurityInfo('re').declarePublic('compile', 'findall',
# 'match', 'search', 'split', 'sub', 'subn', 'error',
# 'I', 'L', 'M', 'S', 'X')
# import re
# allow_type(type(re.compile('')))
# allow_type(type(re.match('x','x')))
# ModuleSecurityInfo('StringIO').declarePublic('StringIO')
# These modules allow access to other servers
# ModuleSecurityInfo('ftplib').declarePublic('FTP', 'all_errors',
# 'error_reply', 'error_temp', 'error_perm', 'error_proto')
# from ftplib import FTP
# allow_class(FTP)
# ModuleSecurityInfo('httplib').declarePublic('HTTP')
# from httplib import HTTP
# allow_class(HTTP)
# ModuleSecurityInfo('nntplib').declarePublic('NNTP',
# 'error_reply', 'error_temp', 'error_perm', 'error_proto')
# from httplib import NNTP
# allow_class(NNTP)
################################################################
# Monkey patch for LP #257276 (Hotfix-2008-08-12)
#
# This code is taken from the encodings module of Python 2.4.
# Note that this code is originally (C) CNRI and it is possibly not compatible
# with the ZPL and therefore should not live within svn.zope.org. However this
# checkin is blessed by Jim Fulton for now. The fix is no longer required with
# Python 2.5 and hopefully fixed in Python 2.4.6 release.
################################################################
# Written by Marc-Andre Lemburg (mal@lemburg.com).
# (c) Copyright CNRI, All Rights Reserved. NO WARRANTY.
import sys
def search_function(encoding):
# Cache lookup
entry = _cache.get(encoding, _unknown)
if entry is not _unknown:
return entry
# Import the module:
#
# First try to find an alias for the normalized encoding
# name and lookup the module using the aliased name, then try to
# lookup the module using the standard import scheme, i.e. first
# try in the encodings package, then at top-level.
#
norm_encoding = normalize_encoding(encoding)
aliased_encoding = _aliases.get(norm_encoding) or \
_aliases.get(norm_encoding.replace('.', '_'))
if aliased_encoding is not None:
modnames = [aliased_encoding,
norm_encoding]
else:
modnames = [norm_encoding]
for modname in modnames:
if not modname or '.' in modname:
continue
try:
mod = __import__(modname,
globals(), locals(), _import_tail)
if not mod.__name__.startswith('encodings.'):
continue
except ImportError:
pass
else:
break
else:
mod = None
try:
getregentry = mod.getregentry
except AttributeError:
# Not a codec module
mod = None
if mod is None:
# Cache misses
_cache[encoding] = None
return None
# Now ask the module for the registry entry
entry = tuple(getregentry())
if len(entry) != 4:
raise CodecRegistryError,\
'module "%s" (%s) failed to register' % \
(mod.__name__, mod.__file__)
for obj in entry:
if not callable(obj):
raise CodecRegistryError,\
'incompatible codecs in module "%s" (%s)' % \
(mod.__name__, mod.__file__)
# Cache the codec registry entry
_cache[encoding] = entry
# Register its aliases (without overwriting previously registered
# aliases)
try:
codecaliases = mod.getaliases()
except AttributeError:
pass
else:
for alias in codecaliases:
if not _aliases.has_key(alias):
_aliases[alias] = modname
# Return the registry entry
return entry
if sys.version_info[:2] < (2, 5):
import encodings
encodings.search_function.func_code = search_function.func_code
##############################################################################
#
# 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
#
##############################################################################
"""Python Scripts standard utility module
This module provides helpful functions and classes for use in Python
Scripts. It can be accessed from Python with the statement
"import Products.PythonScripts.standard"
"""
__version__='$Revision: 1.14 $'[11:-2]
from urllib import urlencode
from AccessControl.SecurityInfo import ModuleSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager
from App.special_dtml import HTML
from DocumentTemplate.DT_Var import special_formats
from DocumentTemplate.DT_Var import whole_dollars
from DocumentTemplate.DT_Var import dollars_and_cents
from DocumentTemplate.DT_Var import structured_text
from DocumentTemplate.DT_Var import sql_quote
from DocumentTemplate.DT_Var import html_quote
from DocumentTemplate.DT_Var import url_quote
from DocumentTemplate.DT_Var import url_quote_plus
from DocumentTemplate.DT_Var import newline_to_br
from DocumentTemplate.DT_Var import thousands_commas
from DocumentTemplate.DT_Var import url_unquote
from DocumentTemplate.DT_Var import url_unquote_plus
from DocumentTemplate.DT_Var import restructured_text
from DocumentTemplate.security import RestrictedDTML
from ZPublisher.HTTPRequest import record
security = ModuleSecurityInfo()
security.declarePublic('special_formats',
'whole_dollars',
'dollars_and_cents',
'structured_text',
'restructured_text',
'sql_quote',
'html_quote',
'url_quote',
'url_quote_plus',
'newline_to_br',
'thousands_commas',
'url_unquote',
'url_unquote_plus',
'urlencode',
)
security.declarePublic('DTML')
class DTML(RestrictedDTML, HTML):
"""DTML objects are DocumentTemplate.HTML objects that allow
dynamic, temporary creation of restricted DTML."""
def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
"""Render the DTML given a client object, REQUEST mapping,
Response, and key word arguments."""
security=getSecurityManager()
security.addContext(self)
try:
return HTML.__call__(self, client, REQUEST, **kw)
finally: security.removeContext(self)
# We don't expose classes directly to restricted code
class _Object(record):
_guarded_writes = 1
def __init__(self, **kw):
self.update(kw)
def __setitem__(self, key, value):
key = str(key)
if key.startswith('_'):
raise ValueError, ('Object key %s is invalid. '
'Keys may not begin with an underscore.' % `key`)
self.__dict__[key] = value
def update(self, d):
for key in d.keys():
# Ignore invalid keys, rather than raising an exception.
try:
skey = str(key)
except:
continue
if skey==key and not skey.startswith('_'):
self.__dict__[skey] = d[key]
def __hash__(self):
return id(self)
security.declarePublic('Object')
def Object(**kw):
return _Object(**kw)
security.apply(globals())
This diff is collapsed.
This diff is collapsed.
'ab'[1]
mab = {'a': 1, 'b': 2}
r10 = range(10)
return r10[3:5][1] == 4 and r10[mab['b']] and 1 < mab['ab'[r10[1]]] < 3
result = []
for c in 'abcdef':
result.append({'a-c': (c <= 'c') or None,
'd-f': (c >= 'd') and 2})
return result == [
{'a-c': 1, 'd-f': 0},
{'a-c': 1, 'd-f': 0},
{'a-c': 1, 'd-f': 0},
{'a-c': None, 'd-f': 2},
{'a-c': None, 'd-f': 2},
{'a-c': None, 'd-f': 2},
]
import string
class foo:
pass
return repr(foo).split()[1], repr(string).split()[1]
x = {'x': 1}
y = range(3)
print 'double'
print printed,
print 'x:', x['x']
print 'y:', y[0], y[1], y[2]
print
return printed
l = []
a, b = 0, 1
while b < 100000000:
l.append(b)
a, b = b, a+b
return l
return container('foo')
# This test is meant to raise a deprecation warning.
a = 0
for x in range(10):
a = a + 1
return a
l1 = [1, 2, 3]
l2 = [l1[0], l1[1]]
l2.extend(l1)
l2.append(4)
del l2[:2]
del l2[0]
l2[-2:] = []
d = {'a': 1, 'b': l2[0]}
d['a'] = 0
del d['a']
return l2, d
# -*- python -*-
# An attempt to bind an illegal name in an except clause
try:
1/0
except ZeroDivisionError, __getattr__:
pass
a = 0
b = 1
try:
int('$')
except ValueError:
a = 1
try:
int('1')
except:
b = 0
return a, b
d = {}
(d['a'], d['b'], d['c'], x) = range(4)
return d, x
# Example code:
# Import a standard function, and get the HTML request and response objects.
from Products.PythonScripts.standard import html_quote
request = container.REQUEST
response = request.response
# Return a string identifying this script.
print "This is the", script.meta_type, '"%s"' % script.getId(),
if script.title:
print "(%s)" % html_quote(script.title),
print "in", container.absolute_url()
return printed
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Python Script',
)">
<p class="form-help">
Python Scripts allow you to add functionality to Zope by writing
scripts in the Python programming language
that are exposed as callable Zope objects. You may choose to upload
the script from a local file by typing the file name or using the
<em>browse</em> button.
</p>
<form action="manage_addPythonScript" method="post"
enctype="multipart/form-data">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
File
</div>
</td>
<td align="left" valign="top">
<input type="file" name="file" size="25" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
<input class="form-element" type="submit" name="submit"
value=" Add and Edit " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="&dtml-URL1;" method="post">
<input type="hidden" name=":default_method" value="ZPythonScriptHTML_changePrefs">
<table width="100%" cellspacing="0" cellpadding="2" border="0">
<dtml-with keyword_args mapping>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top" width="99%">
<input type="text" name="title" size="40"
value="&dtml-title;" />
</td>
</tr>
<tr>
<td align="left" valign="top" nowrap>
<div class="form-optional">
Parameter List
</div>
</td>
<td align="left" valign="top">
<input type="text" name="params" size="40"
value="&dtml-params;" />
</td>
</tr>
</dtml-with>
<dtml-with getBindingAssignments>
<dtml-if getAssignedNamesInOrder>
<tr>
<td align="left" valign="top">
<div class="form-label">
Bound Names
</div>
</td>
<td align="left" valign="top">
<div class="form-text">
<dtml-in getAssignedNamesInOrder>
&dtml-sequence-item;<dtml-unless sequence-end>, </dtml-unless>
</dtml-in>
</div>
</td>
</tr>
</dtml-if>
</dtml-with>
<tr>
<td align="left" valign="top">
<div class="form-label">
Last Modified
</div>
</td>
<td align="left" valign="top">
<div class="form-text">
<dtml-var bobobase_modification_time fmt="%Y-%m-%d %H:%M">
</div>
</td>
</tr>
<dtml-if errors>
<tr>
<td align="left" valign="middle" class="form-label">Errors</td>
<td align="left" valign="middle" style="background-color: #FFDDDD">
<pre><dtml-var expr="'\n'.join(errors)" html_quote></pre>
</td>
</tr>
</dtml-if>
<dtml-if warnings>
<tr>
<td align="left" valign="middle" class="form-label">Warnings</td>
<td align="left" valign="middle" style="background-color: #FFEEDD">
<pre><dtml-var expr="'\n'.join(warnings)" html_quote></pre>
</td>
</tr>
</dtml-if>
<dtml-with keyword_args mapping>
<tr>
<td align="left" valign="top" colspan="2">
<div style="width: 100%;">
<dtml-let cols="REQUEST.get('dtpref_cols', '100%')"
rows="REQUEST.get('dtpref_rows', '20')">
<dtml-if "cols[-1]=='%'">
<textarea name="body:text" wrap="off" style="width: &dtml-cols;;"
<dtml-else>
<textarea name="body:text" wrap="off" cols="&dtml-cols;"
</dtml-if>
rows="&dtml-rows;">&dtml-body;</textarea>
</dtml-let>
</div>
</td>
</tr>
</dtml-with>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit"
name="ZPythonScriptHTML_editAction:method" value="Save Changes">
</dtml-if>
&nbsp;&nbsp;
<input class="form-element" type="submit" name="height" value="Taller">
<input class="form-element" type="submit" name="height" value="Shorter">
<input class="form-element" type="submit" name="width" value="Wider">
<input class="form-element" type="submit" name="width" value="Narrower">
</div>
</td>
</tr>
</table>
</form>
<p class="form-help">
You may upload the source for &dtml-title_and_id; using the form below.
Choose an existing file from your local computer by clicking <em>browse</em>
The contents of the file should be a valid script with an optional
&quot;##data&quot; block at the start. You may click the following link
to <a href="document_src">view or download</a> the current source.
</p>
<form action="ZPythonScriptHTML_upload" method="post"
enctype="multipart/form-data">
<table cellpadding="2" cellspacing="0" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
File &nbsp;
</div>
</td>
<td align="left" valign="top">
<input type="file" name="file" size="25" value="">
</td>
</tr>
<tr>
<td></td>
<td align="left" valign="top">
<div class="form-element">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" value="Upload File">
</dtml-if>
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
Proxy roles allow you to control the access that a script has. Proxy roles
replace the roles of the user who is executing the script. This can be used
to both expand and limit access to resources. Select the proxy roles for
this object from the list below.
</p>
<form action="manage_proxy" method="post">
<table cellpadding="2" cellspacing="0" border="0">
<tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Proxy Roles
</div>
</td>
<td align="left" valign="top">
<div class="form-element">
<select name="roles:list" size="7" multiple>
<dtml-in valid_roles>
<dtml-if expr="_vars['sequence-item'] != 'Shared'">
<option <dtml-if
expr="manage_haveProxy(_vars['sequence-item'])">selected</dtml-if
>>&dtml-sequence-item;</option>
</dtml-if>
</dtml-in valid_roles>
</select>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="SUBMIT" value="Save Changes">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
......@@ -17,6 +17,7 @@ nt-svcutils = 2.13.0
Persistence = 2.13.2
Products.ExternalMethod = 2.13.0
Products.MIMETools = 2.13.0
Products.PythonScripts = 2.13.0
Products.ZCTextIndex = 2.13.0
Record = 2.13.0
tempstorage = 2.11.3
......
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