[ZDP] Ad-hoc Zope FAQ version 0.1.1

Martijn Faassen M.Faassen@vet.uu.nl
Mon, 08 Mar 1999 11:54:15 +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)