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)