[Zope] Ad-hoc Zope FAQ version 0.1.1
Martijn Faassen
M.Faassen@vet.uu.nl
Mon, 08 Mar 1999 11:54:50 +0100
Ad-hoc Zope FAQ version 0.1.1
Note on the use of the Zope mailing lists:
The Zope Documentation Project has its own mailing list
(zdp@zope.org). You are encouraged to use this list for all ZDP
related communications. Please do not send your messages (such as
FAQ contributions) to Martijn Faassen (the current FAQ maintainer)
if your message can be sent to the mailing list instead.
You can subscribe to the ZDP mailing list by using the page::
http://www.zope.org/mailman/listinfo/zdp
Please join and help us document Zope!
Note on the categorizing of this FAQ:
This FAQ is a FAQ in progress. It is uncompletely unstructured;
unrelated questions are mixed haphazardly. If you feel the calling
to help categorize the FAQ into sections, or if you'd like to
maintain a particular FAQ section, please mail to the ZDP
list. Thank you in advance!
Note on the use of StructuredText:
See the (currently) first FAQ entry for information on how to
render this FAQ into HTML using Zope. For more information on
StructuredText see::
http://www.zope.org/Documentation/Reference/StructuredText
but this link is currently not completely up to date; see the
comments in 'lib\python\StructuredText\StructuredText.py' for the
current documentation.
* How do I render the Structured Text FAQ into HTML cleanly? (in Zope)
When including Structured Text (STX) from a DTML Method into
another Document with the 'fmt="structured-text"' attribute, it
will be quoted twice. This is because the '__str__' method of the
'DocumentTemplate.HTML' class automatically quotes its HTML when
called.
You can work around this by calling the STX document's
'read_raw()' method to get the unquoted version.
This way we can use unquoted Structured Text examples in our FAQ::
<!--#var "FAQ_Document_stx.read_raw()" fmt="structured-text"-->
Note that if the STX comes from a property the 'read_raw()'
workaround is not needed.
* How do I return an image from an External Method?
Example (for a png image)::
def foo(self, RESPONSE):
# set the header
RESPONSE['content-type'] = 'image/png'
# return the actual image data
return mkimage()
Another way to set the header information is::
RESPONSE.setHeader('content-type','image/png')
* How can an External Method refer to the objects in the Zope app?
Use an External Method that gets passed 'self'. 'self' is your
hook into Zope world. From 'self' you should be able to get
anywhere you'd like to go.
'self' is the folder in which the external method is
called. External Methods are methods of Folders. Since External
Methods are acquired, self is not always the same Folder in which
the method is defined--it may be a child Folder.
* Is there any way for External Methods to access files on the
filesystem of the server?
An External Method can access anything on the server computer that
the Zope process has access to. Therefore, an External Method can
do just about anything a Python program can do. This is both a
power and a liability.
* Can I restrict what an External Method can do? Is there somekind of
sandbox environment for External Methods?
Right now there is no sandbox execution environment for External
Methods. An External Method has access to everything that the Zope
process has access to. This is one of the reasons why External
Methods are required to be defined in the Extensions
directory--it's easier to keep an eye on them there.
* How do I call an external method from DTML?
Use::
<!--#var "external_method_name(arguments)"-->
to call any External Method in the folder (or acquired by the
folder). The more explicit alternative is::
<!--#var expr="external_method_name(arguments)"-->
The rule is that anything between double quotes in the first
argument of the #var tag is interpreted as an expression
attribute; a first argument without quotes is interpreted as a
name attribute.
* In a DTML expression, how do I access variable names with a '-' in
them? (such as 'sequence-item')
Any variable name (including those with '-') can be accessed in
the '_' namespace. This contains a dictionary of all variables in
the default namespace. To access 'sequence-item' you therefore use::
_['sequence-item']
Another possibly is to use '_.getitem()', like this::
_.getitem('sequence-item')
The difference is that the first renders 'sequence-item' through
DTML, whereas 'getitem()' does not; it returns the raw value of
'sequence-item'.
* How do you safely do a backup of the Zope database? ('data.bbb' in
the 'var' directory)
In Zope 1.10 and later, you can simply back up the
data.bbb. Because of the way the file written (all write
operations are appends), it is highly unlikely for a backup file
created directly to have a problem. If the database is being
written while the file is being copied, then the Zope may discard
the last transaction when reading the copy, if the last record is
incomplete.
Alternatively, you could export the entire site and backup the
export file. This won't be a full backup however, as it won't
contain old revisions of objects.
Yet another alternative is to (mis)use the Pack operation (in the
Zope Control Panel, database management). Pack will create a
Data.bbb.old backup copy of your file before packing (for safety),
which you could then back up. To exploit this side effect you
could do a pack that says something ridiculous (like pack anything
older than thousand days).
* How do I pass a non-form variable to the next requested URL?
Use Hidden fields within the form::
<input type="hidden" name="varname" value="<!--#var varname-->">
* How do I define a variable for use in a document
Set it in REQUEST::
<!--#call "REQUEST.set('varname', value)"-->
* How do I tell the tree tag to show only Folders?
By using the objectValues function in the branches_expr attribute
of the tree tag. The objectValues function of a folder returns all
objects contained by that folder.
You can however, specify a list of object metatypes that it should
return. objectValues(['DTML Document']) will only return all DTML
Document objects in a Folder. Other metatypes you could select on
are: Folder, DTML Method, File, Image, Mail Host, User Folder and
Session. This is not a complete list, as Products can and will
define their own metatypes. If you want to show more types, just
add them to the list, like objValues(['Image', 'File']).
Example of a Folder tree (with links to them)::
<!--#tree branches_expr="objectValues(['Folder'])"-->
<A HREF="<!--#var URL1-->/<!--#var id-->">
<!--#var title_or_id-->
</A>
<!--#/tree-->
* How can I collapse or expand a tree using a URL?
The tree tag will show a tree fully expanded when the 'expand_all'
variable is present in its namespace. So when a URL, showing a
tree, is called with ?expand_all=1, the tree tag will expand all
it's branches. Collapsing can be achieved with collapse_all.
The following example shows a tree with collapse and expand
'buttons'::
<A HREF="<!--#var URL0-->?expand_all=1">
Expand all</A>|
<A HREF="<!--#var URL0-->?collapse_all=1">
Collapse all</A>
<!--#tree--><!--#var id--><!--#/tree-->
* How can I limit a tree to showing only *one* branch at a time?
Use the 'single' attribute for your tree, like in::
<!--#tree single--><!--#var id--><!--#/tree-->
* What is the difference between a DTML Method and a DTML Document?
A DTML Method is what used to be just a Document in pre 1.10
releases. DTML Methods are not objects, they are methods. If you
have a folder called 'foo', and it contained a DTML Method called
'bar', then 'bar' is a method of 'foo'. 'foo' doesn't contain the
object 'bar', it has the method 'bar' bound to it. What this means
is that from 'bar''s point of view, 'self' is a Folder object, and
any properties it sees are properties of 'foo', not itself.
DTML Documents are Zope Objects. Their idea of 'self' is
themselves. They are contained in folders, but are not methods
bound to that folder, they are instances of
'DTMLDocument.DTMLDocument'. They can manage their own Properties
(because they inherit 'PropertyManager.PropertyManager').
Both DTML Documents and DTML Methods can hold DTML and content.
The difference is subtle. DTML Methods would be used when the
answer to the question "Am I showing some other objects content
through me?" is Yes. DTML Documents should be used when the answer
to the question "Am I the content I want to display?" is Yes.
DTML Methods have the advantage that they don't have any
properties, and their perspective of aquisition is from their
containing folders point of view. DTML Documents have properties
which may interfere with aquisition you'd want to do, and
aquisition is from their own point of view, which may not be what
you want.
* How can I show all form data/cookies/REQUEST vars without knowing
their
names?
By using the #in tag, you can easily all show all data in any
dictionary object, like 'REQUEST.form', 'REQUEST.cookies' and even
'REQUEST' itself. Just call the 'items()' method of the
dictionary, and the #in tag will provide a 'sequence-key' and
'sequence-item' variable for every item in the dictionary.
Show showing all form data can be done like this::
<!--#in "REQUEST.form.items()" sort-->
<!--#var sequence-key-->:<!--#var sequence-item--><BR>
<!--#/in-->
Use 'REQUEST.cookies.items()' for all your cookies, and
'REQUEST.items()' for all 'REQUEST' variables, including CGI
Environment variables.
If you don't mind the format, you could use '<PRE><!--#var
REQUEST--></PRE>' as a shortcut.
* What is a Principia Draft?
A Principia Draft is a special copy of any Zope object that is
invisible to visitors of your site. You can make changes to this
copy, and grant access to outsiders to view your changes (or work
along with you). When all changes are done, you can approve all
the changes, or discard them. Approving will make the changes
permanent and visible to your audience. However, currently Drafts
can appear to be broken.
To add a draft, choose Principia Draft from the popup menu and
click 'Add'. Next, you will have to supply an ID for the Draft,
and specify on what base object you are going to work. Clicking
'Add' in this screen will create the Draft for you. Next, you will
have to specify who will have access to the Draft.
Here the problem starts, because of the way Drafts are
managed. First, a Draft doesn't acquire any users from its parent,
so you will have to add anyone who has to have access explicitly,
even if they have already been access to a parent Folder. But the
biggest problem is that you can't reach the UserFolder and
Approval screens via the management screens. This will make a
Draft a bit harder to use, but it still is workable.
This second problem is caused by convention changes in the Folder
object (or the ObjectManager class really, used by Folder). I
think that Folder objects *used* to call 'title_and_id' on a
object when displaying it in it's management screen, but the
current release of Zope constructs the id and title of an object
by using its id and title separately. Principia Drafts made use
of the call to 'title_and_id' to insert links to its UserFolder
and Approval management screens. As this now doesn't happen
anymore, you will have to type in the URLs to the screens
yourself.
You can reach the screens calling the Draft object's
'users__draft__.manage_main' and 'manage_approve__draft__'
methods. The URLS are::
http://yoursever.com/Zope/Draft/users__draft__/manage_main
and::
http://yoursever.com/Zope/Draft/manage_approve__draft__
So, the next step is typing in the URL to the Draft's UserFolder
management screen, and adding all users that should have access to
the Draft. These users can now edit and view all changes made to
this Draft.
When done, you go to the 'manage_approve__draft__' management
screen, and approve, or discard the changes.
* How does my Folder Product load default documents?
Create a list of tuples containing filenames (without '.dtml') and
title pairs::
self.defaultDocs = [
('index', 'NotMail'),
('address', 'Address Book'),
('attach', 'Attach a File'),
('composer', 'Compose a Message'),
('contact', 'Contact Developers'),
('search', 'Search Your Mailboxes'),
('results', 'Your Search Results'),
('folder', 'Mailbox'),
('message', 'Message'),
('help', 'Help'),
('lookup', 'Lookup Users'),
('standard_footer', 'Standard HTML Footer'),
('standard_header', 'Standard HTML Header'),
('noto', 'Message must have at least one recipient.'),
('reminders', 'Your Reminders'),
('save', 'Save Message'),
('logout', 'You have been logged out'),
('GPL', 'The GNU General Public License'),
('sentmail', 'Mail was sent')]
# Then iterate over them and create each one
for id,title in self.defaultDocs:
try:
self.defaultDocFile(id, title, id)
except:
pass
# defaultDocFile is a handy little function:
def defaultDocFile(self, id, title, file):
f=open('%s/Products/NotMail/%s.dtml'
% (SOFTWARE_HOME, file))
file=f.read()
f.close()
self.manage_addDocument(id, title, file)
When you are debugging your DTML, it's a pain to keep deleting and
creating new instances of your object in order to load the new
dtml files off of the disk. This function comes in handy::
def refresh_docs(self, REQUEST):
""" refresh documents from disk (for debug purposes) """
for id,title in self.defaultDocs:
try:
self._delObject(id)
except:
pass
try:
self.defaultDocFile(id, title, id)
except:
pass
return self.manage_main(self, REQUEST)
Then, assuming your object has an attribute 'debug' you can define
some DTML like so in your manage_main screen::
<!--#if debug-->
<p><a href="refresh_docs">Refresh Documents from disk</a></p>
<!--#/if debug-->
* How do I add management tabs to my folder sub-class?
If you subclass 'OFS.Folder.Folder' to build a folderish object,
you may want to keep around the old Folder management tabs, but add
some of your own as well. This can be done by mucking with the
Folder class 'manage_options' attribute. One problem is that you
might want your own custom 'manage_main' but the FolderClass uses
'manage_main' for displaying the contents of the folder. Here is
what I did for my EMarket product.
By examining Folder's manage_options::
for option in Folder.manage_options:
print option
We get the following output::
{'label': 'Contents', 'target': 'manage_main', 'action':
'manage_main'}
{'label': 'Properties', 'target': 'manage_main', 'action':
'manage_propertiesForm'}
{'label': 'Import/Export', 'target': 'manage_main', 'action':
'manage_importExportForm'}
{'label': 'Security', 'target': 'manage_main', 'action':
'manage_access'}
{'label': 'Undo', 'target': 'manage_main', 'action':
'manage_UndoForm'}
{'label': 'Find', 'target': 'manage_main', 'action':
'manage_findFrame'}
Notice that the "Contents" tab calls Folder's manage_main attribute.
Just
rename that in your sub-class with something like::
manage_contents=OFS.Folder.Folder.manage_main
and assign that to a different tab. Here is the beginning of the
MarketItem
class from the EMarket product::
class MarketItem(OFS.Folder.Folder):
"""A MarketItem """
# Specify a name for the item type:
meta_type='MarketItem'
# Specify a relative URL for the icon used to display icons:
icon='misc_/EMarket/marketItem'
# rename Folder's manage_main to manage_contents
manage_contents=OFS.Folder.Folder.manage_main
manage_main=HTMLFile('marketItemEdit', globals())
manage_image=HTMLFile('marketItemImage', globals())
# Specify definitions for tabs:
manage_options=[
{'label':'Attributes', 'action':'manage_main'},
{'label':'ImageUpload','action':'manage_image'},
{'label':'View', 'action':''},
]
# add in the options from the Folder class.... change
# the 'action' for Contents to 'manage_contents'
for item in OFS.Folder.Folder.manage_options:
if item['label'] == 'Contents':
# be sure to use a *copy*
manage_options.append(item.copy())
manage_options[-1]['action'] = 'manage_contents'
else:
manage_options.append(item)