All Zope Howtos Sorted by Title

AlexR
All Zope Howtos Sorted by Title
This page lists all howtos available on the Zope site. They are listed in alphabetical order. The links jump directly to the printable
version of the docs.
URL to this page: http://www.zope.org/Members/AlexR/all_howtos_alpha
Current date/time: 2000/07/05 02:14:6.85475 US/Pacific
#
Title
0
'static' DTML documents from database records
1
(Hopefully) Smarter Forms 0.1
2
(How-to) access the field names and fields of any arbitrary ZSQL
query
[http://www.zope.org/ Members/rossl/GenerateStaticPages]
[http://www.zope.org/ Members/jules/smarterforms_html]
Modified
date
Author
1999/08/25
rossl
2000/01/30
jules
1999/11/09
teyc
2000/05/09
sabaini
2000/01/17
judell
2000/04/25
jwm
1999/09/06
rossl
1999/09/17
jpenny
1999/01/01
rossl
2000/05/08
ddb
2000/05/31
haj
1999/09/21
tazzzzz
1999/08/05
Amos
1999/10/17
Zen
1999/10/06
cba
2000/04/20
Ioan
2000/06/06
shaw
[http://www.zope.org/ Members/teyc/howtoSQLVariables]
3
(mini) display hierarchies with recursion
4
A example
5
A patch to the propertysheet code to fix it's overwrite behaviour.
6
A random password generator
7
Accessing a ZSQL Method from an External Method
8
Acquisition - just say no?
9
Add a Toolbar to the Management Interface
10
Add an 'Manage' entry to IE's context menu
11
Adding ZClass Instances Programmatically
12
Advanced DTML Techniques
13
Advanced ZCatalog Searching
14
After you get 'Zope' out of your URLs...
15
Another Site Map
16
Apache As A Front End To ZServer
[http://www.zope.org/ Members/sabaini/recursionminihowto]
[http://www.zope.org/ Members/judell/CalendarTagExample]
[http://www.zope.org/ Members/jwm/propertysheetPatch]
[http://www.zope.org/ Members/rossl/ZOneTime]
[http://www.zope.org/ Members/jpenny/Accessing_a_ZSQL_Method_from_an_External_Method]
[http://www.zope.org/ Members/rossl/SpecificContext]
[http://www.zope.org/ Members/ddb/quicktools]
[http://www.zope.org/ Members/haj/ContextMenu]
[http://www.zope.org/ Members/tazzzzz/addZClasses]
[http://www.zope.org/ Documentation/How-To/AdvancedDTML]
[http://www.zope.org/ Members/Zen/howto/AdvZCatalogSearching]
[http://www.zope.org/ Members/cba/URL_fixup]
[http://www.zope.org/ Members/Ioan/SiteMap]
[http://www.zope.org/ Members/shaw/HowTo/ApacheFrontEnd]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (1 of 10) [05/07/2000 11:10:35]
AlexR
17
Apache+ZServer+SSL
18
Apache+mod_ssl+PCGI Virtual Serving
19
Apache, Zope and FastCGI
20
Authorization by Hostname or Address
21
AutoGenerate Random ID for objects
22
Breadcrumbs Navigation Trail
23
Build a Searchable Job Board
24
Build a WAP site with Zope
25
Building Zope and Python on MacOS X Server
26
Building dcSybase on Linux
27
CSS & Zope howto
28
Change TinyTable values using DTML
29
Change the port Zope runs on?
30
Changing Base Classes for a ZClass
31
Changing between DTML Methods and Documents.
32
Changing host names dynamically with Apache
33
Choosing to store data in SQL vs ZODB
34
Client-Side Script Includes
35
Compiling ZOracleDA on Linux
36
Connect to Zope via FTP
37
Control Panel Distribution Trickiness
38
Converting ZODB2 databases and export files to ZODB3
[http://www.zope.org/ Members/unfo/apche_zserver_ssl]
[http://www.zope.org/ Members/cavnit/apachevirt]
[http://www.zope.org/ Members/kedai/apache_zope_fcgi]
[http://www.zope.org/ Members/muesli/AuthByAddress]
[http://www.zope.org/ Members/Bill/Documentation/AutoGenID]
[http://www.zope.org/ Members/lstaffor/Breadcrumbs]
[http://www.zope.org/ Members/mukhsein/job_board_howto]
[http://www.zope.org/ Members/Duncan/wap_howto]
[http://www.zope.org/ Members/jshell/buildingZopeOnMacOSXServer]
[http://www.zope.org/ Members/anthony/dcSybase-on-linux]
[http://www.zope.org/ Members/mindlace/css_zope]
[http://www.zope.org/ Members/cguardia/changeTinyTable]
[http://www.zope.org/ Members/Pam/ChangePorts]
[http://www.zope.org/ Members/AlexR/ChangingBaseClasses]
[http://www.zope.org/ Members/andreas/changing_type]
[http://www.zope.org/ Members/dparker/dynamichostnames]
[http://www.zope.org/ Members/anthony/sql_vs_ZODB]
[http://www.zope.org/ Members/Caseman/client_script_howto]
[http://www.zope.org/ Members/anthony/DCOracle-on-linux]
[http://www.zope.org/ Members/mblewett/ZopeFTP]
[http://www.zope.org/ Members/Amos/ControlPanelDistribution]
[http://www.zope.org/ Documentation/How-To/ZODB2ToZODB3]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (2 of 10) [05/07/2000 11:10:35]
2000/06/22
unfo
1999/11/19
cavnit
1999/12/28
kedai
2000/02/04
muesli
1999/08/20
Bill
1999/09/16
lstaffor
1999/11/07
mukhsein
2000/01/20
Duncan
2000/01/29
jshell
1999/11/18
anthony
1999/10/29
mindlace
2000/03/07
cguardia
1999/09/03
Pam
2000/03/26
AlexR
1999/09/06
andreas
1999/10/28
dparker
2000/01/08
anthony
2000/05/15
Caseman
1999/09/30
anthony
2000/06/29
mblewett
1999/09/07
Amos
1999/08/20
Brian
AlexR
39
Create modifiable local variables
40
Creating Smart Images for Dumb Servers
41
Creating a CatalogAware ZClass
42
Custom and international time and date formats
43
DHTML navigation bar
44
DTML Methods vs. DTML Documents How-To
45
DTML Zclass ID Bug Workaround
46
DTML in ZSql Methods
47
Date Pulldown Menu Howto
48
Debugging Zope Python Code
49
Detecting User Roles in DTML
50
Determining object types in Zope
51
Diagnosing Acquisition Problems
52
Disaster Recovery + Avoidance
53
Editing Only Properties
54
FastCGI, virtualhosts and root folders
55
File Upload
56
Filling MULTIPLE SELECTED OPTIONs from database/lists
57
Form Variable Types and Typechecking
58
FreeBSD FreeTDS and SybaseDA
59
From root to subfolder without a redirect
60
Gain Zope Enlightenment By Grokking Object Orientation
[http://www.zope.org/ Members/Duncan/LocalVars]
[http://www.zope.org/ Members/jpenny/smart_images]
[http://www.zope.org/ Members/AlexR/CatalogAware]
[http://www.zope.org/ Members/AlexR/CustomDateFormats]
[http://www.zope.org/ Members/Ioan/NavBar]
[http://www.zope.org/ Members/michel/HowTos/DTMLMethodsandDocsHowTo]
[http://www.zope.org/ Members/AlexR/ZClassIDBug]
[http://www.zope.org/ Members/jpenny/DTML_in_zsql_methods]
[http://www.zope.org/ Members/purpleduck/date_pulldown]
[http://www.zope.org/ Members/klm/debuggingzope]
[http://www.zope.org/ Documentation/How-To/DetectRoles]
[http://www.zope.org/ Members/Duncan/RecogniseTypes]
[http://www.zope.org/ Members/htrd/howto/aq_order]
[http://www.zope.org/ Members/vernier/recovery]
[http://www.zope.org/ Members/rlgines/propertiesOnly]
[http://www.zope.org/ Members/Roug/fastcgi_and_rootfolder]
[http://www.zope.org/ Members/Benno/FileUpload]
[http://www.zope.org/ Members/Roug/select_with_multiple]
[http://www.zope.org/ Members/Zen/howto/FormVariableTypes]
[http://www.zope.org/ Members/TheJester/SybaseDA]
[http://www.zope.org/ Members/cba/URL_subfolder]
[http://www.zope.org/ Members/mcdonc/HowTos/gainenlightenment]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (3 of 10) [05/07/2000 11:10:35]
2000/06/08
Duncan
2000/03/07
jpenny
1999/10/17
AlexR
1999/10/23
AlexR
2000/05/18
Ioan
1999/08/29
michel
1999/10/26
AlexR
2000/05/12
jpenny
2000/06/15
purpleduck
1999/10/22
klm
1999/07/15
Amos
2000/05/25
Duncan
1999/12/07
htrd
1999/10/19
vernier
1999/12/28
rlgines
2000/01/05
Roug
2000/05/04
Benno
2000/01/14
Roug
1999/10/18
Zen
1999/11/25
TheJester
1999/10/06
cba
2000/02/05
mcdonc
AlexR
61
General Zope Design Techniques
62
Generate URLs for Objects
63
GenericUserFolder Walkthrough (GUF)
64
Get the Filename when Uploading a file through Z classes
65
Getting Started With DTML Scripting
66
Getting Started With Zope
67
Gotchas for Zope Beginners
68
HOWTO use Zope and IIS together in remote user mode
69
HOWTO: Debian Package for Products
70
Have a single sql insert statement and have dynamic field queries
71
Help on Structured Text
72
How To Publish Your Own Python Modules
73
How To: Use GenericUserFolder with an SQL database Version 1.1
74
How To: Use the Catalog-Class out of Zope
75
How to Create a Boring Product in Python (v0.1)
76
How to Distribute a Zope Product
77
How to Generate a Form with a Variable Number of Inputs; and
How to Retrieve the Data From the Form
[http://www.zope.org/ Documentation/How-To/DesignTechniques]
[http://www.zope.org/ Members/jim/ObjectURLs]
[http://www.zope.org/ Members/Zen/GenericUserFolder/walkthrough]
[http://www.zope.org/ Members/Roug/getting_the_filename]
[http://www.zope.org/ Members/Pam/DTMLScripting]
[http://www.zope.org/ Members/klm/GettingStarted]
[http://www.zope.org/ Members/jens/docs/newbie_caveats]
[http://www.zope.org/ Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode]
[http://www.zope.org/ Members/vernier/debian]
[http://www.zope.org/ Members/Bill/Documentation/ConditionalSQL]
[http://www.zope.org/ Members/millejoh/structuredText]
[http://www.zope.org/ Members/Amos/ZPublisher]
[http://www.zope.org/ Members/hippy/GUF_SQL_crypt_1_2]
[http://www.zope.org/ Members/kelcmab3/catalog_out_of_zope]
[http://www.zope.org/ Members/gtk/Boring/HowTo-Boring]
[http://www.zope.org/ Members/Amos/CreateProduct]
1999/08/02
Amos
2000/02/16
jim
2000/04/09
Zen
2000/05/15
Roug
1999/09/13
Pam
1999/09/23
klm
1999/09/12
jens
2000/01/16
jephte
2000/02/05
vernier
2000/01/07
Bill
1999/08/17
millejoh
1999/11/29
Amos
2000/03/20
hippy
2000/04/15
kelcmab3
1999/10/26
gtk
1999/09/07
Amos
1999/09/15
jpenny
1999/11/08
petrilli
1999/12/17
teyc
1999/08/03
Amos
1999/10/04
Bill
[http://www.zope.org/ Members/jpenny/variable_length_forms]
78
How to Write a DA
79
How to change the default port when runnning Zope as an NT
Service
[http://www.zope.org/ Members/petrilli/WritingADA]
[http://www.zope.org/ Members/teyc/howNTService]
80
How to create a mail form
81
How to create a variable index page
[http://www.zope.org/ Documentation/How-To/MailForm]
[http://www.zope.org/ Members/Bill/Documentation/variable_index_pages]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (4 of 10) [05/07/2000 11:10:35]
AlexR
82
How to delete a catlogAware object that is no longer catalogued.
83
How to generate random content
84
How to hide some of the management interface
85
How to install UserDb in the root folder.
86
How to make a simple site map simply
87
How to search on date-time fields using ZSQL Methods
88
How to set up and test xmlrpc in Zope
89
How to test your products without (re)starting Zope
90
How to use a different index method than 'index_html'
91
How to view all inherited and acquired attributes and methods of an
object
1999/12/09
Bill
1999/08/02
Amos
2000/02/27
dmost
1999/10/12
ThunderFoot
1999/09/15
djay
1999/10/09
teyc
1999/09/22
teyc
2000/03/08
teyc
1999/09/13
Amos
2000/03/05
dmost
1999/12/23
z113
2000/02/13
element
1999/11/22
kslee
1999/09/16
cybertad
1999/10/04
cybertad
1999/10/14
sabaini
1999/09/04
cba
1999/10/21
tseaver
100 Inserting Data using Z SQL Methods
1999/09/17
jshell
101 Installing Zope under BSDI 4.x
1999/11/08
BitDancer
102 Installing Zope under Windows 95
2000/05/14
adil.h
[http://www.zope.org/ Members/Bill/Documentation/CatalogBadness]
[http://www.zope.org/ Documentation/How-To/RandomContent]
[http://www.zope.org/ Members/dmost/ManagementTree]
[http://www.zope.org/ Members/ThunderFoot/userDb_root]
[http://www.zope.org/ Members/djay/SimpleSiteMap]
[http://www.zope.org/ Members/teyc/howSQLDateTime]
[http://www.zope.org/ Members/teyc/howxmlrpc]
[http://www.zope.org/ Members/teyc/howtoProductTesting]
[http://www.zope.org/ Documentation/How-To/changeIndex]
[http://www.zope.org/ Members/dmost/ViewSelf]
92
How to write your own DTML tag
93
How to: add users in python
94
How-To Change your Default DTMLs
95
How-To: Secrets of working with the tree tag
96
How-To: Zope and Allaire ColdFusion Studio
97
HowTo Store Files Externally
98
Including the contents of an external HTML page
99
Inheriting from ZCatalog as a Container
[http://www.zope.org/ Members/z113/1]
[http://www.zope.org/ Members/element/addingUsersInPython]
[http://www.zope.org/ Members/kslee/default_document_patch]
[http://www.zope.org/ Members/cybertad/how_to/working_with_tree]
[http://www.zope.org/ Members/cybertad/how_to/homesite]
[http://www.zope.org/ Members/sabaini/externalfiles-howto]
[http://www.zope.org/ Members/cba/Get_External]
[http://www.zope.org/ Members/tseaver/inherit_ZCatalog]
[http://www.zope.org/ Members/jshell/ZSQLMethods-InsertingData]
[http://www.zope.org/ Members/BitDancer/bsdi4_x_install]
[http://www.zope.org/ Members/adil.h/win95_install]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (5 of 10) [05/07/2000 11:10:35]
AlexR
103 Installing and Upgrading Zope 2.X
2000/01/17
mcdonc
104 Integrating LoginManager and SMB
2000/05/08
tseaver
105 Linking Domino and Zope (a work in process!)
1999/09/04
cba
106 Local Roles and the Owner Role
1999/11/21
anthony
107 Looping in DTML
1999/08/21
tone
108 Make an Index with Internal Lettered Links
1999/08/25
toyota
109 Make your life easier with INSTANCE_HOME
2000/06/15
4am
110 Making A First Zope Website
1999/09/20
hgebel
111 Making Folder Directory Listings
2000/01/07
gwachob
112 Managing http/https URL Links
2000/03/02
rbickers
113 Minor Nitpicks and Quibbles
2000/01/02
Gregor
114 Mix ZCatalog/ZSearch with Apache
1999/11/01
tseaver
115 Multi Environment Database Access
1999/11/24
peracles
116 Output in columns
2000/05/04
drewp
117 Overlib and Zope
2000/04/19
Ioan
118 Pass parameters implicitly
2000/06/28
paulabrams
119 Passing parameters to a dtml method
1999/11/09
teyc
120 Postmortem Debugging In Zope
2000/04/05
mcdonc
121 Product author's guide to Zope 2.2+ security
2000/06/26
Brian
122 Programming ZopeLDAP
2000/06/19
jshell
123 Recovering Corrupted Data.fs ZODB files
2000/05/02
itamar
124 Removing ZClass Instances Programmatically
2000/02/25
Mammux
[http://www.zope.org/ Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO]
[http://www.zope.org/ Members/tseaver/LoginManager_and_SMB]
[http://www.zope.org/ Members/cba/Domino_and_Zope]
[http://www.zope.org/ Members/anthony/owner_role]
[http://www.zope.org/ Members/tone/looping]
[http://www.zope.org/ Members/toyota/make-index]
[http://www.zope.org/ Members/4am/instancehome]
[http://www.zope.org/ Members/hgebel/HowTo/FirstZope]
[http://www.zope.org/ Members/gwachob/directorylisting]
[http://www.zope.org/ Members/rbickers/http_https_ulinks]
[http://www.zope.org/ Members/Gregor/misc_nitpicks]
[http://www.zope.org/ Members/tseaver/catalog_under_apache]
[http://www.zope.org/ Members/peracles/db_howto]
[http://www.zope.org/ Members/drewp/multiple_columns]
[http://www.zope.org/ Members/Ioan/Overlib]
[http://www.zope.org/ Members/paulabrams/howto_querystring]
[http://www.zope.org/ Members/teyc/howtoDtmlCall]
[http://www.zope.org/ Members/mcdonc/HowTos/pm-debug]
[http://www.zope.org/ Documentation/How-To/ProductAuthorUpdateGuide]
[http://www.zope.org/ Members/jshell/programmingZopeLDAP]
[http://www.zope.org/ Members/itamar/CorruptedZODB]
[http://www.zope.org/ Members/Mammux/removeZClasses]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (6 of 10) [05/07/2000 11:10:35]
AlexR
125 Rewriting rules for Roxen and Zope.
1999/11/04
Bill
126 Roxen Challenger and Zope
1999/11/01
magnus
127 Running Zope off CD ROM
2000/02/08
jens
128 Running Zope on BeOS
2000/01/16
rbickers
129 Sending email attachments (external method)
2000/04/11
rossl
130 Serve PHP/Perl within Zope
2000/03/25
Mamey
131 Serve an external files directly
1999/12/06
LiDongfeng
132 Serving Zope and other content from Apache
1999/09/03
michel
133 Session data stored in URL
1999/12/09
Duncan
134 Setting Up & Using "Pseudo-Images"
1999/09/03
kslee
135 Setting the MIME Type of a DTML Method
2000/04/11
anser
136 Setting up a static "folder"
1999/09/03
kslee
137 Simple But Powerful DTML Tips
1999/10/14
admin
138 Simple Database/form interaction
1999/08/02
Amos
139 Soft References
2000/05/11
jwm
140 Some Neat Tricks with dtml-tree
1999/12/19
anthony
141 Starting and Stopping Multiple Zope Instances
2000/02/04
jec
142 Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
2000/06/26
maryniak
143 SuSE 6.3 Daemon How-To 0.1
2000/01/29
jules
144 The (old) Product API Tutorial
1999/10/24
Zen
145 The DTML Name Space How-To
1999/08/27
michel
146 The Debugger Is Your Friend
2000/03/12
michel
[http://www.zope.org/ Members/Bill/Documentation/RoxenZope]
[http://www.zope.org/ Members/magnus/Roxen]
[http://www.zope.org/ Members/jens/docs/zope_on_cdrom]
[http://www.zope.org/ Members/rbickers/Zope_on_BeOS]
[http://www.zope.org/ Members/rossl/emailattach]
[http://www.zope.org/ Members/Mamey/PHP]
[http://www.zope.org/ Members/LiDongfeng/external_image]
[http://www.zope.org/ Members/michel/HowTos/ApacheRewriting]
[http://www.zope.org/ Members/Duncan/SessionURLs]
[http://www.zope.org/ Members/kslee/pseudo_image]
[http://www.zope.org/ Members/anser/howto_dtml_mimetype]
[http://www.zope.org/ Members/kslee/static_folder]
[http://www.zope.org/ Members/admin/QuickTips]
[http://www.zope.org/ Documentation/How-To/SQLInsert]
[http://www.zope.org/ Members/jwm/soft_references]
[http://www.zope.org/ Members/anthony/tree-coding-tricks]
[http://www.zope.org/ Members/jec/startstop_howto_html]
[http://www.zope.org/ Members/maryniak/zope-on-suse64-howto]
[http://www.zope.org/ Members/jules/SuSE-6.3_html]
[http://www.zope.org/ Members/Zen/howto/ProductAPITutorial]
[http://www.zope.org/ Members/michel/HowTos/NameSpaceHow-To]
[http://www.zope.org/ Members/michel/HowTos/TheDebuggerIsYourFriend]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (7 of 10) [05/07/2000 11:10:35]
AlexR
147 The Let Tag How-To
1999/09/04
michel
148 The Multiple Selection How-To
1999/12/16
limi
149 The With Tag How-To
1999/08/26
michel
150 Thread Safety in Zope 2
1999/08/02
Amos
151 Upgrading a Zope Installation
1999/08/20
tone
152 Use Direct Traversal of SQL Methods
2000/02/20
mcdonc
153 Use Rsync to Backup Zope
1999/10/15
jrush
154 Use SQL and dtml to create drop down boxes and check boxes
2000/04/11
teyc
155 User Defined Roles and Proxies
2000/03/04
lvogel
156 Using Apache with ZServer (NOT Zope.cgi)
1999/12/14
anser
157 Using DTML With Local File System Objects
2000/02/18
jfarr
158 Using Emacs with Zope on RedHat 6.1
2000/01/16
nemeth
159 Using External Methods
1999/09/07
Brian
160 Using Keyword Indexes in ZCatalogs
1999/11/22
AlexR
161 Using SQLSession
2000/01/17
mj, nemeth
162 Using Squid to serve Zope pages
2000/05/07
itamar
163 Using VIM and Zope via FTP
2000/06/06
philh
164 Using ZClient to Access Another Server
1999/09/24
lstaffor
165 Using ZOPE with MS SQL Server
2000/05/03
roberth
166 Using ZSQL Methods with Acquisition
1999/09/03
rob
167 Using Zope on a third-party web host
2000/02/19
gldnspud
168 Using a Chameleon Folder
1999/11/07
hellmann
[http://www.zope.org/ Members/michel/HowTos/LetTagHow-To]
[http://www.zope.org/ Members/limi/multipleselection-howto]
[http://www.zope.org/ Members/michel/HowTos/WithTagHow-To]
[http://www.zope.org/ Documentation/How-To/ThreadSafety]
[http://www.zope.org/ Members/tone/upgrade]
[http://www.zope.org/ Members/mcdonc/HowTos/direct_traversal]
[http://www.zope.org/ Members/jrush/howto_rsync_zope]
[http://www.zope.org/ Members/teyc/howtoSQLFormElements]
[http://www.zope.org/ Members/lvogel/proxy]
[http://www.zope.org/ Members/anser/apache_zserver]
[http://www.zope.org/ Members/jfarr/HowTo/DTML_with_LocalFS]
[http://www.zope.org/ Members/nemeth/usingEmacsWithZope]
[http://www.zope.org/ Documentation/How-To/ExternalMethods]
[http://www.zope.org/ Members/AlexR/KeywordIndexes]
[http://www.zope.org/ Members/nemeth/usingSQLSession]
[http://www.zope.org/ Members/itamar/Squid]
[http://www.zope.org/ Members/philh/vim_html]
[http://www.zope.org/ Members/lstaffor/ZClientMethod]
[http://www.zope.org/ Members/roberth/ZMSSQLServer]
[http://www.zope.org/ Members/rob/SQLAcquisitionHowTo/SQLAcquisition]
[http://www.zope.org/ Members/gldnspud/zope3rdparty]
[http://www.zope.org/ Members/hellmann/Chameleon/Chameleon-How-To]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (8 of 10) [05/07/2000 11:10:35]
AlexR
169 Using a tokens property
2000/05/10
cba
170 Using cron with Zope
2000/02/08
lalo
171 Using selection lists
1999/10/31
AlexR
172 Using the DTML entity syntax
1999/11/10
AlexR
173 Using the Zope Debug Log to Track Down Hangs
2000/02/02
mcdonc
174 Variable SQL Statements
1999/11/17
jpenny
175 VersionFTPServer
2000/03/16
htrd
176 View and print all code from methods in a folder or ZClass
2000/03/29
jwashin
177 What is Acquisition?
1999/09/13
Amos
178 What is Object Publishing?
1999/09/13
Amos
179 When Cookies won't do it
1999/10/16
gaaros
180 Wizards Made Easy
2000/02/29
jspisak
181 XML-RPC How To
1999/10/12
Amos
182 XMLDocument Example
2000/05/05
eukreign
183 Z Catalog Tutorial
1999/09/13
Amos
184 ZAP FAQ
1999/09/09
Amos
185 ZClass Properties
1999/10/12
lstaffor
ZClasses, ObjectManagers and containers: avoiding the resource not
186 found bug
1999/10/06
gtk
187 ZMySQLDA - from download to connection
1999/11/01
alanpog
188 ZMySQLDA on Win32
1999/09/05
philh
189 ZODB How-To
1999/08/27
michel
[http://www.zope.org/ Members/cba/tokens_usage]
[http://www.zope.org/ Members/lalo/cron]
[http://www.zope.org/ Members/AlexR/SelectionLists]
[http://www.zope.org/ Members/AlexR/EntitySyntax]
[http://www.zope.org/ Members/mcdonc/HowTos/DEBUG-LOG]
[http://www.zope.org/ Members/jpenny/Variable_SQL_statements]
[http://www.zope.org/ Members/htrd/howto/VersionFTPServer]
[http://www.zope.org/ Members/jwashin/viewZClassMethods]
[http://www.zope.org/ Members/Amos/WhatIsAcquisition]
[http://www.zope.org/ Members/Amos/WhatIsObjectPublishing]
[http://www.zope.org/ Members/gaaros/localcookies]
[http://www.zope.org/ Members/jspisak/wizard_keys]
[http://www.zope.org/ Members/Amos/XML-RPC]
[http://www.zope.org/ Members/eukreign/XMLDocument%20Example]
[http://www.zope.org/ Documentation/How-To/ZCatalogTutorial]
[http://www.zope.org/ Members/michel/Products/Zap/FAQ]
[http://www.zope.org/ Members/lstaffor/zProperties]
[http://www.zope.org/ Members/gtk/containers]
[http://www.zope.org/ Members/alanpog/zmysqlda_steps]
[http://www.zope.org/ Members/philh/mysql]
[http://www.zope.org/ Members/michel/HowTos/ZODB-How-To]
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (9 of 10) [05/07/2000 11:10:35]
AlexR
190 Zope 2, IIS, and PCGI
1999/09/12
brianh
191 Zope Installation Choices
2000/01/05
guy_davis
192 Zope and Apache on RedHat 6.1
2000/01/19
nemeth
193 Zope/Apache Virtual Host
1999/08/20
jec
2000/06/29
karl
[http://www.zope.org/ Members/brianh/iis_howto]
[http://www.zope.org/ Members/guy_davis/install_routes]
[http://www.zope.org/ Members/nemeth/howtos/zopeandapacherh61]
[http://www.zope.org/ Members/jec/virtual_howto_html]
194
find where an object is published from
[http://www.zope.org/
Members/karl/fakePublishFolder/find%20where%20an%20object%20is%20published%20from]
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/all_howtos_alpha?pp=1 (10 of 10) [05/07/2000 11:10:35]
AlexR
All Zope Howtos Sorted by Date
This page lists all howtos available on the Zope site. The newest docs are on top. The links jump directly to the printable version
of the docs.
URL to this page: http://www.zope.org/Members/AlexR/all_howtos_date
Current date/time: 2000/07/05 02:29:53.3881 US/Pacific
#
0
Title
find where an object is published from
[http://www.zope.org/
Members/karl/fakePublishFolder/find%20where%20an%20object%20is%20published%20from]
1
Connect to Zope via FTP
2
Pass parameters implicitly
3
Product author's guide to Zope 2.2+ security
4
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
5
Apache+ZServer+SSL
6
Programming ZopeLDAP
7
Make your life easier with INSTANCE_HOME
8
Date Pulldown Menu Howto
9
Create modifiable local variables
10
Apache As A Front End To ZServer
11
Using VIM and Zope via FTP
12
Add an 'Manage' entry to IE's context menu
13
Determining object types in Zope
14
DHTML navigation bar
15
Client-Side Script Includes
16
Get the Filename when Uploading a file through Z classes
[http://www.zope.org/ Members/mblewett/ZopeFTP]
[http://www.zope.org/ Members/paulabrams/howto_querystring]
[http://www.zope.org/ Documentation/How-To/ProductAuthorUpdateGuide]
[http://www.zope.org/ Members/maryniak/zope-on-suse64-howto]
[http://www.zope.org/ Members/unfo/apche_zserver_ssl]
[http://www.zope.org/ Members/jshell/programmingZopeLDAP]
[http://www.zope.org/ Members/4am/instancehome]
[http://www.zope.org/ Members/purpleduck/date_pulldown]
[http://www.zope.org/ Members/Duncan/LocalVars]
[http://www.zope.org/ Members/shaw/HowTo/ApacheFrontEnd]
[http://www.zope.org/ Members/philh/vim_html]
[http://www.zope.org/ Members/haj/ContextMenu]
[http://www.zope.org/ Members/Duncan/RecogniseTypes]
[http://www.zope.org/ Members/Ioan/NavBar]
[http://www.zope.org/ Members/Caseman/client_script_howto]
[http://www.zope.org/ Members/Roug/getting_the_filename]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (1 of 10) [05/07/2000 11:26:22]
Modified
date
Author
2000/06/29
karl
2000/06/29
mblewett
2000/06/28
paulabrams
2000/06/26
Brian
2000/06/26
maryniak
2000/06/22
unfo
2000/06/19
jshell
2000/06/15
4am
2000/06/15
purpleduck
2000/06/08
Duncan
2000/06/06
shaw
2000/06/06
philh
2000/05/31
haj
2000/05/25
Duncan
2000/05/18
Ioan
2000/05/15
Caseman
2000/05/15
Roug
AlexR
17
Installing Zope under Windows 95
18
DTML in ZSql Methods
19
Soft References
20
Using a tokens property
21
(mini) display hierarchies with recursion
22
Add a Toolbar to the Management Interface
23
Integrating LoginManager and SMB
24
Using Squid to serve Zope pages
25
XMLDocument Example
26
Output in columns
27
File Upload
28
Using ZOPE with MS SQL Server
29
Recovering Corrupted Data.fs ZODB files
30
A patch to the propertysheet code to fix it's overwrite behaviour.
31
Another Site Map
32
Overlib and Zope
33
How To: Use the Catalog-Class out of Zope
34
Setting the MIME Type of a DTML Method
35
Sending email attachments (external method)
36
Use SQL and dtml to create drop down boxes and check boxes
37
GenericUserFolder Walkthrough (GUF)
38
Postmortem Debugging In Zope
[http://www.zope.org/ Members/adil.h/win95_install]
[http://www.zope.org/ Members/jpenny/DTML_in_zsql_methods]
[http://www.zope.org/ Members/jwm/soft_references]
[http://www.zope.org/ Members/cba/tokens_usage]
[http://www.zope.org/ Members/sabaini/recursionminihowto]
[http://www.zope.org/ Members/ddb/quicktools]
[http://www.zope.org/ Members/tseaver/LoginManager_and_SMB]
[http://www.zope.org/ Members/itamar/Squid]
[http://www.zope.org/ Members/eukreign/XMLDocument%20Example]
[http://www.zope.org/ Members/drewp/multiple_columns]
[http://www.zope.org/ Members/Benno/FileUpload]
[http://www.zope.org/ Members/roberth/ZMSSQLServer]
[http://www.zope.org/ Members/itamar/CorruptedZODB]
[http://www.zope.org/ Members/jwm/propertysheetPatch]
[http://www.zope.org/ Members/Ioan/SiteMap]
[http://www.zope.org/ Members/Ioan/Overlib]
[http://www.zope.org/ Members/kelcmab3/catalog_out_of_zope]
[http://www.zope.org/ Members/anser/howto_dtml_mimetype]
[http://www.zope.org/ Members/rossl/emailattach]
[http://www.zope.org/ Members/teyc/howtoSQLFormElements]
[http://www.zope.org/ Members/Zen/GenericUserFolder/walkthrough]
[http://www.zope.org/ Members/mcdonc/HowTos/pm-debug]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (2 of 10) [05/07/2000 11:26:22]
2000/05/14
adil.h
2000/05/12
jpenny
2000/05/11
jwm
2000/05/10
cba
2000/05/09
sabaini
2000/05/08
ddb
2000/05/08
tseaver
2000/05/07
itamar
2000/05/05
eukreign
2000/05/04
drewp
2000/05/04
Benno
2000/05/03
roberth
2000/05/02
itamar
2000/04/25
jwm
2000/04/20
Ioan
2000/04/19
Ioan
2000/04/15
kelcmab3
2000/04/11
anser
2000/04/11
rossl
2000/04/11
teyc
2000/04/09
Zen
2000/04/05
mcdonc
AlexR
39
View and print all code from methods in a folder or ZClass
40
Changing Base Classes for a ZClass
41
Serve PHP/Perl within Zope
42
How To: Use GenericUserFolder with an SQL database Version 1.1
43
VersionFTPServer
44
The Debugger Is Your Friend
45
How to test your products without (re)starting Zope
46
Change TinyTable values using DTML
47
Creating Smart Images for Dumb Servers
48
How to view all inherited and acquired attributes and methods of an
object
[http://www.zope.org/ Members/jwashin/viewZClassMethods]
[http://www.zope.org/ Members/AlexR/ChangingBaseClasses]
[http://www.zope.org/ Members/Mamey/PHP]
[http://www.zope.org/ Members/hippy/GUF_SQL_crypt_1_2]
[http://www.zope.org/ Members/htrd/howto/VersionFTPServer]
[http://www.zope.org/ Members/michel/HowTos/TheDebuggerIsYourFriend]
[http://www.zope.org/ Members/teyc/howtoProductTesting]
[http://www.zope.org/ Members/cguardia/changeTinyTable]
[http://www.zope.org/ Members/jpenny/smart_images]
2000/03/29
jwashin
2000/03/26
AlexR
2000/03/25
Mamey
2000/03/20
hippy
2000/03/16
htrd
2000/03/12
michel
2000/03/08
teyc
2000/03/07
cguardia
2000/03/07
jpenny
2000/03/05
dmost
2000/03/04
lvogel
2000/03/02
rbickers
2000/02/29
jspisak
2000/02/27
dmost
2000/02/25
Mammux
2000/02/20
mcdonc
2000/02/19
gldnspud
2000/02/18
jfarr
2000/02/16
jim
2000/02/13
element
2000/02/08
jens
[http://www.zope.org/ Members/dmost/ViewSelf]
49
User Defined Roles and Proxies
50
Managing http/https URL Links
51
Wizards Made Easy
52
How to hide some of the management interface
53
Removing ZClass Instances Programmatically
54
Use Direct Traversal of SQL Methods
55
Using Zope on a third-party web host
56
Using DTML With Local File System Objects
57
Generate URLs for Objects
58
How to: add users in python
59
Running Zope off CD ROM
[http://www.zope.org/ Members/lvogel/proxy]
[http://www.zope.org/ Members/rbickers/http_https_ulinks]
[http://www.zope.org/ Members/jspisak/wizard_keys]
[http://www.zope.org/ Members/dmost/ManagementTree]
[http://www.zope.org/ Members/Mammux/removeZClasses]
[http://www.zope.org/ Members/mcdonc/HowTos/direct_traversal]
[http://www.zope.org/ Members/gldnspud/zope3rdparty]
[http://www.zope.org/ Members/jfarr/HowTo/DTML_with_LocalFS]
[http://www.zope.org/ Members/jim/ObjectURLs]
[http://www.zope.org/ Members/element/addingUsersInPython]
[http://www.zope.org/ Members/jens/docs/zope_on_cdrom]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (3 of 10) [05/07/2000 11:26:22]
AlexR
60
Using cron with Zope
61
Gain Zope Enlightenment By Grokking Object Orientation
62
HOWTO: Debian Package for Products
63
Starting and Stopping Multiple Zope Instances
64
Authorization by Hostname or Address
65
Using the Zope Debug Log to Track Down Hangs
66
(Hopefully) Smarter Forms 0.1
67
SuSE 6.3 Daemon How-To 0.1
68
Building Zope and Python on MacOS X Server
69
Build a WAP site with Zope
70
Zope and Apache on RedHat 6.1
71
A example
72
Using SQLSession
73
Installing and Upgrading Zope 2.X
74
HOWTO use Zope and IIS together in remote user mode
75
Running Zope on BeOS
76
Using Emacs with Zope on RedHat 6.1
77
Filling MULTIPLE SELECTED OPTIONs from database/lists
78
Choosing to store data in SQL vs ZODB
79
Making Folder Directory Listings
80
Have a single sql insert statement and have dynamic field queries
81
Zope Installation Choices
[http://www.zope.org/ Members/lalo/cron]
[http://www.zope.org/ Members/mcdonc/HowTos/gainenlightenment]
[http://www.zope.org/ Members/vernier/debian]
[http://www.zope.org/ Members/jec/startstop_howto_html]
[http://www.zope.org/ Members/muesli/AuthByAddress]
[http://www.zope.org/ Members/mcdonc/HowTos/DEBUG-LOG]
[http://www.zope.org/ Members/jules/smarterforms_html]
[http://www.zope.org/ Members/jules/SuSE-6.3_html]
[http://www.zope.org/ Members/jshell/buildingZopeOnMacOSXServer]
[http://www.zope.org/ Members/Duncan/wap_howto]
[http://www.zope.org/ Members/nemeth/howtos/zopeandapacherh61]
[http://www.zope.org/ Members/judell/CalendarTagExample]
[http://www.zope.org/ Members/nemeth/usingSQLSession]
[http://www.zope.org/ Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO]
[http://www.zope.org/ Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode]
[http://www.zope.org/ Members/rbickers/Zope_on_BeOS]
[http://www.zope.org/ Members/nemeth/usingEmacsWithZope]
[http://www.zope.org/ Members/Roug/select_with_multiple]
[http://www.zope.org/ Members/anthony/sql_vs_ZODB]
[http://www.zope.org/ Members/gwachob/directorylisting]
[http://www.zope.org/ Members/Bill/Documentation/ConditionalSQL]
[http://www.zope.org/ Members/guy_davis/install_routes]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (4 of 10) [05/07/2000 11:26:22]
2000/02/08
lalo
2000/02/05
mcdonc
2000/02/05
vernier
2000/02/04
jec
2000/02/04
muesli
2000/02/02
mcdonc
2000/01/30
jules
2000/01/29
jules
2000/01/29
jshell
2000/01/20
Duncan
2000/01/19
nemeth
2000/01/17
judell
2000/01/17
mj, nemeth
2000/01/17
mcdonc
2000/01/16
jephte
2000/01/16
rbickers
2000/01/16
nemeth
2000/01/14
Roug
2000/01/08
anthony
2000/01/07
gwachob
2000/01/07
Bill
2000/01/05
guy_davis
AlexR
82
FastCGI, virtualhosts and root folders
83
Minor Nitpicks and Quibbles
84
Apache, Zope and FastCGI
85
Editing Only Properties
86
How to write your own DTML tag
87
Some Neat Tricks with dtml-tree
88
How to change the default port when runnning Zope as an NT
Service
2000/01/05
Roug
2000/01/02
Gregor
1999/12/28
kedai
1999/12/28
rlgines
1999/12/23
z113
1999/12/19
anthony
1999/12/17
teyc
1999/12/16
limi
1999/12/14
anser
1999/12/09
Bill
1999/12/09
Duncan
1999/12/07
htrd
1999/12/06
LiDongfeng
1999/11/29
Amos
1999/11/25
TheJester
1999/11/24
peracles
1999/11/22
kslee
1999/11/22
AlexR
100 Local Roles and the Owner Role
1999/11/21
anthony
101 Apache+mod_ssl+PCGI Virtual Serving
1999/11/19
cavnit
102 Building dcSybase on Linux
1999/11/18
anthony
[http://www.zope.org/ Members/Roug/fastcgi_and_rootfolder]
[http://www.zope.org/ Members/Gregor/misc_nitpicks]
[http://www.zope.org/ Members/kedai/apache_zope_fcgi]
[http://www.zope.org/ Members/rlgines/propertiesOnly]
[http://www.zope.org/ Members/z113/1]
[http://www.zope.org/ Members/anthony/tree-coding-tricks]
[http://www.zope.org/ Members/teyc/howNTService]
89
The Multiple Selection How-To
90
Using Apache with ZServer (NOT Zope.cgi)
91
How to delete a catlogAware object that is no longer catalogued.
92
Session data stored in URL
93
Diagnosing Acquisition Problems
94
Serve an external files directly
95
How To Publish Your Own Python Modules
96
FreeBSD FreeTDS and SybaseDA
97
Multi Environment Database Access
98
How-To Change your Default DTMLs
99
Using Keyword Indexes in ZCatalogs
[http://www.zope.org/ Members/limi/multipleselection-howto]
[http://www.zope.org/ Members/anser/apache_zserver]
[http://www.zope.org/ Members/Bill/Documentation/CatalogBadness]
[http://www.zope.org/ Members/Duncan/SessionURLs]
[http://www.zope.org/ Members/htrd/howto/aq_order]
[http://www.zope.org/ Members/LiDongfeng/external_image]
[http://www.zope.org/ Members/Amos/ZPublisher]
[http://www.zope.org/ Members/TheJester/SybaseDA]
[http://www.zope.org/ Members/peracles/db_howto]
[http://www.zope.org/ Members/kslee/default_document_patch]
[http://www.zope.org/ Members/AlexR/KeywordIndexes]
[http://www.zope.org/ Members/anthony/owner_role]
[http://www.zope.org/ Members/cavnit/apachevirt]
[http://www.zope.org/ Members/anthony/dcSybase-on-linux]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (5 of 10) [05/07/2000 11:26:22]
AlexR
103 Variable SQL Statements
1999/11/17
jpenny
104 Using the DTML entity syntax
1999/11/10
AlexR
105 Passing parameters to a dtml method
1999/11/09
teyc
(How-to) access the field names and fields of any arbitrary ZSQL
106 query
1999/11/09
teyc
107 Installing Zope under BSDI 4.x
1999/11/08
BitDancer
108 How to Write a DA
1999/11/08
petrilli
109 Using a Chameleon Folder
1999/11/07
hellmann
110 Build a Searchable Job Board
1999/11/07
mukhsein
111 Rewriting rules for Roxen and Zope.
1999/11/04
Bill
112 ZMySQLDA - from download to connection
1999/11/01
alanpog
113 Mix ZCatalog/ZSearch with Apache
1999/11/01
tseaver
114 Roxen Challenger and Zope
1999/11/01
magnus
115 Using selection lists
1999/10/31
AlexR
116 CSS & Zope howto
1999/10/29
mindlace
117 Changing host names dynamically with Apache
1999/10/28
dparker
118 DTML Zclass ID Bug Workaround
1999/10/26
AlexR
119 How to Create a Boring Product in Python (v0.1)
1999/10/26
gtk
120 The (old) Product API Tutorial
1999/10/24
Zen
121 Custom and international time and date formats
1999/10/23
AlexR
122 Debugging Zope Python Code
1999/10/22
klm
123 Inheriting from ZCatalog as a Container
1999/10/21
tseaver
[http://www.zope.org/ Members/jpenny/Variable_SQL_statements]
[http://www.zope.org/ Members/AlexR/EntitySyntax]
[http://www.zope.org/ Members/teyc/howtoDtmlCall]
[http://www.zope.org/ Members/teyc/howtoSQLVariables]
[http://www.zope.org/ Members/BitDancer/bsdi4_x_install]
[http://www.zope.org/ Members/petrilli/WritingADA]
[http://www.zope.org/ Members/hellmann/Chameleon/Chameleon-How-To]
[http://www.zope.org/ Members/mukhsein/job_board_howto]
[http://www.zope.org/ Members/Bill/Documentation/RoxenZope]
[http://www.zope.org/ Members/alanpog/zmysqlda_steps]
[http://www.zope.org/ Members/tseaver/catalog_under_apache]
[http://www.zope.org/ Members/magnus/Roxen]
[http://www.zope.org/ Members/AlexR/SelectionLists]
[http://www.zope.org/ Members/mindlace/css_zope]
[http://www.zope.org/ Members/dparker/dynamichostnames]
[http://www.zope.org/ Members/AlexR/ZClassIDBug]
[http://www.zope.org/ Members/gtk/Boring/HowTo-Boring]
[http://www.zope.org/ Members/Zen/howto/ProductAPITutorial]
[http://www.zope.org/ Members/AlexR/CustomDateFormats]
[http://www.zope.org/ Members/klm/debuggingzope]
[http://www.zope.org/ Members/tseaver/inherit_ZCatalog]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (6 of 10) [05/07/2000 11:26:22]
AlexR
124 Disaster Recovery + Avoidance
1999/10/19
vernier
125 Form Variable Types and Typechecking
1999/10/18
Zen
126 Creating a CatalogAware ZClass
1999/10/17
AlexR
127 Advanced ZCatalog Searching
1999/10/17
Zen
128 When Cookies won't do it
1999/10/16
gaaros
129 Use Rsync to Backup Zope
1999/10/15
jrush
130 Simple But Powerful DTML Tips
1999/10/14
admin
131 HowTo Store Files Externally
1999/10/14
sabaini
132 How to install UserDb in the root folder.
1999/10/12
ThunderFoot
133 XML-RPC How To
1999/10/12
Amos
134 ZClass Properties
1999/10/12
lstaffor
135 How to search on date-time fields using ZSQL Methods
1999/10/09
teyc
136 After you get 'Zope' out of your URLs...
1999/10/06
cba
137 From root to subfolder without a redirect
1999/10/06
cba
ZClasses, ObjectManagers and containers: avoiding the resource not
138 found bug
1999/10/06
gtk
139 How-To: Zope and Allaire ColdFusion Studio
1999/10/04
cybertad
140 How to create a variable index page
1999/10/04
Bill
141 Compiling ZOracleDA on Linux
1999/09/30
anthony
142 Using ZClient to Access Another Server
1999/09/24
lstaffor
143 Getting Started With Zope
1999/09/23
klm
144 How to set up and test xmlrpc in Zope
1999/09/22
teyc
[http://www.zope.org/ Members/vernier/recovery]
[http://www.zope.org/ Members/Zen/howto/FormVariableTypes]
[http://www.zope.org/ Members/AlexR/CatalogAware]
[http://www.zope.org/ Members/Zen/howto/AdvZCatalogSearching]
[http://www.zope.org/ Members/gaaros/localcookies]
[http://www.zope.org/ Members/jrush/howto_rsync_zope]
[http://www.zope.org/ Members/admin/QuickTips]
[http://www.zope.org/ Members/sabaini/externalfiles-howto]
[http://www.zope.org/ Members/ThunderFoot/userDb_root]
[http://www.zope.org/ Members/Amos/XML-RPC]
[http://www.zope.org/ Members/lstaffor/zProperties]
[http://www.zope.org/ Members/teyc/howSQLDateTime]
[http://www.zope.org/ Members/cba/URL_fixup]
[http://www.zope.org/ Members/cba/URL_subfolder]
[http://www.zope.org/ Members/gtk/containers]
[http://www.zope.org/ Members/cybertad/how_to/homesite]
[http://www.zope.org/ Members/Bill/Documentation/variable_index_pages]
[http://www.zope.org/ Members/anthony/DCOracle-on-linux]
[http://www.zope.org/ Members/lstaffor/ZClientMethod]
[http://www.zope.org/ Members/klm/GettingStarted]
[http://www.zope.org/ Members/teyc/howxmlrpc]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (7 of 10) [05/07/2000 11:26:22]
AlexR
145 Adding ZClass Instances Programmatically
1999/09/21
tazzzzz
146 Making A First Zope Website
1999/09/20
hgebel
147 Inserting Data using Z SQL Methods
1999/09/17
jshell
148 Accessing a ZSQL Method from an External Method
1999/09/17
jpenny
149 How-To: Secrets of working with the tree tag
1999/09/16
cybertad
150 Breadcrumbs Navigation Trail
1999/09/16
lstaffor
151 How to make a simple site map simply
1999/09/15
djay
How to Generate a Form with a Variable Number of Inputs; and
152 How to Retrieve the Data From the Form
1999/09/15
jpenny
153 Z Catalog Tutorial
1999/09/13
Amos
154 What is Object Publishing?
1999/09/13
Amos
155 What is Acquisition?
1999/09/13
Amos
156 How to use a different index method than 'index_html'
1999/09/13
Amos
157 Getting Started With DTML Scripting
1999/09/13
Pam
158 Zope 2, IIS, and PCGI
1999/09/12
brianh
159 Gotchas for Zope Beginners
1999/09/12
jens
160 ZAP FAQ
1999/09/09
Amos
161 How to Distribute a Zope Product
1999/09/07
Amos
162 Control Panel Distribution Trickiness
1999/09/07
Amos
163 Using External Methods
1999/09/07
Brian
164 A random password generator
1999/09/06
rossl
165 Changing between DTML Methods and Documents.
1999/09/06
andreas
[http://www.zope.org/ Members/tazzzzz/addZClasses]
[http://www.zope.org/ Members/hgebel/HowTo/FirstZope]
[http://www.zope.org/ Members/jshell/ZSQLMethods-InsertingData]
[http://www.zope.org/ Members/jpenny/Accessing_a_ZSQL_Method_from_an_External_Method]
[http://www.zope.org/ Members/cybertad/how_to/working_with_tree]
[http://www.zope.org/ Members/lstaffor/Breadcrumbs]
[http://www.zope.org/ Members/djay/SimpleSiteMap]
[http://www.zope.org/ Members/jpenny/variable_length_forms]
[http://www.zope.org/ Documentation/How-To/ZCatalogTutorial]
[http://www.zope.org/ Members/Amos/WhatIsObjectPublishing]
[http://www.zope.org/ Members/Amos/WhatIsAcquisition]
[http://www.zope.org/ Documentation/How-To/changeIndex]
[http://www.zope.org/ Members/Pam/DTMLScripting]
[http://www.zope.org/ Members/brianh/iis_howto]
[http://www.zope.org/ Members/jens/docs/newbie_caveats]
[http://www.zope.org/ Members/michel/Products/Zap/FAQ]
[http://www.zope.org/ Members/Amos/CreateProduct]
[http://www.zope.org/ Members/Amos/ControlPanelDistribution]
[http://www.zope.org/ Documentation/How-To/ExternalMethods]
[http://www.zope.org/ Members/rossl/ZOneTime]
[http://www.zope.org/ Members/andreas/changing_type]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (8 of 10) [05/07/2000 11:26:22]
AlexR
166 ZMySQLDA on Win32
1999/09/05
philh
167 The Let Tag How-To
1999/09/04
michel
168 Linking Domino and Zope (a work in process!)
1999/09/04
cba
169 Including the contents of an external HTML page
1999/09/04
cba
170 Setting Up & Using "Pseudo-Images"
1999/09/03
kslee
171 Setting up a static "folder"
1999/09/03
kslee
172 Using ZSQL Methods with Acquisition
1999/09/03
rob
173 Serving Zope and other content from Apache
1999/09/03
michel
174 Change the port Zope runs on?
1999/09/03
Pam
175 DTML Methods vs. DTML Documents How-To
1999/08/29
michel
176 The DTML Name Space How-To
1999/08/27
michel
177 ZODB How-To
1999/08/27
michel
178 The With Tag How-To
1999/08/26
michel
179 'static' DTML documents from database records
1999/08/25
rossl
180 Make an Index with Internal Lettered Links
1999/08/25
toyota
181 Looping in DTML
1999/08/21
tone
182 AutoGenerate Random ID for objects
1999/08/20
Bill
183 Converting ZODB2 databases and export files to ZODB3
1999/08/20
Brian
184 Zope/Apache Virtual Host
1999/08/20
jec
185 Upgrading a Zope Installation
1999/08/20
tone
186 Help on Structured Text
1999/08/17
millejoh
187 Advanced DTML Techniques
1999/08/05
Amos
[http://www.zope.org/ Members/philh/mysql]
[http://www.zope.org/ Members/michel/HowTos/LetTagHow-To]
[http://www.zope.org/ Members/cba/Domino_and_Zope]
[http://www.zope.org/ Members/cba/Get_External]
[http://www.zope.org/ Members/kslee/pseudo_image]
[http://www.zope.org/ Members/kslee/static_folder]
[http://www.zope.org/ Members/rob/SQLAcquisitionHowTo/SQLAcquisition]
[http://www.zope.org/ Members/michel/HowTos/ApacheRewriting]
[http://www.zope.org/ Members/Pam/ChangePorts]
[http://www.zope.org/ Members/michel/HowTos/DTMLMethodsandDocsHowTo]
[http://www.zope.org/ Members/michel/HowTos/NameSpaceHow-To]
[http://www.zope.org/ Members/michel/HowTos/ZODB-How-To]
[http://www.zope.org/ Members/michel/HowTos/WithTagHow-To]
[http://www.zope.org/ Members/rossl/GenerateStaticPages]
[http://www.zope.org/ Members/toyota/make-index]
[http://www.zope.org/ Members/tone/looping]
[http://www.zope.org/ Members/Bill/Documentation/AutoGenID]
[http://www.zope.org/ Documentation/How-To/ZODB2ToZODB3]
[http://www.zope.org/ Members/jec/virtual_howto_html]
[http://www.zope.org/ Members/tone/upgrade]
[http://www.zope.org/ Members/millejoh/structuredText]
[http://www.zope.org/ Documentation/How-To/AdvancedDTML]
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (9 of 10) [05/07/2000 11:26:22]
AlexR
188 How to create a mail form
1999/08/03
Amos
189 Simple Database/form interaction
1999/08/02
Amos
190 General Zope Design Techniques
1999/08/02
Amos
191 How to generate random content
1999/08/02
Amos
192 Thread Safety in Zope 2
1999/08/02
Amos
193 Detecting User Roles in DTML
1999/07/15
Amos
194 Acquisition - just say no?
1999/01/01
rossl
[http://www.zope.org/ Documentation/How-To/MailForm]
[http://www.zope.org/ Documentation/How-To/SQLInsert]
[http://www.zope.org/ Documentation/How-To/DesignTechniques]
[http://www.zope.org/ Documentation/How-To/RandomContent]
[http://www.zope.org/ Documentation/How-To/ThreadSafety]
[http://www.zope.org/ Documentation/How-To/DetectRoles]
[http://www.zope.org/ Members/rossl/SpecificContext]
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/all_howtos_date?pp=1 (10 of 10) [05/07/2000 11:26:22]
'static' DTML documents from database records
How-To: 'static' DTML documents from database
records
Created by rossl. Last modified on 1999/09/21.
No doubt you've built some beautiful, complex web pages out of database backend content. You may also have discovered that
rendering those pages takes a while.
For example, I'm building some pages which have between 30 and 50 elements on them (links to curriculum content with
appropriate header and descriptive data). The combination of dtml and ZSQL takes about 3 seconds per page on a linux/pII box
talking to a nearby sybase database server.
So, it occurs to me that since I don't change the content very often, it might be sensible to generate all the pages one time so they
can be served as if they were static Zope DTML documents. The old "train hard, fight easy" approach.
Turns out to be fairly easy to do, so it might be worth documenting here.
I have a dtml method which calls a bunch of other dtml widgets to format database elements grabbed by some Zsql methods. The
end result is a dynamically generated page on demand. The dtml which actually pulls the whole mess together (called
studentShellGMPID) is included below - not because it's a good example of anything, but it does illustrate two tricks worth
knowing about !
1. it sets some request variables if they're not present in the namespace so I can test it - this is a very useful little trick which
I recommend - set default parameters while you're building form pages ! Note that the viewer is reminded that the default
value was used - also handy discovering that parameters aren't being passed.
2. the form of the calls to one of the widgets includes the use of the two parameters .None and _ plus the setting of some
"form variables" which are needed for some zsql calls in the widget
Here's the code :
<dtml-comment>
a sample student shell to lay out SMP documents using the SMPdocwidget
needs GMPID to identify problem
defaults supplied for testing in the first few lines
August 20, 1999. rml
</dtml-comment>
<dtml-unless GMPID><dtml-call "REQUEST.set('GMPID','1946')">(default gmpid
1946)</dtml-unless>
<dtml-var standard_html_header>
<table border=1 width="95%">
<tr><td colspan="2" align="center">
<dtml-var studentproblembanner>
</td></tr><tr>
<td align="center" valign="top"><dtml-var "SMPdocwidget(_.None,_,ItemId='LE')"></td>
<td align="center" valign="top"><dtml-var "SMPdocwidget(_.None,_,ItemId='TS')"></td>
</tr><tr>
<td align="center" valign="top"><dtml-var "SMPdocwidget(_.None,_,ItemId='LT')"></td>
<td align="center" valign="top"><dtml-var "SMPdocwidget(_.None,_,ItemId='WL')"></td>
</tr><tr>
<td align="center" valign="top" colspan="2">
<dtml-var "showotherSMPlinks(_.None,_)"></td>
</tr>
</table>
<dtml-var standard_html_footer>
Now, this particular dtml method is called with the parameter GMPID set - that's used for the database lookup to assemble all the
relevant database elements.
What I wanted to do was to generate a series of dtml documents which looked like each possible page - so I wrote a little zsql
query which returns all of the possible GMPID values (called getSMPProblems) together with a string useful for a title field, and
included it in the following method (note that the actual method I use has error checking cruft removed for this brief document !) :
<dtml-var standard_html_header>
<dtml-in getSMPProblems>
<dtml-with test>
http://www.zope.org/Members/rossl/GenerateStaticPages?pp=1 (1 of 2) [05/07/2000 11:10:40]
'static' DTML documents from database records
<dtml-call
"manage_addDTMLDocument(id=GMPID,title=DName,file=studentShellGMPID(_.None,_))">
</dtml-with>
</dtml-in>
<dtml-var standard_html_footer>
So, for each database record, this will make a nice new dtml document in a folder called "test". The document contains the html
which studentShellGMPID generates when fed a GMPID and has an ID equal to GMPID together with a nice meaningful title.
Now I can change the front end so that a user is given the pre-generated "static" version of the page rather than making all those
expensive database calls every time. I can regenerate pages dynamically once only whenever any of the content gets changed and
my zope server is much less heavily loaded as a result.
One possible extension would be to turn this into a kind of "server side cache" for optionally dynamic (but expensive to generate)
documents - Construct a mechanism which returns the last update to any component part of the page to be served, compare this
with the time stamp on the document and regenerate it only if needed, otherwise just serve up the static pre-formed one. Best of
both worlds ! I wonder how dynamic the site would need to be to gain a performance advantage using this method ? If you expect
a large number of page hits between changes to any of the components, this method might be useful.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rossl/GenerateStaticPages?pp=1 (2 of 2) [05/07/2000 11:10:40]
(Hopefully) Smarter Forms 0.1
How-To: (Hopefully) Smarter Forms 0.1
Created by jules. Last modified on 2000/01/30.
I hate hitting submit and being told to press the back button. A good 50% of the time the form will reload and lose everything I've
keyed in. Hardly elegant and just one more reason why the Web gets a bad name for usability.
During my transition to being a fully blown Zope wonk, the need to build smart forms is one of the first things I've been playing
with. What's below might not be the most elegant or Zope-ish way of doing things but it works. If you have any better ways of
doing what I've done here, please do let me know.
My first step was to create a separate form method separated from the processing logic:
<form method="post">
<!-- First Name -->
<p>
<b>Your name?</b><br>
<input type="text" name="fname"
<dtml-if fname> value="<dtml-var "fname">" </dtml-if>
>
<!-- /First Name -->
<input type="submit" value="Next>>">
</p>
</form>
I saved this in a method called mainform_include. The beauty of Acquisition is that you can easily segment your forms into a
modular structure. You'll probably ask the same length and type for, in this example, firstname everywhere on your site. So as
long as this form is on the same level or above, you can reference it in your processing logic.
The second step is to write the form handler:
<dtml-var standard_html_header>
<dtml-comment>
One of my pet peeves is filling out a form, only to find something
is wrong. Then I hit the back button to lose all my input. Grrr!
The Web GUI doesn't have to be A Bad Thing and with a little effort
on the part of the programmer/designer, gently prompting a user to
correct their mistakes is quite easy.
This is a simple example of a form that is smart enough to know when
something is wrong and not lose data. All we're asking for is the
user's name -- if it's empty, we print out an error message and don't
continue.
</dtml-comment>
<dtml-if expr="REQUEST.REQUEST_METHOD=='POST'">
<dtml-comment>
If we're here, the user has hit the submit button and we have
form data to consider.
</dtml-comment>
<dtml-call "REQUEST.set('finished','true')">
<dtml-comment>
Assume things are OK by setting finished to 'true'
If they're not, change the value of finished as
we hit an error.
</dtml-comment>
<dtml-unless "fname">
<li>Please fill in your first name below
<dtml-call "REQUEST.set('finished','false')">
</dtml-unless>
<dtml-if "REQUEST.get('finished')=='true'">
<dtml-comment>
If finished is true, all is well in user-land. Show a nice
thank you message and instructions on what to do next.
Your save data code should go here and before the thank you
message is sent.
</dtml-comment>
http://www.zope.org/Members/jules/smarterforms_html?pp=1 (1 of 2) [05/07/2000 11:10:44]
(Hopefully) Smarter Forms 0.1
<p>You're done! Thanks for your input <dtml-var fname>.</p>
<p>You could put a link to somewhere else on your site here...</p>
<dtml-else>
<p>
Please correct this and click the <i>Next</i> button to complete
the form.
</p>
<dtml-var expr="mainform_include()">
<dtml-comment>
Rather than including the form twice, use Acquisition to
slurp it out. If you make changes to your form, you'll only
have to do it once.
</dtml-comment>
</dtml-if>
<dtml-else>
<dtml-comment>
If we're here, it's the first time through. Print out the form
as-is.
If you use a form's POST rather than an href link to get here,
your user will get an error message which will confuse the
whatsits out of them.
</dtml-comment>
<p>
Hello, dear user. Please fill out the form below. Note that the
<b>bold</b> items are required before you can continue.
</p>
<dtml-var expr="mainform_include()">
</dtml-if>
<dtml-var standard_html_footer>
You can try it here if you'd like to see how this works.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jules/smarterforms_html?pp=1 (2 of 2) [05/07/2000 11:10:44]
(How-to) access the field names and fields of any arbitrary ZSQL query
How-To: (How-to) access the field names and
fields of any arbitrary ZSQL query
Created by teyc. Last modified on 2000/06/08.
Introduction
ZSQL is a powerful method of accessing information from relational databases in a platform-independent manner.
Sometimes, it is necessary to access the field names and the values of an SQL Query which cannot be told in advance.
The names attribute
Field names of a ZSQL method can be accessed using the .names attribute
Assumptions
You already have a sql method called SQL
Code:
<dtml-in "SQL().names()">
<dtml-let vname=sequence-item
vvalue="SQL()[0][_['sequence-index']]">
<dtml-var vname>,
<dtml-var vvalue><br>
</dtml-let>
</dtml-in>
or:
<dtml-with SQL>
<dtml-in names>
<dtml-let vname=sequence-item
vvalue="SQL()[0][_['sequence-index']]">
<dtml-var vname>,
<dtml-var vvalue><br>
</dtml-let>
</dtml-in>
</dtml-with>
Comment
The SQL()[0] returns the first row of a SQL query. In your code, you should check that rows exists before trying to access field
values.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howtoSQLVariables?pp=1 [05/07/2000 11:10:46]
(mini) display hierarchies with recursion
How-To: (mini) display hierarchies with recursion
Created by sabaini. Last modified on 2000/05/09.
The tag is too limited with its layout? Want to walk your ZODB? Or just fool around with some cute recursion tricks?
Well I really got mad at the tree tag the other day, so much html hardcoded in there, not as cleanly abstracted as I am used to
with Zope by now. So decided to walk my object hierarchy on my own, and -- it is easier than I expected.
DTML version. First the DTML version:
<dtml-in some_id_listing_method>
<dtml-with sequence-item>
<dtml-var level>: <dtml-var id><br>
<dtml-let level="level+1">
<dtml-var dtml_tree>
</dtml-let>
</dtml-with>
</dtml-in>
The method used with dtml-in should be something that returns object ids. I originally did this with the getReplies() method from
the PTK, but I also tried it with objectItems() and it worked ok.
I called this via some convenience method which set the 'level' var in the namespace, dtml_tree being the method above:
<dtml-let level="0">
<dtml-var dtml_tree>
</dtml-let>
The 'level' var is not needed, just nice to know where you are in the hierarchy...
PythonMethod version. Heres the python method I used to getReplies() from PTK NewsItems:
<params>self, level=0, out=''</params>
for r in self.getReplies():
out = self.tree(
r,
level+1,
out='%s\n%s'
% (
out,
self.oneitem(item_id=r.id) # pass whatever you need
)
)
return out
oneitem() here being a dtml-method that does the html stuff. By the way, I first tried to call oneitem() with args oneitem(_.None,
_, ...), but that didnt quite work. Ok, so I perhaps I didn't get the meaning of _.None, _ in the first place.
Oh, ok. A Disclaimer -- its been a day with far too much trouble with our Oracledb, a firewall going up in flames and I'm
probably a bit tired. So this is more like a bit of relaxing with Zope and not very much tested. I'll go to bed now.
Anyway I was so amazed that this actually worked I had to write it down.
Any questions or hints pls. to me. Thank you for listening.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/sabaini/recursionminihowto?pp=1 [05/07/2000 11:10:49]
A <dmtl-calendar> example
How-To: A example
Created by judell. Last modified on 2000/02/07.
About <dtml-calendar>
The Calendar Tag is a Zope product that creates the DTML tag <dtml-calendar>, and enables DTML documents to render HTML tables
whose cells can be customized to display date-specific information, and link to date-specific actions.
To install it, unzip the file into your Zope root directory. It will create the directory Calendar under ZOPE_ROOT/lib/python/Products.
To activate it, restart Zope.
The point of this example
This example does not try to document everything about Calendar Tag. Refer to the Calendar Tag's README (a page of its management
screen, under Products in the Zope management system), for a complete list of its arguments and properties. Try this search for
mailing-list messages and other How-To's related to Calendar Tag.
What this example illustrates is a minimalistic way to hook Calendar Tag -- which is purely a display mechanism -- to a store of calendar
date. It's easy to imagine a SQL-backed calendar, but this example is not that fancy. It just stores calendar data in ZODB properties.
Displaying date-named ZODB properties in <dtml-calendar>
In this example, the index_html method of a folder displays an instance of the <dtml-calendar> widget. Here's the calendar-related piece
of index_html:
<dtml-calendar>
<dtml-call "setCalendar('valign','top')">
<dtml-let d="date.Date()"
hasdate="hasProperty(d)"
dprop="getProperty(d)">
<dtml-if "AUTHENTICATED_USER.getUserName()=='FredFlintstone'">
<a href="index_html/editCalPropForm?prop=<dtml-var d>&propval=<dtml-var
dprop url_quote>">
<dtml-var "date.dd()">
</a>
<dtml-else>
<dtml-var "date.dd()">
</dtml-if>
<br>
<dtml-if "hasdate==1">
<dtml-var dprop>
</dtml-if>
</dtml-let>
</dtml-calendar>
Here's an image of the calendar display produced by this code:
http://www.zope.org/Members/judell/CalendarTagExample?pp=1 (1 of 3) [05/07/2000 11:10:55]
A <dmtl-calendar> example
In this case, the user is FredFlintstone, so the day numbers are links that lead to a property-editing form. Non-authenticated users can
view the entries for each day, but cannot edit them.
The calendar object is called once per cell, with the date object bound appropriately. This code checks to see whether the container -- in
this case, the index_html document -- has a property which corresponds to the YYYY/MM/DD representation of that date object. In the
above screen, for example, index_html has a property called 2000/01/17 with the string value 'ML King Day, Feature 3'. When there
is such a property, the cell corresponding to it displays the property's value.
Posting and editing <dtml-calendar> entries
As shown above, the display forms a link that an authenticated user can follow to post or edit a calendar entry. The link refers to the
DTML document editCalPropForm, and passes it both the property name (2000/01/17) and the url-quoted property value.
Here's editCalPropForm:
<dtml-var standard_html_header>
<form action="editCalendarProperty">
<input name="prop" type="hidden" value="<dtml-var prop>">
<b><dtml-var prop></b>:
<br>
<textarea name="propval" rows="15" cols="65" wrap="hard">
<dtml-var propval>
</textarea>
<br>
<input type="submit" value="edit">
</form>
<dtml-var standard_html_footer>
It produces a form like the one shown in this image:
http://www.zope.org/Members/judell/CalendarTagExample?pp=1 (2 of 3) [05/07/2000 11:10:55]
A <dmtl-calendar> example
If the current day's property does not yet exist, the TEXTAREA will contain 'None'.
The action wired to this form is an external method, editCalendarProperty:
def editCalendarProperty(self,REQUEST,RESPONSE):
if ( self.hasProperty(REQUEST['prop']) ):
self.manage_changeProperties({ REQUEST['prop'] :
REQUEST['propval'] })
else:
self.manage_addProperty(REQUEST['prop'], REQUEST['propval'],
'string')
return REQUEST.RESPONSE.redirect(REQUEST['BASE2'])
When the property exists, this method updates its value. Otherwise, this method creates the property and adds the new value.
Why an external method? It doesn't have to be done this way, come to think of it. You should also be able to do this in DTML, using
<dtml-call> to invoke the manage_ functions. As Perl hackers say, "There's More Than One Way To Do It."
Jon Udell
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/judell/CalendarTagExample?pp=1 (3 of 3) [05/07/2000 11:10:55]
A patch to the propertysheet code to fix it's overwrite behaviour.
How-To: A patch to the propertysheet code to fix
it's overwrite behaviour.
Created by jwm. Last modified on 2000/05/15.
Update:
Tres Seaver points out that manage_changeProperties actually has the semantics that this patch provides, so use that
method where you can, and only resort to this patch if you have a lot of products using manage_editProperties. To the best of my
knowledge this patch doesn't break anything, but if it does, contact me.
The Problem:
When you create a property sheet, typically in a ZClass product, you have the option of including default values for those
properties [n1]. The problem is that when you actually pass the request variables to that property sheet's manage_editProperties, it
will set any property that's present in the sheet, but not present in the request to a blank value.
The source of the trouble is in the manage_editProperties method in OFS/PropertySheets.py:
def manage_editProperties(self, REQUEST):
"""Edit object properties via the web."""
for p in self.propertyMap():
n=p['id']
self._updateProperty(n, REQUEST.get(n, ''))
return MessageDialog(
title ='Success!',
message='Your changes have been saved',
action ='manage')
When _updateProperty is called, it fetches the value of a property in the sheet from the request, but if that property isn't in the
request, the value returned is None.
The Fix:
This can be fixed with a simple, two line patch:
def manage_editProperties(self, REQUEST):
"""Edit object properties via the web."""
for p in self.propertyMap():
n=p['id']
if (REQUEST.has_key(n)):
self._updateProperty(n, REQUEST.get(n, ''))
return MessageDialog(
title ='Success!',
message='Your changes have been saved',
action ='manage')
Here's a patch against the PropertySheet.py in Zope 2.1.4.
[n1] As of writing (3/5/00) there is another bug in the property sheets (actually, it's in the ZPublisher marshalling code) that forces
you to enter defaults for int, log, float, and date properties, even if you don't want them.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jwm/propertysheetPatch?pp=1 [05/07/2000 11:10:57]
A random password generator
How-To: A random password generator
Created by rossl. Last modified on 1999/09/15.
Need a quick and dirty one-time psuedo-random key for user registration via email passwords or similar ?
Here's one way. :
<dtml-var standard_html_header>
<dtml-let s="_.string.letters + _.string.digits">
<dtml-call "REQUEST.set('k1',_.whrandom.choice(s))">
<dtml-in "_.range(_.whrandom.choice([4,5,6]))">
<dtml-call "REQUEST.set('k1',k1 + _.whrandom.choice(s))">
</dtml-in>
your one time key is <dtml-var k1>
</dtml-let>
<dtml-var standard_html_footer>
This snippet illustrates the use of a dtml loop combined with access to some inbuilt python utilities like the string module
constants. letters and digits. It will return a key of 5,6 or 7 characters - longer or shorter keys can be generated by adjusting the
range - variable length adds to the difficulty of guessing the key.
Note that the Wichmann-Hill generator which is built in to the python interpreter has a relatively short period compared to some
other pseudo-random generators. The seed for this generator is set using the system time at the instant the module is first imported,
so if anyone can find out what that was, they could theoretically figure out the sequence of outputs from this code - in which case
all bets are off ! However, for many purposes this will make a pretty secure (ie hard to guess) onetime key.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rossl/ZOneTime?pp=1 [05/07/2000 11:10:59]
Accessing a ZSQL Method from an External Method
How-To: Accessing a ZSQL Method from an
External Method
Created by jpenny. Last modified on 1999/11/16.
Abstract
You have a database adapter in your Zope site, and some ZSQL Methods defined to access the database. You are working in an
External Method. How do you get to the ZSQL Methods to get to the database to access the data?
An Example
Suppose you have a database that is keyed on a field named sequence_number, and has in it fields item, description, and quantity.
You have a ZSQL method having ID sql_fetch_items with parameter request_number and Query template:
select *
from items
where
request_number = <dtml-sqlvar request_number type=int>
You are bulding an External Method with code like:
def do_something_big(self, requested_number):
What should the code to access the ZSQL Method, and hence the select statement look like?
A brief diversion
The self argument is important! It is the hook that allows the method to get back into the Zope namespace hierarchy; without it
your program cannot even find the ZSQL method. Don't leave home without your self!
Calling the ZSQL Method
This is astonishingly easy. Use
res=self.sql_fetch_item(sequence_number=int(requested_number))
A second brief diversion
Note the use of keyword style parameters. This is important, there is no way to use positional parameters (well, there is a minor
exception in that a single dictionary can be passed as a positional parameter, but, at first cut, it is easier to say that only keyword
style parameters should be used.)
What is res?
Res is a structure with a lot of fields. From our point of view, two are most interesting. res._schema.items() returns a list of form [
(field_name, index_number), ... ]. This will provide the way of using a name to find the value of the corresponding field. The
other portion of res that is really interesting is res[k]. res[k] is the k-th row of the data, if any, returned as a result of invoking the
ZSQL Method. l=len(res) will give you the number of rows returned, and they are numbered from 0 to l-1. res[k][m] will give the
m-th item of row k.
I am not going to discuss the other items of res. You can dig them out using the usual python tools, but from the point of view of
getting ones hands on the data with a minimum of fuss, they are simply not important.
It is really convenient to build an auxilliary dictionary mapping field name to column index number using code like:
fields2index={}
http://www.zope.org/Members/jpenny/Accessing_a_ZSQL_Method_from_an_External_Method?pp=1 (1 of 2) [05/07/2000 11:11:03]
Accessing a ZSQL Method from an External Method
fieldnames=res._schema.items()
for i in range(len(fieldnames)):
fields2index[fieldnames[i][0]]=fieldnames[i][1]
Using fields2index, it is now easy to look up data by name. In the example, the field names are item, description, and quantity.
Looking up the value of the item field of the first row is as simple as
item_val=res[0][fields2index['item']]
Winding up
Well, that is really all there is to it. At this point, you know how to invoke a Zope method from an External Method, what (some
of) the data returned by a ZSQL Method looks like, and how to retrieve a particular datum from the returned data. Working with
the data is up to you!
Also note that you can make ZSQL methods to do inserts, or update, or deletes, or anything else; so you really have complete
control over your database from an External Method. Of course, the result of doing such a call is generally non-interesting.
Exceptions have not been discussed here. As I gain experience with them using them in this context, I will add to this How To.
As ever, corrections and suggestions for improvement can be emailled to jpenny@universal-fasteners.com
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jpenny/Accessing_a_ZSQL_Method_from_an_External_Method?pp=1 (2 of 2) [05/07/2000 11:11:03]
Acquisition - just say no?
How-To: Acquisition - just say no?
Created by rossl. Last modified on 1999/09/21.
Powerful and useful as it is, sometimes acquisition can be a mongrel. In certain situations, you want Zope objects to behave the
same way no matter where they're being called from. One classic situation is wanting a navigation element to always show the
contents of a specific part of your Zope hierarchy. So you write some code which cycles through the objectItems and call it from
your standard_html_header for example.
Unfortunately, if you call it from a folder deep within some Zope site, it acquires the context of the calling object and deals out the
WRONG darn set of objectItems.
I found a really cheezy solution which worked - it's not a general solution but I was desperate...
Inside a multilevel zclass (this would work for folders too), I wanted a navigation sidebar with all of the objects at the top level
(messages in a 3 level max threaded discussion forum) displayed as links. However, I needed this sidebar to show even when
called from deeper in the forum - ie 1 or 2 levels lower - inside my standard_html_header.
I could have written 3 standard_html_headers I guess, but instead, I realised that I could write one version of the navbar code and
force it to work AS IF IT WERE ALWAYS CALLED in the context of a specific level in my zope site hierarchy specifying (for
example) the third level from the top as :- :
<dtml-with "PARENTS[-3]">
<...dtml-navbar_stuff...>
</dtml-with>
I actually made the -3 a configuration item for the zclass so I can drop zclass instantiations at any level of my site, as long as I
make sure the navbar code is forced to use the context corresponding to the right zope tree level. I also added a request.set to the
standard_html_header which calls this code to pass its own id - the navbar won't show a link to the item IF it happens to be called
from the level at which the navbar elements exist - there's no link to the current top level item if that top level item happens to be
on the screen...
The final code looks like this:
<dtml-call "REQUEST.set('callingId',id)">
<dtml-with "PARENTS[forumparentlevel]">
<a href="<dtml-var absolute_url>" ><B>Forum Home</B></a><br><br>
<dtml-in "objectItems()">
<dtml-if "meta_type=='ZFM'">
<dtml-with id>
<dtml-if "callingId <> id">
<a href="<dtml-var absolute_url>" > <dtml-var title> </a><br>
<dtml-else>
<dtml-var title><br>
</dtml-if>
</dtml-with>
</dtml-if>
</dtml-in>
</dtml-with>
Like I said, cheezy zen, but hey. I was sick of hearing the sound of one head (mine) banging....
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rossl/SpecificContext?pp=1 [05/07/2000 11:11:05]
Add a Toolbar to the Management Interface
How-To: Add a Toolbar to the Management
Interface
Created by ddb. Last modified on 2000/05/18.
Warning! This HOW-TO talks about modifying core files in a default Zope installation! Proceed with caution!
When developing a site or a ZClass in Zope, you find yourself adding the same kinds of items over and over. Sometimes, due to
the way the "add" drop down is sorted, the item you wish to add is at the bottom, meaning you have to scroll down every time.
After about the tenth time, it gets tedious.
So I added a DTML Method to my Zope root, and called it "toolbar". I then editted the toolbar method to display links to my most
commonly added items.
Here's the tricky part: I went to the source of my Zope installation and editted the file lib/python/OFS/main.dtml and added
<dtml-if toolbar><dtml-var toolbar></dtml-if>
in the space following the <P> after the <dtml-var manage_tabs> statement.
Now, whenever I need to add a document or a method, there's a link right there at the top of the page. No more searching around
in the drop down.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/ddb/quicktools?pp=1 [05/07/2000 11:11:07]
Add an 'Manage' entry to IE's context menu
How-To: Add an 'Manage' entry to IE's context
menu
Created by haj. Last modified on 2000/05/31.
Here's a variation on the 'Edit This Page - Bookmarklet' HOWTO, using IE's context menu (the right-click popup):
Create an HTML file somewhere on your system (say, c:\manage_me.htm) containing:
<SCRIPT LANGUAGE="JavaScript" defer>
open(external.menuArguments.location.href + '/manage');
</script>
Then, install the following registry keys by pasting the text below into a blah.reg file, then double-clicking on it:
REGEDIT4
[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Manage This Page
(ZOPE)]
@="file://c:\\manage_me.htm"
"contexts"=hex:31
And you should find that when you right click on a page, you get a menu option now to manage that page.
This works with IE 4.0 and newer, and is documented in the Microsoft Knowledgebase in article Q177241, although I found it
first at Blogger's site. Unfortunately, javascript:function() URLs don't seem to work, so you need to have that small HTML file.
Someone please tell me otherwise!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/haj/ContextMenu?pp=1 [05/07/2000 11:11:09]
Adding ZClass Instances Programmatically
How-To: Adding ZClass Instances
Programmatically
Created by tazzzzz. Last modified on 2000/05/03.
Introduction
So, you've got these shiny new ZClasses. They do just what you want, and you've added a bunch through the Zope management
interface. Now, you need to make a method for adding new instances within DTML or Python code.
(If you want to remove ZClass instances, you might want to check out removeZClasses )
Adding ZClass Instances via DTML
The following ideas were taken from KM|Net News. My method for doing this involves a change to the ZClass constructor
method (the one that is automatically created and ends with "_add").
The automatically generated "_add" method has a section at the end for redirecting the user to the proper space in the management
interface. This is not desirable when you are adding an instance via code, so I've added a NoRedir flag. Here is what the last part
of the "_add" method for the ZClass should look like:
<dtml-if NoRedir>
<dtml-else>
<!--#if DestinationURL-->
<!--#call "RESPONSE.redirect(
DestinationURL+'/manage_workspace')"-->
<!--#else-->
<!--#call "RESPONSE.redirect(
URL2+'/manage_workspace')"-->
<!--#/if-->
</dtml-if>
Before adding the ZClass instance via DTML, you'll need to set up the REQUEST variable with any properties that you want the
new instance to have. Be sure you set the ID of the new object up, like so:
<dtml-call "REQUEST.set('id', 'newObId')">
Now, you just need to add the instance via DTML. The exact call you use will depend on where you're calling from. The simplest
case is if you are adding a ZClass within a container ZClass. This case is simple, because the ZClass constructor and ZClass itself
are within the namespace of the object you are adding the new instance to. All you need to do is add the following line to the
DTML Method to add your instance:
<dtml-call "YourZClass_add(_.None, _, NoRedir=1)">
The first two parameters (_.None, _) are used to pass the current namespace into the function. You can read more about _ in the
DTML Guide.
If you are trying to add a ZClass instance from elsewhere in the ZODB, it's just a couple of lines more that are necessary:
<dtml-with "manage_addProduct['YourProduct']">
<dtml-call "YourZClass_add(_.None, _, NoRedir=1)">
</dtml-with>
http://www.zope.org/Members/tazzzzz/addZClasses?pp=1 (1 of 2) [05/07/2000 11:11:12]
Adding ZClass Instances Programmatically
Adding ZClass Instances via Python
The following code was taken from a message by Brian Lloyd on the Zope list. I added the Control_Panel.Products.MyProduct
part so that this external method will work from anywhere in the ZODB:
def addAMyZClass(self, id, REQUEST=None):
""" """
# Get the actual destination object, using the this()
# method, to be sure we get it in the right context..
self=self.this()
# Create the new instance
newob=self.Control_Panel.Products.MyProduct.MyZClass(id)
newob.id=id
# Set properties based on the contents of the Add form
newob.propertysheets.Basic.manage_changeProperties(REQUEST)
# Add a new Folder to the new instance
newob.manage_addFolder('foobar')
# Add the new instance to the target object
self._setObject(id, newob)
return 'OK!'
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/tazzzzz/addZClasses?pp=1 (2 of 2) [05/07/2000 11:11:12]
Advanced DTML Techniques
How-To: Advanced DTML Techniques
Created by Amos. Last modified on 2000/01/13.
Introduction
Document Template Markup Language (DTML) is a central component of Zope. DTML functions both as an HTML formatting
language as well as a simple scripting tool.
This document tries to explain some of the more advanced concepts, features and gotchas of DTML. This document does not
replace the DTML User's Guide. The Guide is the definitive reference to DTML. If you haven't read it at least twice, do so now.
This document is more aimed at explaining some of the ideas and quirks behind DTML, rather than spelling out exactly how to
use it.
Documents versus Methods
DTML primarily displays Zope objects in HTML. This is the driving idea behind DTML Methods--they are methods which allow
Zope objects to represent themselves textually.
DTML is also used to hold textual content which can be viewed as web pages. This is the main idea behind DTML Documents.
As we will see later there are technical differences between DTML Documents and Methods that make them suited for these
different purposes.
Namespaces
The most important service DTML provides is variable lookup services. How is this done? With namespaces.
Like Python, DTML uses multiple namespaces to find a variable when it is referenced. Unlike Python, DTML has a potentially
many heterogeneous namespaces to consult rather than a fixed number of well defined namespaces. This complexity makes
DTML somewhat confusing, but it also makes it quite powerful.
DTML uses a stack of namespaces to lookup variables. First Zope looks for the variable in the top namespace, then in the next one
down and so on until it finds the variable.
Depending on how you call a DTML Document or Method, different namespaces will be placed in the stack.
At different points while rending a DTML document, namespaces can be pushed on, or popped off the namespace stack. That's
why:
<!--#var title-->
doesn't always refer to the same title at different parts of a DTML object.
Sounds confusing, and it is. But you probably already have an implicit understanding of this operation. For example, let's take a
snippet of DTML:
folder's title: <!--#var title-->
<!--#in objectValues('DTML Document')-->
sub-document's title: <!--#var title-->
<!--#/in-->
Suppose this example was in a DTML Method of a Folder entitled "My Folder". Suppose "My Folder" contains two DTML
Documents entitled "Doc 1" and "Doc 2". Then this DTML snippet would render this text:
folder's title: My Folder
sub-document's title: Doc 1
sub-document's title: Doc 2
http://www.zope.org/Documentation/How-To/AdvancedDTML?pp=1 (1 of 6) [05/07/2000 11:11:18]
Advanced DTML Techniques
This seems fairly clear. But if you look closely you'll see that the in tag manipulates the namespace stack so that each time
through the in loop the title variable refers to a different title. What the in tag does is push a namespace on the stack at the
beginning of each loop and pop it off at the end. The namespace it uses is built from the current sequence-item.
Namespaces and the with tag
The with tag does the same sort of namespace manipulation as the in tag, only it is more explicit about it. The with tag pushes
a given namespace onto the stack when Zope comes to the opening with tag, and pops it off the stack when Zope reaches the
closing with tag.
This explicit namespace management can be useful in many situations. For example suppose you have a form which contains an
input named "title". When you submit the form, the DTML Method that handles the form may want to refer to this title. However,
simply referring to the title may not work:
<!--#var title-->
This will find the title of the enclosing Folder, rather than the title in the request's form, since the enclosing Folder's namespace is
on top of the namespace stack by default. One way to solve this problem is to use a with tag to explicitly push the request's
namespace on top of the namespace stack:
<!--#with REQUEST-->
<!--#var title-->
<!--#/with-->
DTML calling arguments
A DTML object's inital namespace stack is set by its calling arguments. DTML objects can be called from Python with three
arguments.
1. a client object
2. a mapping
3. optional keyword arguments
When a DTML object is called, these arguments are pushed onto the DTML object's namespace stack with the keyword arguments
on top, followed by the clients, followed by the mapping.
Now, what does this have to do with Zope? Simple, when Zope's ORB publishes a DTML Document or Method it passes the
client and mapping arguments. The client is the enclosing (or acquiring) Folder. The mapping argument is the HTTP request.
Note that, in the case of DTML Documents, the Document itself is used for variable lookup along with and the client argument.
The upshot is that by default the namespace stack has the request below the enclosing Folder. That's why we need the explicit
with tag to find our form's title.
Types of namespaces
So far we haven't really explained what a namespace is. We know it's something you can look up variables in. In Python,
namespaces are essentially mapping objects which hold the variables defined in classes, instances, and modules. In DTML
namespaces operate in much the same way. In general, any object or mapping can act as a namespace. Variables are looked up in
objects by finding object attributes and methods. Variables are looked up in mappings normally. However, Zope constrains what
you can lookup in a DTML method. See the section on security for more information on lookup constraints.
Idle reflections
Let's take a breather here and reflect a little. We've seen that DTML uses a dynamic stack of namespaces. Often these namespaces
correspond to objects. So we have a scenario in which variables are looked up by traversing layers of objects. This motif is quite
central to Zope. Both object publishing and acquisition echo this theme of object traversal. The Zope ORB works downward
through the object hierarchy examining objects until it finds the requested object. Acquisition works upward through the object
hierarchy binding methods and attributes to called objects. DTML namespaces similarly form a dynamic stack of objects which
http://www.zope.org/Documentation/How-To/AdvancedDTML?pp=1 (2 of 6) [05/07/2000 11:11:18]
Advanced DTML Techniques
are consulted in order to find template variables.
The DTML namespace stack differs from object publishing and acquisition in that it is not tied to the object hierarchy. The stack
is constructed initially by calling a DTML object, and then is modified dynamically in the body of the template.
Indirect variable lookup
Just as you can use Python's getattr and __dict__ in Python to lookup names indirectly, so too in DTML you can lookup
variables indirectly.
Indirect lookup is just a question of consulting the namespace and asking it if it has a variable. In DTML the way to do this is with
the special variable whose name is underscore. The underscore variable has a getitem method, and also can be treated like a
mapping to access the namespace's variables. So for example:
<!--#var foo-->
<!--#var "_['foo']"-->
<!--#var "_.getitem('foo')"-->
All do pretty much the same thing. One subtle difference between these three forms is that the first and second form "render" the
resulting variable, while the third does not. We'll discuss rendering later. Just for completeness we should mention that the
getitem method takes and optional second argument which indicates whether to render or not. By default it does not render.
Since indirection is confusing, you should only use it when necessary. For example, there is really no reason to write:
<!--#var "_['foo']"-->
when you can more simply write:
<!--#var foo-->
If you know that you're interested in foo, just ask for it. There's no need to say "give me the variable whose name is 'foo'", when
you can just say "give me 'foo'".
Normally you will use indirection to access variables whose names you don't know when you are coding the DTML. For example:
The value of <!--#var interesting_attribute-->
is <!--#var "_[interesting_attribute]"-->
Notice that there are no quotes around interesting_attribute in the second var tag. That's because we're not looking for
the variable named interesting_attribute, (in which case we could just write ). What we want instead is the variable
whose name is given by the variable interesting_variable. Whew!
How does this work? Suppose the above DTML fragment was in a DTML Document entitled "My Doc". Then if you called the
Document with a form that assigned interesting_attribute to "title", then the Document would return:
The value of title
is My Doc
Declaring variables
What if you want to declare variables inside a DTML object, how can you do it? Well there are a couple ways. The most common
way to declare a variable which can be used later in the template, but which goes away when the template has finished rendering is
to set a variable in the REQUEST object. This is done with the REQUEST object's set method:
<!--#call "REQUEST.set('food','pork chops')"-->
Now there is a food variable in the REQUEST object's namespace and it can be looked up normally.
http://www.zope.org/Documentation/How-To/AdvancedDTML?pp=1 (3 of 6) [05/07/2000 11:11:18]
Advanced DTML Techniques
Another way to do the same thing is to use the with tag. You can use the with tag to push a mostly empty namespace on the
stack, which only contains some variables which you set. This is done by using the underscore's namespace method. For
example:
<!--#with "_.namespace(food='pork chops')"-->
<!--#var food-->
<!--#/with-->
As you can see, the namespace method takes keyword arguments which set variables in the namespace.
You should note that using the with tag puts your variables on top, so they'll be sure to be found when looked up. Also, your
variables will disappear at the close of the with tag. Setting a variable in the REQUEST makes your variable subject to the
visibility of the REQUEST object's namespace. However, variables set in the REQUEST are persistent through out the entire
template.
New in Zope 2.0 is the let tag which allows you to define namespaces easily.
For example:
<!--#let food="'pork'" name="AUTHENTICATED_USER.getUserName()"-->
<!--#var name--> eats <!--#var food-->
<!--#/let-->
This is quite similar to the with..namespace construct. Variables defined in the let tag can either be rendered normally of
variable expressions, if they are quoted.
Acquisition
So far we have neglected to mention the importance of acquisition in DTML. Acquisition has two major effects with respect to
DTML: objects can acquire DTML Methods, and DTML namespaces corresponding to objects can use acquisition to lookup
variables. Let's take each effect and look at the subtleties.
One of Zope's main cool technologies is acquisition, and acquisition of DTML Methods by objects is a prime example of this
power. As we saw earlier, when a DTML Method is called, its enclosing or acquiring Folder is passed as its client namespace.
This means that if your DTML method refers to a variable named "title", this variable is first looked up in the Folder's namespace.
This allows DTML Methods to act like templates, tailoring their content to the attributes of the object which acquires them.
Another effect of acquisition is felt in the variable lookup process. For example:
<!--#with Foo-->
<!--#var Bar-->
<!--#/with-->
In this DTML fragment the variable Bar is looked up in Foo. Even if Foo does not have a Bar attribute, Bar will be found in
the Foo namespace if the Foo object can acquire Bar. This effect can catch you unaware if you are not careful.
Variable rendering
The DTML var tag does not merely lookup variables, but it "renders" them. The process of rendering is not just coercing objects
into strings, but calling objects if they are callable, and handling DTML objects specially. Zope's rendering rules are designed to
do what Zope thinks is the right way to display an object. However, you may not always want to let Zope render and object
automatically.
For example, Zope normally renders DTML objects referred to in other DTML objects by calling them the with current DTML
namespace stack. However, you may wish to pass a different namespace. For example, suppose you have a DTML method
defined at the top level which displays a site map. This method relies on attributes and methods of the top level Folder to display
its site map. If you refer to this method in a DTML Method deep in your site, Zope will pass it a namespace which doesn't have
the top level Folder on top of the namespace stack. Instead you may wish to manually render the method by calling it with the top
level Folder as a client, and the REQUEST as a mapping:
<!--#var "site_map(PARENTS[-1],REQUEST)"-->
http://www.zope.org/Documentation/How-To/AdvancedDTML?pp=1 (4 of 6) [05/07/2000 11:11:18]
Advanced DTML Techniques
This works because the PARENTS variable is a list of parent objects. So PARENTS[-1], which is the last item in the list, refers
to the top level Folder.
You could achieve similar results using the with tag:
<!--#with "PARENTS[-1]"-->
<!--#var site_map-->
<!--#/with-->
While they both achieve the same results, there is a subtle difference between these two methods. In the first example, the
namespace stack contains two items, PARENTS[-1] and the REQUEST. In the second case it contains an indeterminant number
of items, with PARENTS[-1] on top.
If you wish to get a variable without rendering it, one way is to use a variable expression. For example, <!--#var
expr="parrot"--> differs from, <!--#var parrot--> in that it does not render the variable. Don't forget that if you
wish to get a variable indirectly without rendering it, use getitem. For example:
<!--#var expr="_.getitem(bird_type)"-->
retrieves that variable named by the variable bird_type without rendering it. By contrast:
<!--#var expr="_[bird_type]"-->
renders its results.
Variable expressions
As we've seen, you can use the var tag to not only render variables, but to spell out primitive Pythonish expressions. This facility
allows you to supply arguments when you call variables, and to generally interact with the Zope object system. All sorts of Zope
objects provide all sorts of interesting services which you can access via DTML expressions.
One way to find out about the services available to you through DTML is to consult the Help tab in the management interface. The
object reference which is available there shows you the publicly accessible methods of different classes of Zope objects. This is
quite useful, and guaranteed to be up to date, since it relies on the actual code to generate the reference. One thing to keep in mind
is that any given Zope object will have more methods available than those listed in the Object Reference. Why? Because objects
acquire methods and attributes from their parents.
Names are looked up in variable expressions in the same way they are normally in DTML. So to write an express which evaluates
to foo plus two simply write:
foo plus two equals: <!--#var "foo + 2"-->
There's no need to write DTML inside DTML. In fact you can't, as this common mistake illustrates:
this is an error: <!--#var "<!--#var foo--> + 2"-->
The special underscore variable gives you access to many useful Python functions which you may want to use. See the DTML
User's Guide for a complete list of available functions.
Why not simply write normal Python instead of variable expressions? The reason is that variable expressions provide many
security services which vanilla Python doesn't. For example, variable expressions provide protection against endless loops, and
many kinds of denial of service attacks. DTML expressions also limit visibility into the Zope object system as we will soon see.
Security
DTML gives you a window on Zope's object system. However, it is a limited view. You can only access Zope objects whose
names do not begin with an underscore, and objects which you have adequate permissions to access.
To get around the permissions barrier, Zope provides Proxy Roles for DTML Documents and Methods. Proxy Roles are similar in
http://www.zope.org/Documentation/How-To/AdvancedDTML?pp=1 (5 of 6) [05/07/2000 11:11:18]
Advanced DTML Techniques
spirit to the Unix setuid facility. For example, a DTML Method with a "Manager" Proxy Role could perform restricted
management functions such as deleting Zope objects even when called by a user without the "Manager" role.
Knowing when to say when
No discussion of advanced DTML would be complete without mentioning External Methods. DTML can get pretty complex.
When you find yourself setting lots of variables, and using a lot of variable expressions, it may be time to consider using an
External Method instead. Often it is much easier and clearer to express some computation in Python than it is to express it in
DTML.
Another important tool for fighting complexity is to refactor templates into a number of sub-templates. Finding the right way to
factor your problems into templates, properties, and external methods is an art. Luckily Zope is forgiving, and lets you experiment
quite a bit.
Conclusion
DTML can be complex. One of the most important way to keep track of what's going on is to follow the namespace stack.
Understanding how it works greatly improves your ability to make DTML serve your needs.
Good luck and have fun!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/AdvancedDTML?pp=1 (6 of 6) [05/07/2000 11:11:18]
Advanced ZCatalog Searching
How-To: Advanced ZCatalog Searching
Created by Zen. Last modified on 1999/11/09.
Basics
First read the ZCatalog Tutorial
A ZCatalog does not return object instances - it just returns an object (a Pluggable Brain) that has the fields listed your ZCatalog's
Meta Data tab:
<dtml-comment> Iterate over all items in Catalog
</dtml-comment>
<dtml-in Catalog>
<dtml-var title> <br>
</dtml-in>
Even though the ZCatalog does not return the actual object instance, the object it does return can be used to find the real instance
using ZCatalog's getPath method.
By default, ZCatalog simply does equality matching for FieldIndex index types, or word matching for TextIndex index types. As
of Zope 2.0.1, it does not do substring matching. If multiple search conditions are passed, they are anded together. This search will
locate DTML Documents with the given title, and return a list of hyperlinks:
<ul>
<dtml-in "Catalog(meta_type='DTML Document',
title='The Ying-Tong Song')">
<li>Found Match:
<a href="<dtml-var "Catalog.getpath(data_record_id_)">">
<dtml-var title></a>
</dtml-in>
</ul>
It is possible to pass search criteria to the ZCatalog in a number of ways...
via current REQUEST object:
<dtml-in Catalog>
via named parameters:
<dtml-in "Catalog(title='The Ying-Tong Song')">
via a mapping:
<dtml-in "Catalog({'title':'The Ying-Tong Song'})">
Note that passing variables in the URL automatically sets the corresponding attributes of the REQUEST variable. For example
http://mysite.org/Zope/mysearch?title=Song is equivalent to calling:
<dtml-call "REQUEST.set('title','Song')">
http://www.zope.org/Members/Zen/howto/AdvZCatalogSearching?pp=1 (1 of 3) [05/07/2000 11:11:22]
Advanced ZCatalog Searching
Or
If you pass a list to match instead of a string, a match is found if any of the list items are equal:
<dtml-in "Catalog(meta_type=['DTML Method','ZCatalog'])">
Method or Catalog:
<dtml-var "Catalog.getpath(data_record_id_)"><br>
</dtml-in>
If you need to perform an or operation on different columns, then things get trickier. As of the Zope 2.0.1 release, ZCatalog
cannot support this syntax so more round about methods are required. The following code shows a search that matches objects
with meta types of DTML Method or ZCatalog or objects with the word Song in their title. Note that using this method loads
the entire search into memory:
<dtml-let resa="Catalog(meta_type=['DTML Method','ZCatalog'])"
resb="Catalog(title='Song')"
res="resa[:_.len(resa)] + resb[:_.len(resb)]">
<dtml-in res>
A Match: <dtml-var "Catalog.getpath(data_record_id_)"><br>
</dtml-in>
</dtml-let>
Note that the rather odd syntax for joining the result lists is required due to ZCatalog retrieving results in chunks rather than all at
once. The traditional Python syntax of resa[:] + resb[:] will not work in Zope 2.0.
Ranges
You can search for ranges of values by passing a magic parameter in the format column_usage where column is the Meta
Data column name:
<dtml-comment> Items modified in the last 14 days
</dtml-comment>
<dtml-in "Catalog(bobobase_modification_time=_.DateTime(date)-14,
bobobase_modification_time_usage='range:min')">
New item:
<dtml-var "Catalog.getpath(data_record_id_)"><br>
</dtml-in>
The only other option available at the time of this writing is range:max:
<dtml-in "Catalog(bobobase_modification_time=_.ZopeTime()-14,
bobobase_modification_time_usage='range:max')">
New item:
<dtml-var "Catalog.getpath(data_record_id_)"><br>
</dtml-in>
You can also combine the two to perform a between search:
<dtml-in "Catalog(
bobobase_modification_time=
[_.ZopeTime()-14,_.ZopeTime()-1],
bobobase_modification_time_usage=
'range:min:max')">
New item:
<dtml-var "Catalog.getpath(data_record_id_)"><br>
</dtml-in>
http://www.zope.org/Members/Zen/howto/AdvZCatalogSearching?pp=1 (2 of 3) [05/07/2000 11:11:22]
Advanced ZCatalog Searching
Types
Be careful that the data you submit to the ZCatalog is of the same time that is indexed. It is often worth casting the data to ensure
this:
<dtml-in "Catalog(date=_.DateTime('1999/01/01'))">
Sorting
ZCatalog can sort the results for you, provided the column you wish to sort on is a FieldIndex. Other columns can also be sorted
on using the sort attribute of the dtml-in tag, but this does not take advantage of ZCatalog's indexes:
<ul>
<dtml-in "Catalog(
title='The Ying-Tong Song',
sort_on='meta_type')">
<li>Found Match:
<a href="<dtml-var "Catalog.getpath(data_record_id_)">">
<dtml-var title></a>
</dtml-in>
</ul>
One useful trick is to pass the sort_on variable in the URL. For example:
<h1>Some results</h1>
<ul>
<dtml-in "Catalog(title='The Ying-Tong Song')">
<li>Found Match:
<a href="<dtml-var "Catalog.getpath(data_record_id_)">">
<dtml-var title></a>
</dtml-in>
</ul>
<p><a href='?sort_on=meta_type'>Sort by meta_type</a>
<p><a href='?sort_on=bobobase_modification_time'>Sort
by modification time</a>
2.1 Update
Zope 2.1 (Currently in beta) also allows you to pass a sort_order parameter in the same way as sort_on. If sort_order
is set to reverse or descending then the output order is reversed.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Zen/howto/AdvZCatalogSearching?pp=1 (3 of 3) [05/07/2000 11:11:22]
After you get 'Zope' out of your URLs...
How-To: After you get 'Zope' out of your URLs...
Created by cba. Last modified on 1999/10/07.
If you first installed Zope under another web server like Apache, as I did, by following the directions provided by DC, you ended
up with URLs that had "Zope" in the path, for example:
http://www.mydomain.com/Zope/my_folder/index_html
There are several sources of information on using Rewrite rules under Apache and other methods so that "Zope" is no longer
needed in the URL. So now you have a URL that looks like:
http://www.mydomain.com/my_folder/index_html
But what about links to your site that still include "Zope" in the URL?
Here's how I seems to have worked around this issue using the magic of acquisition:
1. Make a folder at the top level of your Zope hierarchy called "Zope".
2. Change the index_html method in your new Zope folder to look like this::
<!--#call "RESPONSE.setBase('http://www.mydomain.com/my_folder')"-->
<dtml-with my_folder>
<dtml-var index_html>
</dtml-with>
I confess I have not tested this technique very well, let me know if you find problems; specifics would help, fixes would be even
better!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cba/URL_fixup?pp=1 [05/07/2000 11:11:23]
Another Site Map
How-To: Another Site Map
Created by Ioan. Last modified on 2000/04/20.
Site Map
This How-To help you to keep your site map on left all the time.
First look at Overlib and Zope.
Then download Map.zexp which contains
SimpleSiteMap made by djay
and a folder called js which contains Overlib.
You have to move js folder on root of your site.
SiteMap is integrated by a table with:
<table>
<tr>
<td>map</td>
<td>your_content</td>
</table>
I mean you set your standard_html_header:
<HTML>
<HEAD>
<TITLE>
<dtml-var title_or_id>
</TITLE>
</HEAD>
<LINK REL="stylesheet" HREF="/js/overlib.css" TYPE="text/css">
<body BGCOLOR='#AABBFF' TEXT='#000000'
LINK='#0000FF' ALINK='#0000FF' VLINK='#0000FF'>
<DIV ID="overDiv" STYLE="position:absolute; visibility:hide; z-index: 1;"></DIV>
<SCRIPT LANGUAGE="JavaScript" SRC="/js/overlib.js"></SCRIPT>
<center>
<table border=0 WIDTH="100%">
<tr><td WIDTH="20%" VALIGN="top" BGCOLOR="#AAAAFF">
<h1>Site Map</h1>
<dtml-with "PARENTS[-1]">
<dtml-var rF>
</dtml-with>
</td>
<td>
Then set your standard_html_footer:
</td>
</tr>
</table>
</center>
<BR>
<HR>
<dtml-var bobobase_modification_time><BR>
</BODY></HTML>
Just download Map.zexp and have fun!
http://www.zope.org/Members/Ioan/SiteMap?pp=1 (1 of 2) [05/07/2000 11:11:25]
Another Site Map
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Ioan/SiteMap?pp=1 (2 of 2) [05/07/2000 11:11:25]
Apache As A Front End To ZServer
How-To: Apache As A Front End To ZServer
Created by shaw. Last modified on 2000/06/08.
Problem
In my situation, Apache is currently used to serve all our content. Each Apache server typically supports a few virtual hosts. I'd
like to use ZServer to serve some content to take advantage of Zope's features. However, some of our content, eg. third-party
documentation, generated JavaDoc, is not really suitable to be added into the ZODB. In addition, we are likely to continue to use
Apache as we have found it to be very good.
Hence, I needed a way of configuring Apache and Zope to permit me to use Apache as a front end to manage virtual hosts, serve
some content and redirect the remaining URLs to ZServer.
Solution
The How-Tos listed below were very useful in getting me started. I encountered problems as I tried each one, which pushed me
into trying the others. In the end, the problems turned out to be browser related (I think), so I suspect the other solutions work just
as well as mine. However, having to try out several solutions helped me reach a very neat solution (in my opinion).
ZServer Configuration
I used the standard startup script for Zope. This means a ZServer running on port 8080 of my host. I created a Folder called XYZ
in the root folder to represent the (virtual) host xyz.mydomain.com.
SiteAccess Configuration
I added a SiteRoot object into the XYZ folder. It was configured with a blank title, a base equal to
http://xyz.mydomain.com and a path equal to /.
Apache Configuration
My solution uses Apache's re-write rules, which means Apache must be built with mod_rewrite. The key re-write rule uses the
proxy option, which means Apache must also be built with mod_proxy. Below are relevant commands from Apache's
httpd.conf file.
NameVirtualHost 123.456.78.9
<VirtualHost 123.456.78.9>
ServerName xyz.mydomain.com
DocumentRoot /path/to/htdocs
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteLog "/path/to/apache/logs/rewrite_log"
RewriteLogLevel 0
RewriteRule ^/local/ - [L]
RewriteRule ^/(.*) http://xyz.mydomain.com:8080/XYZ/$1 [P]
<IfModule>
<VirtualHost>
The Apache rewrite rules work as follows. The RewriteEngine command turns the rewrite engine on. The RewriteLog
command specifies where to log rewrite actions. The RewriteLogLevel determine how much is logged. A level of 0 is useful
for production. Levels from 1-9 give you useful debugging output.
The RewriteRule commands specify what Apache does with incoming requests to xyz.mydomain.com. The rules are
processed from first to last, hence order is important. The first rule specifies that all requests beginning with /local/ are served
by Apache. (This is achieved by the - implying no action, and the [L] implying that rule processing is to stop.
The second rule, specifies that all remaining requests are forwarded to http://xyz.mydomain.com:8080/XYZ/ using
Apache's proxy module (the [P]).
http://www.zope.org/Members/shaw/HowTo/ApacheFrontEnd?pp=1 (1 of 2) [05/07/2000 11:11:28]
Apache As A Front End To ZServer
Tricks and Traps
I found the order of things important in my solution. You should configure ZServer, install SiteAccess, start ZServer, create any
Folders, add SiteRoot objects and then configure and start Apache.
I found that if I reconfigured Apache and tried to manage the new virtual host before I added the SiteRoot object, I was asked to
login twice. The first login appeared to be to xyz.mydomain.com. The second appeared to be to
xyz.mydomain.com:8080. Once the SiteRoot object was added this problem went away.
The other problem I encountered was a kind of infinite loop. If I started with a vanilla Apache configuration, configured the Zope
side of things (which required logging in) and then reconfigured Apache I got into trouble. When I tried to manage the virtual host
again within Zope the browser kept trying, over and over, to go to the site. A check of the Apache server log revealed repeated
401 errors. 401 is Authorisation Failure, suggesting the browser thought it had access permission but ZServer did not. The only
way around this was to restart the browser. The broswer in question is Netscape 4.73 under NT.
Environment
The above solution has been verified using the following software. However, I don't believe I used any version specific features.
●
Solaris 2.5
●
Python 1.5.2 (with threads)
●
Apache 1.3.12 (with mod_proxy and mod_rewrite)
●
Zope 2.1.6
●
SiteAccess 1.0.1
Relevant How-Tos
Using Apache with ZServer (NOT Zope.cgi)
Serving ZOPE and other content from Apache
ZOPE/Apache Virtual Host
For a full list, search for Apache using the main zope.org search facility.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/shaw/HowTo/ApacheFrontEnd?pp=1 (2 of 2) [05/07/2000 11:11:28]
Apache+ZServer+SSL
How-To: Apache+ZServer+SSL
Created by unfo. Last modified on 2000/06/22.
Note: You may want the printable version of this page.
I've been using Apache with ZServer (not Zope.cgi) for months and am quite satisfied. However, I wanted a way to incorporate
SSL into the mix. So, I downloaded mod_ssl, recompiled Apache, and got to work. Here's how you do it (replace this info with
your server info, of course):
1. Download and install mod_ssl. Use the INSTALL text file as a guide.
However, when you get to STEP 5, follow these instructions instead:
$ cd mod_ssl-2.6.x-1.3.x
$ ./configure \
--with-apache=../apache_1.3.x \
--with-crt=/path/to/your/server.crt \
--with-key=/path/to/your/server.key
$ cd ..
ALL
ALL
ALL
OPTIONAL
OPTIONAL
ALL
[...Now add more Apache modules to the Apache source tree...] OPTIONAL
$ cd apache_1.3.x
$ SSL_BASE=../openssl-0.9.x \
RSA_BASE=../rsaref-2.0/local \
EAPI_MM=../mm-1.0.x \
./configure \
--enable-module=ssl \
--enable-module=proxy \
--prefix=/path/to/apache \
[--enable-shared=ssl] \
[--enable-shared=proxy] \
[--disable-rule=SSL_COMPAT] \
[--enable-rule=SSL_SDBM] \
[--enable-rule=SSL_EXPERIMENTAL] \
[--enable-rule=SSL_VENDOR] \
[...more APACI options...]
$ make
2. Follow the HOWTO: Using Apache with ZServer. (Thanks, Anser, for this HOWTO!)
(I used the "Second example: a complete virtual host" for this implementation)
3. You should now have a section in Apache's httpd.conf that looks like this:
NameVirtualHost 192.192.123.234
<VIRTUALHOST 192.192.123.234>
ServerName www.fightclub.org
ServerAdmin tyler@fightclub.org
ProxyPass / http://zserver.xyz.com:9180/fightclub/
ProxyPassReverse / http://zserver.xyz.com:9180/fightclub/
ProxyPass /misc_ http://zserver.xyz.com:9180/misc_
ProxyPass /p_ http://zserver.xyz.com:9180/p_
</VIRTUALHOST>
4. Replace that entire section with something like the following:
# HTTP Virtual Host
NameVirtualHost 192.192.123.234:80
<VIRTUALHOST 192.192.123.234:80>
ServerName www.fightclub.org
ServerAdmin tyler@fightclub.org
ProxyPass / http://zserver.xyz.com:9180/fightclub/
ProxyPassReverse / http://zserver.xyz.com:9180/fightclub/
http://www.zope.org/Members/unfo/apche_zserver_ssl?pp=1 (1 of 2) [05/07/2000 11:11:32]
ALL
ALL
US
OPTIONAL
ALL
ALL
ALL
ALL
OPTIONAL
OPTIONAL
OPTIONAL
OPTIONAL
OPTIONAL
OPTIONAL
OPTIONAL
ALL
Apache+ZServer+SSL
ProxyPass /misc_ http://zserver.xyz.com:9180/misc_
ProxyPass /p_ http://zserver.xyz.com:9180/p_
</VIRTUALHOST>
# SSL Virtual Host
NameVirtualHost 192.192.123.234:443
<VIRTUALHOST 192.192.123.234:443>
ServerName www.fightclub.org
ServerAdmin tyler@fightclub.org
ProxyPass / http://zserver.xyz.com:9180/fightclub/ssl/
ProxyPassReverse / http://zserver.xyz.com:9180/fightclub/ssl/
ProxyPass /misc_ http://zserver.xyz.com:9180/misc_
ProxyPass /p_ http://zserver.xyz.com:9180/p_
SSLEngine on
SSLCertificateFile /path/to/apache/conf/ssl.crt/server.crt
SSLCertificateKeyFile /path/to/apache/conf/ssl.key/server.key
<Files ~ "\.(cgi|shtml)$">
SSLOptions +StdEnvVars
</Files>
<Directory "/path/to/apache/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
CustomLog /path/to/apache/logs/ssl_request_log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
5. Why the ssl/ at the end of the ProxyPass directive? You'll find out in a minute.
6. Open the Zope management interface and CREATE a new folder called 'ssl'.
7. Create a new SiteRoot within the 'ssl' directory:
Title
SSL Secure Site
Base
https://www.fightclub.org
Path
/
Change
8. Start up your Apache server (using apachectl startssl)and let 'er rip!
Try connecting to http://www.fightclub.org.
Try connecting to https://www.fightclub.org.
Does it work? Good.
9. You can now tell whether or not your user is accessing the secure site by looking at the value of URL1:
<dtml-if "_.string.split(URL1,':')[0] == 'https'">
You're using the secure server!
<dtml-else>
You're using the standard server!
</dtml-if>
Questions / Comments about this HOWTO?
E-mail wmannotj@sol.slcc.edu
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/unfo/apche_zserver_ssl?pp=1 (2 of 2) [05/07/2000 11:11:32]
Apache+mod_ssl+PCGI Virtual Serving
How-To: Apache+mod_ssl+PCGI Virtual Serving
Created by cavnit. Last modified on 1999/12/12.
The following How To is based on a system running on RH 6.1, but has been tested on FreeBSD 3.3 as well.
I will be describing how to set it up on RedHat 6.1, for the domains domain1.com and domain2.com that
will only be serving content using zope.
All the source files are placed under: /usr/local/src
Apache is installed under: /usr/local/apache
OpenSSL is installed under: /usr/local/openssl
Zope is installed under: /usr/local/zope
domain1.com is installed under /home/sites/domain1.com
domain2.com is installed under /home/sites/domain2.com
Requirements:
Zope-2.1.0
Apache-1.3.9
Mod SSL 2.4.9-1.3.9
Python-1.5.2
OpenSSL-0.9.4
Download the following files to /usr/local/src
Install python-1.5.2
rpm -Uvh python-1.5.2-7.i386.rpm
rpm -Uvh python-devel-1.5.2-7.i386.rpm
rpm -Uvh python-tools-1.5.2-7.i386.rpm
rpm -Uvh python-docs-1.5.2-7.i386.rpm
Install OpenSSL-0.9.4
tar zxpvf openssl_0.9.4.tar.gz
cd openssl_0.9.4
./configure
make
make install
Install Apache-1.3.9 & mod_ssl-2.4.9-1.3.9
tar zxpvf apache_1.3.9.tar.gz
cd apache_1.3.9
./configure
tar zxpvf mod_ssl-2.4.9-1.3.9.tar.gz
cd mod_ssl-2.4.9-1.3.9
./configure --with-apache=../apache_1.3.9
cd apache_1.3.9
SSL_BASE=/usr/local ./configure --enable-module=ssl --enable-module=rewrite
make
make certificate
make install
Install Zope
tar zxpvf zope-2.1.0-src.tar.gz
mv zope-2.1.0-src /usr/local/zope
cd /usr/local/zope
python w_pcgi.py
write down the superuser password
Create the following directories
cd /home
http://www.zope.org/Members/cavnit/apachevirt?pp=1 (1 of 4) [05/07/2000 11:11:36]
Apache+mod_ssl+PCGI Virtual Serving
mkdir sites
mkdir sites/domain1.com
mkdir sites/domain1.com/cgi-bin
mkdir sites/domain1.com/zope
mkdir sites/domain2.com
mkdir sites/domain2.com/cgi-bin
mkdir sites/domain2.com/zope
chown -R nobody.nogroup *
Copy the following files from /usr/local/zope
cp -R /usr/local/zope/var /home/sites/domain1.com/zope
cp /usr/local/zope/access /home/sites/domain1.com/zope
cp /usr/local/zope/Zope.cgi /home/sites/domain1.com/cgi-bin
Edit the Zope.cgi files for each domain as follows
domain1.com
vi /sites/domain1.com/cgi-bin/Zope.cgi
#!/usr/local/zope/pcgi/pcgi-wrapper
PCGI_NAME=Main
PCGI_MODULE_PATH=/usr/local/zope/lib/python/lib/python/Main.py
PCGI_PUBLISHER=/usr/local/zope/pcgi/pcgi_publisher.py
PCGI_EXE=/usr/bin/python
PCGI_SOCKET_FILE=/home/sites/domain1.com/zope/var/pcgi.pid
PCGI_ERROR_LOG=/home/sites/domain1.com/zope/var/pcgi.log
PCGI_DISPLAY_ERRORS=1
BOBO_REALM=/usr/local/zope/Zope.cgi
BOBO_DEBUG_MODE=1
INSTANCE_HOME=/home/sites/domain1.com/zope
domain2.com
vi /sites/domain2.com/cgi-bin/Zope.cgi
#!/usr/local/zope/pcgi/pcgi-wrapper
PCGI_NAME=Main
PCGI_MODULE_PATH=/usr/local/zope/lib/python/lib/python/Main.py
PCGI_PUBLISHER=/usr/local/zope/pcgi/pcgi_publisher.py
PCGI_EXE=/usr/bin/python
PCGI_SOCKET_FILE=/home/sites/domain2.com/zope/var/pcgi.pid
PCGI_ERROR_LOG=/home/sites/domain2.com/zope/var/pcgi.log
PCGI_DISPLAY_ERRORS=1
BOBO_REALM=/usr/local/zope/Zope.cgi
BOBO_DEBUG_MODE=1
INSTANCE_HOME=/home/sites/domain2.com/zope
Edit the apache httpd.conf file: /usr/local/apache/conf/httpd.conf
Add the following under the Virtual Host sections
# domain1.com virtual host directives
<VirtualHost 192.168.100.1>
ServerName www.domain1.com
ErrorLog /home/sites/domain1.com/zope/var/apache_error_log
TransferLog /home/sites/domain1.com/zope/var/apache_access_log combined
ScriptAlias /cgi-bin/ /home/sites/domain1.com/cgi-bin/
<Directory "/home/sites/domain1.com/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) /home/sites/domain1.com/cgi-bin/Zope.cgi$1 \
http://www.zope.org/Members/cavnit/apachevirt?pp=1 (2 of 4) [05/07/2000 11:11:36]
Apache+mod_ssl+PCGI Virtual Serving
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
</VirtualHost>
# domain2.com virtual host directives
<VirtualHost 192.168.100.2>
ServerName www.domain2.com
ErrorLog /home/sites/domain2.com/zope/var/apache_error_log
TransferLog /home/sites/domain2.com/zope/var/apache_access_log combined
ScriptAlias /cgi-bin/ /home/sites/domain2.com/cgi-bin/
<Directory "/home/sites/domain2.com/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) /home/sites/domain2.com/cgi-bin/Zope.cgi$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
</VirtualHost>
Add the folowing under the SSL Virtual Host section
# domain1.com SSL Virtual Host
<VirtualHost 192.168.100.1:443>
ServerName www.domain1.com
ErrorLog /home/sites/domain1.com/zope/var/apache_error_log
TransferLog /home/sites/domain1.com/zope/var/apache_access_log combined
ScriptAlias /cgi-bin/ /home/sites/domain1.com/cgi-bin/
<Directory "/home/sites/domain1.com/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) /home/sites/domain1.com/cgi-bin/Zope.cgi$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
SSLEngine on
SSLCertificateFile /usr/local/apache/conf/ssl.crt/domain1.com.crt
SSLCertificateKeyFile /usr/local/apache/conf/ssl.key/domain1.com.key
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
CustomLog /home/sites/domain1.com/zope/var/ssl_request_log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>
# domain1.com SSL Virtual Host
<VirtualHost 192.168.100.2:443>
ServerName www.domain2.com
ErrorLog /home/sites/domain2.com/zope/var/apache_error_log
TransferLog /home/sites/domain2.com/zope/var/apache_access_log combined
ScriptAlias /cgi-bin/ /home/sites/domain2.com/cgi-bin/
<Directory "/home/sites/domain2.com/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) /home/sites/domain2.com/cgi-bin/Zope.cgi$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
SSLEngine on
SSLCertificateFile /usr/local/apache/conf/ssl.crt/domain2.com.crt
SSLCertificateKeyFile /usr/local/apache/conf/ssl.key/domain2.com.key
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
CustomLog /home/sites/domain2.com/zope/var/ssl_request_log \
http://www.zope.org/Members/cavnit/apachevirt?pp=1 (3 of 4) [05/07/2000 11:11:36]
Apache+mod_ssl+PCGI Virtual Serving
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>
Add the following to your /etc/rc.d/rc.local file
# Zope Start Up
echo "Starting domain1.com Zope..."
/usr/bin/python /usr/local/zope/z2.py -p /home/sites/domain1.com/cgi-bin/Zope.cgi -w "" -f "" -m "" \
-Z /home/sites/domain1.com/zope/manager.pid INSTANCE_HOME/home/sites/domain1.com/zope >> \
/home/sites/domain1.com/zope/z2.log 2>&1 &
echo "Starting domain2.com Zope..."
/usr/bin/python /usr/local/zope/z2.py -p /home/sites/domain2.com/cgi-bin/Zope.cgi -w "" -f "" -m "" \
-Z /home/sites/domain2.com/zope/manager.pid INSTANCE_HOME/home/sites/domain2.com/zope >> \
/home/sites/domain2.com/zope/z2.log 2>&1 &
#Start Apache SSL
/usr/local/apache/bin/apachectl startssl
You should now be able to reboot your server, and once it is up again be able to acces your web sites normally.
If you experience any problems let me know so that I can update this howto.
Thanks
Thanks to Digital Creations for a wonderful product.
Thanks to Jim Cain for his "Zope/Apache Virtual Host HOWTO" which got me started on the right road to begin with.
TODO
Basic clean up and intergrate the zope startup into SYSv style scripts
Any other bugs you guys find.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cavnit/apachevirt?pp=1 (4 of 4) [05/07/2000 11:11:36]
Apache, Zope and FastCGI
How-To: Apache, Zope and FastCGI
Created by kedai. Last modified on 2000/06/06.
Apache, Zope and fastCgi
*updated 6/7/00 - changed the fastcgi version to the latest
*updated 3/9/00 - added ppetru's tip to use blocking call. also added the need to update FCGIServer.py from cvs
*updated 1/4/00 - added Roug elegent solution to the extra z's. Also added Sam Gendler's solution using fastcgi with unix socket.
I am no Apache, Zope or fastCGI guru. What i'm doing is collecting all information strewn in the mailing list and putting it all
together
after successfully installing Apache, Zope, and FastCGI. My environment:●
RedHat 6.1 server
●
Intel box
●
python from bin RPMs
●
Zope from src
●
Apache 1.3.9-8 * latest errata, just in case (as informed by Patrick Phalen ).
OK, to the main course.
1. Get the FastCGI module from www.fastcgi.com. Get the latest tar ball (as of 8 May 00 - mod_fastcgi_2.2.4)
2. Read the INSTALL that comes with mod_fastcgi. I compiled mine with apxs that ships with apache.
[root@emedia mine] #cd mod_fastcgi-ver
[root@emedia mod_fastcgi_xxx]# /usr/sbin/apxs -o mod_fastcgi.so -c *.c
gcc -O2 -m486 -fno-strength-reduce -DLINUX=2 -DUSE_EXPAT -I../lib/expat-lite
-DSHARED_MODULE -I/usr/include/apach
e -c fcgi_buf.c
gcc -O2 -m486 -fno-strength-reduce -DLINUX=2 -DUSE_EXPAT -I../lib/expat-lite
-DSHARED_MODULE -I/usr/include/apach
e -c fcgi_config.c
gcc -O2 -m486 -fno-strength-reduce -DLINUX=2 -DUSE_EXPAT -I../lib/expat-lite
-DSHARED_MODULE -I/usr/include/apach
e -c fcgi_pm.c
gcc -O2 -m486 -fno-strength-reduce -DLINUX=2 -DUSE_EXPAT -I../lib/expat-lite
-DSHARED_MODULE -I/usr/include/apach
e -c fcgi_protocol.c
gcc -O2 -m486 -fno-strength-reduce -DLINUX=2 -DUSE_EXPAT -I../lib/expat-lite
-DSHARED_MODULE -I/usr/include/apach
-fpic
-fpic
-fpic
-fpic
-fpic
[root@emedia httpd]# /usr/sbin/apxs -i -a -n fastcgi mod_fastcgi.so
cp mod_fastcgi.so modules/mod_fastcgi.so
chmod 755 modules/mod_fastcgi.so
[activating module `fastcgi' in /etc/httpd/conf/httpd.conf]
3. If all goes well, you'd have FastCGI module compiled and installed.
4. Now you'd need to edit apache httpd.conf. Read the docs (WEBSERVER.txt) that comes with Zope-2.1.1. You can talk
to the fastcgi module using unix socket or TCP port. I use TCP port since there were grumblings in the mailing list saying
that unix socket does not work reliably. However, my site have yet to receive any major hits, so I can't testify to the
reliability of using TCP port. Mail me your experience so that I can include it here.
---httpd.conf---FastCgiExternalServer /home/httpd/html/z -host localhost:8999 -pass-header
Authorization
Options ExecCGI
SetHandler fastcgi-script
------------------
Now all urls pointed to http://myserver.com/z will serve up your Zope contents.
Note that with this setup, the base url becomes http://myserver.com/z
http://www.zope.org/Members/kedai/apache_zope_fcgi?pp=1 (1 of 3) [05/07/2000 11:11:39]
Apache, Zope and FastCGI
In my case, I want to serve Zope from my root URL. We need Apache Rewrite module for this. Add these rules in your
httpd.conf:
------httpd.conf--------RewriteEngine On
RewriteRule ^/(.*) /z/$1 [PT] #* solution from Roug
RewriteRule ^/(.*) /z/$1 [PT]
-------------------------
Note:This tip was submitted by "Ron Bickers" . Previously, i managed to get fastcgi running,
but failed to serve Zope off of the root URL. Thanks, Ron!
Sidenote: With new base URL, reference to absolute_url will return the "z" too. If you are like me, when development of the site
is under way,
changing anything will be a headache. A workaround, create a folder "z" in your Zope. And I think that's inheritance working.
Another elegent solution is to add the additional rewrite rule as shown above.
If you have other dirs not in Zope you'd want to serve, just add those before the /(.*) statement. Tip: the log files are your best
friend!
You will need to restart zope and apache for the changes to take effect.
To use fastcgi with unix socket, try what Sam Gendler has done.
--quote sam--I got it working with Virtual hosts, so that even the root directory of the virtual
host is directed to zope.
The config was as follows:
ServerName www.impossible.com
DocumentRoot /home/httpd/impossible
ServerAlias impossible.com *.impossible.com
ServerAdmin admin@impossible.com
ErrorLog /home/httpd/log/www.impossible.com-error_log
CustomLog /home/httpd/log/www.impossible.com-access_log common
FastCgiExternalServer /home/httpd/impossible \
-socket impossible.soc \
-pass-header Authorization
Options ExecCGI
SetHandler fastcgi-script
I did have to make the file pointed to by FastCgiExternalServer be a file. When I
tried to make it a directory, I was able to access zope, but lots of things didn't
work correctly (could not access manage screen, any files in subdirectories, etc). As
soon as I made it a file, and gave it 0777 permissions, everything worked
flawlessly. Notice that DocumentRoot and FastCgiExternalServer are the same. I
think this is necessary if you want to have the entire site sent to zope. I also had
to sart zope with -F /tmp/fcgi/socketfile.soc, since the mod_fcgi automatically looks
in /tmp/fcgi. You can change that behaviour with a config directive, but I don't
know what it is.
--sam
--end quote---
Problems, experiences, and fixes
●
●
Quoting sam " Every once in a while, the document has http headers visible at the top, almost as though apache put
headers on the document and then zope put headers on as well. I didn't look last time, but next time, I will check and see
which one looks to have put headers on last (the visible ones). A simple reload fixed the problem. --sam "
Quoting me " Zope crashing once in a while, with no apparent reasons. This on RH6.1, apache 1.3.9-8, PIII. But on
Apache 1.3.6, RH5.2, P133, I never had that yet."
Apache with fastcgi sometimes could not handle requests, prompting error 505. To stop or minimize this error, update
http://www.zope.org/Members/kedai/apache_zope_fcgi?pp=1 (2 of 3) [05/07/2000 11:11:39]
Apache, Zope and FastCGI
FCGIServer.py which sits in $ZOPEHOME/ZServer. Get the latest version (v 1.7 as of 3/9/00) from the cvs. Download it, rename
the original FCGIServer.py and replace it with the latest version. Restart Zope for the change to take effect.
Also, use blocking call to fastcgi by adding -appConnTimeout 0 (see below)
--FastCgiExternalServer /home/httpd/html/z -host localhost:8999 -pass-header
Authorization -appConnTimeout 0
---
However, i still experience "(111)Connection refused: FastCGI: failed to connect to server "/home/httpd/html/z": connect() failed
" and " FastCGI: incomplete headers (0 bytes) received from server "/home/httpd/html/z"" with all the updates i did. Anybody
know why?
During my short experience with Zope, mod_pcgi and mod_fastcgi, I would say mod_pcgi provides more statbility, while
mod_fastcgi gives you speed.
I think when mod_fastcgi and zope's support for it matures, mod_fastcgi will be as stable as mod_pcgi.
As always, if you find errors, do point them to me. Also, mail me your experiences, be it good or bad, and your solutions.
Credits go to the mailing list and the zope community. Other useful docs
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/kedai/apache_zope_fcgi?pp=1 (3 of 3) [05/07/2000 11:11:39]
Authorization by Hostname or Address
How-To: Authorization by Hostname or Address
Created by muesli. Last modified on 2000/02/04.
Intro
Most web servers, like Apache, can be configured to restrict access for certain subdirectories based on the IP address of the user.
No password is required: the webserver automatically determines the origin of the connection. This is really handy for groups of
users, like those on a company intranet, who need access to internal documents.
This How-To explains how this is done in Zope's ZServer. I am using Zope 2.1.3, but this technique is fairly standard, and should
work with other versions.
Summary
A new role is created, who is given access to the protected resources. A new user is created that has this new role, and is
configured to accept only the approved addresses.
Step by step
●
●
Create a new role near or in the top of your folder hierarchy. (This should be a place above all possible folders you want
to protect.) The role should have a name along the lines of "internalBrowsing". (Names like this help me remember what
the purpose of the object is.)
Create a new user in the same location named something like "internalUser". Leave the password blank. In the Domains
field, enter in any domain names or IP addresses that you want to allow access to the restricted areas. You can use an
asterisk as a wild card. Seperate multiple names by a space. For example:
*.zope.org 192.168.0.1
...will allow anyone from any host at zope.org to connect, or someone from the host with the address, 192.168.0.1.
●
●
●
In the Roles list, select the new role you created earlier. Click the Change button to save this new user.
You are now ready to restrict access to your chosen folders. Go to any subfolder (or stay in the current one), and click its
Security tab.
Now restrict access to your new role in the standard way: Deselect all the checkboxes under Acquire permission
settings?. There should be a column on the right for your new role. Check the permissions for Access contents
information, Search ZCatalog, Use Database Methods, and View. Click the Change button to save your changes.
And that's it!
I'm very new to Zope. If you have any questions or suggestions, please write to me at robb@acm.org.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/muesli/AuthByAddress?pp=1 [05/07/2000 11:11:42]
AutoGenerate Random ID for objects
How-To: AutoGenerate Random ID for objects
Created by Bill. Last modified on 1999/11/11.
Sometimes, when creating objects, ZClasses for example I want to generate a random ID string, rather than have the user do so. I
put this HowTo together primarily for my reference (and to test out this HowTo object ;-) , however, you may find it useful. If you
do, let me know, I do plan to improve it, so suggestions are welcome.
An example would be in a troubleshooting ticket. The example below (taken from the KM|net News Product) creates one based
upon the current time.
To use it, put this in your 'add constructor' before calling the <dtml-with> tag.
<dtml-call "REQUEST.set('ts', ZopeTime())">
<dtml-call "REQUEST.set('id',_.str(_.int(ts)))">
With this you do not need to ask for an ID, and can thus remove that from the input form. In fact, I strongly recommend that you
do so.
Additional Tips and Tricks
If you want the id to be a human readable representation, change:
<dtml-call "REQUEST.set('ts', ZopeTime())">
to:
<dtml-call "REQUEST.set('ts', ZopeTime().strftime('%Y%m%d%H%M%S'))">
And you will get an id that looks like: 19991105084117
From Jos Yule we learn the following:
And, if you have/want to make the time local to your timezone -
<dtml-var "REQUEST.set('ts', ZopeTime() - 0.2083)">
<dtml-comment>
the 0.2083 is 5/24 (i am 5 hours behind what ZopeTime gives.)
so, simply divide how ever many hours you are off from ZopeTime
by 24 and add/subtract...
</dtml-comment>
<dtml-var "REQUEST.set('ts', ts.strftime('%Y%m%d%H%M%S'))">
<dtml-call "REQUEST.set('id', ts)">
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Bill/Documentation/AutoGenID?pp=1 [05/07/2000 11:11:44]
Breadcrumbs Navigation Trail
How-To: Breadcrumbs Navigation Trail
Created by lstaffor. Last modified on 1999/11/30.
This method creates the text for a type of navigation bar (like you see at Yahoo!) which consists of hyperlinks to each of the
parent objects of the current object; for example:
Home > Admin > Events > Christmas Party
All the logic that depends on (meta_type == 'Message') or (meta_type == 'Reply') handles some complications introduced by
Confera Topics. If you don't have any Confera Topics, you can delete the extra logic.
<!-- Start Breadcrumbs -->
<dtml-unless expr="PARENTS[0]==PARENTS[-1]">
<dtml-call "REQUEST.set('reverse_PARENTS', [])">
<dtml-call "REQUEST.set('reverse_LAST', 0)">
<dtml-in PARENTS skip_unauthorized>
<dtml-if expr="(meta_type == 'Message') or (meta_type == 'Reply')">
<dtml-unless "reverse_LAST==1">
<dtml-call "reverse_PARENTS.insert(0, _['sequence-item'])">
<dtml-call "REQUEST.set('reverse_LAST', 1)">
</dtml-unless>
<dtml-else>
<dtml-call "reverse_PARENTS.insert(0, _['sequence-item'])">
</dtml-if>
</dtml-in>
<dtml-in reverse_PARENTS>
<dtml-if sequence-start><a href="/">Home</a>
<dtml-else>
<dtml-if sequence-end>
<dtml-unless expr="(meta_type == 'Message') or (meta_type == 'Reply')">
&gt;&nbsp;<font size="+1" color="#990000"><b><dtml-var title_or_id></b></font>
</dtml-unless>
<dtml-else>
<dtml-if expr="(meta_type == 'Message') or (meta_type == 'Reply')">
&gt;&nbsp;<a href="<dtml-var absolute_url>"><dtml-var title_or_id></a>
<dtml-else>
&gt;&nbsp;<a href="<dtml-var absolute_url>"><dtml-var id></a>
</dtml-if>
</dtml-if>
</dtml-if>
</dtml-in>
</dtml-unless>
<!-- End Breadcrumbs -->
Here's another implementation by Barry A. Warsaw. This version allows you to assign an optional "nickname" property to any
folder. If a nickname exists, it is displayed instead of the folder's ID (which might be irrelevant) or title (which might be too long).
Also, the Confera logic has been deleted, making things much simpler.
<!-- start Breadcrumbs -->
<dtml-call "REQUEST.set('rev', [])">
<dtml-in PARENTS skip_unauthorized>
<dtml-let item=sequence-item>
<dtml-call "rev.insert(0, item)">
</dtml-let>
</dtml-in>
<dtml-in rev>
<dtml-with sequence-item>
<a href="<dtml-var absolute_url>">
<dtml-if "hasProperty('nickname')"><dtml-var nickname><dtml-else><dtml-var title_or_id></dtml-if></a>
</dtml-with>
<dtml-unless sequence-end>&nbsp;&gt;&nbsp;
</dtml-unless>
</dtml-in>
<!-- end Breadcrumbs -->
http://www.zope.org/Members/lstaffor/Breadcrumbs?pp=1 (1 of 2) [05/07/2000 11:11:47]
Breadcrumbs Navigation Trail
Put one of the above code examples in a DTML method (called, for example, "breadcrumbs"), then invoke the method where you
want the breadcrumbs to show (most likely in "standard_html_header"):
<dtml-var breadcrumbs>
If you have a view_source method implemented on your site, you may experience infinite recursion. If so, try this:
<dtml-unless "_['URL'][-11:] == 'view_source'">
<dtml-var breadcrumbs>
</dtml-unless>
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/lstaffor/Breadcrumbs?pp=1 (2 of 2) [05/07/2000 11:11:47]
Build a Searchable Job Board
How-To: Build a Searchable Job Board
Created by mukhsein. Last modified on 2000/05/02.
How To: Build a searchable Job Board
0.0 Introduction
This short howto is a simple walk-through or tutorial to demonstrate how Zope can be used to build interesting "web-apps". It was
inspired by the Job Board example from the doc: A Technical Introduction to Object Publishing with Zope by Brian Lloyd and
Amos Latteier from digicool.com. It does not however, use the same methods. It's an alternative Job Board. To view a working
example of it, see here.
Note: If you're using Zope-2.1.0 and above, you'll need to disable SubTransactions for the job board
entries' Catalog. Or the searching won't work as expected immediately after you add new entry objects.
So, who is the intended reader?
●
You are relatively new to zope.
●
You have zope set up and running - you get the "Welcome to Zope" page when you access your zope site.
●
You know some dtml (but you're not a jedi yet :))
●
You know how to add, delete etc. objects from the zope odb.
●
You are new to Z Classes and want to know how to build one.
●
You are new to Z Catalogs and want to see a practical example of searching Z Catalogs.
Obviously, this is not intended for Zope gurus. Some things will be rather simplified and not as 'tidy' as one would like. Also, I
will explain a lot of things which would be obvious to the average web guru. I've tried to make this howto accessible to not only
Zope newbies but also to those new to programming in general. So, what are we going to do?
Here's the Scope of this howto:
Scope
The Job Board is a board where anyone can post job offers (like you find in
newspaper classifieds and job "notice boards"). That is, it is publicly postable and
readable. Like a 'real' job board. However, unlike a physical one, you'll also be
able to search the board for jobs based on your own search criteria. (say,
company or job name)
In summary, a user will be able to:
●
●
●
Add a new entry onto the Job Board
View all the entries on the Job Board
Search the Job Board for jobs
Okay, let's begin. Point your browser to your Zope site and log in to the management interface.
1.0 Create the Job Board Folder
The first thing to do is to create the folder where this little app will live. Add a folder with Id "Job_Board". Leave the default
check-boxes on. You'll want an "index_html". Then, add another folder within the Job_Board folder - give it the Id "entries". This
folder will store our job board entries. Next add a ZCatalog object. Give it the Id "Catalog" - the name of this catalog is important.
The 'C' is capital.
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (1 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
2.0 Create the Job Board Entry ZClass
We want to make a template for adding job offers. For this, we'll create a ZClass. The instances are later created by the users
themselves as and when they have new jobs to offer without entering the management interface. Go to the "Products" folder in the
"Control_Panel" at the top. Add a new product with Id "JobBoard". Click on it.
Now add a new Z Class. Give it an Id "job_board_entry", Meta Type "Job Board Entry". Make sure "Create constructor objects" is
checked. In the Base Classes section, add CatalogAware as a base class. Then click "Add".
3.0 Customize the job_board_entry Z Class
Now that we've created the Z Class, it's time to customize it. Click on the job_board_entry class (it's got that "tin can" icon). Let's
create some properties to be the job_board_entry class' attributes. Click on the Property Sheets tab (at the top). Add a new
property sheet by clicking Add. Give the property sheet the Id "entry_info". Click on it.
Let's add some properties. Add these:
Id
Type
Value
position_name
string
[string]
position_about
lines
[lines]
position_pay
float
0.0
org_name
string
[string]
org_homepage
string
[string]
org_about
text
[text]
contact_name
string
[string]
contact_email
string
[string]
contact_phone
string
[string]
offer_expires
string
[string]
title
string
[string]
Apart from the required "0.0" for float type, why did we put values "[string]" etc in the other properties? It's not required but I find
that it's easier to see what types those properties are later. Also, why didn't we use type "date" for the offer_expires property?
Well, we could but then you would not be able to put values like "Never" or "Real Soon" in it. The type "date" is an object with
methods. Not a simple type.
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (2 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
3.1 Customize job_board_entry_add method
When an entry is created, each entry object needs an "id". A unique identifier. Normally, when you create an object using the
management interface, Zope will as you to specify the object's "id". We could do that for our entry object as well, but you'd have
to make the user or zope check for conflicting ids at creation time - this is sub-optimal to say the least.
The solution to this is to get Zope to generate ids automatically. For this, we'll use the trick suggested in "HowTo: AutoGenerate
Random ID for Objects" by Bill (check the "howto" pages on the zope.org website for more tricks and tips!).
Basically, you use a time-stamp generated from the machine's rtc. So, each job_board_entry object will have a string of numbers
for their ids. Something of the form: 940962299. To do that, you need to customize the job_board_entry_add method:
Get to the JobBoardProduct folder. Below the job_board_entry class, you'll see a method labeled "job_board_entry_add". Click on
it. Add the lines in bold to the method:
job_board_entry_add
<HTML>
<HEAD><TITLE>Add job_board_entry</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-comment> We add the new object by calling the class in
a with tag. Not only does this get the thing
added, it adds the new thing's attributes to
the DTML name space, so we can call methods
to initialize the object.
</dtml-comment>
<dtml-comment>
The dtml-calls below are for auto-id during object
initialization.
</dtml-comment>
<dtml-call "REQUEST.set('ts', ZopeTime())">
<dtml-call "REQUEST.set('id',_.str(_.int(ts)))">
<dtml-with "job_board_entry.createInObjectManager(REQUEST['id'],
REQUEST)">
<dtml-comment>
You can ad code that modifies the new instance here.
For example, if you have a property sheet that you want to update
from form values, you can call it here:
<dtml-call
"propertysheets.Basic.manage_editProperties(REQUEST)">
</dtml-comment>
<dtml-call "propertysheets.entry_info.manage_editProperties(REQUEST)">
<dtml-call reindex_object>
</dtml-with>
<dtml-comment> Now we need to return something. We do this via
a redirect so that the URL is correct.
Unfortunately, the way we do this depends on
whether we live in a product or in a class.
If we live in a product, we need to use DestinationURL
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (3 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
to decide where to go. If we live in a class,
DestinationURL won't be available, so we use URL2.
</dtml-comment>
<dtml-if NoRedir>
<dtml-else>
<dtml-if DestinationURL>
<dtml-call "RESPONSE.redirect(DestinationURL+'/manage_workspace')">
<dtml-else>
<dtml-call "RESPONSE.redirect(URL2+'/manage_workspace')">
</dtml-if>
</dtml-if>
</BODY>
</HTML>
Note: The 2 lines (dtml-call) in bold above tells Zope to update the properties of the newly created objects and the then re-index
the ZCatalog, respectively.
If you've got some experience with Zope you quickly realize that if you used the management interface to browse through your
entries that it is difficult to determine the objects just from a number. Not to worry, Zope objects have the attribute "Title" which
we will use to allow humans to tell at a glance what the objects are. Yes, they are the titles in parentheses, next to the object ids.
The nice thing about this is that you can have the same titles for two objects with different Ids. This is analogous to two people
with the exact same name - but having different identities. We will also create the titles automatically as we'll see later.
3.2 Create job_board_entry Z Class view methods
Now then, wouldn't it be great if our job_board_entry class instances could display themselves? Well, they can! Click on the
"Methods" tab. Add these two methods: "index_html" and "content_html". Here's what they look like: (The dtml tags are in bold
to highlight them)
content_html
<TABLE border="1" bgcolor="aliceblue">
<TR><TH colspan="2" bgcolor="lightblue">Job Offer Entry Object:
<dtml-var id></TH><TR>
<TR><TD bgcolor="lightblue"><B>Organization:</B></TD><TD><dtml-var
org_name></TD></TR>
<TR><TD bgcolor="lightblue"><B>Homepage:</B></TD><TD><dtml-var
org_homepage></TD></TR>
<TR><TD bgcolor="lightblue"><B>About:</B></TD><TD><dtml-var org_about
newline_to_br></TD></TR>
<TR><TD bgcolor="lightblue"><B>Position:</B></TD><TD><dtml-var
position_name></TD></TR>
<TR>
<TD bgcolor="lightblue"><B>Description:</B></TD>
<TD>
<UL>
<dtml-in position_about>
<dtml-let item=sequence-item>
<dtml-if item>
<LI><dtml-var item></LI>
</dtml-if>
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (4 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
</dtml-let>
</dtml-in>
</UL>
</TD>
</TR>
<TR><TD bgcolor="lightblue"><B>Pay Offered:</B></TD>
<TD>
<dtml-if "position_pay==0.0">
Contact Us
<dtml-else>
<dtml-var position_pay>
</dtml-if>
</TD></TR>
<TR><TD bgcolor="lightblue"><B>Offer Expires:</B></TD><TD><dtml-var
offer_expires></TD></TR>
<TR><TD bgcolor="lightblue"><B>Contact:</B></TD><TD><dtml-var
contact_name></TD></TR>
<TR><TD bgcolor="lightblue"><B>Email:</B></TD><TD><dtml-var
contact_email></TD></TR>
<TR><TD bgcolor="lightblue"><B>Phone:</B></TD><TD><dtml-var
contact_phone></TD></TR>
</TABLE>
index_html
<dtml-var standard_html_header>
<dtml-var content_html>
<dtml-var standard_html_footer>
You might be wondering, with the index_html being only 3 lines, why not combine the two dtml methods into one? The reason is
this: "index_html" is always the default dtml method called when you view an object in zope (this applies to folders as well). What
this means is that when you view the object on its own, you'll get the content plus the standard html headers and footers. However,
if you're viewing a list of these objects on a page (ie. another dtml method or document), then that page would already have the
standard footers and headers. On that page, you only call the objects' "content_html" methods and not the "index_html" one. If
you're confused, don't worry, this concept will be clearer as we go along.
4.0 Create methods for public object creation
Normally, you can only add new objects through the Zope management interface with the by-now-familiar "Add" button. This is
because you want only users with the right access rights to add objects into you Zope site. (As specified in the various acls in the
Zope folders.) Normally, the "job_board_entry_addForm" would be used to fill in the details (attributes) for an entry - if you're
going to add entry objects through the management interface, you'll need to check that this method is correct.
However, since this is going to be a public posting board, you'll want to allow anyone to add job offers / notices to the job board.
After all, that's what it's for. To do that, we'll add 2 methods in the "entries" folder. (ie. ~/Job_Board/entries/) Go to that folder and
"Add" 2 dtml methods, name them as below.
We will need to create two methods. One is the "job_board_entry_addform" equivalent and the other is the addform processor
method which will process the data from the webform. (dtml tags are in bold)
entry_addForm
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (5 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
<dtml-var standard_html_header>
<H2>Add Job Board Entry</H2>
<FORM action="entry_addProcessor">
<TABLE border="0">
<TR><TH>Organization</TH>
<TD><input type=text name=organization size="35"></TD>
</TR>
<TR><TH>Homepage</TH>
<TD><input type=text name=homepage size="35"></TD>
</TR>
<TR><TH>About</TH>
<td><textarea name="about:text" rows="8" cols="35"></textarea></TD>
</TR>
<TR><TH>Position</TH>
<TD><input type=text name=position size="35"></TD>
</TR>
<TR><TH>Description</TH>
<TD><textarea name="description:text" rows="8"
cols="35"></textarea></TD>
</TR>
<TR><TH>Pay Offered</TH>
<TD><input type=text name=pay value="0.0"></TD>
</TR>
<TR><TH>Offer Expires</TH>
<TD><input type=text name=expires></TD>
</TR>
<TR><TH>Contact</TH>
<TD><input type=text name=name size="35"></TD>
</TR>
<TR><TH>Email</TH>
<TD><input type=text name=email size="35"></TD>
</TR>
<TR><TH>Phone</TH>
<TD><input type=text name=phone size="35"></TD>
</TR>
<TR>
<TD></TD>
<TD><input type=submit value=" Add Entry "><input type=reset value="
Clear Form "></TD>
</TR>
</TABLE>
</FORM>
<P>
<TABLE border="1">
<TR><TH colspan="2" bgcolor="lightblue">Usage</TH></TR>
<TR><TH>Organization</TH>
<TD>Name of organization eg. <I>Malaysian Open Source
Group</I></TD></TR>
<TR><TH>Homepage</TH>
<TD>Website address without "http://" eg.
<I>www.my-opensource.org/oss/</I></TD></TR>
<TR><TH>About</TH>
<TD>Short intro to your organization's activities</TD></TR>
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (6 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
<TR><TH>Position</TH>
<TD>Position offered eg. <I>System Administrator</I></TD></TR>
<TR><TH>Description</TH>
<TD>Short Job description - each line becomes a bullet list
item.</TD></TR>
<TR><TH>Pay Offered</TH>
<TD>In RM eg. <I>2000.0</I> - entering 0.0 will display "Contact
Us" when viewed</TD></TR>
<TR><TH>Offer Expires</TH>
<TD>When this offer expires eg. <I>dd/mm/yyyy</I> or
<I>Never</I></TD></TR>
<TR><TH>Contact</TH>
<TD>The contact person eg. <I>The Manager</I> or <I>Dr.
Nah</I></TD></TR>
<TR><TH>Email</TH>
<TD>Contact person's email eg.
<I>docn@my-opensource.org</I></TD></TR>
<TR><TH>Phone</TH>
<TD>Contact person's phone number</TD></TR>
</TABLE>
</P>
<dtml-var standard_html_footer>
entry_addProcessor
<dtml-var standard_html_header>
<dtml-call "REQUEST.set('org_name', organization)">
<dtml-call "REQUEST.set('org_homepage', homepage)">
<dtml-call "REQUEST.set('org_about', about)">
<dtml-call
<dtml-call
<dtml-call
<dtml-call
"REQUEST.set('position_name', position)">
"REQUEST.set('position_about', description)">
"REQUEST.set('position_pay', pay)">
"REQUEST.set('offer_expires', expires)">
<dtml-call "REQUEST.set('contact_name', name)">
<dtml-call "REQUEST.set('contact_email', email)">
<dtml-call "REQUEST.set('contact_phone', phone)">
<dtml-call "REQUEST.set('title', organization+' - '+position)">
<dtml-with "manage_addProduct['JobBoard']">
<dtml-call "job_board_entry_add(_.None, _, NoRedir=1)">
</dtml-with>
<CENTER>
<H2>Thank you for your entry</H2>
<dtml-var linkto_frontpage>
</CENTER>
<dtml-var standard_html_footer>
The first method is pretty much standard for a webform. Those of you familiar with such things will not find anything out of the
ordinary. However, note the line:
<FORM action="entry_addProcessor">
That's right. Normally, you would put here the url of a cgi script. But in Zope, you just call another method. Cool. The interesting
stuff is in the entry_addProcessor method. This is where we assign the data from the form's fields to the actual object's attributes.
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (7 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
This is done by the many <dtml-call...>.
Look at the dtml-calls. You'll notice that the var-names from the html form are mapped to the objects attributes. Actually, there's
no reason why the attribute names and the webform field names cannot be exactly the same. I made them this way so that you can
see which part is which in the code.
Look at this line:
<dtml-call "REQUEST.set('title', organization+' - '+position)">
This is where we assign the "title" of the object. (remember when we coded the autogenerate IDs?). Here, we set the title so that is
a combination of the organization and the position offered. This makes it easy to spot, at a glance, what those objects are when
you're browsing the list of items in the "entries" folder using the management interface. Note that its possible for two different
objects to have the same title. This is okay, since they would have different object IDs. (ie. those numbers)
Lastly, look at the code snippet below:
<dtml-with "manage_addProduct['JobBoard']">
<dtml-call "job_board_entry_add(_.None, _, NoRedir=1)">
</dtml-with>
The "entry_addProcessor" actually does call the real method to create an "entry" object. But "NoRedir" is set to "1" so that the
browser is not redirected as is the normal case when using the management interface. (Try adding an object normally). So, the user
will see the next lines of HTML acknowledging the addition / creation of the new "entry" object. If an error were to occur while
processing, you won't get to see the "Thank you" message. (Which is good, btw. We don't want to give a false impression, do we?)
Important:The "entry_addProcessor" must be set to "Manager" proxy for it to work. Click on the "entry_addProcessor" dtml
method and click on the "Proxy" tab at the top. Make sure that Proxy Role "Manager" is highlighted and click the "Change"
button. This 'proxying' is what enables an anonymous user to add new entry objects. The "entry_addProcessor" method acts with
the manager's role. This is a very powerful thing so be sure you know what you're doing when you assign public methods the
manager's role. In this Job Board case, we want this ability.
5.0 Viewing the Job Board
Okay, so now we have our class done, we can add new job board entries. First, let's build the actual web page where the job board
will be viewed. The "welcome" page you will see on the demo site is the one below. Customize to your own needs.
index_html
<dtml-var standard_html_header>
<TABLE border="0">
<TR>
<TH bgcolor="lightblue">
<H2>Welcome to the my-oss IT Job Board!</H2>
</TH>
</TR>
<TR>
<TD>
<P>
<B>T</B>his is a simple web application using zope. The purpose of this
website is for people to post job offers and for people to look for
interesting
jobs in the local IT industry (preferably using or building open source
projects).
</P>
<P>
<B>T</B>his site was made primarily to demonstrate the use of <A
href="http://www.zope.org">Zope</A>
as a web application tool to the <A
href="http://www.my-opensource.org">Malaysian Open Source Group</A>
members.
</P>
<P>
<B>H</B>opefully, members of the local open source community will also
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (8 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
find paying jobs
to sustain themselves while they hack open source code. It's also for
companies that are
looking for knowledgeable IT personnel whose job will entail common
open source systems like Apache site admin or programming with egcs and
python.
</P>
<P>
<B>B</B>etter yet, if you happen to be looking for open source
developers for
an open source project, you can post an offer here. (maybe contract
work?)
</P>
<P>
<B>I</B>t is my hope that this site becomes more than a toy and that
both
prospective employers and employees will find it useful.
</P>
</TD>
</TR>
<TR>
<TD>
To see how it works, you can:
<OL>
<LI><A href="viewJobBoard">View the Job Board</A></LI>
<LI><A href="entries/entry_addForm">Add an entry to the Job
Board</A></LI>
<LI><A href="entry_search">Search the Job Board</A></LI>
</OL>
</TD>
</TR>
</TABLE>
<dtml-var ZopeAttributionButton>
<dtml-var standard_html_footer>
Notice this line:
<LI><A href="viewJobBoard">View the Job Board</A></LI>
The "href" is actually referencing a dtml method - in this case "viewJobBoard". Let me explain a bit here. The reason why dtml
methods are called methods is because they are methods of the object in which they live or which they are a part of. In this case, it
is a method of the job board object (which is really just a folder object) that implements a view. Object oriented programmers will
now understand this method concept in Zope. (and in the process, acquire some Zope Zen :-))
So what does the dtml method "viewJobBoard" look like? See below. As usual, dtml is in bold. Note that the method resides
within the "Job Board" folder. (It is a method of that folder object).
viewJobBoard
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (9 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
<dtml-var standard_html_header>
<H2>The Job Board</H2>
<dtml-with entries>
<TABLE border="0">
<dtml-in "objectValues(['Job Board Entry'])">
<TR><TD><dtml-var content_html></TD></TR>
</dtml-in>
</TABLE>
</dtml-with>
<dtml-var linkto_frontpage>
<dtml-var standard_html_footer>
The dtml:
<dtml-in "objectValues(['Job Board Entry'])">
<TR><TD><dtml-var content_html></TD></TR>
</dtml-in>
Instructs Zope to look for objects with that Value attached to them. In this case, all objects that have the "meta-type" of "Job
Board Entry". What this dtml-in does is loop through all the objects within that folder and if the object is that meta-type, its
content_html method is called.
The "linkto_frontpage" line there is just another dtml method (living in the same folder) as a convenient way to make links to the
front page. It is a one liner:
linkto_frontpage
<P><A HREF="Job_Board"><H4>Back to front page.</H4></A></P>
Some of you might be wondering: "Why are we even bothering to create such a simple one liner dtml method?" Well, the reason
is that, now we can use a short single line within any other dtml methods or dtml documents as shown in "viewJobBoard" above.
This same line can be used anywhere dtml is used and any number of times. The benefit? Should we need to change that link, we
only need to do it once (in the actual "linkto_frontpage" dtml method) and it will immediately work throughout the site.
For those who are wondering, yes, that's exactly the reason the dtml methods "standard_html_header" and "standard_html_footer"
exist (and work the way they do). But there's more! What if, for the whole site, you want a certain "standard_html_header" except
for a few folders you want different "standard_html_header" dtml methods? No problem, just make new ones in those folders.
They will override the one in the top level folder. This phenomenon is called "acquisition" in Zope-speak and after you've played
around with Zope, you'll agree it's pretty simple but very powerful.
6.0 Searching for Job Board Entries
Our little job board is now pretty functional as it is and you can already put it to good use. However, like I promised earlier, we
want to go one-up on the real "paper and pins" job boards. You can scan the board for interesting entries but what if the board
became super successful and there are hundreds of entries? This is where automated searching can give our Zope job board the
edge over conventional ones.
Searching (for objects) in Zope is done using the ZCatalog. As you'll see, ZCatalog is pretty nifty, to say the least. You can use it
to catalog and later search for any object or attribute of objects within the context of the Catalog. ZCatalogs are special objects but
still objects nonetheless with their own attributes and methods. We've already created one at the start of this tutorial so all we need
to do is customize it!
6.1 Customizing the ZCatalog
The ZCatalog catalogs objects for quick searching. The first thing we need to do is to customize the MetaData Table. Go to the
Job Board folder object and click on the Catalog object. At the top of the page, click on the MetaData Table tab.
The MetaData Table stores bits of information you want to have ready to display in the results page. They are taken from the
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (10 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
objects themselves and stored in the table. So, each object will have it's own row. You'll want to have a minimum of items stored
here since it's only needed to give a quick look at the objects. For the real details, we will call the objects themselves (via their
content_html method). Data stored in this table will be used to populate the results table generated from the search.
Make sure these items are in the list:
(Some are already listed)
●
id
●
contact_phone
●
offer_expires
●
position_name
●
org_homepage
●
contact_email
●
meta_type
●
contact_name
●
org_name
The next thing to do is to index the catalog with items that will be used to search it. That sounds complicated than it really is.
Basically, the items you specify to be indexed will be used in the search. Click on the Indexes tab at the top.
Add these items to the index list:
(They are all text-indexes)
●
position_name
●
position_about
●
org_name
●
org_about
●
org_homepage
These will also be the fields for the search form as we shall see later.
Next click on the Status tab. Make sure that Substransactions is Disabled. Disable it if it isn't.
6.2 Creating the ZCatalog Search Interfaces
Now that we have a catalog, we'll want to make interfaces for it so we can use it. Go back 'out' of the Catalog. Make sure you can
see the "Catalog" and "Entries" folders on this level. (this is the Job Board folder). Select Z Search Interface and click Add to add
the Z Search Interface.
In the Add form, Click on "Catalog" (ie. make sure it is highlighted), report style set to "Tabular". For the text fields:
Field
Report Id
Report Title
Search Input Id
Search Input Title
Value
entry_report
Entry Search Result
entry_search
Search for Entries
The two dtml methods will be created. We will need to customize the two methods. So, click on them and edit both methods so
they look similar to the examples below.
entry_search
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (11 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
<dtml-var standard_html_header>
<FORM action="entry_report" method="get">
<H2><dtml-var document_title></H2>
Enter query parameters:<BR>
<TABLE>
<TR><TH>Organization</TH>
<TD><INPUT name="org_name"
width=30 value=""></TD></TR>
<TR><TH>Homepage</TH>
<TD><INPUT name="org_homepage"
width=30 value=""></TD></TR>
<TR><TH>About</TH>
<TD><INPUT name="org_about"
width=30 value=""></TD></TR>
<TR><TH>Position</TH>
<TD><INPUT name="position_name"
width=30 value=""></TD></TR>
<TR><TH>Description</TH>
<TD><INPUT name="position_about"
width=30 value=""></TD></TR>
<TR><TD colspan="2" align="center">
<INPUT type="SUBMIT" name="SUBMIT" value="Submit Query">
</TD></TR>
</TABLE>
</FORM>
<P>
<TABLE border="1">
<TR><TH colspan="2" bgcolor="lightblue">Usage</TH></TR>
<TR><TH>Organization</TH><TD>Name of organization eg. <I>Malaysian Open
Source Group</I></TD></TR>
<TR><TH>Homepage</TH><TD>Website address without "http://" eg.
<I>www.my-opensource.org/oss/</I></TD></TR>
<TR><TH>About</TD><TD>Keywords regarding organization's activities eg.
<I>computers</I></TD></TR>
<TR><TH>Position</TD><TD>Position you're interested in eg. <I>System
Administrator</I></TD></TR>
<TR><TH>Description</TH><TD>Keyword from job description eg.
<I>flexible hours</I></TD></TR>
</TABLE>
</P>
<dtml-var standard_html_footer>
entry_report
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (12 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
<dtml-var standard_html_header>
<dtml-in Catalog size=50 start=query_start>
<dtml-if sequence-start>
<dtml-if previous-sequence>
<A href="<dtml-var URL><dtml-var sequence-query
>query_start=<dtml-var
previous-sequence-start-number>">
(Previous <dtml-var previous-sequence-size> results)
</A>
</dtml-if previous-sequence>
<TABLE border>
<TR>
<TH>Organization</TH>
<TH>Homepage</TH>
<TH>Position</TH>
<TH>Offer Expires</TH>
<TH>Contact</TH>
<TH>Email</TH>
<TH>Phone</TH>
</TR>
</dtml-if sequence-start>
<TR>
<TD><dtml-var org_name></TD>
<TD>
<A href="http://<dtml-var org_homepage>">
<dtml-var org_homepage></A>
</TD>
<TD>
<A href="<dtml-var "Catalog.getpath(data_record_id_)">">
<dtml-var position_name></A>
</TD>
<TD><dtml-var offer_expires></TD>
<TD><dtml-var contact_name></TD>
<TD><dtml-var contact_email></TD>
<TD><dtml-var contact_phone></TD>
</TR>
<dtml-if sequence-end>
</TABLE>
<dtml-if next-sequence>
<A href="<dtml-var URL><dtml-var sequence-query
>query_start=<dtml-var next-sequence-start-number>">
(Next <dtml-var next-sequence-size> results)
</A>
</dtml-if next-sequence>
</dtml-if sequence-end>
<dtml-else>
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (13 of 14) [05/07/2000 11:12:00]
Build a Searchable Job Board
There was no data matching this <dtml-var title_or_id> query.
</dtml-in>
<dtml-var linkto_frontpage>
<dtml-var standard_html_footer>
I have not marked any bold items this time since there are many instances where the generated dtml methods differ from the ones
above. Either customize it to your needs or simply copy and paste.
Now that we've got all the pieces done, all we need to do now is create some Job Board Entry objects. (ie. instances of the class)
Go to the Job Board folder and click on the View tab. This will call that folder's index_html method. Select the add new entry and
add one. (You can also call the "enter_addForm" dtml method from within the "Entries" folder directly.) Add another entry. Make
it 3 or 4. Check that the objects are actually generated in the "Entries" folder.
View the created objects and make some searches. This is where you might need to fix some bugs. It happens. I know this is
obvious to many of you but please do check and test your web apps before you make it "live" on the internet or intranet. That's
about it, really. Congratulations for staying with it!
7.0 Conclusion
We have actually covered quite a lot of ground. Specifically:
●
Defining new ZClasses
●
Creating object "publishing" methods (content_html)
●
"Public access" object creation (or class instantiation)
●
Cataloging objects
●
Creating search interface
Which pretty much covers the basics of using Zope and its features. Of course, this tutorial only scratches the surface of what
Zope can do but it is my hope that you, the reader, at least come to appreciate the power and simplicity of the Zope web
application environment. Where to from here? Well, there are a few things you can try like tweaking the methods or maybe add a
hyperlink to the email address to "mailto:" that address. Or make a real product where you can instantiate an "instant" job board.
(hint: you'll need to create another class for this. An "Object Manager" subclass, to be exact.) Or better yet, make a different app
altogether. Check the Zope.org site for user contributed products and howtos. There's quite a few. :-)
Good luck and above all, have fun!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mukhsein/job_board_howto?pp=1 (14 of 14) [05/07/2000 11:12:00]
Build a WAP site with Zope
How-To: Build a WAP site with Zope
Created by Duncan. Last modified on 2000/04/18.
This how-to describes how to use Zope to generate WML pages for use by WAP browsers. This is an ongoing work so please
check back occasionally for changes, or mail me suggestions for improvements.
A site using the techniques described may be seen at http://www.rcp.co.uk/distributed/wapdemo (walkthrough with screenshots),
or http://wap.autolocate.co.uk if you have a mobile phone.
What is WAP?
It is "The de facto worldwide standard for providing internet communications and advanced telephony services on digital mobile
phones, pagers, personal digital assistants and other wireless terminals". Or at least that is what the wapforum mission statement
says.
standard_wap_header
All of my WAP pages use standard_wap_header and standard_wap_footer instead of the html ones. The header contains the
common XML header information, and it also tries to catch ordinary browsers visiting the site, redirecting them to an HTML page
with suitable instructions. For debug support you can put ?debug=1 on the end of the URL to suppress the redirection.
<dtml-if "HTTP_USER_AGENT[:7]!='Mozilla'">
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<!-- content-type set in footer -->
<wml>
<dtml-else><dtml-unless debug><dtml-return
"RESPONSE.redirect('/wap/notwml.html')"></dtml-unless>
</dtml-if>
standard_wap_footer
The content type must be set appropriately for all WML pages. To make life a little easier though, this version of the footer checks
whether the browser is Netscape, and if it is, it sends plain text instead of wml. This lets you view any of your wml pages directly
in Netscape (if you add the debug flag as above).
</wml>
<dtml-if "HTTP_USER_AGENT[:7]!='Mozilla'">
<dtml-call "RESPONSE.setHeader('Content-Type', 'text/vnd.wap.wml')">
<dtml-else>
<dtml-call "RESPONSE.setHeader('Content-Type', 'text/plain')">
</dtml-if>
standard_html_error
When a Zope error occurs, it tries to send back an error message which is in HTML format. Overriding standard_html_error
makes it possible to format error messages for the WAP browser.
Preserving session information
There are two main options for preserving session state. One is to put everything in the URL, which is fine if you don't have too
much information, but it makes the URLs quite long which in turn uses up the 1400 byte page size. The advantage to this is that
pages are cachable, so with luck you can browse much of your site without ever making a phone call.
The other is to use SQLSession with cookie support disabled. If you try this, be sure to set cache expiry times for all of your
pages.
Bitmaps
http://www.zope.org/Members/Duncan/wap_howto?pp=1 (1 of 2) [05/07/2000 11:12:03]
Build a WAP site with Zope
There is no direct support for WBMP files in Zope. It would be possible modify the mime types in
lib/python/ofs/content_types.py, or create an extension that added the extra types so that WBMP files automatically get the correct
mime type when uploaded. The other possible change would be to enhance the Image object type so that it could recognise the
size fields of a WBMP object.
Another possible enhancement to Zope would be to install PIL and add support for WBMP. This would make it possible to upload
GIF files and have Zope manage the conversion automatically. PIL support for WBMP format file is now available here.
At present, the only way to handle WBMP files in Zope is:
●
●
Create an image using your favourite image editor.
Convert it to a WBMP format. You can use Paintshop Pro, or Photoshop using a free plugin (MS windows only). Other
tools exist for Macintosh or Java conversion. A web based convertor may be found at
http://www.applepiesolutions.com/image2.wbmp/.
●
Upload it to your Zope server as a file object.
●
Edit the mime type if neccessary.
●
Insert literal image tags where needed in your WML.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Duncan/wap_howto?pp=1 (2 of 2) [05/07/2000 11:12:03]
Building Zope and Python on MacOS X Server
How-To: Building Zope and Python on MacOS X
Server
Created by jshell. Last modified on 2000/03/13.
How to build Zope 2.1.x and a shared-lib Python
on MacOS X Server
This has been updated so that it should work on all Darwin platforms
Last time I told this story, I was having problems getting dynamic libraries (.so files) to work with Python without configuring
with the --with-next-framework option, which didn't compile to the results I wanted. Below are the steps to building
Python 1.5.2 with threads and dynamic libraries on MacOS X Server and to building Zope 2.1.3.
This how-to assumes basic knowledge of building and configuring Python on Unix.
Python::
Building Python 1.5.2 is relatively easy, but getting shared library support on MacOS X Server is a bit trickier. In order to use
Zope, threads are needed and shared library support is assumed by the Zope builder scripts. This is a way to build a full Python
1.5.2 on MacOS X Server with shared library support and without being compiled for use as a NeXT Framework.
First - configuring
./configure --with-threads --with-dyld --with-host=powerpc-apple-netbsd
Next - MAKE
make LDFLAGS="-undefined suppress"
make install
The LDFLAGS="-undefined suppress" option is passed into the linker and will suppress errors about undefined references.
Without this setting, dynamic libraries will not get past the linker stage. Make install installs Python into the configured area (you
should be root to do this).
Zope::
A change has to be made in one of the files used for building/configuring Zope that will hopefully be ironed out in the near future.
For now, do the following
In inst/do.py, change line 135 from
do('make')
to
do('make LDFLAGS="-undefined suppress"')
(We'll find a way of fixing this in the future so this shouldn't have to be edited. There should be a Python configuration option
when building for setting these LDFLAGS into the default Makefile.pre.in)
Then, for reasons currently unknown to me, cPickle issues have to be resolved. The Zope build process makes two cPickle.so
files. These files appear to be identical and cause a conflict with each other at runtime due to multiple symbol definitions, and they
cause a conflict with the cPickle.so that is built with Python.
Lastly:
cd lib/python/
rm cPickle.so
cd lib/python/ZODB/
mv cPickle.so /usr/local/lib/python1.5/lib-dynload/
You should be root to do the last step of moving the Zope cPickle.so into the default Python area.
http://www.zope.org/Members/jshell/buildingZopeOnMacOSXServer?pp=1 (1 of 2) [05/07/2000 11:12:06]
Building Zope and Python on MacOS X Server
Thoughts and future...
This is still too much to do, in my opinion, but it's the best way I've found yet. Any suggestions about how to get the LDFLAGS
option set at Python configuration time so that the generated Makefiles that Zope uses will have them set, let me know. I tried
setting LDFLAGS as an environment variable before running configure, but it uses $LDFLAGS at the beggining of configuration
to test the C compilor, and since the '-undefined suppress' option is a linker option, an error is returned and configuration stops.
Or, any suggestions or advice on how to set Python up to resolve multiple definitions (due to multiple cPickle.so files)
automatically will be greatly appreciated. The cPickle in Zope is newer than the one in Python 1.5.2, I believe.
On my to-do list now (but pretty far down the list, sadly) is to make this into a MacOS X Server package similar to other Zope
releases (meaning, the Zope package will be a full release with its own Python). The timeframe of this is uncertain.
Last modified 01/29/2000 by Jeffrey P Shell
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jshell/buildingZopeOnMacOSXServer?pp=1 (2 of 2) [05/07/2000 11:12:06]
Building dcSybase on Linux
How-To: Building dcSybase on Linux
Created by anthony. Last modified on 1999/12/14.
To build the Digicool Sybase adaptor on linux,
you'll need to get the current Sybase common
rpm from www.sybase.com. Currently this is
version 11.9.2.
The freely available 11.0.3 will _not_ work, neither
will the current FreeTDS (I tested 0.47). Further, if
you do build against 11.9.2, but use the 11.0.3 shared
libraries, your life will be full of visits from Mr. J.
Random Segfault. Be _very_ careful of this, as it _SUCKS_
to figure it out. If you can, purge all traces of 11.0.3
from your system.
To build and use the adaptor, you'll _only_ need
the sybase-common-11.9.2-1.i386.rpm package
(about 5M). Note that by default this installs
into /opt/sybase-11.9.2, so set the SYBASE
environment variable to this.
Other than that, it just builds and works. It even
works when run against SQL Server 6.5.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anthony/dcSybase-on-linux?pp=1 [05/07/2000 11:12:08]
CSS & Zope howto
How-To: CSS & Zope howto
Created by mindlace. Last modified on 2000/05/11.
skip the intro or, if you're really impatient, go straight to the example
Intro
why styles?
Cascading Style Sheets are a standards-based way of seperating style from content. This is good for a number of reasons:
●
●
●
●
it reduces the size of your HTML by eliminating annoying tags like <FONT> and <CENTER> as well as eliminating
declarations in tags like BGCOLOR, VALIGN, and WIDTH
it will allow the viewer to specify several different views (that you provide) of a page client-side. Mozilla, which will be
the basis for Communicator 5, allows you to do this, and IE5 might(?). Neither of the 4.x browsers let you select style
sheets.
it will allow users to override your view of the page inasmuch as they need to- for example, someone with bad eyesight
could making fonts bigger, or change colors- without affecting the rest of layout. The support for this is the same as the
different stylesheets, above.
With CSS2, you can specify different stylesheets for different media- screen and print are supported by IE5, and I
believe for Mozilla, as well. Other media like braille and emboss let you provide support for blind users. Finally,
tv,handheld, tty and audio will let you tailor your view for alternate rendering devices. The support for these is
not fantastic, currently.
use the recommendation
I have downloaded the spec for CSS2 and printed it out. I strongly recommend you use the most recent spec when coding- you'd
be surprised at how much CSS2 you can get away with. There's a section in the Recommendation that details where CSS2 changes
things from CSS1. They're all very minor, and you shouldn't have to worry about it.
note NS4 can't do CSS2 to save its life. However, NS5 covers a whole lot of ground, and should be out in beta in December. I
recommend coding for mozilla- you can download binaries of the current milestone release and using it as a reference for what
you can get away with. No Netscape user in their right mind will stay with netscape 4.x when the big Lizard is availiable. My rule
of thumb is: looks good in Mozilla, looks close in IE, is readable in NS4.x.
The techniques I describe below can be used in all 4.x browsers
Global Styles
The site usually has a global stylesheet- a look that is common to the whole site. Here I'll describe how to use the <LINK> tag to
define a stylesheet.
I use this method because the browser only downloads a given linked stylesheet once for your whole site. Every byte counts.
Note, however, because of this very fact, any DTML you use will only get interpreted once, when the global stylesheet is
downloaded.
the <LINK> tag
In the <HEAD> of the HTML document, probably in your standard_html_header, you need to add the following line:
<link HREF="<dtml-var "global_css.absolute_url()">" rel="stylesheet" type="text/css">
where global_css is a DTML method in the root folder.
http://www.zope.org/Members/mindlace/css_zope?pp=1 (1 of 4) [05/07/2000 11:12:13]
CSS & Zope howto
The global_css method
In this method should go the styles you want to have across your site. Don't worry if you need to override them for a few
exceptions- I'll explain how in the next section.
IMPORTANT: You must have the following line as the first line of your method:
<dtml-call "RESPONSE.setHeader('Content-Type', 'text/css')">
The next lines are your styles.
Alternate Stylesheets
If you want to specify alternate stylesheets, you may add additional lines, changing the name of the method and the relation:
rel="alternate stylesheet". Alternate stylesheets should also have a title attribute.
You may specify the preferred stylesheet for a given page in a zopeish manner. In the <HEAD> of your
standard_html_header:
<dtml-if preferred><META http-equiv="Default-Style" content="< dtml-var preferred>">
Where preferred is a string property of the folder or document. This assumes that you've already defined the different stylesheets
as mention above.
see 14.3 External Style Sheets of the HTML4.0 Spec for more information.
note I haven't tested this and I don't know how well browsers support it. tell me if it works. I use the <STYLE>tag for individual
pages...
Local Styles
The whole point of this rigamarole is to let the styles change for different sections, if need be. This brings us to local_css.
Back to our old friend, standard_html_header:
adding local styles dynamically
Just below the <LINK> line in standard_html_header, you need to add a line:
<dtml-if local_css><dtml-var local_css></dtml-if>
This says that if there's a method in my namespace called local_css, insert it here.
the local_css method
The local_css file should be a method in a folder where the content (and any content below it) has special formatting
requirements.
The method needs to start with <STYLE type="text/css"> and the styles themselves may be commented out to keep older
browsers that don't recognize <STYLE> from messing things up. It needs to end with </STYLE>
the cascade
Any declarations in the <STYLE> tag override declarations in a linked stylesheet. So if <P> in your linked stylesheet is italic
verdana, and in your local_css you declare P { font-family: garamond; } then the <P> text will be italic garamond.
http://www.zope.org/Members/mindlace/css_zope?pp=1 (2 of 4) [05/07/2000 11:12:13]
CSS & Zope howto
more levels
Ah, so Global and Local styles isn't enough for you, eh?
In any given local_css you can include DTML to make it more dynamic. As an example, inside a local_css file:
<dtml-if here_css><dtml-var here_css></dtml-if>
Now you can have another method, here_css, that contains only CSS declarations in a sub-sub folder.
Example
We're making the Spanish Inquisition website. I want to have a table that is my navbar. Each cell will represent an area of the site,
and I want that cell's color to be different when I am at the "root" for that area, and yet another color when it's in a subfolder of
each area (to visually represent that you can go back to the "root" of the area).
standard_html_header
This sets up the header, defines the stylesheets, and specifies the generic navbar.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40">
<html>
<head><title><dtml-var title_or_id></title>
<link HREF="<dtml-var "global_css.absolute_url()">" rel="stylesheet" type="text/css">
<dtml-if local_css><dtml-var local_css></dtml-if>
</head>
<body>
<h1>Spanish Inquision</h1>
<table class="navbar">
<colgroup span="5" width="5*" >
<tr>
<td ID="nv_about"><A HREF="/about">About Us</a></td>
<td ID="nv_database"><A HREF="/database">Database</a></td>
<td ID="nv_forum"><A HREF="/forum">Forum</a></td>
<td ID="nv_news"><A HREF="/news">News</a></td>
<td ID="nv_pub"><A HREF="/publications">Publications</a></td>
</tr>
</table>
global_css
<dtml-call "RESPONSE.setHeader('Content-Type', 'text/css')">
A { font-family: verdana, arial, sans-serif;}
A:hover { text-decoration: none; }
A:active { color: red; }
A:link { color: green; }
A:visited { color: blue; }
/*in CSS1, link, visited, and active were mutually exclusive.
they're not.*/
In CSS2,
BODY { background-color: white; }
DIV.content {
margin: 10px;
font-family: georgia, garamond, serif;
}
h1, h2, h3, h4, h5 { color: #663333; font-family: Verdana, Arial, sans-serif;}
TABLE.navbar TD { background-color: black; text-align: center; }
/*here we say that the default color of the cells is black.*/
http://www.zope.org/Members/mindlace/css_zope?pp=1 (3 of 4) [05/07/2000 11:12:13]
CSS & Zope howto
local_css
We'll put this local_css method in the /about folder.
<style type="text/css">
<!-- /* I stick my tounge at you, you filthy styleless browsers! */
<dtml-if here_css>
<dtml-var here_css>
<dtml-else>#nv_about { background-color: grey; }
</dtml-if>
/* here we said that in about, the background color of the about cell should be
grey- unless it finds a here_css */
/* here's some other stuff specific to about: */
.defined {
margin-right: 5px;
font-family: verdana, arial, sans-serif;
font-weight: bold;
color: #633;
}
p.subhead {
font-family: verdana, arial, sans-serif;
}
LI { margin-bottom: 3px; }
/* your mother was a hampster and your father smelt of elderberries! */-->
</style>
here_css
here_css method will go into the /about/no_one_expects folder. It's quite short.
#nv_about { background_color: white; }
The Spanish Inquisition now has a site where the navbar is black, the areas get hilighted in grey when you enter them, and in
subfolders of areas, the cell turns white, letting you know that the link will take you back to the top of the area.
wrapup
With CSS and zope, you can make your site's look change dynamically. A fuller implementation of the example would allow users
to pick which stylesheet was their default for a site, all using 100% standard-compliant code.
With CSS2 and DTML the possibilities are even more dramatic- Changing position from absolute to fixed to make a
toolbar "float" when the user selects it, for example.
While you can do some manipulations with CSS on the client side using ECMAScript, with Zope you can dynamically change
any CSS variable to your heart's content.
Questions? Errors? email me
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mindlace/css_zope?pp=1 (4 of 4) [05/07/2000 11:12:13]
Change TinyTable values using DTML
How-To: Change TinyTable values using DTML
Created by cguardia. Last modified on 2000/03/07.
The TinyTable product is a good tool if you need simple table lookup functionality in your Zope apps. However, it's usefulness is
limited by the fact that it doesn't support writing single values to the tables you create.
This DTML method allows the user to change individual table values with a single method call. Right now it only works for tables
which use all-string values.
The TinyTable product has a method for changing the complete table in one step (tinytable.manage_editData). The setTinyValue
method changes the value of a selected table "cell" and then makes a complete string copy of the modified table before feeding it
to the manage_editData method:
<dtml-call "REQUEST.set('tableData','')">
<dtml-in "objectValues(['TinyTable'])">
<dtml-if "table.id==id">
<dtml-call "REQUEST.set('columnText',cols_text())">
<dtml-call "REQUEST.set('columnList',_.string.split(columnText,' '))">
</dtml-if>
</dtml-in>
<dtml-in table>
<dtml-if "_[lookupColumn]==rowIDValue">
<dtml-in columnList>
<dtml-if "_['sequence-item']==editColumn">
<dtml-call "REQUEST.set('tableData',tableData+_.chr(34)+newValue+_.chr(34))">
<dtml-if sequence-end>
<dtml-else>
<dtml-call "REQUEST.set('tableData',tableData+',')">
</dtml-if>
<dtml-else>
<dtml-call "REQUEST.set('value',_['sequence-item'])">
<dtml-call "REQUEST.set('tableData',tableData+_.chr(34)+_[value]+_.chr(34))">
<dtml-if sequence-end>
<dtml-else>
<dtml-call "REQUEST.set('tableData',tableData+',')">
</dtml-if>
</dtml-if>
</dtml-in>
<dtml-else>
<dtml-in columnList>
<dtml-call "REQUEST.set('value',_['sequence-item'])">
<dtml-call "REQUEST.set('tableData',tableData+_.chr(34)+_[value]+_.chr(34))">
<dtml-if sequence-end>
<dtml-else>
<dtml-call "REQUEST.set('tableData',tableData+',')">
</dtml-if>
</dtml-in>
</dtml-if>
<dtml-call "REQUEST.set('tableData',tableData+'\n')">
</dtml-in>
<dtml-call "table.manage_editData(tableData)">
This method makes use of a number of variables which must be passed to it as parameters. Here is how to use it from a DTML
method or document:
<dtml-var "setTinyValue(_.None,_,
table=someTable,
lookupColumn='columnName',
rowIDValue='rowID',
editColumn='columnName',
newValue='changed'
)">
The parameters used are:
http://www.zope.org/Members/cguardia/changeTinyTable?pp=1 (1 of 2) [05/07/2000 11:12:15]
Change TinyTable values using DTML
table. A TinyTable object.
lookupColumn. The string name of the index column.
rowIDValue. The string value to find in the lookupColumn. This will give us the row that is going to be changed.
editColumn. The string name of the column that is going to be changed.
newValue. The string value to set the column to.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cguardia/changeTinyTable?pp=1 (2 of 2) [05/07/2000 11:12:15]
Change the port Zope runs on?
How-To: Change the port Zope runs on?
Created by Pam. Last modified on 1999/09/17.
A common question asked on the mailing lists -- How do I change the port that Zope runs on?
With the current installation of Zope 2.0.x, the port given to Zope is 8080.
Edit your start script and specify a port, as in:
exec /usr/local/bin/python1.5.2/projects/users/jim/z2.py -w80 "$@"
Where -w80 "$@" changes your port to 80
Run z2.py with a -h switch to see the various options you can specify.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Pam/ChangePorts?pp=1 [05/07/2000 11:12:17]
Changing Base Classes for a ZClass
How-To: Changing Base Classes for a ZClass
Created by AlexR. Last modified on 2000/04/06.
Version 0.2
This Howto explains how to add or delete a base class for a ZClass. It is based on information collected from the Zope list. Feel
free to send your feedback.
Version history
●
26th March 2000 - version 0.1 - First draft
●
6th April 2000 - version 0.2 - Added a note based on feedback from Shawn Grant.
What are you talking about?
When creating a ZClass in Zope you select base classes that will be "subclassed". This means that the new ZClass will inherit
these classes, with their properties and methods. You can use this feature to derive your own custom objects from standard Zope
objects such as Folder, Image, DTMLDocument, etc. For more information about ZClasses, see the first chapter in the Zope
Developer's Guide and the Howtos.
For instance, you may have a ZClass called MyClass in a product called MyProduct. To check which base classes are used in
MyClass, take a look at the Basic tab in /Control Panel/Products/MyProduct/MyClass.
Once a ZClass is created, you cannot easily add or delete its base classes to change its features. (You cannot do it in Zope 2.16 and
in previous versions. I hope somebody will provide a patch to add this feature to Zope.)
OK, how can I do it then?
The solution described below is based on a posting from Michel Pelletier and on code written by Andreas Kostyrka. (See this
message).
Caveat - This solution is unsupported by anyone. Make sure you backup your database before using it. When last tested with
Zope 2.1.5 it worked fine but as usual YMMV.
Note also that so far this solution only works for ZClasses located at 1st level in a product, eg. MyProduct/MyZClass. It
doesn't work for ZClasses nested in another ZClass. (If you know a workaround, please let me know.)
Here is information straight from the original message:
Well, install the attached zc.py in your Externals directory. Add
external methods zc_copybases and zc_show (mapped to zc.py/copybases
and zc.py/show) somewhere in your ZOPE. For demonstration I assume
it's in your root folder, and your zope server is called
http://myzope/.
Well, then use your browser to visit the following URLs (this does
more or less exactly what the above python magic does):
http://myzope/zc_show?zclass=YourZClass&product=YourProduct
http://myzope/zc_show?zclass=TempZClass&product=YourProduct
This shows you the baseclasses of the two Z classes.
http://myzope/zc_copybases?
srczclass=TempZClass&srcproduct=YourProduct&
dstzclass=YourZClass&dstproduct=YourProduct
(This should be a one line URL.)
This does the voodoo magic above. And completly without shutting
down your ZOPE, and it does even comment the transaction nicely.
http://www.zope.org/Members/AlexR/ChangingBaseClasses?pp=1 (1 of 2) [05/07/2000 11:12:27]
Changing Base Classes for a ZClass
Changing base classes from a form
I added a form for easier use and regrouped the files in one zip file. To use it:
1. Unpack the zip file. Copy Zc.py to your ZopeDir/Extensions directory. (Create this directory if needed).
2. Copy copybases.zexp to your ZopeDir/import directory. (Create this directory too if needed).
3. Import copybases.zexp somewhere your Zope database, at root level for instance. This will create the copybases
directory. Do not import it in the Products hierarchy because this isn't a standalone product.
4. To view a ZClass base classes, open http://yourzope/yourpath/copybases/formView in your browser
and use the form.
5. To copy the base classes between two ZClasses, use http://yourzope/yourpath/copybases/formCopy.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/ChangingBaseClasses?pp=1 (2 of 2) [05/07/2000 11:12:27]
Changing between DTML Methods and Documents.
How-To: Changing between DTML Methods and
Documents.
Created by andreas. Last modified on 1999/09/15.
The Problem
The ZOPE interface doesn't support any way to easily change the type of some object. This is somehow understandable, because
in most cases it just doesn't make sense. But in the case of DTML Methods and Documents, it would make sense.
Tricking ZOPE
The following steps allow to change the type of a DTML method or document. They do not rely on support by ZOPE, they just
work, because the Edit interfaces between documents and methods are compatible.
1. In the Folder content view, open the object you want to change into a new window (middle button in netscape).
2. In the orginal folder content view, delete the object.
3. Create it from scratch in the type you want to have it.
4. In the new window, created in the first step, click on Change.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/andreas/changing_type?pp=1 [05/07/2000 11:12:29]
Changing host names dynamically with Apache
How-To: Changing host names dynamically with
Apache
Created by dparker. Last modified on 1999/10/29.
A single Zope instance via Zope.cgi under Apache can be made to render pages who's contents appear to be from different hosts
by altering the SERVER_URL environment variable. Situations where this capability is essential are:
●
Apache virtual hosts which share the same Zope instance.
●
Tunneling from one network to another via an HTTP proxy (e.g. from the internet to an intranet).
The trouble with trying to do these things without any "tricks" is that Zope's ZPublisher utilizes its notion of the server it's running
on very extensively; if you don't takes steps to alter Zope's notion of its host, you'll end up with pages that refer to other hosts (and
possibly, hosts that don't exist on the network the user is coming from).
You can remedy this by setting the SERVER_URL environment variable in Apache appropriately just prior to invoking Zope.cgi.
Here are two ways to do it:
1) For Apache virtual hosts that need to point to a single Zope instance, put
SetEnv SERVER_URL http://hostname.org
into the various VirtualHost sections you've defined at a point prior to where Zope.cgi is called (e.g. before a RewriteRule that
runs Zope.cgi). hostname.org should be whatever host you want Zope to pretend it is for the virtual host in question.
2) For Apache+Zope configurations that need to be accessible on both sides of a a tunneling proxy configuration, put
SetEnvIf Remote_Addr "A\.B\.C\.D" SERVER_URL=http://hostname.org
into your Apache configuration someplace before Zope.cgi is called. A\.B\.C\.D is the IP address of the near side of your proxy
server (with periods made literal, 'cause SetEnvIf uses regular expressions), and hostname.org is the name that people on the far
side see your proxy server as. Note that this isn't specific to a VirtualHost configuration, though it'll work in that context as well
(just put the SetEnvIf inside whatever virtual hosts you've defined).
Questions/suggestions?
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/dparker/dynamichostnames?pp=1 [05/07/2000 11:12:31]
Choosing to store data in SQL vs ZODB
How-To: Choosing to store data in SQL vs ZODB
Created by anthony. Last modified on 2000/01/18.
A reasonably common question that comes up on the Zope lists is "when should I store data in an SQL database instead of Zope?"
This how-to lists some of the things to think about when making this decision.
This document should not take the place of your proper design process, but will hopefully give you a few other things to consider.
feedback, suggestions, to anthony@interlink.com.au
Will the data be frequently changing?
If the data is going to be frequently changing, then it's not a good idea to put it in the ZODB. Every time the data changes, a new
record will be appended to the Data.fs file - this could become a problem very quickly.
Phillip Eby noted that this is actually a limitation of the standard Zope FileStorage implementation of a backend data store. Other
packages, such as Ty Sarna's BerkeleyStorage do not have this limitation (although they don't provide the Undo and Version
support, as a consequence. (but then, neither does an SQL database))
Does the data need to be accessed by other
tools?
Do you have a requirement that other tools need to be able to access the data (e.g. various reporting tools). Unless you're willing to
rewrite these tools to all work off the ZODB, again, SQL will be the way to go.
How large are the objects?
Largish blobs of data might be better in the ZODB than in an SQL database. Depending on the database, you may find that the
size limitations of objects that you can insert will constrain you - for example, my reading of the PostgreSQL docs is that nothing
larger than 32k can be inserted (although this is being addressed)
How does your data fit into an SQL database?
Tres Seaver writes:
Another point to consider is how well the data "fits" the storage model. As an example, in a former life I built a system storing
"standard operating procedures" into an RDBMS. Each procedure had one or more steps, each of which might be recursively
decomposed into substeps -- they also might reference diagrams, illustrations, and multimedia content.
Although I managed to make this work in SQL, the impedance mismatch was very high, compared with doing it in a hierarchical
database like ZODB. RDBMS's excel at allowing "slice and dice" queries, at the expense of requiring expensive joins to
implement parent-child links; hierarchical DB's optimize the parent-child relationships, making "cross-family" queries difficult or
even infeasible.
This brings up a good point - if you are actually storing objects, with subobjects, methods, attributes, and the like, you may find
that the work to get the objects into the database in a useful form is high, for not a lot of gain. You might also want to look at some
sort of hybrid solution, where some of the attributes or data goes in the DB while other data goes into the ZODB.
Do you need full text indexing of the data?
Most RDBMSs do not provide any way to full text index data that is stored in them - if this is a requirement for you, consider
using ZODB objects together with ZCatalog to allow this. (noted by Butch Landingin).
Some more notes on indexing and the like, loosely structured...
ZCatalogs are really quite powerful little things - you can do a lot with them, including many things where your first thought
http://www.zope.org/Members/anthony/sql_vs_ZODB?pp=1 (1 of 2) [05/07/2000 11:12:34]
Choosing to store data in SQL vs ZODB
would be to use an SQL database. Just be aware of some of the limitations. The primary one (for me) is the lack of a proper query
language. Sometimes expressing what you want to do in a call to ZCatalog.searchResults() can be a little tricky. ZCatalogs also
lack a lot of the bells and whistles you might be used to in a traditional RDBMS, such as referential integrity, decent schema
management, and the like.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anthony/sql_vs_ZODB?pp=1 (2 of 2) [05/07/2000 11:12:34]
Client-Side Script Includes
How-To: Client-Side Script Includes
Created by Caseman. Last modified on 2000/06/05.
A simple way to organize client-side scripts
Introduction
Most everything you might need to do in a Zope application can be done on the server side with DTML. You may find an
occasion, however when a feature is better implemented on the client side using a scripting language like JavaScript.
Scripting can be especially useful on forms. They enable you to make more intelligent forms that can dynamically change without
refreshing the entire page. They can also be used to "enhance" the presentation of your pages, but beware of relying on scripting to
get your point across.
Scripting the Zope way
Since Zope can serve up anything you like, scripts can obviously just be added to a DTML Document or Method without any real
extra effort. What I have concocted here is an alternative way leveraging the power of Zope to segregate your scripts from your
content and DTML. This will also help you keep your scripts organized and modular so they can be distributed throughout your
site as needed.
The first step is to create a folder to contain your scripts. In my example I have created a folder called java_script in my root
folder. Each script will be in a DTML Method in this folder. It works best if you create separate methods for each specific
function. You could also group similar script functions into script "library" methods. The script methods do not need to contain
any tags, although you can use DTML in your script methods to do cool things like fill arrays from a database using a Z SQL
Method.
Next, we modify our friend and ally standard_html_header so that it inserts scripts for any documents that request them.
Simply insert the DTML code below within the <HEAD> section of your standard_html_header.
<dtml-if scripts>
<dtml-comment>
Include Java Scripts Specified by Scripts Property
</dtml-comment>
<META HTTP-EQUIV="Content-Script-Type" CONTENT="text/javascript">
<SCRIPT language="JavaScript"><!-<dtml-with java_script>
<dtml-in scripts>
//
// Script of <dtml-var sequence-item>
//
<dtml-var "_.getitem(_['sequence-item'],1)">
</dtml-in>
</dtml-with>
//--></SCRIPT>
</dtml-if scripts>
The above example assumes you are using JavaScript but with minor modifications, it would work with any scripting language.
Example: A JavaScript Enabled Document
First off, we need a a JavaScript to play with for our example. Below is a very simple JavaScript that filters "$" and "," from a
field so that you can process the inputted value as a number. Copy this code into a DTML Method in your java_script folder
called money_filter.
function money_filter(str) {
re = /\$|\,/g;
// remove "$" and ","
return str.replace(re, "");
}
http://www.zope.org/Members/Caseman/client_script_howto?pp=1 (1 of 2) [05/07/2000 11:12:37]
Client-Side Script Includes
Now somewhere else in your site, create a DTML Document containing the following:
<dtml-var standard_html_header>
<FORM>
Enter a dollar amount including the "$":
<INPUT type=text name=amount value="$1,000.00">
<INPUT type=button value="Fix It!"
onClick="this.form.amount.value=money_filter(this.form.amount.value)">
</FORM>
<dtml-var standard_html_footer>
Now, add a lines property to your DTML Document called scripts with a value money_filter. Save the changes and when
you view the document, and voila, the appropriate script is inserted for you from your java_script folder. View the source to
see exactly what's going on.
Another Method
The method described above inserts the actual scripts into the content of your documents when they are rendered by Zope. Scripts
can also be passed by reference if you prefer. This means the script text will not appear directly in the source of the document. To
have Zope insert scripts this way, insert this into your standard_html_header instead:
<dtml-if scripts>
<dtml-comment>
Include Java Scripts Specified by Scripts Property
</dtml-comment>
<META HTTP-EQUIV="Content-Script-Type" CONTENT="text/javascript">
<dtml-with java_script>
<dtml-in scripts>
<SCRIPT language="JavaScript"
src="<dtml-var BASE0>/java_script/<dtml-var
sequence-item>">
</SCRIPT>
</dtml-in>
</dtml-with>
</dtml-if scripts>
There are advantages and disadvantages to this method. One disadvantage is that the browser must make a separate request for
each script the first time a page is opened. This is most costly if you have many small script files included. You may also find that
you must insert the following line at the top of your script methods for it to work for all browsers:
<dtml-call "RESPONSE.setHeader('Content-Type', 'application/x-javascript')">
A potential advantage to using this method is that the browser can cache the scripts separately from the page contents, so that
pages sharing the same scripts load faster and place less burden on the server. Both ways work equally well, so you can decide for
yourself which is better.
Conclusion
The power inherent in Zope makes this so very simple. We are leveraging the powers of aquisition to make this work. The beauty
of this method is that it is invisible to any documents that do not contain a scripts property. So, nothing is inserted in the
header of a document that does not specifically request a script. But, you can add scripts to any document at will by adding a
scripts property to it with the ids of one or more script methods in your script folder. Also, you can assign a scripts property to
a folder so that all the documents in the folder automatically aquire the scripts!
If you are looking for more example JavaScripts of all kinds, check out The JavaScript Source .
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Caseman/client_script_howto?pp=1 (2 of 2) [05/07/2000 11:12:37]
Compiling ZOracleDA on Linux
How-To: Compiling ZOracleDA on Linux
Created by anthony. Last modified on 1999/09/30.
The standard ZOracle DA includes a couple of
'Setup' files in the src/ directory, but does
not include one for Oracle on Linux. This Setup
file works for me (Oracle 8.0.5, Redhat Linux
5.2 and 6.0)
# --- snip here ----*shared*
ORACLE_INCLUDES=$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public
-I$(ORACLE_HOME)/network/public -I$(ORACLE_HOME)/plsql/public
ORACLE_L=-lnetv2 -lnttcp -lnetwork -lncr -lnetv2 -lnttcp -lnetwork -lclient -lvsn -lcommon -lgeneric -lmm -lnlsrtl3 -lcore4
-lnlsrtl3 -lcore4 -lnlsrtl3 -lnetv2 -lnttcp -lnetwork -lncr -lnetv2 -lnttcp -lnetwork -lclient -lvsn -lcommon -lgeneric -lepc -lnlsrtl3
-lcore4 -lnlsrtl3 -lcore4 -lnlsrtl3 -lclient -lvsn -lcommon -lgeneric -lnlsrtl3 -lcore4 -lnlsrtl3 -lcore4 -lnlsrtl3 -lsql -lm -lm
ORACLE_LIBS=$(ORACLE_HOME)/lib/ -L$(ORACLE_HOME)/rdbms/lib $(ORACLE_HOME)/rdbms/lib/defopt.o
$(ORACLE_HOME)/lib/sscoreed.o $(ORACLE_HOME)/lib/nautab.o $(ORACLE_HOME)/lib/naeet.o
$(ORACLE_HOME)/lib/naect.o $(ORACLE_HOME)/lib/naedhs.o $(ORACLE_L) $(ORACLE_L)
oci_ oci_.c -I$(ORACLE_INCLUDES) -L$(ORACLE_LIBS) -DDCORACLE8
Buffer Buffer.c
#--- end Setup file --© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anthony/DCOracle-on-linux?pp=1 [05/07/2000 11:12:40]
Type_Document_Title_Here
How-To: Connect to Zope via FTP
Created by mblewett. Last modified on 2000/06/29.
Zope has a built-in FTP server that is turned on by default in the normal installation process.
To connect to your FTP server you will need an FTP client (obviously) and the following information:
hostname: this is host name of your zope machine. eg hostname.edu.au
(don't add any "http:" or "ftp" prefixes on the front of your host name)
Port Number: The Zope FTP is on Remote Port 8021 by default (unless you specified differently in your installation process)
(most FTP programs are set to use Port 21 by default so you may need to tinker with the "Options" or "Advanced" tabs of your
FTP programs.)
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mblewett/ZopeFTP?pp=1 [05/07/2000 11:12:41]
Control Panel Distribution Trickiness
How-To: Control Panel Distribution Trickiness
Created by Amos. Last modified on 1999/09/15.
When I make a Product distribution in the Control Panel and then install the Product in another Zope, the Product now appears as
a closed box not an open box. What's going on?
The thing to understand is that creating a Product in Control Panel, is not the same thing as installing a Product distribution. A
Product distribution is a closed type of Product that cannot easily be redistributed and probably shouldn't be customized very
much. The job of a Product distribution is to distribute Control Panel objects to people in a simple way which provides limited
customizability. Often you will later send out an updated version of the distribution which will overwrite the old version when it is
installed.
If you want to move Control Panel Products between Zopes so that the Product is fully modifiable and equivalent to the original
you should use the standard Zope import/export facility. This is exactly the same thing you would do to move any other type of
Zope object back and forth between Zopes.
You should be aware that Zope does not have any kind of diff facility so that if you move a Control Panel Product SuperDoc
object back and forth between Zopes you will overwrite any existing Control Panel Product SuperDoc when you import the new
version. Be careful.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Amos/ControlPanelDistribution?pp=1 [05/07/2000 11:12:43]
Converting ZODB2 databases and export files to ZODB3
How-To: Converting ZODB2 databases and export
files to ZODB3
Created by Brian. Last modified on 1999/09/15.
Zope 2 uses the ZODB3 database format, and will not use older ZODB2 databases (.bbb files) or ZODB2 export files (.bbe files).
To use databases or export files created by versions of Zope prior to 2.0, you need to first convert the file to ZODB3 format. There
is a script named "bbb.py" in the "utilities" directory of your Zope installation which you can use to convert databases and export
files to ZODB3 format.
For complete usage information, use Python to run the bbb.py script with no arguments. This will print usage documentation for
the script.
Note - The following examples assume that the "utilities" directory of your Zope installation is your current working directory,
and that the files you are converting have been copied to that directory.
To convert a ZODB2 database named "Data.bbb" to a new file "Data.fs" in the new ZODB3 format, you would use the command
line:
python1.5.2 bbb.py -f Data.fs Data.bbb
To convert a ZODB2 export file named "export.bbe" to a new file "export_zodb3.bbe" in the new ZODB3 format, you would use
the command line:
python1.5.2 bbb.py -x -f export_zodb3.bbe export.bbe
Note that for export files, you must use the -x argument to let the script know that you are converting an export file and not an
entire database.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/ZODB2ToZODB3?pp=1 [05/07/2000 11:12:46]
Create modifiable local variables
How-To: Create modifiable local variables
Created by Duncan. Last modified on 2000/06/08.
DTML programmers generally have a choice between global variables (using REQUEST.set to update them), or variables created
using dtml-let which cannot be reassigned after their initial creation. Sometimes it can be useful to have general purpose
modifiable local variables, and this HowTo tells you how to do it.
Step 1 is to write a method in python. This can be done as a Python Method, or as an External Method.
If you have PythonMethods installed, add the following PythonMethod at an appropriate place:
Id: locals
Title: **args
Parameter list: **args
Body:
dict = {}
def set(private__dict=dict, **args):
if args.has_key('set'):
raise 'KeyError', 'Cannot assign to set'
private__dict.update(args)
dict.update(args)
dict.update({'set': set})
return dict
If you are using an External Method, create a file locals.py in the Extensions directory with the following contents:
def locals(**args):
dict = {}
def set(__dict=dict, **args):
if args.has_key('set'):
raise KeyError, 'set'
__dict.update(args)
dict.update(args)
dict['set'] = set
return dict
then add an External Method that with id, function name and filename all locals.
Now you are all set to use local variables, here is some sample DTML that creates some variables and plays around with them:
<dtml-with "locals(a=5, b=['Hello'])" mapping>
a=<dtml-var a missing="unset"><br>
b=<dtml-var b missing="unset"><br>
c=<dtml-var c missing="unset"><br>
<dtml-call "set(c=3, a=4)">
<dtml-call "b.append('Zope')">
a=<dtml-var a missing="unset"><br>
b=<dtml-var b missing="unset"><br>
c=<dtml-var c missing="unset"><br>
<dtml-call "set(c='99', a=44)">
<dtml-call "b.append('World')">
a=<dtml-var a missing="unset"><br>
b=<dtml-var b missing="unset"><br>
c=<dtml-var c missing="unset"><br>
</dtml-with>
The output from this should look something like:
a=5
b=['Hello']
c=unset
a=4
b=['Hello', 'Zope']
c=3
http://www.zope.org/Members/Duncan/LocalVars?pp=1 (1 of 2) [05/07/2000 11:12:48]
Create modifiable local variables
a=44
b=['Hello', 'Zope', 'World']
c=99
Points to watch
You must use the mapping option on the dtml-with. (It is non trivial to do a version of this code that does not require the mapping
option.)
Initialise as many variables as you wish in the call to locals.
The set function may then be used to update the existing variables or create new ones as desired.
The only restriction is that you cannot create a variable called set.
If your code after doing this starts to resemble a plate of spaghetti, then stop what you are doing and re-write in Python instead.
Be careful if you try to nest locals blocks. The set function in the innermost one will hide the outer set functions, so you cannot
modify variables in outer blocks, only hide them.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Duncan/LocalVars?pp=1 (2 of 2) [05/07/2000 11:12:48]
Creating Smart Images for Dumb Servers
How-To: Creating Smart Images for Dumb
Servers
Created by jpenny. Last modified on 2000/03/08.
Let me set up a scenario. You already have a website. It is served by a popular web server, let us say, just for the sake of
argument, apache. Now, apache does pretty well with fairly static content. On an important page, (like the home page), you want
to display an "Announcements" image, linked appropriately, but only when you actually have current announcements. How can
you do this?
This HOWTO shows one method of using Zope to generate a smart image that appears to Apache to be just another ordinary
image. That is, we are going to fake Apache out so that it displays a mutable image from static content!
A solution
Create a database
I can't really tell you how to do this, it depends on what database you are using. This is something that is probably not going to be
varying too rapidly, so you could use the Zope database, but I am going to show the solution using an external SQL database. My
database has date fields born_on, use_by, url, and title. Born_on and use_by are used to decide how the smart image should
appear. Url and title are used to generate the announcement list (if there are any!)
The Apache Side
The static portion of the site needs to have what it thinks is a reference to a static image. In my case this looks like:
<A HREF=http://www.mycompany.com:8080/announcements>
<IMG SRC=http://www.mycompany.com:8080/announcements/new.gif
BORDER=0 ALT="">
</a>
Now, this looks like 100% unadulterated static content, as far as Apache is concerned. From Apache's point of view, there is
nothing out of the ordinary going on, at all!
The Zope Side--ZSQL Methods
We need a SQL method that will be used to decide which form the smart image, refered to by Apache as new.gif, will take.
I simply defined a ZSQL method which has id 'have_announcements' and Query Template
select count(*)
from announcements
where
born_on <= 'now'::datetime and
'now'::datetime <= use_by
Incarnating the image
I need to have logic in the selection of the form that the image will take. DTML Methods are the easiest way of introducing logic
into a Zope site! In the Apache side, I said that it would be at announcement/new.gif. So I had better create a folder
announcements, and in it place a DTML method called new.gif.
Note, Zope conventions are that one does not normally use periods in object names. Doing so might confuse acquisition and
generally make object reference difficult. We are not going to use deep heirarchies here, so these problems can be glossed over.
Zope does allow periods in object names!
The contents of my method are:
http://www.zope.org/Members/jpenny/smart_images?pp=1 (1 of 2) [05/07/2000 11:12:52]
Creating Smart Images for Dumb Servers
<dtml-in have_announcements>
<dtml-if "count > 0">
<dtml-return "new">
<dtml-else>
<dtml-return "no">
</dtml-if>
</dtml-in>
Pretty simple, eh?
More Details
<dtml-return> is one of the less used elements of dtml. Here, since we are double-quoted, we are asking it to return either the
value of object new, or no, depending on having current announcements.
The obvious idea is to make new and no Zope Images, import the image and be done! Unfortunately, this won't work.
The problem is that the evaluation of a Zope Image returns something like
<IMG SRC=... height=... width=...>
That is, a HTML reference to the image is returned, rather than the image itself.
To get the contents of the image, we can define new and no to be Zope Files, import the image into the File, and everything will
work.
Wrapping Up
This gives us a more dynamic website, while allowing static portions to remain largely untouched. Of course, we still have to
create the index_html for the link to http://www.mycompany.com:8080/announcements. This is also easy. My index_html looks
like:
<dtml-var standard_html_header>
<dtml-in have_announcements>
<table>
<TR><TH>Announcement<TH>Release Date
<dtml-in fetch_fresh_announcements start=query_start>
<TR><TD>
<A HREF=<dtml-var "url">><dtml-var "title"></a>
<TD>
<dtml-var "born_on">
</dtml-in>
</table>
</dtml-in>
fetch_fresh_announcements is another ZSQL method that has Query Template:
select born_on, url, title from announcements
where
born_on <= 'now'::datetime and
'now'::datetime <= use_by
order by born_on desc
Looking at this, there is really nothing hard in any step. It is just a matter of putting all of the easy, little components together to do
the job at hand.
If you want to see this in action, (and we have announcements!), go to http://www.universal-fasteners.com. Look for the jittery
"New" graphic. You might want to order a couple million buttons after the visit, so be careful!
Comments and questions to jpenny@universal-fasteners.com. Thanks for your attention, you may now resume your regular duties.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jpenny/smart_images?pp=1 (2 of 2) [05/07/2000 11:12:52]
Creating a CatalogAware ZClass
How-To: Creating a CatalogAware ZClass
Created by AlexR. Last modified on 1999/10/31.
Version 0.4
I put together this Howto based on information from the zope@zope.org list. I used many contributions from Kevin Dangoor,
Michel Pelletier and Martijn Pieters. If I overlooked important information from anyone, please let me know. Errors and
inaccuracies are mine.
Version History
●
Version 0.4 - 31st October 1999 - Added feedback from Butch Landingin
●
Version 0.3 - 26th October 1999 - Added feedback from Martijn Pieters and Itamar Shtull-Trauring
●
Version 0.2 - 22nd October 1999 - Minor text changes
●
Version 0.1 - 17th October 1999 - First draft
CatalogAwareness
What is CatalogAwareness? A ZClass is CatalogAware when it automatically adds, deletes and updates itself from a Catalog. This
is a very powerful feature because it allows the user to easily search your content.
Standard file objects are not self-cataloging by default.
●
●
As a brute force measure to catalog non catalogable objects, you can tell the catalog to "sweep" your whole Zope system
to catalog objects that match certain criteria. For more details about ZCatalogs, see the ZCatalog tutorial.
If you want standard objects to self-registrer in a catalog, so you must first wrap them in an object that is self-cataloging.
For instance, by creating a ZClass that first subclasses CatalogAwareness, and then File, you can create a ZClass that
stores your PDF files and also catalogs itself in the catalog.
This document expects a basic knowledge of ZClasses and describes the additional actions necessary to make a
fully-CatalogAware ZClass. For more information about ZClasses, see the Z Class Properties Howto and the Z Classes section in
the Zope Developer's Guide. The examples in this document are based on the Z Class Properties Howto.
Constructing the ZClass
Subclassing CatalogAware
When creating the ZClass, you need to select CatalogAware (_ZClass_for_CatalogAware) as the first base class. So, if you are
creating a folderish ZClass, you would select CatalogAware first, and then select ObjectManager.
Note -- In the Control Panel > Products folders, if I click on PDFProduct (my example class) and then on PDFClass, the base
classes are listed on the Basic tab. In this example, the base classes are ZObject, _ZClass_for_CatalogAware,
_ZClass_for_File. So the _ZClass_for_CatalogAware is not listed first. ZObject will always come first. It's OK as long as
you added CatalogAware first.
Editing the Constructor Methods
If you define custom properties, you can add input fields for them in the PDFClass_addForm form method so that you can enter
information directly when creating the class instance. See the Z Class Properties Howto.
If you subclass CatalogAware in your class, your instance will be added automatically to the default catalog when created. This
catalog is identified by acquisition. AFAIK, the new meta type is correctly registered in the catalog. However, properties are not
registered. Moreover, this won't work if you use a custom catalog name such as "catalogue". To solve these problems, edit the
PDFClass_add constructor method. It first contains the following code section:
<!--#with "PDFClass.createInObjectManager(REQUEST['id'], REQUEST)"-->
<!--#comment-->
http://www.zope.org/Members/AlexR/CatalogAware?pp=1 (1 of 4) [05/07/2000 11:12:56]
Creating a CatalogAware ZClass
You can ad code that modifies the new instance here.
For example, if you have a property sheet that you want to update
from form values, you can call it here:
<!--#call "propertysheets.Basic.manage_editProperties(REQUEST)"-->
<!--#/comment-->
<!--#/with-->
Change it to:
<!--#with "PDFClass.createInObjectManager(REQUEST['id'], REQUEST)"-->
<!--#call "propertysheets.InstanceConstants.manage_editProperties(REQUEST)"-->
<!--#call reindex_object-->
<!--#/with-->
Here, PDFClass is the name of the class and InstanceConstants is the name of the property sheet I created for the class.
Customized Catalog Names
By default new objects are only registered in the standard catalog, i.e. "Catalog". If you use a custom catalog name (eg.
"catalogue"), you'll need to work a little longer:
●
You'll need to carry out one code change in the CatalogAwareness.py module.
●
You'll need to add a line in the class constructor method PDFClass_add to redefine the default catalog name.
Patching CatalogAwareness.py
There is a CatalogAware bug in the Zope 2.0.1 version. It has been fixed in the CVS database. You can fix it in your server by
looking for the following line in the install_dir\LIB\PYTHON\Products\ZCatalog\CatalogAwareness.py file:
def manage_editCataloger(self, default):
and changing it to
def manage_editCataloger(self, default, REQUEST=None):
Note - Additionally, Butch Landingin suggested changing the last line in manage_editCataloger method. This line reads:
return self.manage_main(self, REQUEST, manage_tabs_message=message)
It could be changed to:
if REQUEST:
return self.manage_main(self, REQUEST, manage_tabs_message=message)
What this will do is to allow you to call manage_editCataloger to modify the default catalog programatically (e.g. call it
from another DTML method) if you dont pass it the REQUEST parameter -- whereas the current code automatically returns you to
a screen. (Note this additional change is not necessary for the basic CatalogAware feature to work.)
Adding Code to the Constructor Method
The default catalog name is stored internally in the default_catalog variable. To change it, just add the following lines to
the code snippet we added to PDFClass_add:
<!--#call unindex_object-->
<!--#call "manage_editCataloger('catalogue', REQUEST)"-->
<!--#call index_object-->
Here catalogue is the name of the catalog I want the object instances to register into.
So our final code is:
<!--#with "PDFClass.createInObjectManager(REQUEST['id'], REQUEST)"-->
<!--#call "propertysheets.InstanceConstants.manage_editProperties(REQUEST)"-->
<!--#call unindex_object-->
<!--#call "manage_editCataloger('catalogue', REQUEST)"-->
http://www.zope.org/Members/AlexR/CatalogAware?pp=1 (2 of 4) [05/07/2000 11:12:56]
Creating a CatalogAware ZClass
<!--#call index_object-->
<!--#/with-->
Why do we unindex the object first? When changing the default catalog, the object is already indexed in the old catalog (if there
was a catalog with the default name, i.e. Catalog), because adding a CatalogAware-based ZClass instance causes it to add itself to
the default catalog.
The object is not yet indexed in the new default catalog, that's why we should not use the reindex_object method, which first
removes the object from the catalog. Right now, this is not an error, it silently ignores this. In the future however, this may become
an error.
[To do: how about adding a drop-down list to choose a catalog? Can I register a class instance in several catalogs?]
Changing the Objects
If you use a standard propertysheet management screen, your changes do not get automatically propagated to the Catalog. You'll
need to create your own editing method which changes the properties and then reindexes the objet.
Here is how:
1. First, display the methods for the PDFClass (this is in Control_Panel / Products / PDFProduct / PDFClass).
2. Add two new methods. In my setup, the 1st is called edit_propertiesForm. This is a form for editing properties
(no kidding). The 2nd is called edit_properties. This is the method that records the changes.
3. In the form, add the following code and customize it with your own properties and your standard header and footer:
<form action="edit_properties"><table>
<tr><th>Id:</th>
<td><dtml-var id></td>
</tr>
<tr><th>Title:</th>
<td><input type=text name="title" size=50 value="<dtml-var title>"></td>
</tr>
<tr><th>Pub Date:</th>
<td><input type=text name="pub_date" value="<dtml-var pub_date>"></td>
</tr>
<tr><th>Type:</th>
<td><input type=text name="content_type" value="<dtml-var content_type>"></td>
</tr>
<tr><th>Last modified:</th>
<td><dtml-var bobobase_modification_time></td>
<tr><td></td><td><input type=submit value="Edit"></td></tr>
</table></form>
Note - This code is very basic. Itamar Shtull-Trauring provided code samples to create more complex propertysheets.
You can download them from here. Note you'll need to replace the propertysheet name (InstanceConstants here)
with your own name. You'll also probably need to adapt them for you.
4. In the edit_properties method, add the following code. Customize it will your standard header and footer.
<html><body>
<dtml-call "propertysheets.InstanceConstants.manage_changeProperties(REQUEST)">
<dtml-call reindex_object>
<p>Changes saved.</p>
</body></html>
Here again, InstanceConstants is the name of the property sheet I defined for the class.
5. Last, display the Views tab in the Control_Panel / Products / PDFProduct / PDFClass screen and map
edit_propertiesForm to a view name. I mapped it to the Properties view.
Voilà. Any properties that your ZClass instances have will automatically show up in the Catalog, and the Catalog will
stay up to date with changes.
http://www.zope.org/Members/AlexR/CatalogAware?pp=1 (3 of 4) [05/07/2000 11:12:56]
Creating a CatalogAware ZClass
Duplicating the Objects
You can copy and paste a class instance to duplicated it. The new instance is indexed correctly.
Moving the Objects
In my server, I cannot cut and paste to move a class instance. I get an AttributeError. [More to come on this issue]
If you have the same problem, use copy and paste to duplicate the class instance, then delete the original copy. The
catalog is updated correctly.
Deleting the Objects
Your objects are removed from the Catalog upon deletion if deleted directly or if the folder they are in is deleted.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/CatalogAware?pp=1 (4 of 4) [05/07/2000 11:12:56]
Custom and international time and date formats
How-To: Custom and international time and date
formats
Created by AlexR. Last modified on 2000/01/28.
Version 0.2
This howto explains how to create custom and international formats to insert time and date values in Zope. I gathered this
information from the zope@zope.org mailing list. Feel free to get in touch about this document.
Version history
●
23rd October 1999 - version 0.1 - First draft
●
26th October 1999 - version 0.2 - Added references to the ZQRG
Simple timestamps
To enter the edit time for an objet, use:
<dtml-var bobobase_modification_time>
To enter the current time in Zope, use:
<dtml-var ZopeTime>
In both cases the result is a long timestamp in GMT format. Example:
1999/10/22 13:01:33.059 GMT+1
C-style format strings
If you want another format, you can use C-style format strings. Example:
<dtml-var bobobase_modification_time fmt="%d/%m/%Y - %H:%M">
You get the following results:
22/10/1999 - 11:01
Here is a table of useful C-style time and date formats. I copied it from a Linux reference document (I can't remember which one)
for the C strftime function. Note that the output is GMT-based. So you cannot use these codes directly to format date and time
values for say European timezones.
%a
%A
%b
%B
%c
%d
%H
%I
%j
%m
%M
%p
%S
%U
%w
%W
%x
An abbreviation for the day of the week.
The full name for the day of the week.
An abbreviation for the month name.
The full name of the month.
A string representing the complete date and time; on my
computer it's in the form: 10/22/99 19:03:23
The day of the month, formatted with two digits.
The hour (on a 24-hour clock), formatted with two digits.
The hour (on a 12-hour clock), formatted with two digits.
The count of days in the year, formatted with three digits
(from 001 to 366).
The month number, formatted with two digits.
The minute, formatted with two digits.
Either AM or PM as appropriate.
The second, formatted with two digits.
The week number, formatted with two digits (from 00 to 53;
week number 1 is taken as beginning with the first Sunday
in a year). See also %W.
A single digit representing the day of the week:
Sunday is day 0.
Another version of the week number: like %U, but
counting week 1 as beginning with the first Monday in a year.
A string representing the complete date; on my computer
it's in the format 10/22/99.
http://www.zope.org/Members/AlexR/CustomDateFormats?pp=1 (1 of 3) [05/07/2000 11:13:00]
Custom and international time and date formats
%X
%y
%Y
%Z
%%
A string representing the full time of day (hours, minutes,
and seconds), in a format like the following example: 13:13:13
The last two digits of the year.
The full year, formatted with four digits to include
the century.
Defined by ANSI C as eliciting the time zone, if available;
it is not available in this implementation (which accepts %Z
but generates no output for it).
A single character, %.
Note you can also use the following syntax:
<dtml-var "bobobase_modification_time().strftime('%d/%m/%Y - %H:%M')">
For more information about these formats, see the Zope Quick Reference Guide (section about the var tag).
The toZone method
The toZone('myTimeZone') method can be used to convert time/date values to a specific timezone. myTimeZone is a string
that holds the name of the timezone. Example:
<dtml-var "bobobase_modification_time().toZone('US/Indiana-Starke')">
The output is:
1999/10/22 12:35:32.481 US/Indiana-Starke
The timezone names are defined in the Zope_dir\lib\python\DateTime\DateTimeZone.py file. I couldn't find one
for most European timezones (Zope 2.0.1). Chris Allen has provided files to support Australian timezone names.
You can also add the C-formats mentioned above. Example:
<dtml-var "bobobase_modification_time().
toZone('US/Indiana-Starke').strftime('%d/%m/ %Y - %H:%M')">
Or
<dtml-var "bobobase_modification_time(). toZone('US/Indiana-Starke')"
fmt="%d/%m/%Y - %H:%M">
Correcting a date or time value
You can add a value to a date to create a later date. You can also substract a value to create a sooner date. Example:
<dtml-var "ZopeTime() + 2">
The result date is 2 days from now, i.e. "1999/10/24 12:12:14.932 GMT+1" at the time of writing.
Metropolitan French time is GMT + 2 hours during summer time. 2 hours are 0.0833 day. So I guess I could use the following
code to get a correct French date format:
<dtml-var "bobobase_modification_time() + 0.0833" fmt="%d/%m/%Y - %H:%M">
This works. However, it is quite ugly and it will break when we change to winter time. I'd need to store the correction value in a
global variable.
Build-in functions
Martijn Pieters provided a better solution because AFAIK it doesn't need to be updated for winter time:
<dtml-with bobobase_modification_time>
<dtml-var "'%s/%s/%i - %s' % (dd(), mm(), year(),
TimeMinutes())">
</dtml-with>
The output is:
22/10/1999 - 20:31
http://www.zope.org/Members/AlexR/CustomDateFormats?pp=1 (2 of 3) [05/07/2000 11:13:00]
Custom and international time and date formats
This is a Python expression. The 1st, 2nd and 4th functions return a string value. That's why we use the %s placeholder in the
format. The 3rd function returns an integer. Hence the %i placeholder.
To customize this example for your own purpose you may need to modify or add placeholders. Example:
<dtml-with bobobase_modification_time>
<dtml-var "'This is %s, %s/%s/%i, %s' % (Day(), mm(), dd(),
year(), TimeMinutes())">
</dtml-with>
The output is:
This is Friday, 10/22/1999, 20:44
For more information about the dd(), mm(), year(), TimeMinutes() and similar functions, see the
Zope_dir\lib\python\DateTime\DateTime.html document in your Zope directory. See also the Zope Quick
Reference Guide (DateTime section)
The complete expression is a bit complex. You could store it in a DTML method at the root of your Zope server and call it from
documents in subdirectories. Example:
<dtml-var myDateTime>
The output is:
22/10/1999 - 20:50
I guess you could also write a small Python external method to easily store custom date formats. I haven't tried that yet. If you
have code examples to offer feel free to send them over for inclusion in this document.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/CustomDateFormats?pp=1 (3 of 3) [05/07/2000 11:13:00]
DHTML navigation bar
How-To: DHTML navigation bar
Created by Ioan. Last modified on 2000/05/21.
Intro
Here you'll find a quick and useful DHTML navigation bar.
This How-To is based on some scripts made by Mike Hall, http://www.brainjar.com
The whole ideea is from http://dynamicdrive.com
To start use the navigation bar you must download NavBar.zexp
To see the menu create folders with title and
under this folders you must create DTML Documents or Files.
If your objects don't have title, it will not be shown!
Don't forget to move the js folder on the root of your site!
You may add overlib support for more fun!
What's inside of NavBar.zexp
A folder js with JavaScripts files,
standard_html_footer and standard_html_header.
standard_html_header:
<HTML>
<HEAD>
<TITLE><dtml-var title_or_id></TITLE>
<script language="JavaScript" src="/js/navbar.js"></script>
<script language="JavaScript" src="/js/dhtmllib.js"></script>
<script language="JavaScript" src="/js/menu.js"></script>
</HEAD>
<body BGCOLOR='#AABBFF' TEXT='#000000'
LINK='#0000FF' ALINK='#0000FF' VLINK='#0000FF' onload="navBarInit()">
<br>
standard_html_footer:
<BR>
<BR>
<HR>
Last update: <dtml-var bobobase_modification_time><BR>
</BODY></HTML>
navbar.js, dhtmllib.js are static files.
menu.js is build on the fly :
menu.js:
This is the biggest secret. I mean is too long to be listed here.
Is based on sub DTML Method which will create the submenu.
Important:
The menu is created by folders (with title) from your root site.
The submenu for each one is created from documents and files.
http://www.zope.org/Members/Ioan/NavBar?pp=1 (1 of 2) [05/07/2000 11:13:03]
DHTML navigation bar
The subfolders and files from them are ignored.
Tip:
On sub file from js folder you may include folders with :
<dtml-in "objectItems(['Folder','DTML Document','File'])">
istead of
<dtml-in "objectItems(['DTML Document','File'])">
and you'll have access to the subfolders, not only docs or files.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Ioan/NavBar?pp=1 (2 of 2) [05/07/2000 11:13:03]
DTML Methods vs. DTML Documents How-To
How-To: DTML Methods vs. DTML Documents
How-To
Created by michel. Last modified on 1999/09/15.
What is the difference between DTML Documents and DTML
Methods?
The Short Answer
DTML Documents are used to hold content. If you want to store a large doucment, or lots of text, or a body of information, use a
DTML Document.
DTML Methods are used to carry out actions. If you want to render another object (like a DTML Document), execute a DTML
script, or generate lots of dynamic content, use a DTML Method.
The Long Answer
DTML Methods are method objects. They do not act like most other Zope objects because they are methods of the folder that
contains them. For example, if you had a folder called Folder and a DTML method in that folder called Method:
AFolder/
AMethod
AMethod is a method of AFolder. This means that AMethod does not have any of it's own attributes or properties, it uses those
of AFolder. If you put the following DTML string in AMethod:
<dtml-var id>
and view the AMethod method, the document will render the string AFolder. This is because when AMethod resolves the name
id, it will resolve to AFolder's id, because as a method, AMethod does not have it's own id
DTML Documents, on the other hand, are Zope objects. Although they act much like DTML Methods, there are subtle
differences. For example, if you created a DTML Document in the folder AFolder called ADocument, and you put the above
DTML string into ADocument and viewed it, it would render to the string ADocument, not AFolder like a DTML Method
would.
One way to think about all this is in terms of the DTML namespace . When in a certain portion of DTML, the namespace available
to you is in a stack like data structure. when referencing an attribute, first the top item of the stack is looked at, then the next, and
so on until the attribute is found or the stack is exausted and an error is raised.
The DTML namespace stack is built from the ZPublisher request. Let's suppose that our folder F in the example above, was really
a subfolder of middle, which itself was a subfolder of top. Thus, the layout looks like this:
top/
middle/
AFolder/
AMethod
ADocument
By traversing the URL http://machine:port/top/middle/AFolder/AMethod you are building up a DTML namespace stack that
contains, from top to bottom, AFolder, middle and top. Thus, while in the AMethod Method, any attributes referenced are first
look for in AFolder, then middle, and then top (you can think of the namespace stack as being the request path of the object from
the root, but upside down). Note that AMethod isn't in the namespace stack even though it is in the request. This is because DTML
Methods don't have their own namespaces, and thus do not push a new namespace onto the namespace stack.
Accessing http://machine:port/top/middle/AFolder/ADocument however is different because ADocument is a DTML Document it
is it's own object and has it's own namespace. When accessing D with the URL above, a new namespace is pushed onto the stack,
and any references to attributes in ADocument are first look for in ADocument, then AFolder, then middle and lastly top.
http://www.zope.org/Members/michel/HowTos/DTMLMethodsandDocsHowTo?pp=1 (1 of 2) [05/07/2000 11:13:07]
DTML Methods vs. DTML Documents How-To
Also, you many notice that the DTML Document managment interface has a Properties view and the DTML Method
managment interface does not. This is because DTML Documents can have properties like all other Zope objects. DTML Methods
share the properties of the folder than contains them.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/michel/HowTos/DTMLMethodsandDocsHowTo?pp=1 (2 of 2) [05/07/2000 11:13:07]
DTML Zclass ID Bug Workaround
How-To: DTML Zclass ID Bug Workaround
Created by AlexR. Last modified on 2000/03/02.
Version 0.2
This short howto contains information collected on the zope-dev@zope.org list. I used contributions from Ian C. Blenke, Toby
Dickenson, Anthony Pfrunder and Jason Spisak. Feel free to get in touch about this document.
Version history
●
Version 0.2 - 2nd March 2000 - Added feedback from Steve Spicklemire
●
Version 0.1 - 23rd October 1999 - 1st draft
The Zclass ID bug
This problem happens (in Zope 2.0.1) when you create a new product with a Zclass that subclasses the DTML Document or
Method classes. In my case I also subclassed CatalogAware so that the new product instances could register themselves into the
catalog.
You then create an instance of this product and try to access its id property from a Python or DTML method. Example:
This document is <dtml-var id>.
It doesn't work. When viewing you get something like This document is <cstring>. or This document is
<string>. Also, the id is empty when you try to edit it in a property sheet and it's not available from the catalog search results
(though the object is correctly indexed if CatalogAware or indexed manually).
If you don't subclass DTML Method or DTML Document the problem doesn't occur.
Using the setName method
Toby Dickenson suggest the following solution: add a setName call to the class constructor method. If your class is called
myZClass for instance you will need to edit myZClass_add. Example:
<!--#with "myZClass.createInObjectManager(REQUEST['id'], REQUEST)"-->
<dtml-call "setName(REQUEST['id'])">
<!--#/with-->
I used the setName line before any manage_editProperties or catalog lines. This worked for me.
Creating a class instance in Python
Jason Spisak also mentioned that you can use an external method to get class instances with id's.
Example code (I didn't use it myself):
from DateTime import DateTime
from OFS import DTMLMethod
def make_resume(self,rfile,id,title)
ob=ob.__of__(self)
if rfile:
my_ob=self.Control_Panel.Products.MyClass()
my_ob.id=id
my_ob.propertysheets.MyProperties.manage_changeProperties(
myproperty=rfile.read())
my_ob.manage_edit("""<!--#var standard_html_header-->
<!--#var myproperty--><!--#var
standard_html_footer-->""",title)
http://www.zope.org/Members/AlexR/ZClassIDBug?pp=1 (1 of 2) [05/07/2000 11:13:10]
DTML Zclass ID Bug Workaround
my_ob=my_ob.__of__(ob)
ob._setObject(id, my_ob)
With this code you get an id just fine.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/ZClassIDBug?pp=1 (2 of 2) [05/07/2000 11:13:10]
DTML in ZSql Methods
How-To: DTML in ZSql Methods
Created by jpenny. Last modified on 2000/05/12.
A Problem
Suppose I have a form that accepts several inputs and has a find operation that can be permitted on the input data. For simplicity, I
am going to limit myself to 2 inputs, which I am calling item_class and major_group. The input form looks like:
<form action=edit_item_classes method=post>
<table>
<tr><td>Item Class
<td><input type=text width=20 name=item_class>
<tr><td>Major Group
<td><input type=text width=20 name=major_group>
<tr><input type=button name=action value=find>
</table>
</form>
Which renders as:
Item Class
Major Group
The Question: How do I write the ZSQL method that acquires the data and the DTML Method that displays it?
I will show two alternatives below, and let you choose according to your taste.
Alternative 1: Simple ZSQL Methods and More
Complex DTML Method
Here we will need 3 simple ZSQL Methods and a DTML emthod that looks like:
<dtml-elif "REQUEST['action'] == 'find'">
<dtml-call "REQUEST.set('major_group',
_.string.upper(REQUEST['major_group']))">
<dtml-call "REQUEST.set('item_class',
_.string.upper(REQUEST['item_class']))">
<dtml-if "major_group == '' and item_class == ''">
<P>I need a Major Group or an Item Class (or both) before I can
perform the find.
<dtml-elif "major_group <> '' and item_class <> ''">>
<dtml-in find_given_both start=query_start>
<form action=<dtml-var "URL0"> method=post>
<table>
<TR><TD><B>Item Class: </b>
<TD><input type=text size=2 name=styleCode value="<dtml-var
"item_class">">
<TR><TD><B>Major Group: </b>
<TD><input type=text size=15 name=major_group value="<dtml-var
"lower_post">">
</table>
<table>
<TR><TD><B>Actions:</b>
<TD><input type=submit name=action value="find">
</table>
</dtml-in>
<dtml-elif "major_group <> ''>
<dtml-in find_given_major_group start=query_start>
<form action=<dtml-var "URL0"> method=post>
http://www.zope.org/Members/jpenny/DTML_in_zsql_methods?pp=1 (1 of 3) [05/07/2000 11:13:13]
DTML in ZSql Methods
<table>
<TR><TD><B>Item Class: </b>
<TD><input type=text size=2 name=styleCode value="<dtml-var
"item_class">">
<TR><TD><B>Major Group: </b>
<TD><input type=text size=15 name=major_group value="<dtml-var
"lower_post">">
</table>
<table>
<TR><TD><B>Actions:</b>
<TD><input type=submit name=action value="find">
</table>
</dtml-in>
<dtml-else>
<dtml-in find_given_item_class start=query_start>
<form action=<dtml-var "URL0"> method=post>
<table>
<TR><TD><B>Item Class: </b>
<TD><input type=text size=2 name=styleCode value="<dtml-var
"item_class">">
<TR><TD><B>Major Group: </b>
<TD><input type=text size=15 name=major_group value="<dtml-var
"lower_post">">
</table>
<table>
<TR><TD><B>Actions:</b>
<TD><input type=submit name=action value="find">
</table>
</dtml-in>
</dtml-if>
ZSQL Method find_given_both has arguments item_class and major_group and body:
select * from item_classes
where
item_class = <dtml-sqlvar item_class type=string>
and
major_group = <dtml-sqlvar major_group type=string>
ZSQL Method find_given_item_class has only argument item_class and body:
select * from item_classes
where
item_class = <dtml-sqlvar item_class type=string>
ZSQL Method find_given_major_group has only argument and major_group and body:
select * from item_classes
where
major_group = <dtml-sqlvar major_group type=string>
Alternative 2: More Complex ZSQL Method and
Simple DTML Method
Here I will define a single ZSQL method that contains DTML level logic, and a simpler DTML method. The portion of the DTML
method that handles find looks like:
<dtml-elif "REQUEST['action'] == 'find'">
<dtml-call "REQUEST.set('major_group',
_.string.upper(REQUEST['major_group']))">
<dtml-call "REQUEST.set('item_class',
_.string.upper(REQUEST['item_class']))">
<dtml-if "major_group == '' and item_class == ''">
<P>I need a Major Group or an Item Class (or both) before I can
perform the find.
<dtml-else>
<dtml-in find start=query_start>
<form action=<dtml-var "URL0"> method=post>
<table>
http://www.zope.org/Members/jpenny/DTML_in_zsql_methods?pp=1 (2 of 3) [05/07/2000 11:13:13]
DTML in ZSql Methods
<TR><TD><B>Item Class: </b>
<TD><input type=text size=2 name=styleCode value="<dtml-var
"item_class">">
<TR><TD><B>Major Group: </b>
<TD><input type=text size=15 name=major_group value="<dtml-var
"lower_post">">
</table>
<table>
<TR><TD><B>Actions:</b>
<TD><input type=submit name=action value="find">
</table>
</dtml-in>
</dtml-if>
</dtml-if>
There is a single ZSQL method in this alternative. In my case, it is simply called find. It has arguments major_group and
item_class and body:
<dtml-if "major_group <> '' and item_class <> ''">
select * from item_classes
where
item_class = <dtml-sqlvar item_class type=string>
and
major_group = <dtml-sqlvar major_group type=string>
<dtml-elif "major_group <> ''">
select * from item_classes
where
major_group = <dtml-sqlvar major_group type=string>
<dtml-else>
select * from item_classes
where
item_class = <dtml-sqlvar item_class type=string>
</dtml-if>
Note how much simpler the logic of the DTML method is. Also note that ZSQL methods do permit DTML inside their body.
However, note that only one select statement should be executed under any possible logic combination, you will get back only a
single res structure, and at best, the results of previous selects will be discarded.
A warning
This is trickier reading than you may think; item_class and major_group are being heavily overloaded. At times, each refers to an
element of a HTML form, a member of the REQUEST dictionary, a formal parameter, and a datum in a column of a database (via
a res structure). Read carefully, and you will be far along in the Zen of ZSQL methods when you have a clear grasp of which is
which. (Your code will actually be far easier to understand if you take advantage of this overloading and do not use different
variable names for each context.)
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jpenny/DTML_in_zsql_methods?pp=1 (3 of 3) [05/07/2000 11:13:13]
Date Pulldown Menu Howto
How-To: Date Pulldown Menu Howto
Created by purpleduck. Last modified on 2000/06/15.
Phil Aylesworth
phila@purpleduck.com
There are several problems with entering dates into a form in Zope. You don't have any control over whether a user enters a valid
date or not. Zope handles lots of date formats very well but doesn't handle 2 digit years well at all. It has a default century of 1900
so years from 2000 have to be entered as 4 digit years. You can change this in the DateTime.py module but then 99 would mean
2099. This causes even worse problems because Zope can't format years that high! I hope this gets fixed before we get to whatever
year that is.
Also, it is difficult for users to know whether to type yyyy/mm/dd or mm/dd/yyyy or dd.mm/yyyy or what!
To solve this I have written a pair of external methods to create a pull down menu with the month name, day of month, and year.
The second will put the information from the menu back together and return a string of the date. (This part needs attention)
It is a rather feable attempt and I hope to get feedback from people on how to make it better and then maybe I will even
understand how some of the things I did work. (I looked at a lot of other code to try to figure out how to work with Zope objects
from python.) In fact, right now it only works if the date is a value of the calling object, in my case a Zclass. It gives an error if the
date is in REQUEST. Can anyone help me here?
In the edit form use:
<dtml-var "datepull('the_date')">
the_date is a string containing the name of the variable that contains the current value of the date (the quotes are therefore
required). The menu will consist of 3 variables created using this name, the_date_mo, the_date_dd, and the_date_yr. This way you
can have more than one date on a form because each will have its own set of names. At this time the_date must exist and be a
valid date object. Since Zope does not allow null values for dates, I use a date of 01/01/1980 as a null date. This can be changed in
the code if you need to. I don't know how early a date Zope can handle and that was okay for me so I am stuck with it now. It will
display this date as TBD in the month and blanks in the other two.
After the user submits the form the 3 variables have to be put back together. The second method, datepush does this. Again you
pass the name of the variable and the REQUEST object. It will get the three variables from the REQUEST object and format a
string of the date. I need to work on this so that it will just set the REQEST variable so that you don't need this mess in your
DTML but for now it works.
In the DTML method that is called by the form use:
<dtml-call "REQUEST.set('the_date', ZopeTime(datepush(REQUEST, 'the_date')))">
To Do
datepush method should returns a Zope date object or just sets the value of the_date in REQUEST to a zope date object.
Make the TBD value setable from the datepull method. This way you can use it how ever you want.
Make it default to today if 'the_date' does not exist.
How to install
Create a file containing the following code called datepull.py and put it in your extensions directory. In Zope create 2 external
methods. One called datepull and one called datepush somewhere where you can use them.
#################################################
http://www.zope.org/Members/purpleduck/date_pulldown?pp=1 (1 of 3) [05/07/2000 11:13:17]
Date Pulldown Menu Howto
datepull python methods
by Phil Aylesworth
phila@purpleduck.com
version 0.1.0, June 15, 2000
#################################################
import string, sys, regex
from time import time, gmtime, localtime, asctime
import DateTime
# set these to change the behavior
prevyr
= 1
# number of years previous to show in year pull down
futyr
= 3
# number of future years to show in year pull down
showtbd = 'TBD'
# include TBD in pull down and what string to use. Empty string for
no TBD entry
tbd_date = '1980/01/01'
# increment futyr so that it works as expected
futyr
= futyr + 1
########################################
# display a set of pull down menues
#
#
with the current date selected
#
########################################
def datepull(self, tagname):
months
=
('','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')
this_yr = localtime(time())[0]
Day
= int(getattr(self, tagname))
value
= DateTime.DateTime(Day, 'Canada/Eastern')
value_yr = value.year()
value_mo = value.month()
value_dd = value.day()
value_st = "%4d/%02d/%02d" % (value_yr, value_mo, value_dd)
string
= ''
if showtbd:
if (value_st==tbd_date):
tbd = 1
value_mo = 0
value_yr = 0
value_dd = 0
else:
tbd = 0
# Month
string = string + "<select name=\"%s_mo:int\">\n" % tagname
if showtbd:
if tbd:
string = string + " <option value=\"0\"
selected>%s</option>\n" % showtbd
else:
string = string + " <option value=\"0\">%s</option>\n" %
showtbd
for month in range(1,13):
if (month==value_mo):
string = string + " <option value=\"%d\"
selected>%s</option>\n" % (month, months[month])
else:
string = string + " <option value=\"%d\">%s</option>\n" %
(month, months[month])
string = string + "</select> \n\n"
# Day
string = string + "<select name=\"%s_dd:int\">\n" % tagname
if showtbd:
if tbd:
string = string + " <option value=\"0\" selected></option>\n"
else:
string = string + " <option value=\"0\"></option>\n"
http://www.zope.org/Members/purpleduck/date_pulldown?pp=1 (2 of 3) [05/07/2000 11:13:17]
Date Pulldown Menu Howto
for day in range(1,32):
if (day==value_dd):
string = string + "
selected>%d</option>\n" % (day, day)
else:
string = string + "
(day, day)
string = string + "</select>, \n\n"
<option value=\"%d\"
<option value=\"%d\">%d</option>\n" %
# Year
string = string + "<select name=\"%s_yr:int\">\n" % tagname
if tbd:
if showtbd:
string = string + " <option value=\"0\" selected></option>\n"
for year in range(this_yr-1, this_yr+4):
string = string + " <option value=\"%d\">%d</option>\n" %
(year, year)
else:
if showtbd:
string = string + " <option value=\"0\"></option>\n"
for year in range(value_yr-1, value_yr+4):
if (year==value_yr):
string = string + " <option value=\"%d\"
selected>%d</option>\n" % (year, year)
else:
string = string + " <option
value=\"%d\">%d</option>\n" % (year, year)
string = string + "</select>\n"
return (string)
########################################
#
Put the date back together again
#
########################################
def datepush(self, REQUEST, tagname):
yr = tagname + "_yr"
mo = tagname + "_mo"
dd = tagname + "_dd"
y=REQUEST.get(yr)
m=REQUEST.get(mo)
d=REQUEST.get(dd)
if m==0:
string = tbd_date
else:
string = "%4d/%02d/%02d" % (y, m, d)
return string
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/purpleduck/date_pulldown?pp=1 (3 of 3) [05/07/2000 11:13:17]
Debugging Zope Python Code
How-To: Debugging Zope Python Code
Created by klm. Last modified on 2000/03/15.
This is really a work in progress: i'm putting it out now because it's pretty juicy, rather than waiting 'til it's presentable. I hope to
polish it, and add some juice, over time.
Debugging Zope Python Code
Here are some hints about python-level (external method, product, and core) zope debugging. These are the techniques i depend
on - ones that i could not do without.
(See also Michel Pelletier's more recent The Debugger is Your Friend with more details about some of the stuff i cover here, and
no details about some of the other stuff...)
All entail some kind of privileged access - either to the code and in some cases to the to the host installation or to the superuser
password. Ie, these techniques are for tightly coupled debugging, close to the server. They are also for recent zopes - 2.0 or later.
Make sure you're using python 1.5.2 everywhere...)
Using Tracebacks to do the equivalent of print-flag debugging
When working on a web site - particularly a remote one! - you can't just put prints in your code to narrow down on where and how
bugs, particularly fatal ones, are occurring. Ie, you don't have the luxury of an output stream, or even files, where you can send
debugging flag output. Zope has some provisions, however, that can be just about as good, or even better, provided you know
about them.
First of all, when running Zope normally it hides on the error pages that the web server produces the python traceback for the
error, as an HTML "" comment. So if you an error page, do a "View source" to see the traceback.
You get lots of extra mileage for debugging when you run Zope under ZServer [which docs should i ref?] with "debugging mode"
activated. To do so on Unix, you have to include the -D option in the ZServer start script - either by passing it on the command
line or by including it in the text for the script. In Win32 you have to run Zope from a DOS command, eg with start.bat, not as a
service. I'll say more about this below - among other things, it exposes the tracebacks on error pages, so you don't have to do a
View Souce.
Whether or not running debug mode, in lotsa situations you can use these tracebacks for a form of flag output by putting, eg:
raise "DEBUG", "Issue %s has %s items" % (self.id, self.values())
to see the value of x in the middle of a computation.
The string "DEBUG" is not special to Zope - i typically use it for this sort of thing so i can tell what it is if i happen to leave one in
accidentally, and it makes it easy to search through for "DEBUG", etc. (Note that your program must not be catching this
exception - though it's unlikely to be doing an except "DEBUG":, it may be doing a bare except: and suppressing it. I
would be suspicious of such code - in many or even most cases this is bad programming practice, because it can hide bugs that
you should be identifying.)
One particularly nice - and important - nuance here is that the Zope publisher will notice the uncaught exception, passing it along
but first aborting the current transaction. This means that any changes cause by the current request up to the point of the raised
exception will be nullified (in most cases - obviously, if your program sends an email before the exception is raised, there's no way
of calling it back). This means that you can redo an activity again and again with no accumulated effects, until your remove your
debugging exception!
Under strategic conditions - eg, when other people are accessing the site - you may want to condition the debugging flag according
to your login:
if AUTHENTICATED_USER.getUserName() == 'klm':
raise "DEBUG", "Issue %s has %s items" % (self.id, self.values())
As mentioned above, there are more benefits to running ZServer in debug mode.
http://www.zope.org/Members/klm/debuggingzope?pp=1 (1 of 3) [05/07/2000 11:13:22]
Debugging Zope Python Code
●
●
In addition to exposing the tracebacks on the error pages, it also sets zope so it notices changes to External Methods files
and automagically reloads them - so your debugging flags or code fixes take effect without having to restart the site.
The tracebacks are html_quoted. Objects that have mapping behaviors - eg, python dictionaries, and most notably the
Zope REQUEST object - are displayed in a nice key/value table layout, making it easy to examine the REQUEST and
other environment settings. Eg::
raise "DEBUG", (REQUEST.AUTHENTICATED_USER.getUserName(), REQUEST)
will first show the user and then show a nicely formatted
display of the request - yay!
●
Zope tracebacks pay special attention, printing out the value of a variable named __traceback_info__ if it finds it
in the stack frame where the error occurred. Eg::
__traceback_info__ = (x, y, z)
to snapshot the values of vars x, y, and z, in case an exception
does occur in the function/class/method, even if you don't put
an explicit raise in yourself.
●
Unlike the exception values, you have to explicitly html_quote values that include special html entities - the classic case
is python types, including instance types, which begin with a <, so that the succeeding characters would be invisible until
the closing >. So, eg::
from DocumentTemplate import html_quote
...
raise "DEBUG", html_quote(`self`)
In general, for complex objects it's often a good idea to
stringify the values::
__traceback_info__ = (`x`, `y`, `z`)
●
●
On Unix type systems the start script won't fork in the background, and it reports the port connections it establishes. (In
Win32 you have to run Zope from a DOS command, eg with start.bat, not as a service.) This is crucial for interacting
with Python's pdb debugging from a running Zope - see below.
In unix-type systems, a [Restart] button is added to control panel. Restart enables you to pick up changes to the core
python and compiled lower-level code without having to reinvoke any external commands.
[What are the drawbacks, besides exposing the tracebacks to everyone?]
Stepping in a Running Zope with Python's PDB Debugger
[I haven't thought about platform concerns here, only about doing it from Unix - i'm not sure if or how this works under Win32.]
One unexpected benefit of running Zope with ZServer in debug mode (above you is that it enables you to enter Python's PDB
debugger and step through the active code! Briefly, to do this:
●
You need to be running ZServer in debug mode
●
Where you want to start debugging, put::
import pdb; pdb.set_trace()
●
●
If you do this in an external method, zope will notice the changed file, reload it, and enter the debugger next time you hit
that point in the code.
If in a product or other (eg, core) Zope code, you'll have to restart zope - go to the control panel and hit the restart button.
When it hits that line, the shell running zope will show the pdb prompt, and you can step in, over, and around your code and the
underlying Zope core python code. Note that ZServer will be blocked on your activity - other people visiting the site will not get a
response until you finish!
http://www.zope.org/Members/klm/debuggingzope?pp=1 (2 of 3) [05/07/2000 11:13:22]
Debugging Zope Python Code
Ran out of time for now - in case my raw outline notes help:
. using the medusa monitor to connect with a running zope
; Prerequisites:
, You must be running ZServer, which must be configured (i think
it's default) to start the medusa monitor server. If you run
with -D, as above, it will report the port you need to know.
, You must know your superuser password, and it must be encoded
in the access file as plain text, not encrypted!
, You have to be on the host - despite specifying the host in
the monitor_client command.
; in a recent zope checkout (or 2.0 distribution) on the host:
; cd ZServer/medusa
; python monitor_client.py localhost <monitor port>
, the port is reported in the ZServer startup
, must be python 1.5.2 or later
; >>> import Zope
; >>> app = Zope.app()
; now you can access objects in the ZODB and poke around with them:
, >>> control_panel = app.Control_Panel
, >>> control_panel.Database.manage_pack(days=2)
, Whee! You just packed your database...
. What about using ZClient??
Suggestions, corrections, inspirations to Ken Manheimer .
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/klm/debuggingzope?pp=1 (3 of 3) [05/07/2000 11:13:22]
Detecting User Roles in DTML
How-To: Detecting User Roles in DTML
Created by Amos. Last modified on 1999/09/15.
You may want to customize the look and feel of a page based on the roles of the user viewing it. How can this be done in DTML?
You can find out if a user has a role with a User object's has_role method:
<!--#if "AUTHENTICATED_USER.has_role('somerole')"-->
...
<!--#/if-->
Note: AUTHENTICATED_USER is the user who is currently viewing a page. This user may be the Anonymous user if they have
not logged in.
Note: You may also pass a list of roles to has_role.
Zope 2 introduced the notion of local roles which complicates things a little. It is possible to have different roles for different
objects. So if I am a Manager I may also have a local role of Owner on a Folder.
To get the real roles that the user has in the context of a given object (which takes into account local roles given in that object and
in other object higher up in the hierarchy), you need to also pass in an object for context. For example:
<!--#if "AUTHENTICATED_USER.has_role('Owner',this())"-->
You own this Folder.
<!--#/if-->
Note: this() returns the object on which a DTML method is being run.
You can also test for permissions on objects like so:
<!--#if "AUTTHENTICATED_USER.has_permission('View',someObject)"-->
...
<!--#/if-->
Using permissions rather than role will tell you if a user is authorized to perform a give action on a given object.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/DetectRoles?pp=1 [05/07/2000 11:13:24]
Determining object types in Zope
How-To: Determining object types in Zope
Created by Duncan. Last modified on 2000/05/25.
Usually it is sufficient in Zope to simply use an object as though it was already of the correct type and catch any exceptions that
are thrown. Sometimes it is important to know in advance exactly what type an object is.
The following PythonMethod takes an object as its argument, and returns the a string identifying the object type. Zope objects
return the meta_type string, other possible results include string, integer, number (i.e. numeric but not an integer), None,
list, tuple, dict, sequence (has a len method but doesn't look very like a list or tuple) and unknown.
Create a PythonMethod called safetype.
Title:
safetype(object) -> type as string
Parameter list:
object
Body:
if object is None:
return 'None'
s = str(object)
if object==s:
return 'string'
try:
t = object.meta_type
return t
except:
pass
try:
l = len(object)
if s[0]=='[': return 'list'
if s[0]=='(': return 'tuple'
if s[0]=='{': return 'dict'
return 'sequence'
except:
pass
try:
t = object.timeTime()
return 'DateTime'
except:
pass
try:
val = int(object)
if val is object:
return 'integer'
return 'number'
except:
return 'unknown'
Here is a DTML Method to test it:
<dtml-var standard_html_header>
<dtml-in "[(1, 'a string'), (2, 55), (3, 55.0),
(4, ['a', 'b', 'c']), (5, (1, 2)),
(6, _.DateTime()), (7, this()), (9, _.None), (10, {}) ]">
http://www.zope.org/Members/Duncan/RecogniseTypes?pp=1 (1 of 2) [05/07/2000 11:13:27]
Determining object types in Zope
<dtml-let item=sequence-item>
<dtml-var "`item`" html_quote> has type <dtml-var "safetype(item)"><br>
</dtml-let>
</dtml-in>
<!-- These two are not included in the list, because they would be
prematurely rendered. -->
<dtml-var "`safetype`" html_quote> has type <dtml-var "safetype(safetype)"><br>
<dtml-var "`test1`" html_quote> has type <dtml-var "safetype(test1)"><br>
<dtml-var standard_html_footer>
Which produces the following output:
a string has type string
55 has type integer
55.0 has type number
['a', b, 'c'] has type list
(1, 2) has type tuple
DateTime(2000/05/25 12:31:2.422 GMT+0) has type DateTime
<Folder instance at 0173B710> has type Folder
None has type None
{} has type dict
<PythonMethod instance at 0171C830> has type Python Method
<DTMLMethod instance at 01724D90> has type DTML Method
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Duncan/RecogniseTypes?pp=1 (2 of 2) [05/07/2000 11:13:27]
Diagnosing Acquisition Problems
How-To: Diagnosing Acquisition Problems
Created by htrd. Last modified on 1999/12/07.
A common problem when debugging a Zope application is that you are often not 100% certain where an object might acquire its
attributes from. This function is suitable for use as an external method. Given an object, it will return a list of the repr's of the
objects in its acquisition tree, in the order that they would be searched. I often call this from dtml as:
<ul><dtml-in "aq_order(my_object)"><li>&dtml-sequence-item;</li></dtml-in></ul>
def aq_order(ob):
"""Return a list of object repr's, showing the order in which
the various objects in the acquisition tree will searched for
attributes.
"""
return _aq_order(ob,[])
def _aq_order(ob,old):
if hasattr(ob,'aq_self'):
# It's an acquisition wrapper
return _aq_order(ob.aq_self,old) + _aq_order(ob.aq_parent,old)
else:
# Its a real object
if ob in old:
# This has already been looked up
# return [ '(%s)' % repr(ob) ]
return []
else:
old.append(ob)
return [ repr(ob) ]
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/htrd/howto/aq_order?pp=1 [05/07/2000 11:13:32]
Disaster Recovery + Avoidance
How-To: Disaster Recovery + Avoidance
Created by vernier. Last modified on 1999/11/16.
Disaster Recovery
version 0.4
Here's Hoping to Save You 1/2 day Troubleshooting when you install Products or new versions of zope.
Revision History
0.4: added appendix B: fastcgi for Debian
●
0.3: added appendix A: Beta Zope upgrade on Debian
●
0.2: rewrote sections (2 warnings) based on better experience
●
0.1: description of my first recovery experience
This HOWTO is the result of my experience recovering from un-intended trouble caused by the ZLogger and ZPythonMethod
Products. (nov 6,99) I also include an Appendix on setting up zope 2.1.0 on Debian) I realise now that this HOWTO can be
summarized in two tips:
Warning: When installing a product into the zope library tree, it might change permissions of lib/python/lib/Products and
thus cripple zope causing all sorts of bizarre errors
Warning: When installing a new product, you may need corresponding python modules
I realized this today (nov 4 99), and these should be just tips, However, before this insight, I wrote this HOWTO which might still
be of some general use: (note: I speak from a Debian/GNU Linux perspective)
Here is the recipe for disaster prevention: Absolutely NO WARANTEE. You are on your own. Caveat Emptor. Before you install
a new product and something goes awfully wrong in your zope website, then do these:
●
Make a backup of the database , for example:
cd /var/lib/zope/var
cp Data.fs Data.fs.oct19
●
Make a backup of /usr/lib/zope, for example:
cd /usr/lib
cp -v zope zope.oct19
●
NOW is the time to install your new Product, comforted by your recent backups!
There are several styles of Product installations:
Most of the ones I tried went something like this: (read the READMEs first of course)
cd /usr/lib/zope
tar -xzvof path/Product_name.tgz
(this is the type of Product I speak about in this HOWTO)
●
restart zope with: zopectl restart
now check your zope website: is everything OK? if yes, rejoice and forget about the rest of this HOWTO.
●
critical: Check the permissions of lib/python/lib/Products/ it should be 755 for the directories, 644 for files (i.e. world
http://www.zope.org/Members/vernier/recovery?pp=1 (1 of 3) [05/07/2000 11:13:42]
Disaster Recovery + Avoidance
readable all along the tree)
●
●
zopectl restart ... does it work now?
Check if the relevant python modules are installed. (And no, I don't know of a standard way of doing this... but I provide
a hint for Debian/GNU Linux users at the foot of this document). Install the missing python modules.
●
zopectl restart .... does it work now?
●
If not, then Delete the package from where it got installed, for example:
cd /usr/lib/zope
rm -Rf lib/python/Product/name_of_product
●
Restart zope: zopectl restart
Did it solve the problem? If yes, rejoice and forget about the rest of this HOWTO
●
If not, check your database with tranalyser.py (which is listed under Products on www.zope.org) and try:
python trananalyser.py Data.fs
Does it show any problems like truncations? if so,
cp Data.fs.backup Data.fs
chmod 0600 Data.fs
or truncate it at the suggested byte (tranalyser output) with
split -b #byte Data.fs
cp xaa Data.fs
and try again with zopectl restart
●
if not, then delete the entire zope library:
rm -Rf /usr/lib/zope
and reinstall zope and all the Products you use such as tinytables, squishdot, etc... up to but not including the latest one
which caused the problems.
●
zopectl restart
For me that finally did it.
NOTES:
1. If (like me) you did not follow the correct order above, then you will have to turn your brain on and use good old
common sense and logic to figure out your safest way out of the woods... the recipe above is useful.
2. I found tranalyser.py to be a very nice view of what is in Data.fs (the database) ... and I did fool around at length with
truncating the database with "split -b # Data.fs" which worked but was never sufficient due to something outside the
database having been somehow changed (as described above)
3. (For the Debian types only:) I used debsums zope|grep FAILED and found 38 files that failed the checksum test, but this
seems to be the case even if the zope website is installed totally from scratch. Those files are all python compiled
(probably as part of the installation process)
4. For Debian/GNU linux installations, these are python potato packages I found to be important:
❍
python-bobo
❍
python-bobodtml
❍
python-bobopos
❍
python-pygresql (for postgresql stuff)
http://www.zope.org/Members/vernier/recovery?pp=1 (2 of 3) [05/07/2000 11:13:42]
Disaster Recovery + Avoidance
5. Thanks to Amos who indicated publicly that one of the problems with that website when changing hardware was that he
did not install all the requesite python modules and that caused bizarre errors ... makes me feel less foolish :-)
Appendix A: Beta Zope on Debian
Here are some notes based on my experience installing the latest zope 2.1.0 on a Debian/GNU Linux system:
The ideal is for a Debian maintainer to update the zope package so that this is all done automatically. Please note, that follwoing
the zope instructions directly works but does not store the files in the same places as the current debian zope package; this is why I
provide instructions for those who want to keep the same debian file structure:
●
Make Backup
●
unpackage the tarball in /tmp
●
./install -u www-data
●
cp -av lib/python/* /usr/lib/zope/lib/python
●
cp -av ZServer/* /usr/lib/zope/ZServer
●
cp -v z2* /usr/lib/zope
●
cp -v zpasswd* /usr/lib/zope
●
cd /usr/lib/zope; edit z2.py and change the settings appropriately: especially "here", "PCGI_FILE=/usr/lib/cgi-bin/zope"
and "user=www-data" and see appendix B for fastCGI
●
edit /usr/sbin/zopectl and add "python" in front of the line that starts z2.py
●
it worked for me :-) (nov. 6, 99)
Appendix B: FastCGI and Debian
●
Install the package libapache-mod-fastcgi
●
change z2.py : line about FCGI: FCGI="/var/lib/zope/var/FCGI" or something like that
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/vernier/recovery?pp=1 (3 of 3) [05/07/2000 11:13:42]
Editing Only Properties
How-To: Editing Only Properties
Created by rlgines. Last modified on 1999/12/28.
This How-To is the result of an issue that I ran into on my Zope development site. I have seen it brought up on several occasions
within the Zope discussion group. I can't say that I have seen a decent solution which suited my needs. Hopefully, this solution
will be valuable to others.
The issue at hand is one of editing the Properties of a resource without placing the user within the entire management structure.
I have worked this solution out on an NT platform and have tested it there. Unfortunately, I can not speak to how well it may or
may not work on a *nix platform. In theory, there should be no difference.
Properties.dtml
There are two steps to this solution. The first is to develop an interface which can be used by our users to edit the properties of a
resource without giving them direct access to the remaining management screens or interface.
I did this by making a copy of the properties.dtml file located in the /lib/python/OFS directory. I named the copy
propertiesOnly.dtml. I then edited out everything that that I did not want my users to see. I have included the file here for
completeness. view propertiesOnly.dtml
PropertyManager.py
propertiesOnly.dtml provides the user interface. We now need a mechanism for calling this page. To do this I edited the
Propertymanager.py file.
Change 1
#Existing HTML Line. Leave in place.
manage_propertiesForm=HTMLFile('properties', globals(),
property_extensible_schema__=1)
# Added to support a properties edit only page (keep the section above)
manage_propertiesOnlyForm=HTMLFile('propertiesOnly', globals(),
property_extensible_schema__=1)
Change 2
#This is the original edit method keep this one in place
def manage_editProperties(self, REQUEST):
"""Edit object properties via the web."""
for p in self._properties:
n=p['id']
self._setPropValue(n, REQUEST.get(n, ''))
return MessageDialog(
title ='Success!',
message='Your changes have been saved',
action ='manage_propertiesForm')
# Add Interface used to support just the properties change page.
to the above method.
def manage_editPropertiesOnly(self, REQUEST):
"""Edit object properties via the web."""
for p in self._properties:
n=p['id']
self._setPropValue(n, REQUEST.get(n, ''))
return MessageDialog(
title ='Success!',
http://www.zope.org/Members/rlgines/propertiesOnly?pp=1 (1 of 2) [05/07/2000 11:13:46]
Add this in addition
Editing Only Properties
message='Your property changes have been saved',
action ='manage_propertiesOnlyForm')
At this point I would delete the PropertyManager.pyc file. Stop Zope and restart it. You should now be able to add a call like the
following to any of your DTML Documents or Methods. (remove the #'s)
<#a href="<#dtml-var absolute_url>/<#dtml-var id>/manage_propertiesOnlyForm">edit
properties.<#/a>
I hope this helps. Feel free to send comments and suggestions to Ron Gines
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rlgines/propertiesOnly?pp=1 (2 of 2) [05/07/2000 11:13:46]
FastCGI, virtualhosts and root folders
How-To: FastCGI, virtualhosts and root folders
Created by Roug. Last modified on 2000/01/11.
For previous readers: Previously I had a totally different set of rewrite rules. The new ones are lifted straight from the HOW-TO
for Persistent CGI and they work flawlessly for FastCGI for me.
How it works:
I basically followed kedai's approach, and I used the RewriteEngine to make it work in the rootfolder. This is what I put in the
Serverconfig (beware you must fit these lines into your own file at the relevant places:
LoadModule fastcgi_module
libexec/mod_fastcgi.so
LoadModule rewrite_module
libexec/mod_rewrite.so
AddModule mod_fastcgi.c
AddModule mod_rewrite.c
FastCgiExternalServer /disk1/fastcgi/zope.fcgi -host localhost:8889 \
-pass-header Authorization
Then I created a virtual host:
<VirtualHost 12.34.56.78>
ServerAdmin helpdesk@my.domain
DocumentRoot /disk1/htdocs
ServerName fastcgi.my.domain
ServerAlias fastcgi
DirectoryIndex index_html
RewriteEngine On
RewriteRule .*\.php - [L]
RewriteRule ^/(.*) /disk1/fastcgi/zope.fcgi/$1 [L]
<Directory /disk1/fastcgi>
AddHandler fastcgi-script .fcgi
AllowOverride none
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
The file /disk1/fastcgi/zope.fcgi doesn't have to exist, but the directory most likely most. Otherwise Apache will write a warning in
error_log.
About the FastCGI module
I downloaded mod_fastcgi_SNAP_Oct06 from www.fastcgi.org. As I also want WEBDAV to work I have commented out some
lines in mod_fastcgi.c line 1358:
/* We don't do anything
#if 0
if (r->method_number ==
r->allowed |= (1 <<
r->allowed |= (1 <<
return DECLINED;
}
#endif
but GET and POST */
M_OPTIONS) {
M_GET);
M_POST);
Vanilla FastCGI simply checks if the method is GET or POST and declines if otherwise.
If you want to serve PHP as well as Zope
It is possible to server PHP pages from Apache and the Rest from Zope if you add this line just below the RewriteEngine On:
RewriteRule .*\.php - [L]
You will also want to avoid that people can do funny tricks with your PHP scripts through WEBDAV, so you add these lines
somewhere near the bottom of your <VirtualHost> envelope.
<LocationMatch ".*\.php">
<Limit PUT DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
order deny,allow
http://www.zope.org/Members/Roug/fastcgi_and_rootfolder?pp=1 (1 of 2) [05/07/2000 11:13:48]
FastCGI, virtualhosts and root folders
deny from all
</Limit>
</LocationMatch>
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Roug/fastcgi_and_rootfolder?pp=1 (2 of 2) [05/07/2000 11:13:48]
File Upload
How-To: File Upload
Created by Benno. Last modified on 2000/05/04.
Introduction
This Howto very briefly discusses how to create a form to upload a file and then access this file from python.
HTML Form Data
The first bit you need todo is set up your form in html. The form part should look like this:
<form method="post" action="you_form_handler" enctype="multipart/form-data">
The import part to note here is the enctype part which is different from normal forms.
Now that your form can handle file upload you still need to insert file upload
elements. An example is set out below:
<input type="file" name="attached_file">
This gives you a nice box as demonstrated below:
This should render correctly in any modern browser.
Python Side
This assumes you want todo something with the given file in python. If you need to use
it in DTML or elsewhere I can't really help you unfortunately.
In you python external method you can now reference REQUEST.form['attached_file'] as a
normal file. You can perform things such as read() on the object.
The returned object also has some other helpful properties.
filename
This is the file name of the file which has been uploaded.
headers
This is a block of text representing the MIME headers of the uploaded files. (You
maybe interested in the MIME RFC for more information on these headers.
User friendly headers
The headers are returned as a block of text, you might find it easier if they were
actually a dictionary. Below is some code I have used todo this. It may need
modification, and I haven't actually used it recently, so use at own risk.
def headers2dictionary(self, text):
"""
Taking text in the format of a header block convert to a dictionary
"""
last_dict = None
resultDictionary = {}
if text != None:
for each in string.split(text, '\r\n'):
http://www.zope.org/Members/Benno/FileUpload?pp=1 (1 of 2) [05/07/2000 11:13:51]
File Upload
line = string.split(each, ':')
if len(line) > 1:
last_dict = string.upper(line[0])
resultDictionary[string.upper(line[0])] =
string.join(line[1:], ':')
elif len(line[0]) > 1 and line[0][0] in string.whitespace and
last_dict is not None:
resultDictionary[last_dict] = resultDictionary[last_dict] +
line[0][1:]
return resultDictionary
Conclusion
Well that should be about it! If you have any feedback please email me at
benno@sesgroup.net.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Benno/FileUpload?pp=1 (2 of 2) [05/07/2000 11:13:51]
Filling MULTIPLE SELECTED OPTIONs from database/lists
How-To: Filling MULTIPLE SELECTED OPTIONs
from database/lists
Created by Roug. Last modified on 2000/02/01.
I have a list of URLs that can be in more than one categories. All of this comes from three tables in a MySQL database. What I
wanted to do was to produce a form where the administrator can modify the categories the URL is attached to.
The M-M table holds the many-to-many relation between the URL and the category. It has the columns catid and linkid. Its name
is c_l.
First I wanted to make a list of all possible categories the URL can be attached to. That was real simple. I created a SQL-method
called list_categories, that selected id and title from the category table. Then I built my <SELECT> statement.
First attempt
<SELECT NAME="categories:list:int" MULTIPLE SIZE="8">
<dtml-in list_categories>
<OPTION VALUE="<dtml-var id>"><dtml-var title></OPTION>
</dtml-in>
</SELECT>
The problem with the code above is that it doesn't show the categories the URL already is attached to. Therefore I had to become
creative as the solution to this "challenge" has not been documented before on Zope.
First I had to create a SQL method that listed the category id's for the link the user is modifying. All the method does is select
catid from c_l WHERE linkid=<dtml-sqlvar id type=int> Since I will be referencing the list for every single category I have I
save the output to a list. That is the first four lines of the script. Then all I have to do is stick an if statement in there with the until
now in the Zope community undocumented in operator.
Second attempt
http://www.zope.org/Members/Roug/select_with_multiple?pp=1 (1 of 3) [05/07/2000 11:13:55]
Filling MULTIPLE SELECTED OPTIONs from database/lists
<dtml-call "REQUEST.set('selectedCats',[])">
<dtml-in list_selected>
<dtml-call "selectedCats.append(catid)">
</dtml-in>
<SELECT NAME="categories:list:int" MULTIPLE SIZE="8">
<dtml-in list_categories>
<dtml-if "id in selectedCats">
<OPTION SELECTED VALUE="<dtml-var id>"><dtml-var title></OPTION>
<dtml-else>
<OPTION VALUE="<dtml-var id>"><dtml-var title></OPTION>
</dtml-if>
</dtml-in>
</SELECT>
What we want to do now is store the modified values in the database. For this I have created a complex SQL method that first
updates the link information such as title, description etc. Then it deletes all references in the M-M table where the linkid matches
the record we are updating. And the last part inserts a row into the M-M table for each category the user selected.
SQL Method to update link information
Id
Arguments
update_link
id title urllink description topchannel categories:list:int=""
UPDATE link SET
title=<dtml-sqlvar title type=string>,
urllink=<dtml-sqlvar urllink type=string>,
description=<dtml-sqlvar description type=string>,
topchannel=<dtml-sqlvar topchannel type=int>
WHERE id=<dtml-sqlvar id type=int>
<dtml-var sql_delimiter>
Query template
DELETE from c_l
WHERE linkid = <dtml-sqlvar id type=int>
<dtml-if categories>
<dtml-in categories>
<dtml-var sql_delimiter>
INSERT INTO c_l
VALUES (
<dtml-var "_['sequence-item']"> ,
<dtml-sqlvar id type=int>
)
</dtml-in>
</dtml-if>
Then I created the DTML method that will be called from the form tag.
Calling the SQL Method
<dtml-with REQUEST>
<dtml-call update_link>
</dtml-with>
Since I accidentially named the key of the record id and that collides with the document id, I had to push the REQUEST
namespace to the top of the stack.
Note: The SQL Method is very complex, and one could argue to split it up into three simple SQL methods, but the significant
benefit of using one method is that for those databases that support it an automatic transaction start/end (So I've heard) is created
http://www.zope.org/Members/Roug/select_with_multiple?pp=1 (2 of 3) [05/07/2000 11:13:55]
Filling MULTIPLE SELECTED OPTIONs from database/lists
around the SQL method and you will therefore get a rollback should something go wrong.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Roug/select_with_multiple?pp=1 (3 of 3) [05/07/2000 11:13:55]
Form Variable Types and Typechecking
How-To: Form Variable Types and Typechecking
Created by Zen. Last modified on 1999/10/19.
Most of this information was stolen from the old Trinkets Tutorial by Jim Fulton (Digital Creations), which will probably
disappear when the old Zope.org site is no more, so I'm preserving some still relevant sections here for posterity.
Form variable types
Normally, Zope copies values from form fields as strings. Zope can be told to perform type conversions automatically by
including type information in form variable names.
In the form generated by the downloadForm method, the input tag for the hidden field, secret, has a name attribute of
secret:int. The :int part of the name indicates to Zope that the field should be converted to an integer before passing it to
the published method.
Currently supported type conversions
Type
float
int
long
string
required
text
lines
tokens
date
list
tuple
record
ignore_empty
default
records
Python Object Passed
Float
Integer
Long integer
String
Non-blank string
String with carriage-return newline pairs converted to newlines.
List of values separated by newlines.
List of values entered as multiple space-separated tokens in a single field.
Date Time instance
List
Tuple
Record
Ignore empty
default
List of records
Examples of some supported types
RECORD
<INPUT TYPE="text" NAME="person.name:record" SIZE="30">
The input tag for the name field is named person.name:record. The :record part of the name indicates to Zope that the
field should be converted to a record before passing it to the published method. A record variable will be created called person
and it will have an attribute called name.
http://www.zope.org/Members/Zen/howto/FormVariableTypes?pp=1 (1 of 2) [05/07/2000 11:13:58]
Form Variable Types and Typechecking
IGNORE_EMPTY
<INPUT TYPE="text" NAME="person.email:record:ignore_empty" SIZE="30">
When the email form field is left blank, :ignore_empty skips over the variable rather than returning a null string as its value.
When the record person is returned it will not have an attribute called email if the user did not enter one.
DEFAULT
<INPUT TYPE="hidden" NAME="person.toppings:record:list:default" VALUE="All">
<select multiple NAME = "person.toppings:record:list:ignore_empty">
<option>Bacon</option>
<option>Cheese</option>
<option>Tomato</option>
<option>Pepper</option>
<option>Egg<option>
</select>
The default type allows a specified value to be inserted when the form field is left blank. In the above example, if the user does not
select values from the list of toppings, the default value will be used. The record person will have the attribute toppings and its
value will be the list containing the word All (if the field is empty) or a list containing the selected toppings.
RECORDS
<P><H2>Member #1<H2></P><BR>
<P>Please enter your name:<BR>
<INPUT TYPE="text" NAME="member.name:records:ignore_empty" SIZE="30"><BR>
your email:<BR>
<INPUT TYPE="text" NAME="member.email:records:ignore_empty" SIZE="30"><BR>
your age:<BR>
<INPUT TYPE="hidden" NAME="member.age:int:records:default" Value="0"<BR>
<INPUT TYPE="text" NAME="member.age:int:records:ignore_empty"></P><BR>
<P><H2>Member #2</H2></P><BR>
<P>Please enter your name:<BR>
<INPUT TYPE="text" NAME="member.name:records:ignore_empty" SIZE="30"><BR>
your email:<BR>
<INPUT TYPE="text" NAME="member.email:records:ignore_empty" SIZE="30"><BR>
your age:<BR>
<INPUT TYPE="text" NAME="member.age:int:records:ignore_empty"><BR>
</P>
In this sample the form asks for a name and age for two different members of a club. The :records part appears in each input
tag. This type will create a list called members that will hold records (in this case it will hold two records). Each record can have
the attributes name, email and age.
Note:
Before creating a second record and adding an attribute, Zope gets the last record in the list and checks to see if that record has the
attribute. If the first record doesn't have the attribute, it inserts the attribute and value instead of creating a new record. For
example, if the user entered a name and age for Member #1 and only an email for Member #2, the list will have only one record.
That record will contain the name and age attribute from Member #1 as well as the email attribute from Member #2.
Type checking
If a value entered in a form does not match a type specification, or if a form value is omitted, Zope automatically generates an
error response. This frees the programmer from writing some error checking code. Note that as of Zope 2.0.1, this error response
is your standard Zope error/traceback screen, so I wouldn't recommend using it to enforce numeric or date types unless you
combine it with fancy JavaScript or just don't care.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Zen/howto/FormVariableTypes?pp=1 (2 of 2) [05/07/2000 11:13:58]
FreeBSD FreeTDS and SybaseDA
How-To: FreeBSD FreeTDS and SybaseDA
Created by TheJester. Last modified on 1999/11/25.
How to get the Sybase DA Adapter
working with FreeTDS
You will need to know at least some Python, and C to attempt this
I assume you know how to use basic tools like tar and rpm, if not try using man(1)
The links below do not directly point to individual files, Sybase in particular can require a license, rather they point to the websites
required to retrieve the files.
Caveats READ CAREFULLY
If the mixing of Open Source and Commercial Tools offends you, you should go away now. I am a Software Engineer I will use
any tools at my disposal to get the job done.
The diff files available are done straight from my distribution, I haven't really made any effort to clean them up, as it is a gross
hack. In fact I have hacked the FreeTDS code to only talk TDS 4.2 (you will have to find where I commented out the TDS Code
and patch it back to the level you want)
I have placed call backs in the DA code that send out error messages. This is because this version doesn't have ct-diag. The
call-backs are simple to remove, but, the debug output will be useful to you until you're sure things are working.
This is not meant to be anything more than what it is -- a hack to let me get my Zope/FreeBSD box talking to a legacy MS-SQL
6.5 Server. I am not responsible for any data loss, productivity loss, grey hairs, loss of libido, or any adverse side effects
associated with you attempting to do what I have done :-)
This is not a supported product, either by me, or by Digital Creations
If you get it working, I'm very happy for you. :-)
Required Files
Item
Version
FreeTDS
Version 0.47
Sybase Linux Distribution sybase-ase-11.0.3.3-2.i386.rpm or later
Sybase DA
V2
Sybase Patch File
n/a
FreeTDS Patch File
n/a
Procedure (from what I remember)
Make a scratch directory, all commands will be relative to this directory
Unpack your freetds distribution to freetds
Unpack your Sybase distribution Note: Strip off the absolute pathnames
Unpack your SybaseDA distribution
http://www.zope.org/Members/TheJester/SybaseDA?pp=1 (1 of 2) [05/07/2000 11:14:01]
FreeBSD FreeTDS and SybaseDA
You should now have a freetds directory, an opt directory, and a lib directory
cd to the freetds directory and do a ./configure Not sure this is necessary since the patch will clobber your source with my
configuration... cd ..
Copy the contents of opt/sybase/include to freetds/include
Unpack and apply the Sybase Diff File.
Unpack and apply the Freetds Diff File.
At this point you should be able to compile the FreeTDS library. A make install will install it into /usr/local/freetds unless you
changed the install path when you did the configure.
Unpack and apply the SybaseDA Diff File.
cd to lib/python/Products/SybaseDAv2/src
make the DA
test it as per the README file.
If all goes as planned cp the SybaseDAv2 directory to your Zope installation
and restart. You should be able to add Sybase connections.
What to do if it doesn't work
Debug it! Fix it! Do another How-To :-)
I've saved you quite a few hours of work getting it to this point, so feel free to improve on my efforts.
Other Comments
If I had the time I'd probably do one of two things;
Make the FreeTDS code work using the standard FreeTDS header files
Make a FreeTDS Adapter. This is probably a worthwhile project in it's own right (in the spirit of Open Source)
I think it really is a shame that you came here :-) It's much more fun to start the adventure on your own and see how far you can
get.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/TheJester/SybaseDA?pp=1 (2 of 2) [05/07/2000 11:14:01]
From root to subfolder without a redirect
How-To: From root to subfolder without a redirect
Created by cba. Last modified on 2000/04/22.
I have a bunch of folders at the top of my Zope hierarchy, but I want visitors to go to a specific subfolder. At the same time, I'd
rather just tell people my URL is "www.mydomain.com", rather than "www.mydomain.com/target_folder".
For a while I just put a redirect in the index_html in my Zope root, like so:
<!--#call "RESPONSE.redirect('target_folder/index_html')"-->
But then I realized there's a better way, using the Magic of Acquisition. Here's how I seems to have worked around this issue:
Change the index_html method in your top level Zope folder to look like this:
<!--#call "RESPONSE.setBase('http://www.mydomain.com/target_folder')"-->
<dtml-with target_folder>
<dtml-var index_html>
</dtml-with>
Now browsers to http://www.mydomain.com will see index_html in target_folder, and all other links should work correctly, too.
Update
Several folks have pointed out that I could have achieved similar results using the SiteAccess product. I started using SiteAccess
when I added another domain name that I needed to host.
Unfortunately, the SiteAccess documentation was not as clear as I would have liked. What follows is my quick-n-dirty summary
of the necessary steps.
1. Create a method in your root, call it anything you want. It should look similar to::
<dtml-unless "REQUEST.path and REQUEST.path[0][:6]=='manage'">
<dtml-in "[('skylance.loadlinux.com', 'LoadLinux'),]">
<dtml-if "HTTP_HOST[-_.len(_['sequence-key']):]==_['sequence-key']">
<dtml-call "REQUEST.path.append(_['sequence-item'])">
<dtml-return " 'ignored' ">
</dtml-if>
</dtml-in>
</dtml-unless>
2. Add a "Set Access Rule" in your root folder. It will ask you to type in the id of the method you created in step 1. It will
then set up forwarding.
3. In your LoadLinux folder, add a "SiteRoot". I think you can accept the default values, or you can use
http://skylance.loadlinux.com/ as the base and /LoadLinux as the path.
4. Now if you surf to http://skylance.loadlinux.com/ you should end up in your LoadLinux folder (but the URL will be
http://skylance.loadlinux.com/)
5. If you can't add the "Set Access Rule" and "Site Root" objects, then either you haven't installed the SiteAccess product
correctly, or you need to restart Zope.
6. If your site is totally disfunctional - i.e. demands authorization but nothing works - and you have installed or upgraded to
Zope 2.1.5 or higher, make sure you have SiteAccess 1.0.1 or better!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cba/URL_subfolder?pp=1 [05/07/2000 11:14:03]
Gain Zope Enlightenment By Grokking Object Orientation
How-To: Gain Zope Enlightenment By Grokking
Object Orientation
Created by mcdonc. Last modified on 2000/02/06.
"But as the open-source world is more (for the lack of a better word) idealistic and bent on presenting things in a unique
light, you see Zope repeatedly referred to as an object publishing system, whatever the hell that means."
-- Kevin Reichard, A Review of The Zope Application Server LinuxPlanet, December 1999
Audience
This document is about Zope and object orientation. If you're a nonprogrammer or a programmer who has only a passing
understanding of object orientation, this document is for you. If you're an "OO zealot" already, it's not going to be very helpful. Go
watch television instead.
Introduction
My first experience with Zope was about eight months ago. I was basically a Perl programmer -- not a particularly good one,
either. While looking for an intranet solution for a client who wanted to "adopt a Linux strategy", I stumbled upon Zope. It was
clearly much more comprehensive in scope than anything I could ever hope to come up with, so I embarked on the journey of an
implementation.
I remember from that time that DTML was the enemy. Every time I thought I "got it", Zope made it clear that I didn't. Surrounded
by piles of dog-eared How-Tos and the Zope Guides I toiled for an inestimable amount of time to understand the subtle difference
between <dtml-var mymethod>, <dtml-var "mymethod">, and <dtml-var "mymethod()">.
I'll let you in on a little secret: I'm no freaking genius. I have serious and embarrassing problems doing long division. If a
calculator isn't handy, don't look my way when it comes time to divide 60 in to 86,400. I have to study the instructions when I put
together prefab furniture like there's going to be a prefab furniture inquisition the next day. When I'm done, I tape the directions to
the bottom of the furniture, so when the time comes that I have to dissemble the piece, I'll be able to take it apart without hacking a
leg off. I'm clearly no Stephen Hawking. But, by God, I am persistent!
In the Mesozoic era of my (as yet incomplete) path to Zope Enlightenment, I printed out the contents of months worth of mailing
list posts and read reams of them on the train back and forth from my job. I knew Zope was powerful and mysterious, and I vowed
to myself that I would unlock this power and clarify its mysteries by hitting it like a punching bag. Luckily, this "work hard, not
smart" strategy paid off in the form of a shaky, immature understanding of Zope's various syntaxes. But it was only because I was
almost fanatically persistent at gathering and assimilating relevant information that I was able to attain even this level of
ignorance.
I loved the product, I became obsessed with it. I shortly found myself begging for a job at Zope's publisher, Digital Creations and
to my surprise and delight, they took pity on me and let me sit in a cubicle in Fredericksburg, Virginia, USA. I now get to stare
intently at a computer screen for ten hours a day, type a lot of funny-looking words in rows next to each other, look at the pictures
in a lot of big thick fancy books, and otherwise generally pretend I understand a lot of stuff. I love it. They try to stop me from
coming to the office by saying things like "leave immediately, we showed you the restraining order yesterday!" I keep coming in.
Sheesh. It's not like you can't fake a restraining order. But I digress.
Since our first spats, DTML and I have kissed and made up. Sort of. I still get tangled up in it like an otter in a plastic six-pack
ring sometimes, but I'm getting better. As I look back over the time spent so far, one thing is clear: for duffers like me, the Zope
learning curve is pretty steep. It's clear that there are plenty of syntax issues and implementation-specific warts to overcome. But,
in my opinion, the real battle between Zope and the Common Man is not one of syntax, it's instead one of terminology and design
methodology.
Say What?
Zope is a fantastic environment on which to base almost any web effort. It is superior, in my (admittedly biased) opinion, to any
other comparable product. It is unique. It's a killer app. It crushes most competing commercial applications in scope and depth. It's
beautiful. It is a mechanism by which you can put an application on the web quickly and it is a tool with which you can allow
folks to more granularly and securely manage Web content. It's open source and it's free. It's scalable. It's got a wide audience. It's
a tough product to beat on many levels.
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (1 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
But it has flaws. One of its particularly onerous flaws is that it currently lacks clear documentation suitable for consumption by a
programmer whose best efforts have been executed in Perl or VBScript or other primarily "procedural" programming languages.
Digital Creations has acknowledged that documentation for the product trails behind its development effort by a considerable
stretch, and it is working towards marshalling adequate resources to properly remedy the "documentation problem". Some clever
folks have banded together to form the Zope Documentation Project which has as its goal the furthering of friendlier
documentation for Zope as well.
Here's the "real deal" for now, however. It's going to be hard. You're going to need to invest time and effort. You won't get much
handholding from the documentation. Enlightenment will come slowly. You will need to rely on scattered bits of information on
the Zope site and on the Zope mailing list and piece them together to get your world view. You're going to drive yourself nuts
trying to make "that one thing" work. In short, you're going to get ticked off. Fast.
Now, please understand that this isn't always necessarily Zope's fault. Zope has been engineered by people who are different than
you and me. We just want to get the problem solved fast. (My favorite ".sig" on Usenet is that of a guy in some of the Perl
newsgroups that reads 'I don't care if it's ugly. DOES IT WORK???!') We don't care what the hell goes in the code as long as it
works when we "ship it." These weird Zope people, on the other hand, want to get the problem solved once and forever, thereby
letting other people on their own solve the same problem fast by reusing their code. They are also concerned with "time to
market", they're not in this business to help you build "three-yeared architectures." They just go about dealing with the tough
problems in a different --and arguably better-- way.
With this as a context, please know that I found that much of my early hard work in trying to understand the product was
misplaced. You probably will too. What I lacked was the "big picture Why" of the product. I was working so hard to understand
the syntax and the flavor of the product's implementation that I failed to get a grounding in the basis of its architecture.
Understanding Zope terminology requires an understanding of the architectural problems it's trying to address in the world of
Web-application-building. While other competing products billed as "application servers" go about doing the same sorts of things
a dramatically different way, Zope is an application framework which lets you publish objects on the web.
Let's repeat that, because it's important. Zope lets you --quite literally-- publish pieces of code that are referred to as objects
through a web interface. This is its raison d'etre. Z Object Publishing Environment is it's name, and it takes it seriously. We of
course can't tell the suits that this is the real goal of Zope, because the word "object" makes them nervous, so instead we've settled
on "application server" and/or "content management system" as a descriptive phrase for the product. They understand those
phrases because they've read them in Information Week. Thus our tag line is a concession to marketing reality. But since there's no
suits around right now as far as I can tell, we can talk about what Zope really is.
Currently, the popularity of Perl and VBScript for building Web applications is unassailable. Perl is chosen commonly because it
lets people put something together quickly with a minimum of planning and ceremony. This is unquestionably beneficial. But it
also has drawbacks. When you slap something together in Perl (or in any language), the code over time becomes hard to maintain
as functionality is bolted on to it, as people who authored it leave the company, etc. Most Web applications I've seen (and
authored!) in Perl approach problems from the perspective of "OK, I have this database over here I need to get stuff from and
show it to the user in his browser, how can I do that the fastest way?" The coding starts as soon as you figure that question out.
Additionally, you commonly start "from scratch" when building an application. Sure, you might use a couple modules that
someone else has kindly contributed to CPAN, or you might have thought ahead and packaged up some functionality in your own
modules. But when it boils down to you, probably 80% of what you do is the "same old stuff" over and over again with a slightly
different flavor, and you rewrite this stuff from scratch each time.
For example, take "untainting" form responses that come back from HTTP requests in a query string. In a common Perl script, you
may have a subroutine that takes a value submitted from a form and checks it against a regular expression that makes sure it
doesn't contain any "bad stuff" that might crash your app or be a security problem. This is such a common task that it's tempting to
write a module to do it. You can do this. I did. But it lacked. I found myself needing to add features to it for a single
implementation that didn't make sense in the context of the module, and I ended up cutting and pasting stuff from the module right
into my code and tweaking it slightly. When it had bugs, well, I just went and fixed them, right in that implementation-specific
code. Why didn't I just solve it once and build it in to my application framework, available for every application I wrote without
modification? Because it's hard to do, that's why. Geez, I'm no frigging Einstein. I can barely balance my checkbook. I didn't want
to try to build an application framework. It's a task that's just way beyond my capabilities -- I'm just not that smart. And, you see,
this is where it starts to get interesting.
Zope tries to solve some of these kinds of problems for you once and forever. It does this by making heavy use of object
orientation and structured design. It solves some of these problems for you in a generic way by including code that contains
Heavy Mojo and Strong Kung Fu. This is the code that gets handed down from the Mountaintop by the Zope Gods. You are
expected to make use of this stuff in a more specific way within your applications. In order to do this properly, you do need to
understand some of the terminology the Zope Gods use. When a Zopista or Pythoneer uses the term "subclass", you may shrink
back in fear, confident that this is a term that is reserved for Alpha Geeks only. But before you rake yourself over the coals trying
to understand the syntax of DTML and the wheres and whys of the Catalog, it's wise to get a grounding in the basics of object
orientation (OO, for short). Without it, you're going to drive yourself absolutely bonkers attempting to decipher the gibberish that
passes through the mailing list and other Zope resources.
If you persevere, after a couple of months of working with Zope in an OO-ish way, you'll come out of your grimy cave with a
heightened conviction that you're still a moron. However, the time you've invested will pay terrifying dividends in your
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (2 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
productivity. You will crush your coworkers' best efforts trivially by putting something together in Zope in hours that would have
taken them weeks in raw Perl or ASP. You will be able to intelligently answer questions like "Why doesn't my SQL Method work
when I call it via URL traversal?" You will factor content like nobody's business. Customer requests to add features will no longer
always require you to make major architectural changes to your applications. You will have become, in short, a minor Zope deity.
You will eventually grow to be a true Zope God in time.
My worst fear, however, is that you'll never make it this far. I fear you'll see the "traceback error that broke the camel's back", get
discouraged, and slink back to comp.lang.perl.misc or the Microsoft Knowledge Base confident that "those Zope people
are nuts and I'm glad they don't have my home address." I'm hoping I can help a little bit by getting you excited and productive
more quickly through explaining some of the subtleties Zope as an object publishing system in plain words instead of typical OO
and Zopish doublespeak. Hopefully this document will serve as a sort of "glue interface" between reality and the Zope world at
large, and it will demystify some of the terminology used within Zope resources.
Python
Zope is written primarily in the Python scripting language. Python is an exceptionally simple language. Its syntax is easy to write,
read and understand. It's good for use in the types of problems you may more typically solve using Perl or UNIX shells or similar
tools. You can write great one-off and throwaway scripts using Python. For example, you can write ten lines of code that busts up
a big file into pieces and formats it in a special way for you. You can write such a script in ten minutes, then just throw it away,
because you'll probably never need it again. It's not quite as useful as Perl in this capacity (you need to type more), but it's damn
close.
But Python, unlike its many of its scripting language cousins, it's also a naturally "scalable" scripting language. Once you start to
dig a little deeper in to it, you'll see that it encourages a programming style that values code reuse and object orientation. Applied
properly, Python allows you to build huge, towering applications quickly out of lots of little pieces and snippets, each of which has
its own very formal, foreign, and impenetrable name. I don't want to knock any other languages here, that's not my intent, you can
clearly build big, beautiful applications in almost any language. Perl versions after 5.000 include much of the same functionality
and VBScript's built-in functionality leans towards object orientation. But it's my assertion that few Perl or VBScript programmers
understand how to make use of these features, and instead code in a purely procedural style. If you're used to a procedural
(sometimes called structured) programming style where the goal is to code things quickly, such as that used most CGI
programming projects in Perl or ASP apps in VBScript, you're going to get a little lost in the intricacies of building applications
out of pieces of code that can stand alone.
If you're like I used to be, you will have absolutely nothing good, bad or indifferent to say about the object oriented nature of
Python or any of that other stuff, because none of it really helps you get your job done. Your opinion may run to the tune of "at the
end of the day, fancy names and formal structures don't get the screaming customer off my back. Now get out of my face, I need
to finish this whooziwhatzis in two hours or I'm gonna get ripped a new one." I agree. Been there. But bear with me. This stuff is
designed to help you work faster, better, and cheaper not to slow you down or to mire you in computer science gobbeldygook.
Time spent up front will pay off big time in the end.
You don't necessarily need to know Python before you begin to use Zope. I didn't. But boy, it helps. You'll probably want to pick
up a copy of "Learning Python" [Bibliography] as a reference for some of the syntax used within Zope, as it actually belongs to
Python.
Objects
Object orientation is largely defined by the process of breaking a big problem down into manageable pieces. Furthermore, it
promotes the principle that these pieces should work as independently as possible from one another. It does this by dictating that
you should design your code using objects. Zope is an example of a highly object-based application.
In a typical procedural application, you will have two things.
●
Code. This will obviously be your Perl scripts or your ASP pages or what-have-you.
●
Data. This will be stuff that you want to store, commonly in a relational database or filestore.
In a canonical OO application, and sometimes in Zope, you will have one thing:
●
Objects. Objects will store both your code and your data.
Abstractly, objects are representations of real-world things. A plane. A boat. A planet. Spools of cable. Whatever. Basically,
anything that is a noun can be represented as an object. In common applications you'll have a customer object, a contact object, an
account object, maybe a news item object. In Zope, almost everything is an object. Folders are objects, HTML content can be
treated as an object, Zope "Products" are objects, ZClasses are objects, ad infinitum. Breaking your problem space into objects
makes code easier to write because you only have to worry about a single entity while writing the code, making it easier to define
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (3 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
smaller problems within that limited problem space.
An object is defined by what it knows, and what it does.
What an object knows is defined as an attribute (or sometimes a property) of an object. For example, a salt shaker object might
"know" that it is a quarter-full. The data point "a quarter-full" is an attribute of the salt shaker object. This might be represented in
code as saltShakerFillLevel = "a quarter-full". In Zope, maybe a Folder has a property named
saltShakerFillLevel that is set to a certain value by the content manager. Same thing! Lo and behold, if you attach enough
attributes to arbitrary objects, a collection of related objects starts to smell a little like a database, doesn't it? This is what I mean
when I say that objects store data.
What an object does is defined as a method of the object. For example, even though the salt shaker object knows that it's a
quarter-full, it needs a mechanism to report that fact to an asker. Why, you say, can't I just ask what an object's
saltShakerFillLevel value is instead of constructing a whole method to report just that one little piece of data? Because
attributes can be changed over time within an object, it doesn't make sense to just walk up to the object and try to beat it up for its
saltShakerFillLevel. Somebody might have changed the object's internal structure, and it might now be called
saltShakerVolumeLeft or somesuch. Therefore, it makes more sense to construct a method by which the salt shaker object
can tell you its fill level, independent of the mechanism by which it computes that attribute. This is a real benefit in OO
programming because you can construct objects that expose a predefined interface via methods to callers, but you retain the
freedom to change the internal structure of the attributes that are searched to provide that information to the caller.
In procedural programming, if a routine expects to see a global variable named Saltshakerfilllevel, you won't easily be
able to change that variable name in the future unless you go and change it in all the places in your code in which it appears. Not
as much so with OO. Wisely-constructed 00 applications are loosely coupled, meaning that an implementation of an object may
change without negative effect to the system as long as its interface to callers remains constant. An example of highly coupled
objects, however would be two objects who depend on each other's actual implementations to do their work. Highly coupled
objects might, for instance, go in and just try to grab each others' attributes without going through any sort of method interface.
This negatively impacts the reusability of the respective objects' code base, because the objects depend on each others' actual
implementations. When objects depend on each others' implementations, it makes it hard to decouple them, and can result in
spaghetti code. A canon of OO is to isolate your code in objects so you can reuse them over and over again by just plugging them
in somewhere independent of other objects in a system.
A method of an object might also do something other than report back to a caller. It can go do some computation and return
nothing at all. You could tell a space shuttle object, for instance, to "lift off" without asking it for a status on the way up. (This is
sometimes called a function). An object can even maybe go instantiate some other objects. A fish object might be told to give birth
to a hundred other fish objects.
You'll often see methods denoted in this kind of syntax: getSaltShakerFillLevel(). The parenthesis at the end of the
method representation denotes that it may accept arguments, which are values that can be passed in to the method to clarify the
question. For example, if we had a multinational salt shaker, we might want to ask it to give us its fill level in different units:
getSaltShakerFillLevel(unit="milligrams")
or:
getSaltShakerFillLevel(unit="teaspoons")
The method can be given some smarts to provide the proper response based on the question.
So what does this mean in the context of Zope? One of the nice things about Zope, and something that's in all of our marketing
material is that we provide an "object database". Strictly speaking, to develop an application in Zope, you never really need to
interface to an external database of any kind. Instead, you have the option to store your data exclusively in the Z Object Database
(ZODB), which is an integral part of Zope. As a matter of fact, you can't really run Zope without using it. Oh, sure, you can hook
up Oracle to Zope, but you don't really absolutely need to if you don't already have stuff in Oracle that you want to get at from
Zope or if you're writing an application that doesn't need to share its data with other non-Zope apps. On the other hand, you'll
probably never write an application in Zope that doesn't make use of the ZODB. It's at the core of Zope and it's what makes Zope
different than all the other products it competes with.
The ZODB takes care of all the niggling little details of setting the right ones and zeroes that make your objects persistent.
Persistent objects are just that. They're persistent. They don't go away. If you've done any development, you know that when you
write a program and you set a variable in your code, as soon as you stop the program, generally that variable and its associated
value "disappears". Lost forever. Not so with Zope. The ZODB "remembers" the state of most objects, including their methods,
their attributes, and other associated metadata, so that when you shut Zope down, the next time you bring it up, all that stuff will
still be there, and your salt shaker will still be a quarter full. This is also why we can unflinchingly call it a database even though it
may not seem much to you like the databases you're used to.
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (4 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
Herein is also the crux of what makes Zope programming different from procedural programming ala Perl and/or VBScript. In
procedural programming, generally you write some code that goes and rudely grabs some data from a database somewhere, your
code does some stuff with it, and maybe goes back and rudely updates the database with some changes. In good Zope
programming, however, objects ask and tell other objects for or about information via methods. Once this is done, your job as a
programmer is done too. You don't have to worry about writing the proper SQL code to grab the data from the database, then
writing code to go do some stuff with the data, then writing some more SQL to go update the data ad infinitum. Ecch! Ptthuy!
Drudgery! Instead, you just need to worry about making objects interact with each other so they tell and ask each other the right
things at the right times. The rest is taken care of for you through persistence and black-box-type stuff inside the ZODB that you
generally need not concern yourself with.
I should qualify this little rant by saying that you can just as easily use Zope as a procedural programming system to access
relational datastores. You can do the old write a SQL call, do some stuff, write another SQL call dance within Zope. This is
somewhat akin to using a Porche to carry a load of melons to sell at the market, but we support it very well. Digital Creations
often writes a lot of applications like this for customers. Lots of people use it this way, for better or worse.
Anyway, the point I'm desperately trying to make here is that Zope treats almost everything that gets defined inside an application
as an object. That's why, for example, a DTML Method is named what it is. It is a method. It's a method, in many cases, of its
containing Folder object. It accepts arguments, just like most other kinds of methods, and it returns a response, just like most other
kinds of methods. The arguments it accepts are in the form of key-value pairs passed to it, and the response is the HTML it
generates. Simple.
Classes and Instances
Have you ever taken a philosophy class? If so, you'll probably dimly remember something about Plato's Forms. One of Plato's
Forms was Beauty. Plato's Beauty Form was the essence of beauty, but it wasn't tangible. It was the abstract representation of
Beauty. All the things that are beautiful in the world, according to Plato, were derived in some way from the Form of Beauty.
Flowers, mountains, scenic rivers and so on shared a commonality: they were all derived from the Form of Beauty. The Form of
Beauty, in turn, generated all beautiful things.
Now what the hell am I talking about? And what does this have to do with object oriented programming and Zope?
Well, classes are a lot like Plato's Forms. We already talked about objects. Unlike many philosophical discussions, in terms of
computer programming, it's not masturbatory to talk about the origin of our objects.
Classes prototype object instances. OK. Let's repeat that one. Classes are bits of code that implement a prototype of an object
instance. When I say "prototype", I'm not using the word in the classical engineering sense of a throwaway construct. I'm using it
in the sense that a prototype is the entity on which something is based. Object instances are based on classes, therefore classes
prototype object instances.
Enough of all this doubletalk, what's class really then? If we compare an instance to a class, and continue this unbearably hokey
Plato metaphor, a class is to an object instance as Plato's Beauty is to a flower. A class is the template on which an object instance
is based. Therefore, you can sort of think of classes as generators of objects. They don't themselves really do anything except
provide a mechanism and an interface to generate the objects that are based on them.
So let me see one, you say. No problem! I'll demonstrate in Python:
class Spam:
def __init__(self, cansize="2 lbs", texture="sticky"):
self.cansize=cansize
self.texture=texture
def showspam(self):
print "I am a can of Spam. My size is " + \
self.cansize + " texture is " + self.texture
Now what in God's name does that do, you ask?
Let's say I have a need to keep track of three cans of Spam. The first can is 2lbs, sticky. The 2nd can is 4lbs, mildly repulsive. The
3rd can is 10lbs and is like dog doo.
I could just put all this information in a table in a relational database. You know, columns for weight and stickiness, rows for each
can of Spam. But watch what happens in OO-style programming where the data is kept side by side with the code:
myfirstcan = Spam(cansize="2lbs", texture="sticky")
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (5 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
I just created a new Spam object. It's 2lbs and sticky. I'll go make some more:
mysecondcan = Spam(cansize="4lbs", texture="mildly repulsive")
mythirdcan = Spam(cansize="10lbs", texture="like dog doo")
Lo and behold I now have three object instances that are based on the Spam class. We don't really have three cans of Spam, of
course, but we have objects that represent them pretty well (what else is there to know about Spam but how much and how bad?)
Let's say I want to get a report from one of my instance objects. I'll just go ask it:
print mysecondcan.showspam()
I am a can of Spam.
repulsive
My size is 4lbs texture is mildly
While I'm going to leave it up to the textbooks to describe the guts of what all the code represents, the point is that we have a
class. This class defines an initialization method that generates an object instance and sets some attributes on the generated object
from arguments passed in to the object (self.cansize gets set to "4lbs", self.texture gets set to "mildly repulsive").
We can generate an arbitrary number of Spam objects based on our spam class. We've already made three: myfirstcan,
mysecondcan, and mythirdcan. We can go add myfourthcan, ad infinitum.
Great, you say, how does this help me when the stew hits the fan?
Let's say you've got a relational database and a procedural application that goes and grabs columns from the database in multiple
places within the application. And you've got a table structure something like:
myfirstcan
mysecondcan
mythirdcan
Size
2lbs
4lbs
10lbs
Texture
sticky
mildly repulsive
like dog doo
You can write SQL to grab this stuff out of the database, no problem. Let's say you've generated a piece of code that does the
equivalent of our showspam() method. It goes in, gets a row, and spits out:
I am a can of Spam.
My size is 10lbs texture like dog doo
Seems ok so far. But what happens when you've got multiple applications accessing the same database? Let's say that you're
servicing both the Spam marketing department and the Spam shipping department, and you've written an application for each one
that needs to get to that table. What will you need to do when your requirements change? For instance, let's say we've got a
requirement to add a column "SHAPE" to the Spam table. Both the marketing department (running your application "SellSpam
2000") and the shipping department (running "ShipSpam 2000") want access to this new data.
I'll tell you what you're going to do. You're going to first go change the database schema. Then you're going to go in with your
editor and change BOTH SellSpam 2000 and ShipSpam 2000 to work in the desired way against the new schema. Don't tell me
otherwise, I can see you doing it now.
But what of our object-oriented example? We don't need to go in and change a steenking bunch of separate crusty old programs
we have lying around! No sir! We just go change the class to represent the new data and reporting structure:
class Spam:
def __init__(self, cansize="2 lbs", texture="sticky",
shape="round"):
self.cansize=cansize
self.texture=texture
self.shape=shape
def showspam(self):
print "I am a can of Spam. My size is " \
+ self.cansize + " texture is " + self.texture
print "My shape is " + self.shape
Because a class is an atomic part of your system, and you've practiced good design, both your marketing and shipping department
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (6 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
OO applications are making use of the SAME Spam class. So Now when we generate new classes, we can do so by just calling its
constructor with another argument:
myfourthcan=Spam(cansize="20lbs", texture="putrid",
shape="square")
print myfourthcan.showspam()
I am a can of Spam.
My shape is square
My size is 20lbs texture is putrid
Admittedly, we still have some work to do on our Spam class to get it to intelligently update all the "old" Spam instances that are
lying around with no shape, but Pay No Attention To The Man Behind The Curtain. This isn't important right now. The point is
that you're making your changes in ONE PLACE with an OO system, while in a comparable procedural system, you potentially
need to make a similar change to many different application instances that access your tables.
This is what's known as extensibility. It's a key benefit of object oriented programming and design.
Zope classes can be coded in Python. You can make full use of the Python toolset to create custom classes for use within Zope.
Zope also has the notion of a ZClass. A ZClass is a type of object that performs the function of a Python class (under the hood, it
actually is a Python class), but you can design it through the web instead of in a text editor.
How Classes and Instances Relate to The Zope Management
Interface
Because Zope is an object-oriented system, and because it provides a persistence mechanism, adding instances to the ZODB is
generally as easy as "calling" a class. When you use the Zope management interface, many times you're calling lots of classes and
asking them to make instances of themselves. This is called instantiation. When you instantiate, for example, an DTML Method
into a Folder named "Foo" within Zope, here what's happening:
●
●
●
●
By selecting "DTML Document" in the "Add" list within the Zope management interface, you call the Foo Folder's
manage_addProduct method which is a "factory method" defined in the FactoryDispatcher class with
arguments that indicate we'd actually like to call the methodAdd.dtml document up out of our
lib/python/Products/OFS directory. Whew. There's sure a lot of stuff going on there! Nasty. But it works! You
generally never need to touch this stuff, it's just there, primarily courtesy of Jim Fulton.
The methodAdd.dtml document gets called up out of our filesystem and its contents get displayed in our client
browser. This is the form we see titled "Add DTML Method".
We do some paperwork, filling in at least an "ID" --we'll give it an ID of 'someid'-- and we click "Add". Note that our
form's "ACTION" is addDTMLMethod.
Upon clicking "Add", our form contents get shuffled around inside Zope a bit and wind up as arguments to the
addDTMLMethod method of the DTMLMethod module. Ugh. As bad as this sounds, it looks better expressed like this:
DTMLMethod.addDTMLMethod(id="someid")
●
The addDTMLMethod method does a little sniffing around to make sure we've filled in the right values, and then it calls
a method named _setObject on the Foo Folder with a DTMLMethod object as an argument, which actually creates
the DTML Method with some default contents inside Foo Folder. It then redirects us back to the default management
interface screen.
Holy hat, that's a lot of work! Yeah, yeah, I know. It's hard. But this is the kind of stuff that's going on all the time underneath the
hood inside Zope. What's different and wonderful about this as opposed to just having a couple of Perl scripts that add an HTML
file to the filesystem is that all this work is being done by objects whose actions we can change. We could make all of the objects
that get instantiated into Foo Folder be automagically redirected over to a top-secret NSA supercomputer if we wanted to. And
we'd only need to go do it in one place. All we'd need to do is change some of the behavior of the code that wraps the Folder
object, maybe in the _setObject() method. But that's actually not such a hot idea. Better, we can use all the functionality of
the Folder class that sends objects over to the NSA by creating our own custom folder class that inherits stuff from the existing
Folder class. But I'm getting ahead of myself. More about this later.
Note that because lowly objects in Zope, such as a Folder, have intelligence (in the form of methods), you can script operations
within Zope by calling methods on their class instances. For example, instead of using the management interface within Zope to
create a DTML Method, we can write a Python External Method that does the same. Or you can use XML-RPC to do this from
another computer on the same network. You can use XML-RPC to call the addDTMLMethod() method from a Perl or Java
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (7 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
application running on a system over the Internet.
All these methods of creating and using objects in Zope are aimed at serving the purpose of it's name: the Z Object Publishing
Environment.
Relating Instantiation to the Object Database
Persistence lets us "set-and-forget" object instances. Once we instantiate an object like a Folder, Zope takes care of doing the
paperwork involved in registering it in the system, writing its metadata and its attribute/method values to disk, etc. We never even
have to think about it. To us, all we're doing is adding a Folder. Under the hood, there's a great deal of work involved. But most of
the time, you're shielded from that.
Inheritance
Hold on to your ankles, It's time to start reusing code. We all now seem have a shaky understanding of objects, classes, instances,
methods, and attributes. It's time for the dreaded inheritance explanation. This isn't going to be a very long or very good
explanation, because, to be honest, I'm sick of writing. But it should give you some ideas.
Let's assume you've written a Python module (.py file) named "Chicken.py" that defines a Chicken class.:
# Chicken module
class Chicken:
def __init__(self, name):
self.name=name
def speak(self):
return "bock, bock"
def sayname(self):
return self.name
We can use this class to make a chicken inside the Python interpreter:
>>> from Chicken import Chicken
>>> mychicken=Chicken("Bruce")
>>> mychicken.speak()
'bock, bock'
>>> mychicken.sayname()
'Bruce'
Now, let's say that we are using out Chicken module quite nicely inside an application. We're making chickens like nobody's
business for a couple of our customers. But one customer wants a special kind of chicken. You know the customer. He's a mean,
dimwitted man. But he insists that your chickenmaking module isn't the "end-all-be-all" of chickenmaking modules, and he wants
it customized to his taste. He specifically wants his Chickens to fly. Well, we all know that chickens can't fly, but the customer
is always right, so you agree to give it a whirl.
Now you could go and change your Chicken class at his site within the application to suit his tastes. In some cases, this wouldn't
be such a bad idea. But if you're like me, you are dumb as a rock, and you're going to forget that he has a special Chicken class
six months from now, and you'd only serve to confuse yourself when you go to maintain his code. Therefore, you'd like to create a
FlyingChicken class just for this customer that inherits the methods of the normal Chicken class:
# FlyingChicken module
from Chicken import Chicken
class FlyingChicken(Chicken):
def fly(self):
return "I can't fly, I'm a chicken"
Now when you call up the FlyingChicken class in the Python interpreter, you get it to do stuff like this:
>>> from FlyingChicken import FlyingChicken
>>> myflyingchicken=FlyingChicken("Bruce")
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (8 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
>>> myflyingchicken.speak()
'bock, bock'
>>> myflyingchicken.sayname()
'Bruce'
Looks the same as the output from the Chicken class, doesn't it? Yes, it does, that's because the FlyingChicken class
inherited methods from the Chicken class. The FlyingChicken can do everything a normal Chicken can do, PLUS:
>>> myflyingchicken.fly()
'I can't fly, I'm a chicken'
Cool, huh? You've got a new kind of object (a class) that inherited the methods of an existing class. Our FlyingChicken class
subclassed the Chicken class. Our Chicken class is a superclass of the FlyingChicken class. Anything a Chicken can
do, a FlyingChicken can do too. You were able to extend the class definition of a Chicken by creating a FlyingChicken
class that has a fly() method. Normal Chickens can't fly -- neither can out FlyingChicken, but at least it tells us so
instead of breaking. If we tried to make the Chicken class fly in Python, we'd get a traceback error. Let's try it:
>>> from Chicken import Chicken
>>> mychicken = Chicken("Bruce")
>>> mychicken.fly()
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: fly
>>>
This is bad. Let's not try to make our regular Chickens fly from now on.
The point here is that we've created a new specialized class, reusing all our old code that will make our customer happy.
In Python (unlike some other object-oriented languages), you can have multiple inheritance. This means that instances can inherit
methods and attributes from more than one class. Let's create a FlyingSchwartzeneggerChicken that inherits from both
Chicken and FlyingChicken:
from Chicken import Chicken
from FlyingChicken import FlyingChicken
class FlyingSchwartzeneggerChicken(Chicken, FlyingChicken):
def sprayCrowdWithGunfire(self):
return "bang."
Now we've got a new class FlyingSchwartzeggerChicken that inherits from both Chicken and FlyingChicken.
Instances generated from it can give us their name, speak, fly, and spray a crowd with gunfire:
>>> from FlyingSchwartzeneggerChicken import FlyingSchwartzeneggerChicken
>>> mynewchicken = FlyingSchwartzeneggerChicken("Bruce")
>>> mynewchicken.sayname()
'Bruce'
>>> mynewchicken.speak()
'bock, bock'
>>> mynewchicken.fly()
'I can't fly, I'm a chicken'
>>> mynewchicken.sprayCrowdWithGunfire()
'bang.'
>>>
By subclassing existing classes, you can create some pretty useful, complex stuff. In Zope, you can subclass things like the Folder
class, to create your own masterpiece folder that includes some additional functionality. You can also subclass Python classes in
ZClasses and vice versa.
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (9 of 10) [05/07/2000 11:14:16]
Gain Zope Enlightenment By Grokking Object Orientation
Other Stuff
Other things that you're going to want to know about (but that I'm wimping out on describing here because I'm frigging exhausted
already) are polymorphism, encapsulation, and acquisition. Polymorphism and encapsulation are standard object-orientation
terms. Acquisition is a Zope specialty, and isn't discussed in any object orientation textbooks, but it's really useful, and particularly
mind boggling.
ZODB Storage Implementation (A Pertinent Aside)
Zope itself has been designed from the ground up as an object-oriented application. Its code is primarily a bunch of objects talking
to each other. It is a great meta-example of a well-designed, well-implemented OO system. The ZODB was itself designed and
implemented from an OO perspective.
Currently, the most common implementation of the ZODB storage interface is to store it as one big honking file on your server
computer's hard drive. However, in the tradition of good OO programming, the ZODB is not implicitly tied to this
one-monster-file-based storage implementation.
We sell a product called "ZEO" (Zope Enterprise Option) that effectively spreads the ZODB over multiple computers arbitrarily
networked together. As a sort of metatestament of just how much easier life can be when you practice good OO (as Jim Fulton, the
originator of most of Zope's core code, "benevolent dictator" of Zope proper, and self-described "OO zealot" does), consider that
the ZEO code required to spread ZODB storage over an arbitrary number of networked computers as opposed to its
single-server-single-big-honking-file-based counterpart is less than 100 additional kilobytes per server, required no significant
architectural change to Zope itself, and does not require Zope programmers to change any of their code to accommodate the new
storage.
Now stop and think to yourself what that means. Zope itself consists of about two megabytes of code and ancillary data. Jim was
able to reuse all the code that ships with Zope, add an additional 100kb of code to the existing code base, and come up with a
"transparent" load-balancing and failover solution for Zope in a matter of a few weeks. It works. We sell this product now. We
charge a lot of money for it, and well we should. It might have taken another company man-years to retrofit a bad design for this
functionality. It took Jim much less time. We are able to do this because Jim has ate, breathed, slept, and thought in terms of OO
and good design since the inception of what is now Zope. If you practiced good OO and good design, you could be the recipient of
such benefits too. Zope can help you do that. It won't force you to be that smart, but it can help.
[Bibliography]
The Object Primer, The Application Developer's Guide To Object-Orientation Scott Ambler, 1996, SIGS Books - A gentle,
comprehensive introduction to object orientation with a sense of humor, written in plain words. Get it now.
Learning Python Mark Lutz and David Ascher, 1999, O'Reilly Books - A great introduction to the Python programming language.
Required.
Programming Python Mark Lutz, 1996, O'Reilly Books - The "other book" about Python by Mark Lutz. Lots of code examples
and more thorough treatment of most issues. Not absolutely required, but helpful.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mcdonc/HowTos/gainenlightenment?pp=1 (10 of 10) [05/07/2000 11:14:16]
General Zope Design Techniques
How-To: General Zope Design Techniques
Created by Amos. Last modified on 1999/09/15.
How can you take advantage of Zope's object publishing environment to build dynamic web applications? Here are some useful
Zope building techniques.
Designing with Zope
Don't try and accomplish everything in one go. Factor out services to be performed by different objects. Build your object
collections and services incrementally. Don't be afraid to use Undo. Move common services to the top of the object hierarchy and
move special case behaviors deeper into the hierarchy.
Factoring Common Content
Each document you create should only hold content that is specific to it. In other words if different documents have a bunch of the
same content it them, you should factor it out. To factor content out, remove it from your document and create a new document at
the same level or higher in the object hierarchy to hold the content. Then use a var tag to insert the common content into your
document. Don't forget that documents can call documents can call documents, etc. By factoring out common content you can
arrange your site so that one change will ripple across many objects. This is good--it saves you time and ensures consistency in
your site.
Properties may also be appropriate for common content. Use properties when you have small pieces of content that must be of a
certain type--i.e. integers.
Factoring Common Services
Zope creates dynamic web applications. Zope isn't just about serving up content--it's about dynamically providing appropriate
content. This is the difference between web sites and web applications.
Since Zope objects provide not only content, but services as well, and since acquisition works with all Zope objects, the same
rules about factoring content applies to factoring services.
For example, User Folders provide authentication and authorization services. If you have a bunch of content which should only be
accessible to some users, instead of creating a bunch of User Folders to protect each sensitive document, you should create one
User Folder sufficiently high in the object hierarchy so that all sensitive documents can be placed beneath it.
Iterative Design
When you design with Zope you are building a dynamic entity. It's much harder to exactly know the best way to design a dynamic
system than it is to design a static system. So chances are that your very first attempt to put together something will be less than
perfect. That's OK. Zope has Undo facilities so that you can try out different things without worrying about destroying your
creation. Zope also includes Versions which allow you to make significant changes in live sites without disrupting them. When
you work in a Version, only you see the changes you are making; everyone else sees the same old site without the changes you are
making in the Version.
In general while developing in Zope you should get things to a stable place and then start working in Versions. When your work
reaches another reasonable point, commit the Version. Now all you changes are made live immediately. Continue to refine your
site in this way. In fact many people can work on a site simultaneously in different (or the same) Versions.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/DesignTechniques?pp=1 [05/07/2000 11:14:19]
Generate URLs for Objects
How-To: Generate URLs for Objects
Created by jim. Last modified on 2000/02/16.
(Note that the impatient can jump to the end of the how-to to get the answer without reading through the rational. :)
It's common to need to provide URLs for objects. There are a number of ways that this can be done. You can just give a relative
URL using the object name, as in:
<a href="spam">
This works even if the object spam is not in the current folder but is acquired. A significant disadvantage of this approach is that,
if the object is acquired, it can drive persistent spiders insane because it sets up situations where a program following links
generates an unlimited sequence of strange but valid URLs.
To see how this can happen, imagine that the above reference appears in the index_html page of the eggs sub-folder of spam.
Suppose that spam has an index_html that points to eggs. Now imagine the following spider requests:
1. Visit spam, see relative reference to eggs
2. Visit spam/eggs, see relative reference to spam
3. Visit spam/eggs/spam, see relative reference to eggs
4. Visit spam/eggs/spam/eggs, see relative reference to spam
...
Another problem with relative URLs is that they defeat image caching. To see why, imagine that you have an image in your root
folder named logo. You could reference this image using a relative reference:
<img src="logo">
but this will make logo appear to the cache as a separate object, with a separate URL in each folder.
It's better to use absolute references. An easy way to build an absolute reference is to prepend the request variable URL1, as in:
<a href="&dtml-URL1;/spam">
But this suffers from the same problem as a relative URL, since it's really still a relative reference.
(Note that we use the entity reference form of the var tag here because we're sticklers for writing, uh, well-formed HTML. We
could, instead, have used:
<a href="<dtml-var spam>">
but this looks too weird and might upset an HTML editior.)
To generate a true absolute reference to an object, you can call it's absolute_url method, as in:
<dtml-with "_(abs_spam=spam.absolute_url())">
<a href="&dtml-abs_spam;">
</dtml-with>
This is good because we get the same URL for spam no matter where we reference it. It also doesn't matter where spam is, as
long as we can acquire it.
This version is a bit long-winded. We had to resort to an expression to call absolute_url and we had to use a with tag,
because we can't use an expression in an entity reference.
http://www.zope.org/Members/jim/ObjectURLs?pp=1 (1 of 2) [05/07/2000 11:14:21]
Generate URLs for Objects
Fortunately, the var tag has an option, url, for inserting the absolute_url of an object. We can use this to simplify the
reference quite a bit:
<a href="&dtml.url-spam;">
So, in summary, when you want to generate a URL for an object, use the entity-reference version of the var tag with the url
option.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jim/ObjectURLs?pp=1 (2 of 2) [05/07/2000 11:14:21]
GenericUserFolder Walkthrough (GUF)
How-To: GenericUserFolder Walkthrough (GUF)
Created by Zen. Last modified on 2000/06/09.
GenericUserFolder Walkthrough
This document will take you click-by-click through getting a working GUF installation up and running. The result won't be
terribly useful, but will prove that the product is working and give you a running installation to start customizing to your own site.
1. Extract the GUF distibution at the top of your Zope installation. This will create lib/python/Products/GenericUserFolder
in the right location. This step is operating system dependant.
2. Confirm the permissions of the extracted files are correct. There is no information in the product that needs to be kept
secure, so setting permissions so everyone can read the files is fine. There is no need for anyone to be able to write to the
files. This is operating system dependant.
3. Restart Zope. From here on, everything is done within Zope so all references to folders are Zope folders.
4. Connect as the superuser (or other account that has Manager access to the root folder), and go to the Management
interface.
5. Confirm that the product exists in the Control Panel -> Products folder, and that it appears to be loaded correctly (the icon
isn't the broken product icon).
6. Create a folder which you want to be protected using the GUF, ensuring Create public interface is checked
and Create user folder is not checked. I'll create one called GUFTest in my root folder.
7. Select the new folder using the tree. You may need to click on Refresh at the bottom of the tree before it is visible.
When selected, the folder should have nothing in it but a document called index_html
8. Create a GenericUserFolder object in this folder. Accept all the defaults. Select the newly created acl_users folder. When
selected, the GUF's Contents panel is displayed, and you should see it contains several DTML methods called things like
docLogin, userList, userAuthenticate etc.
9. Click on the User List tab in the GUF's management screen. You should see that there are two users defined (fred and
jorge).
10. Click on jorge. This will take you to the User Info panel. Note that jorge belongs to two roles (defaultrole and
specialrole).
11. Click on the Security tab. Unselect all check boxes in the Acquire permission settings column. In the
Anonymous column, enable the Can Login and Logout and Access contents information boxes.
When this is done, click on the Change button at the bottom.
12. Select the folder you created in step 5 (GUFTest in my case). Click on its Security tab to select its Security Management
screen.
13. When you scroll down to the bottom of this screem, you will see the User defined roles controls. Enter
specialrole in the text box and click on Add Role. Once this is done, you will still be on the folder's Security
Management screen, but a new column has been added for giving permissions to the newly created role. Note that roles
(like most of Zope) are case sensitive.
14. Uncheck all checkboxes in the Acquire permission settings column. In the specialrole column click the
checkboxes to grant the Access contents information and View permissions. Click on the Change button to
save these changes, and click on Ok when the next screen pops up.
15. Close all of your browsers windows and restart it. This is needed to ensure that you are no longer logged in as a Manager.
16. Enter the URL for the folder you created in step 5 (eg. http://mysite:8080/GUFTest).
1. The default login screen should appear. Enter jorge as the username and secret as the password. Click Ok.
2. The index_html document should display.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Zen/GenericUserFolder/walkthrough?pp=1 [05/07/2000 11:14:24]
Get the Filename when Uploading a file through Z classes
How-To: Get the Filename when Uploading a file
through Z classes
Created by Roug. Last modified on 2000/05/15.
The upload of Image and File in Zope has the very neat feature that if you don't enter an id, then it will use the file name of the
uploaded file.
I wanted to do the same thing for a Z class, which was a subclass of File. After a few tribulations, this is what I came up with.
You can get the fully qualified filename from the formfield object, in this case called 'file' by getting the attibute called filename. I
then do some hokus pokus with rfind to just get the last part of the filename. I took that part directly from the manage_addFile
method in Zope. Then I weed unwanted characters. Spaces and slashes are converted to underscore and a lot of other unwanted
characters are simply deleted with the translate method. Here is how it looks in the MyFile_add method:
...
<dtml-let
fileid="file.filename[_.max(_.string.rfind(file.filename, '/'),
_.string.rfind(file.filename, '\\'),
_.string.rfind(file.filename, ':'),
)+1:]"
transtab="_.string.maketrans('/ ','__')">
<dtml-unless "REQUEST.has_key('id') and REQUEST['id'] != ''">
<dtml-call "REQUEST.set('id',_.string.translate(
fileid,transtab,'?&!;()<=>*#[]{}'))">
</dtml-unless>
</dtml-let>
<dtml-with "MyFile.createInObjectManager(REQUEST['id'], REQUEST)">
<dtml-call "propertysheets.Basic.manage_editProperties(REQUEST)">
</dtml-with>
...
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Roug/getting_the_filename?pp=1 [05/07/2000 11:14:26]
Getting Started With DTML Scripting
How-To: Getting Started With DTML Scripting
Created by Pam. Last modified on 2000/02/19.
WHAT IS DTML?
DTML is the markup language used by Zope web application. The DTML acronym stands for Document Template Markup
Language. DTML is used for generating textual information to be used in your Zope application. Within Zope, DTML is used
primarily to generate HTML objects and can also be used to generate SQL commands.
WHERE IS DTML Used?
DTML scripting is used in DTML documents and DTML methods. The basic difference between the DTML documents and
methods are properties. DTML Documents are used to hold content and they can have properties. DTML Methods are used to
carry out actions and they acquire their properties from the object they belong to. For a more detailed information DTML Methods
vs. DTML Documents How To explained the whys of DTML methods and DTML documents.
HOW TO Script With DTML?
The basic DTML script is very similar to HTML and Python tags. Let's start with two examples of the DTML tags that are
available with Zope.
<dtml-var standard_html_header>
<dtml-var standard_html_footer>
As you can see, the syntax of the tag is <dtml-somekindoftag> and </dtml-somekindoftag>. Some of the tags are singular and
some require duplex tags to denote beginning and ending. In the standard html header and footer tags above, Zope allows you to
create the content of the header/footer so that it can be used automatically through out the web site. The standard_html_header and
standard_html_footer are themselves DTML documents.
Okay, that is clear as mud. Now, you want to see an example of a DTML script and what it does. For those who like to jump into
code directly, the Zope DMTL Reference is available on line at http://www.zope.org/Documentation/Guides/DTML. But, we will
go through an example, step by step to clear away the mud.
<HTML>
<HEAD>
<TITLE><dtml-var
title_or_id></TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<TABLE WIDTH="100%" BORDER=0>
<TR>
<TD><dtml-var logo></TD>
<TD WIDTH=16>&nbsp;</TD>
<TD><dtml-var title_or_id>
</TD>
<TR>
<TD COLSPAN=3>&nbsp;</TD>
</TD>
<TR>
<TD><dtml-if sidebar>
<dtml-var sidebar>
http://www.zope.org/Members/Pam/DTMLScripting?pp=1 (1 of 3) [05/07/2000 11:14:28]
Getting Started With DTML Scripting
</dtml-if sidebar>
</TD>
<TD><&nbsp;</TD>
<TD>
You will notice in the example, the DTML scripting is used to place the objects title, logo and sidebar into the HTML document.
These objects can be full DTML documents or methods. For example, the sidebar DTML Method list the latest news items along
the side of the web page being shown. The logo is a simple image file and the title_or_id will return the title or id of the object.
Similarily, you can replace the standard footer with the following example.
</TD>
</TR>
<TR&;gt;
<TD>&nbsp;</TD>
<TD>&nbsp;</TD>
<TD>
<dtml-if copyright>
<P ALIGN="CENTER"><dtml-var
copyright></P>
</dtml-if></TD>
</TR>
</TABLE>
</BODY>
</HTML>
By replacing this DTML script for the standard_html_footer DTML document, the web pages will have the copyright object
displayed with each page. Notice that the <dtml-if> and </dtml-if> DTML tags are duplex. The DTML tags are explained in
detail in the Zope DTML Reference.
Where Does This Lead?
This How-To leads to the variety of information available about DTML, a powerful language. This how-to can only be a starting
off point. The links below will give you more information about the dynamics of DTML "Document Template Markup
Language".
●
Advance DTML Techniques
●
Detecting User Roles in DTML
●
What is the difference between DTML Documents and DTML Methods?
●
The DTML Name Space
●
The With Tag How-To
●
Making 'static' DTML documents from database records
●
Looping in DTML
●
How-To: Secrets of working with the tree tag
●
The Let Tag How-To
●
Changing between DTML Methods and Documents
●
Zope Quick Reference 0.6
http://www.zope.org/Members/Pam/DTMLScripting?pp=1 (2 of 3) [05/07/2000 11:14:28]
Getting Started With DTML Scripting
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Pam/DTMLScripting?pp=1 (3 of 3) [05/07/2000 11:14:28]
Getting Started With Zope
How-To: Getting Started With Zope
Created by klm. Last modified on 2000/02/14.
About this How-To
This will probably always be work-in-progress, but i think it's basically useful at this point. (klm 9/22/1999).
This how-to is about Zope, a network application server platform. If "network application server platform" doesn't answer your
questions, then this how-to may be for you - it's intended to as orientation, a concise Zope overview. Ultimately, i hope it's useful
to you as a jumping-off point for further investigations. As such, I'll touch on many points, and tend to not delve very deeply into
any. When possible, however, I'll include links to resources which do go into greater detail, which you can pursue as suits your
purposes.
Here is what's covered:
●
What Zope Is
This section provides some fundamental orientation, and then bunches of pointers to manuals, guides, and other reference
materials.
●
Getting Zope Going
This is a bit of extra information for someone considering running Zope - including a wild, seat-of-the-pants estimate at
the minimum hardware.
What Is Zope?
Formally, Zope is a web application server [1]. This means that Zope is related to typical web servers, which "publish"
information on the web. However Zope is much more. Zope provides mechanisms for processing the content, and a layer on top of
that for managing content via the web.
●
Content management
Zope provides a comprehensive framework for management of web content, via the web itself. An extensive, easy-to-use
management interface, comprehensive access control facilities, transactional operation (eg, generic undo for any
management operations), rich document types, and other features enable much more than just the ability to change
content via the web. These capabilities enable highly flexible and assured delegation of site management without any
resort to programming.
The primary Zope management manuals are:
●
❍
the Zope Content Managers Guide (ZCMG)
❍
the Zope Administrator's Guide (ZAG)
Programming Web Sites
Zope provides facilities, via the content management framework and through lower level interfaces, for implementing
programs that function as part of the web site infrastructure. In essence, Zope enables you to transform parts of your web
site into programs that interoperate with each other, other content on the site, and other systems on your host and out on
the network.
The canonical manual for Zope programmers is under development - it will be called the Zope Developers Guide. There
is a bunch of more specific leads in the More Leads section below.
●
The Combination
In reality, the Content Management interface is built on top of Zope's programming facilities. These facilities (and many
others) are available as programming components. In turn, while there are many ways that a programmer can change and
extend Zope, much programming can be accomplished via the content management interface using a standard form of
http://www.zope.org/Members/klm/GettingStarted?pp=1 (1 of 3) [05/07/2000 11:14:32]
Getting Started With Zope
Zope content, known as Document Template Markup Language (DTML).
In general, the content management interface is instrumental in programming Zope, and conversely, content managers
can avail themselves of programming benefits in manageable steps, according to their level of comfort and desire.
●
The Open Source Context
It so happens that Zope's political/social context is another crucial factor in the systems character. Zope is a prominent
example of an Open Source product, a phenomenon whereby some or all software related to a system is freely available
to the public in source form. While Zope is a central product of Digital Creations a commercial company, it is available
to everyone free of charge, and in source form. Though sometimes surprising, this can work in the interest of everyone Zope's creators and users. (For a serious peek at the rationale behind this business model, see the presentation by Hadar
Pedhazur, a primary Digital Creations investor.)
One crucial consequence of being open source is that the users - content managers, programmers, and in between - can be
intimately involved in the development of the system, feeding back information about problems, fixes, and extensions, to
contribute to its growth. This has particular significance in the context of the Web/Internet, where collaboration something which Zope itself can facilitate - can have profound benefits.
In fact, the Zope site provides a central basis for collaboration and education about Zope, with community members
publishing various contributions just by creating and releasing them on the site. That URL is http://www.zope.org.
The Zope mailing lists, hosted at zope.org, provide another central gathering place. They are potent sources of education,
support, and collaboration opportunities. A roster of these lists is found at:
http://www.zope.org/Resources/MailingLists
More Leads
Zope Intro:
●
DevShed Intro - Brian Lloyd (Digital Creations) presents an overview of Zope
●
Jon Udell's Byte Intro - A bit of crossover from a mainstream computer tech magazine
●
Bobo and Principia - Amos Latteier's (DC) more technical introduction to Zope, soon after it became "Zope"
Zope Content Management:
●
The primary Zope content managers manual is the Zope Content Managers Guide (ZCMG).
Zope Programming:
●
●
●
Zope distributions include basic documentation in the doc subdir, in a bunch of text files. There's also a README.txt in
the distribution root directory.
Developer Documentation has the growing repertoire of central docs.
Contributed How-Tos document Zope tips, tricks, and techniques. The page is consolidated from the items that Zope.org
members create and designate for release.
●
Contributed Tips are like the How-Tos, but i think they're supposed to be a bit smaller.
●
The Zope Documentation Project has a growing collection of community generated documentation.
The Community/Open Source:
●
●
Steve Litt, June 1999 Troubleshooting Pro Magazine Article - Substantial details of the what and why of the Open Source
decision.
Hadar Pedhazur, an investor in Digital Creations, presents some of the reasoning that goes into an Open Source business
model.
●
The Zope Site.
●
Zope Mailing Lists
[1] Zope is not, by any means, limited to web protocols. FTP and webdav are some other supported access modes; the web just
happens to be the most prominent application domain.
http://www.zope.org/Members/klm/GettingStarted?pp=1 (2 of 3) [05/07/2000 11:14:32]
Getting Started With Zope
Getting Zope Going
Preliminary Requirements and Obtaining Zope
Generally, you need a computer with a network connection. (You can do without the network connection, but you definitely need
the computer.-)
Zope will run on Microsoft Windows (9x and NT) and many Unix variants. Precompiled binaries are available for many of these
platforms, and Zope comes with a built in Web server, so you don't need one of your own - and you can hook up Zope with the
prevalent Web servers, if you wish.
Zope is also available in source form, consisting of mostly Python code plus several C extension modules - so if you wish to build
it yourself, you will need Python 1.5.2 or later and a C compiler.
We have no definite answer concerning suitable system capabilities. The parameters vary drastically depending on your
requirements. That said, we'll venture a seat-of-the-pants minimum system.
For the reference point I'll use an intel processor, since they're so prevalent.
For basic operation, you can probably make do with:
●
a 166 MHz pentium class machine, or equivalent,
●
with upwards of 64 MB of memory when running Windows 9x, or
❍
possibly less memory when running Linux
❍
probably more memory when running under Windows NT
For what it's worth, your mileage may vary all other things being equal - caveat emptor. (All warranties are void if you fail to
fasten your seatbelts...-)
Installing Zope and Connecting with the Web
See the Zope Administrator's Guide.
The various Zope distributions include complete installation instructions - it all starts from the README.txt in the base directory.
There are some HowTo's with useful suplemental information - notably, the Zope/Apache Virtual Host HowTo .
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/klm/GettingStarted?pp=1 (3 of 3) [05/07/2000 11:14:32]
Gotchas for Zope Beginners
How-To: Gotchas for Zope Beginners
Created by jens. Last modified on 1999/11/21.
Here is a quick listing of typical problems that I ran into with my RedHat 6.0 installations and that seem to come up in the mailing
list repeatedly (because they are not yet adequately mentioned in the documentation):
●
●
●
●
●
●
●
●
After installation make sure the "var" subdirectory of your Zope install directory is writable by the account running Zope
(normally "nobody"), otherwise Zope will not start.
If you run Zope with PCGI through apache make sure that you set the appropriate file permissions on Zope.cgi after
copying it to apache's cgi-bin directory. User "nobody" (or whatever username the Zope process runs under in your
situation) needs to be able to Read and Execute the Zope.cgi file. Zope.cgi's mode after building it is 711, you should
chmod it to 755. Not doing this results in the ubiquitous errors like "Error parsing PCGI info file" which had me start
tinkering with its contents before I realized the real problem.
If you cannot log out after starting Zope using the "start" script in the Zope install directory without bringing down Zope
you might want to edit the start script and remove the "-D" option in it. "-D" stands for debug, the process won't
automatically detach from the terminal it was started on when -D is specified.
I had problems getting Zope to interact with Oracle, which turned out to be yet another file/directory permissions
problem. Zope will try to invoke the appropriate Oracle applications under its account (usually "nobody"), so the
$ORACLE_HOME/bin directory contents need to be readable and executable for the account under which Zope runs.
To change the port number on which Zope's built-in ZServer webserver listens you can invoke the "start" script for Zope
with the switch "-w portnumber" or just edit the start script and add it right there.
As the documentation tells you, Zope 2.x needs python 1.5.2. If you decide to compile python yourself make sure to run
the "configure" script with the "--with-threads" switch, otherwise Zope will not run at all.
In order to use the password encryption functionality of the "zpasswd" script your python installation needs to have the
modules for whatever encryption scheme you want to use, like the SHA-module to use SHA encryption.
If you installed Zope for use with PCGI and Apache and you can access the Zope example page but not the /manage
pages your problem might lie in the Apache RewriteRules. The line beginning with "RewriteRule" in your httpd.conf
should look like this:
RewriteRule ^/Zope(.*) /absolute/path/to/Zope.cgi$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
Apart from the fact that this needs to be in one line (and without the "\" backslash) like the documentation says, you will
unfortunately come across a few well-meaning but erroneous suggestions. The big stickler is here (indicated by the caret
underneath the code):
^/Zope(.*)
^
and
/absolute/path/to/Zope.cgi$1
^
Some documents will have a forward slash "/" in front of the "(" in the first line and the "$" in the second line. To be on
the safe side do not insert these slashes.
If it still does not work and you're running SuSE Linux you should look at my tip describing a bug in SuSE's apache
configuration file at http://www.zope.org/Members/jens/docs/suse_cgi.
●
Using the RewriteRules gives you big flexibility in terms of where your Zope content sits in the "hierarchy". The
standard rule shown above will put the Zope content root at http://myserver/Zope/... . To use only Zope content and make
it appear directly at http://myserver/... , you need to change the element
^/Zope(.*)
in the Rule to look like:
^(.*)
●
Trying to make an Oracle database interact with Zope by using the DCOracle package and the ZOracleDA database
adapter can be problematic. If you follow the compilation instructions for the DCOracle package you are most likely to
run into linker problems because certain library files cannot be found. This is because, as the comments in the Setup
definition file say, Oracle seems to change the libraries contained with the database with every small revision.
In my case (Oracle 8.0.5 Standard) i had to edit the "Setup" file in the ZOracleDA "src" directory and edit the line
beginning with "ORACLE_L". Close to the end of that long line is a sequence "-lsocket -lnsl -lgen -ldl -lelf -laio
-lposix4" which refers to libraries not shipping with version 8.0.5 Standard of the database. Deleting that sequence
allowed me to successfully compile the package.
http://www.zope.org/Members/jens/docs/newbie_caveats?pp=1 (1 of 2) [05/07/2000 11:14:35]
Gotchas for Zope Beginners
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jens/docs/newbie_caveats?pp=1 (2 of 2) [05/07/2000 11:14:35]
HOWTO use Zope and IIS together in remote user mode
How-To: HOWTO use Zope and IIS together in
remote user mode
Created by jephte. Last modified on 2000/05/25.
Notes (2000/05/25)
●
●
you have to install the jcNTUserFolder before you put Zope in REMOTE user mode.
zope in its current form can't handle REMOTE user authentication mode together with standard authentication mode.
When you install Zope as as pcgi process to get through IIS, it is the only way to be authenticated and get, for example, to
the management screens. When you install jcNTUserFolder as the root user folder, you have to choose the NT user that
will be mapped to the super user. The first time, you have to log with that user. Note that this super user needn't be a
privileged one under NT.
HOWTO use Zope with IIS in remote user mode
Note: my native language is not English and writing in english is a time consuming task as far as I'm concerned. So the howto has
been written in french, and I attempted to translate in english afterhand. If someone can provide a better translation, he is
welcome.
●
Lire le HOWTO, en francais
●
Read the HOWTO, in english
HOWTO (in french)
Introduction
Zope détache la gestion de l'authentification (qui est l'utilisateur?) et de l'autorisation (que lui est-il permis de faire?). Quand Zope
tourne avec un serveur web par exemple avec un wrapper PCGI, il peut fonctionner dans un mode appelé remote user mode,
ou il délègue la gestion de l'authorisation au serveur web. Ceci est pratique quand la gestion utilisateur a déjà été mise en place, ou
que le serveur web utilise une méthode non standard pour authentifier l'utilisateur (comme IIS par exemple).
Ce document explique comment interfacer Zope et IIS de façon qu'IIS gère l'authentification des utilisateurs. De cette façon, l'on
peut configurer IIS pour utiliser le mode d'authentification par stimulation/réponse (qui est censé être plus sûr) pour la sécurité, et
Zope pour la puissance...
Authentification sous Windows NT
Avant d'aborder l'interfaçage avec Zope, il faut comprendre comment fonctionne l'authentification sous Windows NT.
Quand un utilisateur ouvre une session sur un serveur NT, il doit fournir un nom d'utilisateur, ainsi qu'un nom de domaine par
rapport auquel il faut être authentifié. Ce nom de domaine est soit le nom du serveur NT, auquel cas il s'agit d'une ouverture de
session locale sur le serveur et donc ce serveur doit effectuer l'authentification, soit un nom de domaine dans lequel se trouve le
serveur NT, auquel cas l'authentification est effectuée par le controleur principal de domaine ou par un controleur secondaire.
Ainsi, le serveur NT a besoin de savoir sur quel domaine l'utilisateur veut ouvrir une session pour savoir quel serveur effectuera
l'authentification. C'est pour cela qu'en interne, un utilisateur sous NT est en fait écrit sous la forme DOMAINE\Utilisateur. Notez
que le nom de domaine et d'utilisateur n'est pas sensible à la casse.
Interfaçage de Zope avec IIS
Pour interfacer Zope avec IIS en utilisant PCGI, suivez les instructions de brianh à l'adresse
http://www.zope.org/Members/brianh/iis_howto. Cependant, ne désactivez pas l'authentification par stimulation/réponse de
Windows NT. Ceci permet à IIS d'utiliser une méthode sûre pour authentifier l'utilisateur. Et comme de toute façon Zope ne
s'occupera pas de l'authentification, autant utiliser ce que IIS a de plus sûr.
Dans le gestionnaire de services internet, cliquez avec le bouton droit sur Zope.cgi, affichez les propriétés, et dans l'onglet sécurité
http://www.zope.org/Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode?pp=1 (1 of 5) [05/07/2000 11:14:40]
HOWTO use Zope and IIS together in remote user mode
de fichier, cliquez sur modifier le contrôle d'authentification. Vous devez:
●
●
●
Désactiver les connexions anonymes. Si vous voulez vous assurer que seuls les utilisateurs du domaine puissent utiliser
Zope. Il y a aussi l'avantage que l'on saura à tout moment quel est le nom de connexion de l'utilisateur qui utilise Zope. Si
vous voulez autoriser les connexions anonymes, cochez la case. Notez que si vous désactivez les connexions anonymes,
cela ne vous oblige pas à donner des rôles autres qu'Anonymous dans Zope. Les utilisateurs ne doivent pas être anonymes
vis à vis de IIS, mais peuvent continuer à avoir le rôle Anonymous sous Zope.
Décocher authentification de base, sauf si vos utilisateurs utiliseront des navigateurs incompatibles avec l'authentification
par stimulation/réponse de Windows NT, comme Netscape Navigator par exemple. Actuellement, seul Internet Explorer
à partir de la version 3.0 supporte cette méthode d'authentification
Cocher l'authentification par stimulation/réponse de Windows NT pour une sécurité maximale, et si les navigateurs
utilisés par vos utilisateurs supportent cette méthode.
Notez qu'il faut qu'au moins une des méthodes d'authentification soit cochée, sinon cela signifie que Zope doit gérer lui-même
l'authentification, ce qui n'est pas ce que nous voulons.
Notez que cette configuration se fait fichier par fichier dans le gestionnaire de services internet, et donc qu'il n'est pas nécessaire
de modifier les paramètres pour tout le site.
Sauf si vous avez désactivé les connexions anonymes, vous devez pouvoir à ce niveau accéder aux ressources non protégées de
Zope.
Comment fonctionne l'authentification
Si l'on essaye d'accéder à une ressource protégée, Zope renvoie un en-tête HTTP 401-Unauthorized. IIS s'engage alors dans un
dialogue avec le navigateur web pour essayer d'authentifier l'utilisateur.
Notez que Zope n'est à aucun moment engagé dans ce processus. Tant que l'utilisateur ne s'est pas correctement authentifié (nom
et/ou mot de passe incorrect), IIS continue à dialoguer avec le navigateur.
Quand l'utilisateur s'identifie correctement (nom/mot de passe correspondant à un utilisateur valide sur le domaine spécifié), et
que cet utilisateur a les droits pour accéder à Zope.cgi sur le serveur, alors Zope est lancé avec l'information sur l'utilisateur qui se
présente dans la variable REMOTE_USER.
Notez que cette information est sous forme canonique, c'est à dire DOMAINE\Utilisateur. Zope cherche ensuite cet utilisateur
dans sa propre base d'utilisateurs (super utilisateur, contenu des User Folders), et vérifie les rôles de cet utilisateur. Si cet
utilisateur n'est pas trouvé (auquel cas il obtient le rôle Anonymous), ou si ses rôles ne correspondent pas aux exigences pour
accéder à la ressource protégée, Zope renvoie l'en-tête HTTP 401-Unauthorized au serveur web, et le processus d'authentificatin
recommence.
Notez donc que l'authentification se fait en deux temps:
●
●
d'abord l'utilisateur doit exister sur le domaine et il doit avoir les droits sur la ressource Zope.cgi pour que
l'authentification puisse se faire par IIS
puis l'utilisateur doit exister dans Zope sous forme canonique pour que des rôles puissent lui être attribués.
Configuration de Zope en mode remote user
Il faut configurer Zope pour qu'il passe en mode remote user. ceci se fait en supprimant le mot de passe du super utilisateur dans le
fichier access du répertoire d'installation de Zope. Par exemple, si ce fichier contient:
superuser:password
Il faudra le remplacer par:
superuser:
Attention! La documentation de Zope précise qu'il faut que l'utilisateur superuser existe au niveau du serveur web, en
l'occurence IIS.
Et c'est là qu'est le problème. Zope est sensible à la casse pour la gestion des utilisateurs, alors que IIS ne l'est pas. Heureusement,
http://www.zope.org/Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode?pp=1 (2 of 5) [05/07/2000 11:14:40]
HOWTO use Zope and IIS together in remote user mode
IIS envoie toujours les informations utilisateur sous la forme DOMAINE\Utilisateur. Le domaine est donc en majuscule, et
Utilisateur est la copie conforme de la définition faite dans le gestionnaire des utilisateurs sous NT. Ainsi, si l'utilsateur MikeP est
créé sous NT dans le domaine wild_west, et que l'on veut que cet utilisateur soit le super utilisateur, il faudra que le fichier access
contienne:
WILD_WEST\MikeP:
Redémarrez Zope après avoir fait cette modification.
Création des utilisateurs sous Zope
Comme dit précédemment, Zope est sensible à la casse. Si un objet UserFolder classique est utilisé pour gérer l'autorisation des
utilisateurs, ceux-ci doit être créé sous forme canonique dans le UserFolder si l'on veut pouvoir leur donner des rôles.
Il y a aussi possibilité d'utiliser un UserFolder tel que NTUserFolder de htrd ou jcNTUserFolder de jephte. Je ne sais pas pour
NTUserFolder, mais jcNTUserFolder n'est pas sensible à la casse, ce qui résoud le problème. C'est pour permettre de saisir le nom
du super utilisateur indépendamment de la casse que jcNTUserFolder demande le nom de l'utilisateur NT qui fera office de super
utilisateur lors de sa création.
Résumé
En résumé, voici les étapes à franchir pour interfacer Zope avec IIS en mode remote user:
●
●
●
●
En suivant les instruction de brianh, interfacer Zope avec IIS avec PCGI.
Choisir la méthode d'authentification utilisée par IIS: de base ou par stimulation/réponse de Windows NT, en fonction
des navigateurs clients. Il est possible de choisir les deux, auquel cas les deux types de clients (compatibles et non
compatibles) peuvent naviguer sur le site web.
Configurer Zope en mode remote user, en faisant attention d'écrire le nom du super utilisateur sous forme canonique
DOMAINE\Utilisateur
Utiliser un UserFolder insensible à la casse tel jcNTUserFolder ou créer les utilisateurs sous forme canonique dans le
UserFolder si l'on utilise celui livré avec Zope.
HOWTO (in english, sort of)
Introduction
Zope separates authentication (who is the user) from authorization (what is he allowed to do?). When Zope is interfaced with a
web server, for example through PCGI, it can run in remote user mode, that is it can delegate authentication to the web server.
This can be handy when the web server uses a non standard authentication scheme (e.g. IIS)
This document explains how to use Zope and IIS in remote user mode, to allow IIS to handle authentication, in challenge/response
mode for example.
Authentication under Windows NT
Before we can deal with interfacing Zope and IIS, we have to understand how does authentication work under Windows NT.
When a user open a session on a Windows NT server, he has to provide a username, and a domain name from which he wants to
be authenticated. This domain name may be the NT server computer name, and the user opens a session locally (in that case, the
NT server must perform the authentication itself), or it can be a domain name into which the NT server exist, and the
authentication is performed by the PDC (primary domain controller) or a BDC (backup domain controller).
It is clear that the NT server needs the domain name on which the user wants to open the session to be able to delegate
authentication to the right NT server. A canonical username under NT is written DOMAIN\Username. Note that the canonical
username is not case sensitive
Interfacing Zope and IIS
Follow instructions given by brianh at http://www.zope.org/Members/brianh/iis_howto. But you don't have to disable
http://www.zope.org/Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode?pp=1 (3 of 5) [05/07/2000 11:14:40]
HOWTO use Zope and IIS together in remote user mode
challenge/response authentication. This does not matter because Zope won't do authentication.
Go to IIS Manager, right-click on Zope.cgi, select Properties, click on the file security tab and then on authentication control. You
have to:
●
●
●
uncheck allow anonymous connexions if you want only users from the domain to browse the site (this is handy in the
case of an intranet). Check the box if you needn't the user identify himself before browsing your Zope site
uncheck basic authentication, unless your users use browsers not compatible with the challenge/response protocol (i.e. all
browsers except Internet Explorer >=3.0)
check challenge/response authentication, if your users use Internet Explorer
Note that you must check basic authentication or challenge/response authentication, or both. If you check None, this means Zope
must handle authentication itself which is not what we want.
That configuration can be done on a file by file basis in the management console, so you needn't change the parameters for all the
site.
Unless you disabled anonymous connexions, you should now be able to browse public (i.e. not protected) Zope ressource.
What is the authentication protocol
When you try to browse a protected ressource, Zope send a 401- Unauthorized HTTP header.
IIS chats with the web browser to try to authenticate the user. Zope is not involved at all in this process. IIS verify that the
user/password in correct (match with a valid nt user), then verify that this user has sufficient right on the Zope.cgi ressource (ACL
settings)
Only if all those don't fail that Zope is given the user identity in REMOTE_USER variable in the canonical form
DOMAIN\Username.
Zope looks for that user in its user database (in the User Folder or any other user folder product), and check what roles are given to
that user. If the user does not exists, or if he has not the required roles, Zope responds with 401-Unauthorized again, and the entire
authentication process stard again.
You have noted that the authentication is done in two phases:
●
First the user must exist on the domain and have permission on the Zope.cgi ressource for IIS to accept him
●
Second, that user must exist in Zope in canonical form for roles to be given to him
Putting Zope in remote user mode
The file access in Zope installation directory must not contain any password for the super user to enable remote user mode. For
example, il access contains:
superuser:password
It has to be replaced with:
superuser:
Be careful! Zope documentation says that the superuser user exists from the web server point of view.
And here lies the problem. Zope is case sensitive. IIS is not. Fortunately, IIS always send user information in canonical form
DOMAIN\Username. The domain name is uppercase, and the user name is shown exactly the way it has been typed in the NT user
manager. For example, if user MikeP from domain wild_west is to be the super user, the access file must contain:
WILD_WEST\MikeP:
Restart Zope after this modification
http://www.zope.org/Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode?pp=1 (4 of 5) [05/07/2000 11:14:40]
HOWTO use Zope and IIS together in remote user mode
Creating users under Zope
Zope is case sensitive. If the default User Folder is used to manage users, they have to be created in the canonical form
You can also use NT User Folder product like htrd's NTUserFolder or jephte's jcNTUserFolder. I don't know about
NTUserFolder, but I wrote jcNTUserFolder to be case insensitive. This is why upon creation of jcNTUserFolder, you must choose
the nt user who will be the super user, to be able to input the user name case insensitive
Summary
In summary, this is the steps to follow to interface Zope with IIS in remote user mode:
●
Following brianh's instructions, configure PCGI
●
Choose authentication method to be used by IIS: basic, challenge/response or both
●
Configure remote user mode in Zope, and be sure to use the canonical form form the super user name.
●
use a User Folder product which is case insensitive, or create users in the canonical form in Zope.
That's all folks!
Send comments/patches to minf7@educ.univ-reunion.fr
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jephte/HOWTO/IIS_and_Zope_in_REMOTE_USER_mode?pp=1 (5 of 5) [05/07/2000 11:14:40]
HOWTO: Debian Package for Products
How-To: HOWTO: Debian Package for Products
Created by vernier. Last modified on 2000/02/05.
Debian Packaging of Products
There exists an official package for zope on www.debian.org and a handful of zope products are available. This HOWTO is for
those of us who want a single package , a snapshot of all the latest useful products from www.zope.org/Products
Quick Solution
just download my latest zope-products.deb debian package zope-products.deb
Custom Solution
I only package the products that I think are useful for a school server. This is my bias. If you want to package a different set of
products, here is a recipe (assuming you have a debian system or debian packaging tools on another linux distribution):
Caveat: this is a quick and dirty method of creating debian packages in order to keep the products within the very useful debian
management system.
1. create a directory , ex: zope-products
2. create therein a subdirectory DEBIAN (all uppercase) and create a file therein called control
3. you can also optionally create files called preinst and postinst in which you can write scripts that will be activated before
and after installation. (check mine for an example where I force ownership by root.www-data)
4. in the control file goes the fields found in all debian packages, just cut, paste and modify from an existing control file in
another debian package (such as mine)
5. back to zope-products directory, and create usr/lib/zope
6. move to zope-products/usr/lib/zope
7. tar -xzvof /path-to-downloaded/some-zope-product.tgz
8. watch out for Products that use the Extensions and/or import instead of the lib/python/Products method
9. for the cases above, I created a soft link between var/lib/zope/import and /usr/lib/zope/import (same for Extensions)
10. move just under the zope-products folder and type
dpkg-deb -b zope-products
11. this produces zope-products.deb which you can install with dpkg -i zope-products.deb
please check www.debian.org for real information on how to
create proper debian packages
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/vernier/debian?pp=1 [05/07/2000 11:14:42]
Have a single sql insert statement and have dynamic field queries
How-To: Have a single sql insert statement and
have dynamic field queries
Created by Bill. Last modified on 2000/01/08.
Conditional SQL in Zope
Bill Anderson
Abstract
This document briefly covers how to set up a single ZSQL method to search on variable fields in Zope. This is an intermediate
level How-To.
What the heck am I talking about?
On one of my sites, I have a database contaiing a usert able. In this table, there are several unique fields a user might want to
search on to get to a certain user, or type of user. Well, here I was creating a ZSQL method to search a given table for users by
several fields. Quite frankly, that got old real quick, not to mention the space it takes up. So, I thought to myself What if I
could have single ZSQL method, called GetUser that would allow it to be queried on a
given field?. So I started on an hour-ish long journey to find out. This How-To is the fruit of that labor.
The ZSQL Method
(First, the methods, then the explanation.)
Create a new ZSQL Method in the standard way, GetUser in this example.
Give it the follwing argument:
FieldName Pattern
Give it the following body:
SELECT * FROM[ TableName]
WHERE <dtml-var FIeldName> = <dtml-sqlvar Pattern type=nb>
<dtml-if MembersOnly>and IsMember=1 </dtml-if MembersOnly>
<dtml-if ManagersOnly> and IsManager=1 </dtml-if ManagersOnly>
Using The ZSQL Method
I am not going to cover making the search form, since there is already bountiful documentation/products to do that. I am going to
delve a little deeper into how you can use this ZSQL Method. In my example, I have a webpage that lists the users in the database
(like the Members page at zope.org), with each name being a link to detailed user information from the database. This page is built
by using a GetAllUsers ZSQL Method that just returns the UserName.
The trick is to get GetUser to look up (in this case) by the UserName. I do it like this:
<a href="GetUserResults?FieldName=UserName&Pattern=<dtml-var UserName>"><dtml-var RealName></a>
What this does, is to set both variables. I could have searched on RealName by changing the field to RealName (note: since in my
case RealName copntains spaces (FirstName LastName), I would need to add the url_quote variable to the dtml tag.).
http://www.zope.org/Members/Bill/Documentation/ConditionalSQL?pp=1 (1 of 2) [05/07/2000 11:14:45]
Have a single sql insert statement and have dynamic field queries
OK, That was fairly simple but what else?
Glad you asked. Remember in my situation I needed to select users by types of users as well? In the database, I have boolean
fields such as IsMember, IsManager and so on. so let's say I wanted to search for all Members. Take a peek at this code:
<dtml-in ``GetUser(MembersOnly=1)''>
<a href=''GetUserResults?Pattern=<dtml-var UserName>&FieldName=UserName> > <dtml-var RealName> </a>
</dtml-in>
Yes, I can hear you now. I know full well I did not specify MembersOnly and ManagersOnly in the argument list. That is the
really cool part; you don't have to!
So what happens then?
Well, the variables MembersOnly and ManagersOnly are available to the ZSQL Method, but they are not required! This means if
you don't use them, Zope won't complain about them missing. this way, you can set conditionals in the method, and only pass
them as true when you want to. Now,I realize this may not appear that usefull and powerful at forst, bu tlet it sink in. Try it out.
Once you've tried it, I think you'll really begin to like it. I assume you can do this with any variable in the REQUEST object.
So there you have it, one ZSQL Method, many ways of using it. Send comments to Me.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Bill/Documentation/ConditionalSQL?pp=1 (2 of 2) [05/07/2000 11:14:45]
Help on Structured Text
How-To: Help on Structured Text
Created by millejoh. Last modified on 2000/06/26.
Introduction
Structured Text, being a highly Cool Thing, is a tool that Should Be Known To All. It is excellent for quickly prototyping Web
pages for our favorite playground: Zope.
However I find myself unable to always remember all of the functionality of the structured text document, ergo the raison d'etre
for this HOWTO.
This is almost just a copy and paste of documentation I found in the source code. When I first put this document together I was
unaware that I didn't know how to actually include structured text into my DTML methods and documents. For those of you
facing similar quandries I have included a short example at the end of this document on how to include structured text in DTML
documents. Don't worry- it is really quite simple once you know the magical incantantions. Enjoy!
Suggestions of any sort are always appreciated.
Structured Text Manipulation
Parse a structured text string into a form that can be used with structured formats, like html.
Structured text is text that uses indentation and simple symbology to indicate the structure of a document.
A structured string consists of a sequence of paragraphs separated by one or more blank lines. Each paragraph has a level which is
defined as the minimum indentation of the paragraph. A paragraph is a sub-paragraph of another paragraph if the other paragraph
is the last preceding paragraph that has a lower level.
Special symbology is used to indicate special constructs:
●
A single-line paragraph whose immediately succeeding paragraphs are lower level is treated as a header.
●
A paragraph that begins with a -, *, or o is treated as an unordered list (bullet) element.
●
●
●
●
●
●
●
●
●
A paragraph that begins with a sequence of digits followed by a white-space character is treated as an ordered list
element.
A paragraph that begins with a sequence of sequences, where each sequence is a sequence of digits or a sequence of
letters followed by a period, is treated as an ordered list element.
A paragraph with a first line that contains some text, followed by some white-space and -- is treated as a descriptive list
element. The leading text is treated as the element title.
Sub-paragraphs of a paragraph that ends in the word example or the word examples, or :: is treated as example
code and is output as is.
Text enclosed single quotes (with white-space to the left of the first quote and whitespace or puctuation to the right of the
second quote) is treated as example code.
Text surrounded by * characters (with white-space to the left of the first * and whitespace or puctuation to the right of
the second *) is emphasized.
Text surrounded by ** characters (with white-space to the left of the first ** and whitespace or puctuation to the right of
the second **) is made strong.
Text surrounded by _ underscore characters (with whitespace to the left and whitespace or punctuation to the right) is
made underlined.
Text enclosed by double quotes followed by a colon, a URL, and concluded by punctuation plus white space, or just
white space, is treated as a hyper link. For example:
"Zope":http://www.zope.org/ is ...
http://www.zope.org/Members/millejoh/structuredText?pp=1 (1 of 2) [05/07/2000 11:14:48]
Help on Structured Text
Is interpreted as <a href="http://www.zope.org/">Zope</a> is .... Note: This works for relative as
well as absolute URLs.
●
Text enclosed by double quotes followed by a comma, one or more spaces, an absolute URL and concluded by
punctuation plus white space, or just white space, is treated as a hyper link. For example:
"mail me", mailto:amos@digicool.com.
Is interpreted as "<a href="mailto:amos@digicool.com">mail me</a>."
●
Text enclosed in brackets which consists only of letters, digits, underscores and dashes is treated as hyper links within the
document. For example:
As demonstrated by Smith [a12] this technique is quite effective. [r1]
Is interpreted as ... by Smith <a href="#a12">[12]</a> this .... Together with the next rule this
allows easy coding of references or end notes.
●
Text enclosed in brackets which is preceded by the start of a line, two periods and a space is treated as a named link. For
example:
.. [a12] "Effective Techniques" Smith, Joe
Is interpreted as <a name="a12">[12]</a> "Effective Techniques" .... [r2] Together with the
previous rule this allows easy coding of references or end notes.
[r1] According to the HTML 4.0 specification identifiers must start with a letter so using references like [12] is a bit of a no-no,
though it does work (at least in all the browsers I've tried). The StructuredText Python module should probably be updated to do
something nice like prepend ref whenver it sees only numbers in a link.
[r2] The use of name here is deprecated, id should be used instead. Not there is much you as the document author can do about
this since it's StructuredText that is generating the HTML (and it probably should be modified to do the right thing) but at least
when somebody comes to nitpick you can say "Yes, I know. Everybody knows about this, now go away and let me get some work
done before I have to show just how strong my Kung Fu really is."
Including Structured Text in DTML
I had the hardest time actually getting my structured text to render into HTML until I learned the following incantation:
<dtml-var stx_doc_name fmt=structured-text>
Without the fmt=structured-text part you will just get the raw text, (something like this ) most likely not what you want.
Other Resources
It is well worth your time to check out Tres Seaver's Structured Text Document product. Conveniently provided is a ZClass one
can use to wrap one's STX content. Get it, use it, and be happy.
There are a few minor caveats to its use that should have absolutely no bearing on 99% of what one would want to use this
product for. Nonetheless, do check the release notes for those details before you use the product (but you were going to do that
regardless of what I said, right?).
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/millejoh/structuredText?pp=1 (2 of 2) [05/07/2000 11:14:48]
How To Publish Your Own Python Modules
How-To: How To Publish Your Own Python
Modules
Created by Amos. Last modified on 1999/11/29.
One little known fact about Zope is that it consists of a number of components which allow you to publish your own Python
objects on the web without using the Zope management interface. This is different than writing a Zope Product--it is a more
stripped down method of using object publishing, which will be familiar to folks who used to use "Bobo".
To publish your own objects with ZPublisher you need to find a way to get a web server talking with ZPublisher. The
simplest way to do this is to use ZServer.
In the z2.py start script there is a comment which describes how to tell ZServer to publish your own Python module. The
basic procedure is to create a zhhtp_handler and install it in the web server:
zh = zhttp_handler(MODULE, '', HTTP_ENV)
hs.install_handler(zh) # hs is a zhttp_server object
zhttp_handler takes three arguments:
●
●
●
a module name or module object -- This indicates the Python module to publish.
an optional prefix string -- This tells the web server where to locate the published module in its URL space. If you are
only publishing one module, an empty string is a good choice for the prefix.
an optional environment dictionary -- This dictionary contains environment variable setting which will override default
values.
z2.py comes configured to publish the Zope module. The simplest way to reconfigure z2.py to publish your own module is to
comment out the part where z2.py publishes Zope and add in a stanza to publish your own module:
#zh = zhttp_handler(MODULE, '', HTTP_ENV)
#hs.install_handler(zh) # hs is a zhttp_server object
sys.path.insert(0,'/path/to/my/module')
# add MyModule to the Python path
my_handler = zhttp_handler('MyModule', '') # create a handler
hs.install_handler(my_handler)
# install it in the http server
Now publish your module by running z2.py:
z2.py -D -f '' -p '' -m '' -w 8080
This should start ZServer on port 8080 publishing your module. You can then access your published module through the web at:
http://localhost:8080/
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Amos/ZPublisher?pp=1 [05/07/2000 11:14:51]
How To: Use GenericUserFolder with an SQL Database.
How-To: How To: Use GenericUserFolder with an
SQL database Version 1.1
Created by hippy. Last modified on 2000/06/01.
How To: Use GenericUserFolder with
an SQL Database.
Version: 1.2
Version History
Version: 1.2 (June 2000)
Addition of MS Access via ODBC instructions (thanks to Alan Capesius.
Version: 1.1 (March 2000)
Minor corrections to markup (thanks to Danny Sung).
Addition of MySQL instructions (thanks to Dennis Baker).
Version: 1.0 (December 1999)
First release.
Introduction
This How-To shows how to customize GenericUserFolder(GUF) to use an SQL database as its repository for usernames,
passwords, roles and anything else you want to store about a user.
I used a Postgres database and some of the SQL may be specific to Postgres, particularly the use of 'nextval' and 'currval' for
controlling unique indexes (I don't know enough about other RDBMS to know whether this is portable, perhaps you could
advice?).
At the end of the HowTo are some instructions for getting things to work with MySQL. Thanks go to Dennis Baker for sending
these to me.
This How-To also describes how to patch Zope to allow the 'crypt' module to be called from DTML. This enables GUF to store all
its passwords in an encrypted format.
I assume that you already have a database installed and accessible from Zope.
The following tutorial is a result of notes I made while learning how to deploy GUF in my particular curcumstance, it broken
down into the following steps:
●
1. Create the database tables.
●
2. Patch Zope to allow the crypt module to be called.
●
3. Create an instance of GUF.
●
4. Create a database connection.
●
5. Create the SQL methods to access the database.
●
6. Customize the GUF instance to call the SQL methods.
●
7. Create user forms to manage passwords.
●
8. Set permissions to protect the use database.
●
9. Force GUF to see the changes.
●
10. Using MySQL.
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (1 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
●
11. Using MS Access with ODBC.
1. Create the database tables.
Create the following tables in the database (this is the postgres SQL to create the tables). I choose the normalize the database and
use unique ids to link the tables together, you could do without the ids and just use the username and groupname as keys if you
like:
CREATE SEQUENCE "user_id_seq";
CREATE SEQUENCE "group_id_seq";
CREATE TABLE "users" (
"user_id" int4 NOT NULL,
"username" text,
"password" text);
CREATE TABLE "groups" (
"group_id" int4,
"groupname" text);
CREATE TABLE "user_group_map" (
"user_id" int4,
"group_id" int4);
CREATE UNIQUE INDEX "users_pkey" on "users" using btree ( "user_id"
"int4_ops" );
2. Patch Zope to allow the crypt module to be
called.
If you want to encrypt your passwords you have two options:
1. a. if you don't mind changing the source code you can patch the Zope sources to add 'crpyt' to the list of modules that
Zope allows to be called from DTML.
2. b. if you don't want to change the source code you can add an External Method to perform the encryption.
I only deal with the first method, because it is the one I chose. You can skip this step and return to it after you have everything
running without encryption.
Find the file [ZOPEHOME]/lib/python/DocumentTemplate/DT_Util.py, you will need to have access to the filesystem on which
Zope is installed to be able to do this.
Take a copy of the file encase you mess things up.
Find the part of the file that looks like this:
d['string']=string
d['math']=math
d['whrandom']=whrandom
try:
import random
d['random']=random
except: pass
Insert the follow code fragment (is purpose is self explanatory):
try:
import crypt
d['crypt']=crypt
except: pass
The code should now look like this:
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (2 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
d['string']=string
d['math']=math
d['whrandom']=whrandom
try:
import crypt
d['crypt']=crypt
except: pass
try:
import random
d['random']=random
except: pass
You must now restart Zope so that the changes to the source will take effect.
3. Create an instance of GUF.
The ground work is now in place and you can start the work inside Zope. First task is to set up GUF as it comes 'out-of-the-box'.
You should read the instructions on the README tab of the product.
It is best to play with acl_users folders in a Zope folder that does not contain anything important until you get it working. It is
possible to lock yourself out of a folder if you get things wrong.
Create a new folder to testing things out call it 'test_guf' and select both 'Create public interface' and 'Create user folder'.
Delete the 'acl_users' folder in 'test_guf'.
Create a GenericUserFolder called 'acl_users' in 'test_guf'.
Set the permissions as directed in GUF readme
Now ensure that you can log in as jorge:secret
4. Create a database connection.
Insert a 'ZPyGreSQLDA' object in the acl_users folder and give it the connection string for the database created in step 1.
5. Create the SQL methods to access the
database.
Now we must create the SQLMethods used to access the database. We create the following methods:
SQL_add_use
Adds a new entry to the usertable and sets a default role.
SQL_change_password Changes the password of an existing user
SQL_get_password
return the password for a given user
SQL_get_roles
return the roles set for a given user
SQL_get_user_list
return a list of all the users
This is not a complete list of all of the functions that you may need to perform, for instance you would probably want to delete
users, change roles, add roles etc. but it gives a reasonable idea from which you can role your own.
The contents of these methods follows:
SQL_add_user
Arguments
username password
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (3 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
insert into users
values (nextval('user_id_seq'),
<dtml-sqlvar username type=string>,
<dtml-sqlvar password type=string>)
<dtml-var sql_delimiter>
insert into user_group_map (user_id, group_id)
select currval('user_id_seq'), group_id
from groups
where groupname = 'Subscriber'
SQL_change_password
Arguments
username password
update users
set password = <dtml-sqlvar password type=string>
where
username = <dtml-sqlvar username type=string>
SQL_get_password
Arguments
username
select password as real_password
from users
where users.username = <dtml-sqlvar username type=string>
SQL_get_roles
Arguments
username
select groupname
from groups, users, user_group_map
where
users.username = <dtml-sqlvar username type=string>
and
users.user_id = user_group_map.user_id
and
groups.group_id = user_group_map.group_id
SQL_get_user_list
Arguments
None
select username from users
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (4 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
6. Customize the GUF instance to call the SQL
methods.
Now we must change the DTML in the GUF default methods so that they call our new SQL methods:
If you are going to use encrypted passwords change userAuthenticate to:
<dtml-in "SQL_get_password(username=username)">
<dtml-if "_.crypt.crypt(password,'ab')==_['sequence-item'].real_password">
<dtml-return "_.int('1')">
</dtml-if>
</dtml-in>
<dtml-return "_.int('0')">
If you are not using encrypted passwords change userAuthenticate to:
<dtml-in "SQL_get_password(username=username)">
<dtml-if "password==_['sequence-item'].real_password">
<dtml-return "_.int('1')">
</dtml-if>
</dtml-in>
<dtml-return "_.int('0')">
change userList to:
<dtml-return "SQL_get_user_list()">
change userRoles to:
<dtml-call "REQUEST.set('ret','')">
<dtml-in "SQL_get_roles(username=username)">
<dtml-call "REQUEST.set('ret', ret + ' ' + _['sequence-item'].groupname)">
</dtml-in>
<dtml-return ret>
7. Create user forms to manage passwords.
To demonstrate how to create forms that can be used to administer you users try to following for and response method:
create a DTMLMethod called change_password_form containing:
<HTML>
<HEAD>
<TITLE>Change Password</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<h1>Change Password</h1>
<FORM ACTION="change_password_report" METHOD="POST">
<TABLE>
<TR>
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (5 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
<TD ALIGN="LEFT" VALIGN="TOP">
<STRONG>Username</STRONG>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="text" NAME="username" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<STRONG>Password</STRONG>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="PASSWORD" NAME="password" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<STRONG>New Password</STRONG>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="PASSWORD" NAME="first_password" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<STRONG>Confirm</STRONG>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="PASSWORD" NAME="second_password" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="SUBMIT" NAME="submit" VALUE=" Ok ">
</TD>
</TR>
</TABLE>
</FORM>
</BODY>
</HTML>
create a DTMLDocument called change_password_report called:
<!--#var standard_html_header-->
<dtml-call "REQUEST.set('val','0')">
<dtml-in "SQL_get_password(username=username)">
<dtml-if "_.crypt.crypt(password,'ab')==_['sequence-item'].real_password">
<dtml-call "REQUEST.set('val','1')">
</dtml-if>
</dtml-in>
<dtml-if "val == '1'">
<dtml-if "first_password==second_password">
<dtml-call "REQUEST.set('password',_.crypt.crypt(first_password,'ab'))">
<dtml-call SQL_change_password>
<h2>Your Password has been changed</h2>
<p>Thank you.</p>
<dtml-else>
<h2>Failed</h2>
<p>Passwords don't match</p>
<p>First: <dtml-var first_password></p>
<p>Second: <dtml-var second_password></p>
</dtml-if>
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (6 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
<dtml-else>
<h2>Failed</h2>
<p>Old Password does not match.</p>
</dtml-if>
<!--#var standard_html_footer-->
8. Set permissions to protect the use database.
It is now time to setup the permissions on the GUF objects to ensure that the SQL methods can only be called the code that we
have created.
Create a user defined role called Authoriser in the acl_users folder do not give it any permissions.
set the 'Use Database Methods' permission for 'Authoriser' on all the SQLMethods created above.
set the Proxy role "Authoriser" on:
●
change_password_report
●
userAuthenticate
●
userList
●
userRoles
9. Force GUF to see the changes
All is now ready. Because of some oddities in the permissions stuff in Zope we must flush the GUF cache before any of out
changes will come in to affect.
From the 'cache' tab of the acl_users folder click the flush button to have all the changes take effect.
10. Using MySQL
Provided by Dennis Baker.
There are a couple of minor differences to get MySQL to work. The primary difference is that MySQL supports a
AUTO_INCREMENT feature on int data types. Below are the two different files:
This is the script for creating the tables, it replaces the code in Step 1.
CREATE TABLE users (user_id int NOT NULL auto_increment,
username char(15),
password char(15), index (user_id));
CREATE TABLE groups (
group_id int not null auto_increment,
groupname char(15), index (group_id) );
CREATE TABLE user_group_map (
user_id int,
group_id int );
This is the dtml code for the SQL_add_user SQL method, it replaces the code in step 5.
SQL_add_user
Arguments
username password
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (7 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
insert into users (username,password) values (
<dtml-sqlvar username type=string>,
<dtml-sqlvar password type=string>)
<dtml-var sql_delimiter>
insert into user_group_map (user_id, group_id)
SELECT LAST_INSERT_ID(), group_id
from groups where groupname = 'Subscriber'
Using MS Access via ODBC
Provided by Alan Capesius.
Getting MS Access tables to work (via ODBC) requires counter/autonumber = fields.=20
How to Create the Tables Manually
A. Using Access create a table named users with the following = fields:
●
user_id - AutoNumber
●
username - Text
●
password - Text
Using Access create a table named groups with the following = fields:
●
group_id - AutoNumber
●
groupname - Text
Using Access create a table named user_group_map with the = following fields:
●
user_id - INTEGER
●
group_id - INTEGER
How to Create the Tables Using an SQL Method
SQL_add_user
Arguments
None
CREATE TABLE users (user_id COUNTER, username TEXT, password TEXT)
<dtml-var sql_delimiter>
CREATE TABLE groups (group_id COUNTER, groupname TEXT)
<dtml-var sql_delimiter>
CREATE TABLE user_group_map (user_id INTEGER, group_id INTEGER )
Replace the SQL_add_user method code with:
SQL_create_tables
Arguments
username = password
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (8 of 9) [05/07/2000 11:14:57]
How To: Use GenericUserFolder with an SQL Database.
insert into users (username,password)=20
values (
<dtml-sqlvar username type=3Dstring>,
<dtml-sqlvar password type=3Dstring>
)
<dtml-var sql_delimiter>
INSERT INTO user_group_map ( group_id, user_id )
SELECT Max(groups.group_id) AS Expr2, Max([users].[user_id]) AS Expr1
FROM groups, users
WHERE (((groups.groupname)=3D'specialrole'))
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/hippy/GUF_SQL_crypt_1_2?pp=1 (9 of 9) [05/07/2000 11:14:57]
How To: Use Catalog Classes out of Zope
How-To: How To: Use the Catalog-Class out of
Zope
Created by kelcmab3. Last modified on 2000/04/15.
Abstract
By nature I am very curiuos and in the first days, I use Zope I want to know how to use the Catalog-Class outside of Zope. I
kneeded an hour or two to let it work. I didn't found anything in the documentation so I think someone else could use this
information.
The Catalog Class in a Code example
Consider the following code:
import sys
sys.path.append('/opt/Zope/lib/python')
import ZODB
import Persistence
from Persistence import Persistent
from ZODB.FileStorage import FileStorage
from Products.ZCatalog.Catalog import Catalog
### create some classes to be persistent
class Nobody( Persistent ):
def identify( self ):
print "I am Nobody!"
class Person( Persistent ):
def __init__( self, first, name, email, friend=Nobody() ):
self.first = first
self.name = name
self.email = email
self.friend= friend
def identify( self ):
print "I am " + self.first + " " + self.name
print "and my Email is " + self.email
### OK, the ZODB-API together with a Catalog-Class
class Application:
def __init__( self, file='db.fs' ):
self.file= file
self.db = ZODB.DB( FileStorage( file ) )
self.co = self.db.open()
self.root= self.co.root()
if self.root.has_key( 'cat' ):
self.cat= self.root['cat']
else:
self.cat
= Catalog()
### This is, how I get it to work
self.cat.aq_parent= self.root
self.root['cat']= self.cat
### Add indexes to the Catalog-Class
### The names must match the fields of the Person-Class!
self.cat.addIndex('first','FieldIndex' )
self.cat.addIndex('name' ,'FieldIndex' )
self.cat.addIndex('email','FieldIndex' )
get_transaction().commit()
def storePerson( self, person ):
uid= id( person )
print "stored as " + str( uid )
http://www.zope.org/Members/kelcmab3/catalog_out_of_zope?pp=1 (1 of 2) [05/07/2000 11:14:59]
How To: Use Catalog Classes out of Zope
self.root[uid]= person
### Let the Catalog know from this object
self.cat.catalogObject( person, uid )
get_transaction().commit()
def searchPerson( self, **kw ):
r= self.cat.searchResults( kw )
paths= self.cat.paths
root = self.root
k= []
for i in r:
id= i.data_record_id_
k.append( root[paths[id]] )
return k
The hack is the line "self.cat.aq_parent=self.root". Without this line I'm running mad because whenever the Catalog founds a
matching class, an Exception occurs that the catalog-class has no attribute called aq_parent. Setting it to "None" doesnt work, so I
set it to self.root. I really dont now if this is the right way, but it seems to work! If someone knows it better, let me know!
Here is the code-example on how to use the classes above:
### test1.py
from defs import Application, Person
a= Application()
p1= Person( 'marian','kelc','marian.kelc@ruhr-uni-bochum.de' )
p2= Person( 'tanja', 'kreierhoff','tanja.kreierhoff@web.de', p1 )
p1.friend= p2
a.storePerson( p1 )
a.storePerson( p2 )
### test2.py
from defs import Application, Person
a= Application()
### perform searches with keyword-arguments
ids= a.searchPerson( name='kelc' )
for i in ids:
i.identify()
print "Friend is:"
i.friend.identify()
i.friend.friend.identify()
print str( id( i ) )
print str( id( i.friend.friend ) )
Hm, the last two lines show you, that the ZODB is really aware on circular object references! :-) I hope, this could be helpful for
someone!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/kelcmab3/catalog_out_of_zope?pp=1 (2 of 2) [05/07/2000 11:14:59]
How to Create a Boring Product in Python (v0.1)
How-To: How to Create a Boring Product in
Python (v0.1)
Created by gtk. Last modified on 1999/10/26.
This document is a companion to the Boring Product, which I hacked together so that budding Zope programmers out there could
get into coding products in Python quickly. If you want to see all of the code in one place at once, grab Boring and have a look. If
you want to know what the code does, read on.
Why code a Product in Python?
Any markup language pretending to be a programming language is going to run out of gas pretty quickly. On the other hand,
coding an entire content management system from scratch is lots of hard work, even in a language as fiendishly easy as Python.
Zope provides you with the best of both worlds by letting you code in either DTML or Python as appropriate for the task.
External methods provide a quick way for your DTML documents to call functions coded in Python. They're most useful in the
case where you have some Python which does something already, and just need to call it. You could code entire web interfaces to
back-end systems using nothing but external methods.
If you want to be able to use the Zope management interface to create and manage your objects, though, you need to implement a
Product. Whilst any Product can use external methods, sometimes it's just simpler to code the object from scratch in Python itself
and be done with it.
What do you need?
__init.py__
Zope product initialization is handled through an initialization module named __init__ in your product's directory. The
initialization module's job is to tell Zope about the product's classes and perform any run-time initialization required for the
product as a whole. It should not contain any of your product's working code.
__doc__ = """Boring initialization module."""
__version__ = '0.1'
I'm not entirely sure that Zope itself does anything with the doc and version strings, but anything that could potentially be used
later by automatic code documentation tools sounds good to me. :)
import Boring
Don't forget to import the modules containing your product's classes! Otherwise you'll have a really hard time referring Zope to
them later.
initialize(): what a relief!
Zope 1 needed to be told about your product's capabilities through lots of dictionaries defined in the __init__ module. That was a
little unwieldy, to say the least. Zope 2 now supports the initialize function, which has lots of default arguments and does most of
the hard work for you.
All of the heavy lifting of product initialization is now handled through initialize, which is passed an instance of
App.ProductContext to customize through its registerClass method. Your initialize function needs to call registerClass once for
each of your product's classes that you need to be handled through the Zope management interface. It looks like this:
def initialize(context):
try: # to register the product.
context.registerClass(
Boring.Boring,
constructors = (
Boring.manage_addBoringForm,
Boring.manage_addBoring),
icon = 'item.gif'
)
#
#
#
#
#
Which is the addable bit?
Constructors;
Add goes to the first one.
Both get permissions.
Icon for the product.
http://www.zope.org/Members/gtk/Boring/HowTo-Boring?pp=1 (1 of 4) [05/07/2000 11:15:04]
How to Create a Boring Product in Python (v0.1)
except: # if you can't register the product, tell someone.
import sys, traceback, string
type, val, tb = sys.exc_info()
sys.stderr.write(string.join(traceback.format_exception(type, val, tb), ''))
del type, val, tb
The except code is from Jonothan Farr's LocalFS product. If there's a problem with your product, Zope will sometimes provide
you with access to "broken product" and a backtrace of what went wrong. I suspect that's only for problems in your class modules
themselves, however. Jonothan's code provides traceback on the console for anything that happened in registerClass(), assuming
you're running Zope in debug mode.
registerClass(): what's going on?
__init__ is just a life-support system for that one call to registerClass, so it's worth taking a closer look at what it does. Here's the
definition of registerClass:
def registerClass(self, instance_class=None, meta_type='',
permission=None, constructors=(),
icon=None, permissions=None, legacy=(),
):
Look at all those default arguments! Unlike the old Zope 1 method, where you had to go to all of the hard work even if you
weren't particularly interested in customizing your object's behaviour, registerClass will do as much of the work for you.
Here are the arguments:
instance_class
The class you're registering with Zope. Don't trust the source, which as of Zope 2.0.1 still claims this argument is "not
currently used". If you don't specify instance_class, some of the other defaults won't work. I found it much easier to
supply it.
meta_type
The Zope meta_type of the object, used in many places including the drop-down list for adding an object to a folder. If
you don't specify this, Zope will use the meta_type attribute of your class -- if you specified instance_class, that is.
permission
The name to be used for the permissions associated with your class' constructor methods (none of which are in the class
itself; see the coverage for Boring itself). If you don't supply a name, Zope will sensibly use the meta_type.
constructors
A list of constructor methods for your class. The first of these will be called by the Zope management interface when
someone selects your class from the Add drop-down list. You should list any other methods in your class module
involved in construction so that the "Add Whatevers" permissions are applied to them.
icon
The name of the image file used to label any instances of this class in Zope's management interface.
permissions
Any additional permissions you want to associate with your class. If you define them in the class itself, you don't need to
duplicate effort and put them here, so for most purposes the default will do just fine.
Since I've defined meta_type in my Boring module, I don't need to specify meta_type when calling registerClass(). I also don't
need permission because the meta_type name will do just fine, or permissions because I define everything else in the class. All
that's left is the class itself, its constructors, and the icon.
Boring.py
Now that __init__ more or less takes care of itself in Zope 2, the only heavy lifting to do is in your product's main module. First
up, the documentation strings:
__doc__ = """Boring product module."""
__version__ = '0.1'
Now, some more comprehensive imports:
from Globals import HTMLFile
from Globals import MessageDialog
from Globals import Persistent
http://www.zope.org/Members/gtk/Boring/HowTo-Boring?pp=1 (2 of 4) [05/07/2000 11:15:04]
How to Create a Boring Product in Python (v0.1)
import OFS.SimpleItem
import Acquisition
import AccessControl.Role
# Various acquisition types.
# Zope Security management.
What do they all do?
Globals.HTMLFile
Provides a callable object wrapped around a DTML file. You don't want to litter your Python source with stray fragments
of DTML. Put it in a file in your product directory where it belongs.
Globals.MessageDialog
Provides a response designed for use in the Zope management interface. I think.
Globals.Persistent
Why we're getting this from Globals rather than Persistence, I'm not particularly sure, but here 'tis anyway. Persistent
makes our object stick in the ZODB.
OFS.SimpleItem
SimpleItem.Item is a minimally interesting Zope class.
Acquisition
Provides Zope acquisition services to this class so that it can inherit methods and properties from its containers in Zope.
AccessControl.Role
Provides Zope security services.
Of these, the first we'll use is HTMLFile, in the constructor:
manage_addBoringForm = HTMLFile('boringAdd', globals())
def manage_addBoring(self, id, title='', REQUEST=None):
self._setObject(id, Boring(id, title))
if REQUEST is not None:
return self.manage_main(self, REQUEST)
manage_addBoringForm and manage_addBoring fulfil the promise we made in __init__ to provide Zope constructor methods for
this class. The former (excuse the pun) is called by Zope when someone chooses to add our class, and should return a form the
user can fill in to specify the instance's configuration. The easiest way to do this is code the form in DTML (or pure HTML) and
use HTMLFile to make it callable.
manage_addBoring just creates a new Boring, calls the _setObject method obtained from somewhere to register it with Zope, and
returns the main management form for the class.
Having provided the functions Zope requires as a wrapper around our class, we can get around to the business of the class itself:
class Boring(
OFS.SimpleItem.Item,
# A simple Principia object. Not Folderish.
Persistent,
# Make us persistent. Yaah!
Acquisition.Implicit, # Let us use our parent's variables and methods.
AccessControl.Role.RoleManager # Security manager.
):
Boring inherits a few Zope classes. I'd go into them, but I'm not yet entirely sure what they all do. :)
Since we didn't specify the meta_type Zope needs to be able to identify us in __init__, registerClass() looks it up in our class:
meta_type = 'Boring'
We define the tabs available in the management pane as follows:
manage_options = (
{'label': 'Edit',
{'label': 'View',
{'label': 'Security',
)
'action': 'manage_main'},
'action': ''},
'action': 'manage_access'},
Each tab needs a label and an action. Action can be either a method in the class or an attribute that happens to be a callable object,
like an HTMLFile object. Empty actions default to index_html. The manage_access method is provided by the
AccessControl.Role.RoleManager object we inherit.
Permissions are set with a list that associates permissions with methods:
__ac_permissions__=(
http://www.zope.org/Members/gtk/Boring/HowTo-Boring?pp=1 (3 of 4) [05/07/2000 11:15:04]
How to Create a Boring Product in Python (v0.1)
('View management screens',
('Change permissions',
('Change Borings',
('View Borings',
)
('manage_tabs', 'manage_main')),
('manage_access',)
),
('manage_edit',)
),
('',)
),
Again, '' defaults to index_html.
Our class initialization method just has to do the basic work:
def __init__(self, id, title=''):
self.id = id
self.title = title
A couple more HTMLFile objects provide our index_html and manage_main "methods":
index_html = HTMLFile('index', globals())
manage_main = HTMLFile('boringEdit', globals())
Finally, a genuine manage_edit method handles the FORM results from manage_main:
def manage_edit(self, title, REQUEST=None):
self.title = title
if REQUEST is not None:
return MessageDialog(
title = 'Edited',
message = "Properties for %s changed." % self.id,
action = './manage_main',
)
MessageDialog handles, I suspect, that nice interface kludge whereby when you submit some changes you're directed back to the
edit form but with some text at the top describing what you just did.
That's all there is!
What if I don't want to be Boring?
Once I've done some more research, I'll post an Interesting class which handles advanced stuff like acting as a container and being
able to be used as a parent of a ZClass. If you couldn't be bothered waiting, there's plenty of source to dissect.
Enjoy!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/gtk/Boring/HowTo-Boring?pp=1 (4 of 4) [05/07/2000 11:15:04]
How to Distribute a Zope Product
How-To: How to Distribute a Zope Product
Created by Amos. Last modified on 1999/09/15.
So you want to distribute a Zope add-on or "Product". Great, fame and fortune await you. Here is a recipe to get you going.
1. Create your Zope product locally and make sure it works right.
2. Make sure to include a version.txt and a README.txt file in your Product's directory. This makes it easier on
your users.
3. Tar and gzip your product in such a way as to allow folks to expand it in their Zope directory. For example, your files
should look like, lib/python/Products/MyProduct/foo.py
4. On Zope.org create a Software Product object.
5. Go to the Properties tab of your Software Product and provide information about your product including a description,
the license terms, etc. For the URL properties such as changes_url you can either use absolute URLs to reference remote
pages, or relative URLs to access local Zope objects.
6. Now your ready to upload your tarball. Inside your Software Product create a Product Release object. Give the release
object the same id as your tarball, for example, DeadParrot-1.0b2.tgz
7. Go to the Release tab on your new Product Release and set the properties to describe your release.
8. You should be all set. Now test it to make sure that everything is fine by viewing your Software Product. You should be
able to see all the releases and should be able to download them.
9. Once it looks good request that it be cataloged by going to the Catalog tab of the Software Product and the Product
Release objects.
10. You may now wish to create a News Item object to notify folks of your new cool Zope product. You can create this News
Item anywhere in your Member folder. Don't forget to include a link in the body of the news item to your Software
Product object, and to request that the News Item be cataloged.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Amos/CreateProduct?pp=1 [05/07/2000 11:15:06]
How to Generate a Form with a Variable Number of Inputs; and How to Retrieve the Data From the Form
How-To: How to Generate a Form with a Variable
Number of Inputs; and How to Retrieve the Data
From the Form
Created by jpenny. Last modified on 1999/10/22.
How to Generate a Form with a Variable Number
of Inputs; and How to Retrieve the Data From the
Form
Perhaps an example of why anyone would want to do something so decidedly odd sounding is in order. At my place of
employment, one of the things that we do is produce sample cards for customers. Sample cards can be of virtually any theme, like
color, logo, type, and more particularly, can call for almost any number of items to be attached. Sample cards can range from
about 5 items to about 200 items.
Now, I do not want to generate a form that has space for 200 items in it, and require everyone to use it. First, that would mean that
the submit buttons would have to be placed multiple times or that the submit button would be a long ways away from the last item
entered. Further, I do not know that 200 is a hard upper limit, someone may need more.
So, I will follow another strategy, I will ask the user, (how novel), in another form how many rows of items they need to have. I
will then generate a form having this number of items, allow the user to fill it in, catch the submission, and store the items.
Now, turning from why to how, I will need the following, a preliminary fixed-length form that includes a field containing the
number of rows needed in the variable length form; an object to catch the submission and generate the variable length form; and
another object to catch the submission from the variable length form and store the information. I am also going to need a trivial
External Method.
Beginning with the External Method, I created a little python file, called range.py in the Extensions subdirectory of my top-level
Zope directory. It contains simply:
def Range(x):
return map(str, range(int(x)))
It is vaguely possible that someone who needs this technique will have trouble reading this, so please don't be offended that I
explain this code. This says that Range is a function that must be supplied with one argument. Inside the function, we have
nothing but python standard functions. Working from the inside out, we convert x to an integer, and apply the range function. The
range function, in this form, builds a list of integers, containing 0 through x-1. The str function converts things to string, and the
map function applies the given function to each item of the list given as its second argument. So, the result of calling Range is a
list containing ['0', '1', ... , 'x-1']. We will see later that it is convenient to have strings here.
Now, go in to the Zope manage function, and somewhere above any multirow forms, add the Range External Method
Getting the Number of Lines in the Variable Length Form
Build a DTML Method that has something like:
<h2 align=center>Sample Room Request Form</h2>
<form method="post" action="save_form">
...
<strong>Number of Items Needed:</strong>
<input type=text name=item_count size=3 value=10>
(You will fill in the actual items in a moment)
<center><input type=submit name=submit value="Fill in Items">
<input type=submit name="cancel" value="cancel"></center>
</form>
<!dtml-var standard_html_footer>
The ellipsis denote that there is a lot of irrelevant (to this task) information being suppressed. This should result in a form that
http://www.zope.org/Members/jpenny/variable_length_forms?pp=1 (1 of 4) [05/07/2000 11:15:11]
How to Generate a Form with a Variable Number of Inputs; and How to Retrieve the Data From the Form
looks like:
Sample Room Request Form
...
Number of Items Needed:
10
(You will fill in the actual items in a moment)
Fill in Items
cancel
Generating the Variable Length Form
We need to add a Folder named save_form to the Folder in which our initial request form resides. And there, we add an
index_html. It contains something like:
<!--#var standard_html_header-->
<h2 align=center>Add Lines to Sample Room Request</h2>
...
<dtml-if "not REQUEST.has_key('db_error') and not REQUEST.has_key('is_cancel')">
<form method="post" action="save_items">
<dtml-if "_.int(item_count)>0">
<table>
<TR><TH>
<TH>ITEM DESCRIPTION
<TH>ITEM NUMBER
<TH>QUANTITY DESIRED
<dtml-in "range(item_count)">
<TR><TD><dtml-var "_.int(_['sequence-item'])+1">
<TD><input type=text size=30 name="desc<dtml-var sequence-item>">
<TD><input type=text size=15 name="item<dtml-var sequence-item>">
<TD><input type=text size=10 name="qty<dtml-var sequence-item>">
</dtml-in>
</table>
</dtml-if>
<center><input type=submit name=submit value="Save Changes">
<input type=submit name=cancel value=cancel></center>
<input type=hidden name=sales_request_number
value="<dtml-var sales_request_number>">
<input type=hidden name=item_count value="<dtml-var item_count>">
</form>
</dtml-if>
Again, some authentication stuff and some database stuff has been omitted. Suppose the user had entered 3 in the item_count field
of the previous form. Then the form generated by this DTML will look like:
Add Lines to Sample Room Request
...
0">
ITEM DESCRIPTION
ITEM NUMBER QUANTITY DESIRED
1
2
3
Save Changes
cancel
Now, this form must be caught and the data in it retrieved. Again, we make another folder, in the directory of this second form.
http://www.zope.org/Members/jpenny/variable_length_forms?pp=1 (2 of 4) [05/07/2000 11:15:11]
How to Generate a Form with a Variable Number of Inputs; and How to Retrieve the Data From the Form
Our form method said that we we going to call it save_items. Make the folder, and make an index_html in it.
Handling the Information Submitted from the Variable Length
Form
Handling this form would seem strictly routine, until you realize that you do not know the actual names of the items on the form.
So, we are going to have to get tricky and generate the required names on the fly.
This last index_html looks something like:
<!--#var standard_html_header-->
<h2>Save Items of Sample Room Request</h2>
<dtml-comment>add each line (of requested items) to the database</dtml-comment>
<dtml-comment>Technical note: There are at least three tricky things here:
1) sequence-item is the name of th current row-number of the dtml-in
loop,
_['sequence-item'] is its value.
2) 'desc'+_['sequence-item'] is the name of the description of data
in the current row, _['desc'+_['sequence-item']] is the actual
value
of this data
3) The set saves the data in a variable as named in the parameter list
of sql_add_sample_room_items.
4) sql_add_sample_room_items implicitly grabs its parameters.
</dtml-comment>
<dtml-in "range(item_count)">
<dtml-call "REQUEST.set('desc', _['desc'+_['sequence-item']])">
<dtml-call "REQUEST.set('item', _['item'+_['sequence-item']])">
<dtml-call "REQUEST.set('qty', _['qty'+_['sequence-item']])">
<dtml-call sql_add_sample_room_items>
</dtml-in>
...
This DTML generates no output at all, to this point. You need to add a "click on me to return to main menu" button, or redirect the
user, or something. This would be in the portion hidden in the ellipsis.
The dtml-in loop contains two interesting techniques. Before we get to the meat of the loop, looking back at the second form, the
variable length form, you will see that item_count was made a hidden variable, so that it would be available here.
So, we use range to build a loop having the correct number of items in it. Inside the loop, we have expressions like:
['desc'+_['sequence-item']]). What does this expression do? Working from inside out, 'sequence-item' is the name of the current
element of the list, _['sequence-item'] is its value, one of ['0', '1', ... , 'item_count-1']. 'desc' + _['desc'+_['sequence-item']] will take
on the values 'desc0', 'desc1', 'desc2', ..., as control passes through the loop.
This means that each time we pass through the body of the loop, we are generating the names of the input fields of the loop. Now
we need to get the contents of field. _[name] does that, so to get the contents of the current description, we use:
_['desc'+_['sequence-item']].
Now, we have to do something with this information. I want to store it in a database. I have a ZSQL Method defined to do so. The
method expects its parameters to be in variable named 'desc', 'item' and 'qty'. So, we tuck away a copy in the REQUEST, using
REQUEST.set('desc', _['desc'+_['sequence-item']]). Simple.
Another Way
This comes from Martijn Pieters. I have not yet tested it.
You could also use the :records marshalling directive (you know, like :int, :float, and :list). It creates a list of records out of web
POSTed data.
If you to use the following DTML:
<dtml-in "range(item_count)">
<TR><TD><dtml-var "_.int(_['sequence-item'])+1">
<TD><input type=text size=30 name="items.desc:records">
<TD><input type=text size=15 name="items.item:records">
<TD><input type=text size=10 name="items.qty:records">
http://www.zope.org/Members/jpenny/variable_length_forms?pp=1 (3 of 4) [05/07/2000 11:15:11]
How to Generate a Form with a Variable Number of Inputs; and How to Retrieve the Data From the Form
</dtml-in>
The receiving method would be handed a list called 'items' where each listmember is a recordobject with the properties desc, item
and qty.
You could loop through them like this:
<dtml-in items>
<dtml-var desc>: <dtml-var item>, <dtml-var qty>
</dtml-in>
and, iirc, you can call your SQL method:
<dtml-in items>
<dtml-call sql_add_sample_room_items>
</dtml-in>
This has the added advantage that if you do not fill out one of the lines, no record will be created for it.
Discussion of the Difference between the Methods Presented
The first method is more 'primitive', it involves some messy work with the _ namespace. The second method is a bit shorter and
much easier to read. The first method, as shown, will create a row in the database for every line entered, even if all the fields are
blank. The second method does not. The first method depends only on basic zope and namespace manipulations. The second
method depends on the :records construct that is documented only in the Trinkets tutorial. The Trinkets tutorial has this to say (in
context of a multi-line form requesting name, age, and email address):
Note:
Before creating a second record and adding an attribute, Zope gets the last record in the list and checks to see if that record has
the attribute. If the first record doesn't have the attribute, it inserts the attribute and value instead of creating a new record. For
example, if the user entered a name and age for Member #1 and only an email for Member #2, the list will have only one record.
That record will contain the name and age attribute from Member #1 as well as the email attribute from Member #2.
This note worries me a bit. In this context, it appears that we have a possiblity of getting data mixed up.
For more details of the the options available for form variables, see Zen's Form Variable Types and Typechecking,
Hope this helps someone. Corrections, suggestions for improvement, etc. can be sent to jpenny@universal-fasteners.com
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jpenny/variable_length_forms?pp=1 (4 of 4) [05/07/2000 11:15:11]
How to Write a DA
How-To: How to Write a DA
Created by petrilli. Last modified on 1999/11/08.
HOW-TO: Writing a New Database Adapter
Introduction
Developing a new Zope Database Adapter is a relatively straight forward process. This document will assume that you have both
knowledge of the database you are working with, Python, object-oriented programming and also currently have an interface
exposed at the Python level that provides the standard Python DB-API v1 functionality. Zope uses substantially less functionality
than is provided by the DB-API specification, however this is a good starting point for developing a new database adapter.
Architecture
The architecture of the Zope object-relational integration is principly straight-forward and is derived from an early project for the
Principia product called "Aquaduct," which does the hard part of providing ZSQLMethods as well as the ability to provide
"pluggable brains" in the data framework. The architecture is composed of a few core pieces:
●
ZODB3 and the Transaction Manager
●
ZSQLMethods
●
Database Adapter
●
Python/C integration with relational database
This document will only deal with the first and third, leaving the rest for further study in the future.
ZODB3 and the Transaction Manager
One of the core concepts behind the Zope applicaton framework is the pervasive nature of transactions inside of all components. A
transaction is defined as a "unit of work" which should from the outside feel atomic. What this means is that either everything
inside the transaction must succeed, or it will all be "rolled back." This means that if you're halfway through editing someone's
back account, and you find a problem (or your system failes), then the data will still maintain its integrity.
The Transaction Manager (TM) in Zope is a rather advanced implementation of a distributed commit protocol. This allows
transactions to span multiple data-repositories---both the internal object database as well as external sources such as Oracle,
Sybase, etc. There is a simple API that must be implemented in your DA for this to function correctly and it will be explained
below.
A side note is that some relational databases do not have the concept of transactions. This is troublesome, as it places a huge
exposure point in your application logic. While Zope will roll back data changes in situations as appropriate, this is impossible in
some databases, and thereby you may find your data in an inconsitent state. It is important to understand the risks associated with
using a database sans transactions. We will discuss how to implement a DA in this transaction-free environment.
Database Adapter
The Database Adapter (DA) provides the API interface to the database itself that is used by things such as the transaction manager
and ZSQLMethods. There are several service levels that are available, depending on what functionality you desire to expose. Not
all databases provide all functionality (such as browsing the table space).
The DA does not need to concern itself strongly with the implementation of Zope as much of this is implemented through mix-in
classes and the framework built over the DA. This can reduce the actual Zope DA code size to only a few hundered lines.
Threading and Serialization
There are three different distinct levels of DA threading support that we identify:
Level 1: A DA which functions correctly only in a Zope environment with one application thread. This is of minimal value.
http://www.zope.org/Members/petrilli/WritingADA?pp=1 (1 of 4) [05/07/2000 11:15:17]
How to Write a DA
Level 2: A DA which serializes (either internally or through the TM) all requests and provides safety regardless of the number of
threads in the application.
Level 3: A DA which provides no restrictions on the number of threads, connections and concurrent activities. This is the prefered
state as it provides a scalable solution.
There is one issue with some databases. There are certain client libraries which have a bizarre restriction on the number of
connections in a single thread. While we can guarantee that there will not be more than active copy of a Database Connection
instance active in a single thread, we can not guarantee that there will not be multiple Database Connection instances active to
seperate (or even identical) databases activated by a single thread. If this restriction exists in the client library, then this restriction
must be documented (and should probably be provided in the product README.txt that shows up on the Control Panel).
Implementation Details
There are several interfaces that a DA must provide:
●
Transaction interface
●
Search interface
In addition, there is one optional interfaces:
●
Browse interface
The rest of this document will deal with documenting these interfaces and providing some guidance for their implementation.
Transaction Interface
The tranaction interface in Zope2 is actually much simpler than in previous releases. This has been gained by providing two
mix-in classes that implement much of the more complicated aspects of the transaction machienery. The DA is responsible only
for implementing a couple methods.
The mix-in classes are:
Shared.DC.ZRDB.TM.TM: Provides standard functionality for Level 3 Database Adapters.
Shared.DC.ZRDB.THUNK.THUNKED_TM: Provides an automatical global serialization for L2 and L1 Database Adapters.
This is suboptimal, but is a good starting point for implementing the necessary serialization for a non-threadsafe DA.
What this means to the developer is that class definitions are much easier. The standard idiom for a DA class is:
from Shared.DC.ZRDB.TM import TM
class DB(TM):
def __init(self,connection_string=''):
...
This is substantially easier than in a prior release.
The class of the DB needs to provide three methods which the transaction machienery will use to implement the Transaction
Manager. Those methods are:
def _begin(self):
# Whatever needs to be done for beginning transactions
...
def _finish(self):
# Standard for a COMMIT
...
def _abort(self):
# Standard for a ROLLBACK
...
This is the end of this. However, there are some issues related to some oddities of database implementations.
http://www.zope.org/Members/petrilli/WritingADA?pp=1 (2 of 4) [05/07/2000 11:15:17]
How to Write a DA
Implicit Transaction Begins
Most databases do not use begin to start a transaction, but instead use an implecit begin when a modification to the database is
made, or when something like SELECT * FROM foo FOR UPDATE is executed. This is something simple, as it requires a
_begin like:
def _begin(self):
pass
And you're done!
Implicit Transaction Commits
These are amazingly dangerous, but unfortunately they are the default configuration for the more unenlightened database systems.
What this means is that there is an implicit begin and commit wrapping around all SQL statements. This is frightening as it
basically undermines the ability to rollback a transaction. If the database you are working with does this, then you must turn it
off. Should you choose not to do this, you will experience data corruption if your application ever has an error, and your
transaction system is effectively non-existent.
Search Interface
The search interface is the method used by ZSQLMethods to implement the functionality of the integration. It is implemented by a
single defintion:
def query(self, src=None, max_rows=99999999):
# Magic here!
...
The first thing to do is define the arguments to the function.
self: The database connection object.
src: The SQL to be used in the query.
max_rows: Maximum number of rows to return to Zope.
So what does this method have to do inside? Well, it turns out that it doesn't have to do that much, and most of it is obvious.
The first thing the method has to do is register with the transaction machienery, this is done by calling:
self._register()
which is inherited from the superclasses. This lets the transaction machienery know that you're interested in what's going on. Once
this is done, the method is responsible for executing the DQL/DML/DDL and returning the results back to Zope.
Normal Result Format
Results should be returned from the query method as a tuple of two objects:
items: Describes the columns
result: List of tuples of the results from the database
Note that the result return is identical to the results of a fetchmany method in the DB-API. The items component is a bit
more complex. items is a list of mapping objects which have the following construction::
{'name': name, type: type, width: width, null: null_ok }
These pieces are:
http://www.zope.org/Members/petrilli/WritingADA?pp=1 (3 of 4) [05/07/2000 11:15:17]
How to Write a DA
name: The name of the column
type: The data type of the column (see the list below)
width: Width of a text column
null: Whether the column can be null or not
The available data types are:
s: String
d: Date
i: Integer
n: Number (i.e. floating point)
Browse Interface
Currently, Zope does not have a "browse interface," and this is up to the individual DAs to implement.
Miscellaneous Details
There are some remaining issues that need to be dealt with that don't fit into the interfaces that are normally discussed.
Module Design
The DA must be implemented as an official Python module. There are certain aspects that are standard idioms, and while you can
differ, it is recommended that you follow them. In addition, it is also a Product in the Zope sense. You should examine the Product
How-To to understand the construction of Products.
__init__.py
__init__.py is responsible for registering the new DA with the Zope framework. In addition, it might do any kind of startup
proceedures that need to be accomplished before a DA is created.
DA.py
This is the product itself, which provides the following method:
manage_addFOOBARConnection: This does the actual add of a database connection to an ObjectManager class.
In addition, it implements the following classes, as appropriate to the capabilities it is providing:
Connection: The actual connection object (REQUIRED).
Browser: The main interface
db.py
This file provides the actual implementation of the interfaces and all necessary logic.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/petrilli/WritingADA?pp=1 (4 of 4) [05/07/2000 11:15:17]
How to change the default port when runnning Zope as an NT Service
How-To: How to change the default port when
runnning Zope as an NT Service
Created by teyc. Last modified on 2000/04/09.
Update
Hugo Ramos notified me that there is an error in the documentation below.
Where it is written:
'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Zope2.0\Parameters '
one should read:
'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ZopeDIR\Parameters '
where ZopeDIR = Your instalation directory!!!!
Registry entries
Here are the registry keys associated with the Zope service.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Zope2.0
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Zope2.0\Enum
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Zope2.0\Parameters
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Zope2.0\PythonClass
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Zope2.0\Security
Setting the service parameters
The Parameters key contains the command line to run zope. add any extra command line options here.
example: to serve on port 80
-w80
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howNTService?pp=1 [05/07/2000 11:15:20]
How to create a mail form
How-To: How to create a mail form
Created by Amos. Last modified on 1999/09/15.
Zope makes it pretty easy to accomplish simple CGI style tasks with a little DTML. One common task everyone wants to do is to
create a form that sends email. Here's a simple example showing how to create a mail form with Zope. All it takes is a Mail Host
object, and two DTML Document Objects.
Create a Mail Host
Start by creating a Mail Host object. This object will allow Zope to send email. If you have already defined a Mail Host object,
skip to the next step. You probably want to create this object at the top level of Zope so that all objects will have access to mail
facilities.
To create a Mail Host, choose "Mail Host" from the pop-up list of objects to add, and click "Add". You will next need to define
your Mail Host. The import thing to specify is the SMTP Host. This should be the name of a computer which you can use to send
mail. It may be the same as the localhost computer.
Create a Form
Next create two documents, one to collect the data, and one to send the mail.
You can create these Documents at any level in Zope that can acquire the Mail Host.
Let's call the form Document Feedback, and the mail sending Document SendFeedback. You should give the Feedback
Document DTML similar to this:
<!--#var standard_html_header-->
<H2>We want your input!</H2>
<form action="SendFeedback" method="post">
Your Name: <input type="text" name="name" size="40">
<br>
<textarea name="comments" rows="10" cols="50">
Type your comments here.
</textarea><br>
<input type="submit" value="Send Feedback">
</form>
<!--#var standard_html_footer-->
As you can see it defines a form whose action is SendFeedback. So this Document calls the other Document.
The form defines two variables, name, and comments.
Create a Form Processor
The SendFeedback Document should send the mail, and give the user some assurance that the mail has been sent. Here's one
way to do that:
<!--#var standard_html_header-->
<!--#sendmail mailhost="MailHost"-->
To: Feedback Recipient <form_recipient@example.com>
From: Zope Feedback Form <zope_form@example.com>
Subject: Feedback from the web
Feedback from : <!--#var name-->
Comments:
<!--#var comments-->
<!--#/sendmail-->
<h1>Thank you for your input!</h1>
<p>Your comments have been sent.</p>
http://www.zope.org/Documentation/How-To/MailForm?pp=1 (1 of 2) [05/07/2000 11:15:23]
How to create a mail form
<!--#var standard_html_footer-->
This Document consists of two parts, the first part inside the sendmail tag is the text of an email message. The sendmail tag
forwards an email message to a Mail Host for sending. It does not display anything in the browser window. As you can see, you
must define the email recipient, sender and subject. For the recipient you should use your email address, or the address of the
person who will read the feedback. For the sender you should again use your own email address, or that of a system administrator.
The second part of the Document displays a confirmation message to the user which lets them know that their comments have
been sent.
Setting Permissions
As it is you have everything you need to build a working mail form. The only missing ingredient at this point is deciding on a
permissions policy. By default, only users with the manage role can send mail. This is to keep anonymous users from abusing
your mail sending facilities. In our case, however, we want to allow anonymous users to be able to send comments via mail.
We could change the permission settings on our Mail Host, but this would defeat the conservative default policy and might not be
appropriate for the whole site. Instead we can solve this problem with "Proxy roles". Proxy roles define additional roles for a
Document. Normally a Document can only access resources if the current user has adequate permissions. Proxy roles allow a
Document to behave as though the current users had additional roles.
So if you give our SendFeedback Document the Manager Proxy-role, then it will be able to send mail. The Document will
behave as though the user accessing the Document has the Manager role, even if the user is not a manager.
To set the Proxy-roles, click the "Proxy" tab on the management screen while editing the SendFeedback Document. Select
manage and click "Change".
Testing the Mail Form
That's it, you've just built a feedback form.
Now test the mail form to make sure it works. The most likely problem that you will encounter is that you may not have selected
the right computer for your SMTP host. You may have to contact your system administrator to find out the correct configuration.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/MailForm?pp=1 (2 of 2) [05/07/2000 11:15:23]
How to create a variable index page
How-To: How to create a variable index page
Created by Bill. Last modified on 1999/10/04.
What do you mean by variable index pages?
The best way I can describe it is by example. I'll use my situation. I started off wanting to create a Z-Class for members; a
Member Object, which I could add, and get a default index page, but still allow the member to customize it. Unfortunately, I was
unable to get a default page built that would show up in the contents section for the member, it being a method at that point.
What I wanted was the best of both worlds, user customization and standard layout for those who didn't want to change anything.
it was time for some magic.
So, how did you do it?
I have a Z-Class called Members, which happens to inherit Object Manager and CatalogAware. I gave it a property called
Index_Page, which has a default of . In the ZClass I create two DTML Methods, one index_html looks like this:
<dtml-var "_[Index_Page]">
And another which contains the default look of the index page.
What happens, is that when you click on the Member's link you get the contents of the page specified in the Index_Page property!
This way, the member can create their own index page, name it something other than index_html and make it their default by
changeing the Index_Page property. If they don't change anything, my default is still seen.
What's the magic?
The magic lies in the calling of the variable. You want the contents of the name of the variable listed in a variable. This trick is
really cool, I would reccomend you check out the Advanced DTMl Techniques How-To for learning more about this.
Well, that was the solution I used, others may be more or less elegant and functional. Hope it helps you.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Bill/Documentation/variable_index_pages?pp=1 [05/07/2000 11:15:26]
How to delete a catlogAware object that is no longer catalogued.
How-To: How to delete a catlogAware object that
is no longer catalogued.
Created by Bill. Last modified on 1999/12/09.
Recently, I had the sad fate of needing to delete some items that are catalog aware, but had been deleted from the catalog (don't
ask). I tried updating the catalog, but even though they appeared in the new catalog, they still did not allow me to delete them
(They complained about not being indexed).
So, on a hunch, I exported my catalog, and deleted it. Then I deleted the miscreant objects, and imported the catalog back into
zope.
It worked flawlessly.
This was with Zope-2.1.0. Until such time as this is fixed (if possible), this servers as an expedient workaround. Just remember to
import your catalog back to it's original position. :-)
Note, I also had a miscreant object that, while visible normally, would not let me edit it. It, too, was complaining about the catalog
(though I don't recall the exact error), and this worked as well.
YMMV
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Bill/Documentation/CatalogBadness?pp=1 [05/07/2000 11:15:28]
How to generate random content
How-To: How to generate random content
Created by Amos. Last modified on 1999/09/15.
Sometimes it's nice to display different content each time a person visits a page. One way to achieve this is to randomly include a
different piece of content each time a page is loaded. Zope makes it simple.
Create the content
The first thing to do is to create the content that will be randomly chosen. For example, let's say we want a random greeting to
appear on a page.
The first step in realizing this goal is to create a number of greetings. Let's say we create three documents: 'GoodMorning',
'HappyBirthDay', and 'GoAway'. Each document contains a different greeting message. We place these documents inside a Folder
named 'greetings'. This way we can tell Zope where to look for our greetings.
Insert the content
Finally create a document that will randomly include one of the greetings. To actually insert the greeting into the document we
need to use a special DTML tag. This is how it should look:
<!--#var expr="_.whrandom.choice(greetings.objectValues())"-->
This tag looks complex, how does it work? Let's start from the inside. '_.whrandom' refers to the whrandom module which is a
standard Python module for generating random numbers. '_.whrandom.choice' is a function in the whrandom module that
randomly chooses an item from a list.
So we are randomly choosing a item form a list which is described by 'greetings.objectValues()'. 'greetings' is the name of our
Folder that contains the greeting documents, and 'objectValues' is a standard Zope Folder method that returns the list of items
contained in a Folder.
So the funny looking variable expression means "insert an object which is randomly chosen from the objects inside the greetings
folder".
There are lots of other ways to generate random or changing content, but this is one of the simplest.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/RandomContent?pp=1 [05/07/2000 11:15:30]
How to hide some of the management interface
How-To: How to hide some of the management
interface
Created by dmost. Last modified on 2000/02/27.
Ive just been getting stared with Zope and one
of the first things I found myself wanting to do
was to hide some of the management interface.
Logging on to the Zope.org to enter my How-To, I
realised how obvious this is. Unfortunately, it isnt
documented anywhere, so here it is:
You can display a management interface for a given
object by using the url:
http://myzope/object/subobject/manage
A frame is generated for managing "object/subobject"
and objects above subobject in the zope tree are
not accessible from this management interface.
Another very usefull technique for hiding parts of
the management interface is the ability to create
zClasses within zClasses. Objects of the class of the
inner class can only ever be created as the contents
of the outer class.
Where I have a heirarchy of zClasses, I have found it usefull
create a base class at each level, which is subclassed
by variants at that level.
e.g.
SiteClass
--SectionBaseClass
----StoryBaseClass
----Story1Class(StoryBaseClass)
----Story2Class(StoryBaseClass)
--Section1Class(SectionBaseClass)
----Story3Class(StoryBaseClass)
--Section2Class(SectionBaseClass)
----Story4Class(StoryBaseClass)
with this layout, the "add object" lists
Site object:
--SectionBase object
--Section1 object
--Section2 object
SectionBase object:
--StoryBase object
--Story1 object
--Story2 object
Section1 object:
--StoryBase object
--Story1 object
--Story2 object
--Story3 object
etc etc.
I leave more exiting applications of this news
as an excersise for the reader.
http://www.zope.org/Members/dmost/ManagementTree?pp=1 (1 of 2) [05/07/2000 11:15:32]
How to hide some of the management interface
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/dmost/ManagementTree?pp=1 (2 of 2) [05/07/2000 11:15:32]
How to install UserDb in the root folder.
How-To: How to install UserDb in the root folder.
Created by ThunderFoot. Last modified on 1999/10/12.
You can install UserDb in your root folder, you just have to be _very_ careful
that you don't lock yourself out of your site.
There are a few mistakes to watch out for...
Firstly do NOT delete your Users folder expecting to be able to install the new
UserDb one. As soon as you delete the old one you no longer have management
access (I think the superuser password still works at this point), but once
you install the UserDb folder the superuser password _stops_ working and
you no longer have permissions to do anything and it will kick you out.
This happened to me and was quite annoying. (I had to mess around with the
python code and pretty much comment out a lot of the security so I could
get back in to my site to set up the UserDb properly...)
So what you want to do is make a temporary folder, set your UserDb object
up in there and make sure it is working _perfectly_, that it is looking at
the right database, that you have users set up properly and that they have
the right permissions (including you having management permissions.)
Then you _copy_ that UserDb object and, immediately on deleting the Users
folder in root, paste the already configured one in.
At this point it will probably bring up the password window asking you to
authenticate. If you are using Cookie-based authentication this password
window NO LONGER WORKS, so cancel out of it.
Go directly to the URL:
domain.com:port/acl_users/docLogin
and log in there.
You HAVE to login this way from now on (if you are using cookie-based
authentication), so you probably want to have a "Login" link on your front
page that leads here.
It's probably wise to back up your zope db before you do this just in case
you make a mistake and can't get back in.
If you do get locked out of your site you can try commenting out the first
two lines of the validate method in UserDb.py:
def validate(self,request,auth='',roles=None):
# if self.cookie_mode:
# return self.cookie_validate(request, auth, roles)
return self.std_validate(request, auth, roles)
This will disable cookie-based authentication and make the password window
work off your UserDb database. This does not use the cookies properly
though so you won't want to leave it like this. Use this to get back in
and fix your site up properly. (This still requires you to have the proper
users set up in your database, so if that is your problem you will need to
get external access to your database and enter them manually.)
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/ThunderFoot/userDb_root?pp=1 [05/07/2000 11:15:35]
How to make a simple site map simply
How-To: How to make a simple site map simply
Created by djay. Last modified on 1999/09/16.
This code is too simple for a product but I thought
that beginners may get something from this, if not the
reuse of the code as is.
On my site I wanted a one page site map showing the
structure of my site with all the important pages available.
The Zope folder structure gave almost gave me this but the
trick was how to weed out all the code type objects from
the pages that are of interest to a user. I did this by only
showing pages that had a title. Whats more I didn't show folders
that had no title either. This meant it was resonably simple
to hide wizard type pages that required input inside an
untitled folder.
Anyway, heres the code. Below is the simple SiteMap document
that starts a recursive DTML method call.
<!--#var standard_html_header-->
<!--#with aq_parent-->
<!--#var recurseFolder-->
<!--#/with-->
<!--#var standard_html_footer-->
The DTML method "recurseFolder" looks like this
<ul>
<!--#in "objectItems(['DTML Document','Image','File'])"-->
<!--#if "AUTHENTICATED_USER.has_permission('View',_.getitem('id',1))"-->
<!--#if "_.getitem('id',1) != 'index_html' and title != ''"-->
<li><a href="<!--#var "absolute_url()"-->"><!--#var title--></a>
<!--#if "(ZopeTime() - bobobase_modification_time()) < 14"-->
<img src="<!--#var "images.imgNew.absolute_url()"-->" alt="*NEW*">
<!--#/if-->
</li>
<!--#/if-->
<!--#/if-->
<!--#/in-->
<!--#in "objectItems(['Folder'])"-->
<!--#if "title != '' and AUTHENTICATED_USER.has_permission('View',_.getitem('id',1))"-->
<li>
<a href="<!--#var "_.getitem(id).absolute_url()"-->"><!--#var title--></a>
</li>
<!--#with sequence-item-->
<!--#var recurseFolder-->
<!--#/with-->
<!--#/if-->
<!--#/in-->
</ul>
This code in the average case will produce a resonably good sitemap. It will
only have links to pages the current user can view. It will indicate which
documents have changed in the last 2 weeks.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/djay/SimpleSiteMap?pp=1 [05/07/2000 11:15:38]
How to search on date-time fields using ZSQL Methods
How-To: How to search on date-time fields using
ZSQL Methods
Created by teyc. Last modified on 2000/04/08.
BACKGROUND
In dtml, we can easily restrict the rows we select if they were strings, integers or floats. However, restricting the rows we select by
dates is not straightfoward, especially if you are using the ODBC DA. This is how I got mine to work.
HOW
arguments:
Mydate:date
Query template:
select * from activity where activity_date= { ts '<dtml-var Mydate fmt=ISO>'}
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howSQLDateTime?pp=1 [05/07/2000 11:15:39]
How to set up and test xmlrpc in Zope
How-To: How to set up and test xmlrpc in Zope
Created by teyc. Last modified on 2000/03/18.
Main idea
Zope is a not only a web server, but also an xmlrpc server. This means in addition to getting a html rendered zope object, you can
execute object.method calls on the server and get the results returned in xml.
What follows is a more detailed elaboration of Eric Kidd's example of xmlrpc.
Step by step guide
1. In your Zope directory, create a directory called Extensions. This is the where all your external python functions can be
written
2. In the Extensions directory, create a text file called hello.py with the following:
def hello_world(name):
return "Hello, " + name + "!"
3. In your Zope manage screen, create a folder called test
4. In the test folder create an External Method.
5. Set the ID to pyhello, Function to hello_world and Module to hello
6. Convince yourself that the external method works:
http://localhost:8080/test/pyhello?name=Chui+Tey
7. Now pretend you are on another machine far, far, away.
Run python
8. Include path to yourzopedir/lib/python, and import the xmlrpc library
import sys
sys.append('C:\\progra~1\\teyzope\\lib\\python')
import xmlrpclib
9. Now try the following
s=xmlrpclib.Server("http://localhost:8080")
print s.Zope.test.pyhello("Chui Tey")
10. You can also do this
s=xmlrpclib.Server("http://localhost:8080/Zope/test/")
print s.pyhello("Chui Tey")
s=xmlrpclib.Server("http://localhost:8080")
print s.Zope.test.pyhello("Chui Tey")
11. Or just for fun try
s=xmlrpclib.Server("http://localhost:8080/Zope/")
print s.index_html()
In which case the server returns the index_html as a string
12. For even more fun, change hello.py to
def hello_world(name):
from time import time, localtime, asctime
return asctime(localtime(time())) ,"Hello, " + name + "!"
and try the calling the routines again.
http://www.zope.org/Members/teyc/howxmlrpc?pp=1 (1 of 2) [05/07/2000 11:15:42]
How to set up and test xmlrpc in Zope
Passing arguments to dtml methods
New
1. In the root folder create this dtml method
Id: dtmlHello
Hello there <dtml-var username!>
2. Test this on your web browser:
http://localhost:8080/dtmlHello?username=Chui+Tey
3. Now in python
s=xmlrpclib.Server("http://localhost:8080/Zope/")
mapping = {'username':'Chui Tey'}
or
mapping = {}
mapping['username']='Chui Tey'
print s.dtmlHello(mapping)
Links
1. Must read if you are calling ZClasses through XMLRPC. XML RPC Woes
What else
You can also directly implement an xmlrpc server by subclassing the xmlrpcserver.py, available for download from Pythonware
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howxmlrpc?pp=1 (2 of 2) [05/07/2000 11:15:42]
How to test your products without (re)starting Zope
How-To: How to test your products without
(re)starting Zope
Created by teyc. Last modified on 2000/03/08.
How to debug product development
Abstract
Simple Python classes do amazing things once they inherit Zope machinery classes such as Acquisition, Persistent, Access
control.
Testing your products without restarting the server is possible if you knew how.
Steps
These worked for me in Windows, ymmv.
1. CD "\Program files\website\lib\python"
2. "\Program files\website\bin\python.exe"
3. >>> import Zope, ZPublisher
4. >>> from Products import ZGDChart
5. >>> shortcut= ZGDChart.ZGDChart.ZGDChart
Just a comment about the long incantation.
ZGDChart is Products/__init__.py
ZGDChart.ZGDChart is Products/ZGDChart.py
ZGDChart.ZGDChart.ZGDChart is Products/ZGDChart.py/ZGDChart class
6. >>> c = shortcut(id='newid')
7. Test a method on the new instance:
>>> c.version()
8. do some changes in ZGDChart.py
9. >>> reload (ZGDChart.ZGDChart)
10. repeat steps 5 - 6 until product works.
Links
You should read the article by M.Pelletier - the debugger is your friend.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howtoProductTesting?pp=1 [05/07/2000 11:15:44]
How to use a different index method than 'index_html'
How-To: How to use a different index method
than 'index_html'
Created by Amos. Last modified on 1999/09/15.
By default, when a Folder is published, it will return its index_html method. What if you don't want to call your default
method index_html? What if you want to call it index.html or spam.html?
If you want your default documents to be called spam.html, create a new DTML Method in your top level Folder named
index_html, containing only the line:
<dtml-var spam.html>
Now if you go to a URL, say:
http://mydomain.com/Root/Folder1/Folder2/
The published Folder will automatically acquire the index_html from the top level Folder. Because index_html simply
renders the local spam.html, spam.html effectively becomes your default method.
The same lesson applies for any other default method name you want to use. Just create a top-level index_html method that
references the name of the default method you want to use.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/changeIndex?pp=1 [05/07/2000 11:15:47]
How to view all inherited and acquired attributes and methods of an object
How-To: How to view all inherited and acquired
attributes and methods of an object
Created by dmost. Last modified on 2000/03/05.
I had been wanting to see exactly what methods and attributes a given object in a zope path would have access to. After buggering
around with the monitor for a while, I created an external method which does the job for me.
Put the file browse.py into your Extensions directory, Create a DTML method called 'viewself' (or whatever) in your Zope root
folder Upload the file 'viewself.dtml' into your 'viewself' DTML method
You will now be able to get a listing of all attributes and methods for your object by using the url:
http://yourzope/path/to/your/object/viewself
Bugs: I havent been able to get this line to work properly:
self.typestr = str(type(value))
the result of str(type(obj)) always sees to be an empty string, even though comparing types seems to work. If anyone has any clues
as to why this might be, let me know.
Related Files:
browse.py
viewself.dtml
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/dmost/ViewSelf?pp=1 [05/07/2000 11:15:49]
How to write your own DTML tag
How-To: How to write your own DTML tag
Created by z113. Last modified on 2000/01/04.
This problem appeared when i tried to use dtml-tree and dtml-sqlvar tags in my Zope-pcgi application on Apache web server. It
was strange for me that Zope tell me "Unexpected tag, ... blah blah". And i decided to force Zope DocumentTemplate to
understand these useful tags. In short time i understood that it's possible to write your own tag.
All things i wrote here is a result of Zope code studying .
Therefore, all this information comes without any warrantee.
So, let's start.
Assume that you wish to develop your own DTML tag wich will be appeared as "dtml-mytag" in your DTML files. Generally, you
have to do two things: create class represented your tag and add a "import" line to DocumentTemplate package init file. The best
way is to create a package for your tag file. Make shure this package is in python search path, update PYTHONPATH
environment variable if need. Summarize:
1. Create directory Mytagpackage
2. In this directory create two files: __init__.py and Mytag.py . File Mytag.py will contain code for your tag class. File __init__.py
should contain "import Mytag" line.
3. Find the file site-packages/DocumentTemplate/__init__.py in your python directory. Modify this file by adding "import
Mytagpackage" line at the end of file.
4. Make shure that Mytagpackage directory resides in Python search path.
5. Write something in Mytag.py file :).
Ok, what the "something" should be.
############################################
#Mytag.py
############################################
from DocumentTemplate.DT_Util
import *
from DocumentTemplate.DT_String import String
# others imports
# useful parsing methods you'll need
# explained later
you want
#
class MyTag:
name = 'mytag'
# name attribute used when tags been parsed.
#your tag, then, must start with 'dtml-mytag'
# here __init__() code resides.
# the args argument passed to construstor
# it may looks like this one (generally stolen from sqlvar.py)
def __init__(self, args):
args = parse_params(args)
name=name_param(args,'mytag',1)
self.__name__= name
self.args=args
#
#
#
#
#
now, the core of you tag class = method render()
__call__ wil be redefined, so render() will be called when MyTag object called
md argument passed to render()
md is a dictionary mapping attribute names to values
finally, render() returns some_value to be placed where the tag resides.
def render(self, md):
name=self.__name__
args=self.args
# your useful code here
#
return some_value
# __call__ redefining
__call__ = render
# other useful methods if need
http://www.zope.org/Members/z113/1?pp=1 (1 of 2) [05/07/2000 11:15:51]
How to write your own DTML tag
#
# Add our tag to DocumentTemplate known tags !
# commands is a dictionary indexed by tag names DocumentTemplate knows.
String.commands['mytag'] = MyTag
############################################################# end Mytag.py
Shawn Murphy propose a valueable tip (it's a fragment of his letter here):
I recently went through the pain of figuring this out
created was a container (with a beginning and an end)
was running into was that if there were any dtml tags
beginning and my end tag then my tag would fail. The
being:
too. The tag I
and the problem I
at all between my
solution ended up
def __init__(self,blocks):
tname, args, section=blocks[0]
self.section=section
...
def render(self,md):
input = render_blocks(self.section.blocks,md)
# input now contains all the interpreted dtml in the container
...
----------------------------------------------Any comments, suggestions, if fully appreciated.
please send it to z113@sbet.com
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/z113/1?pp=1 (2 of 2) [05/07/2000 11:15:51]
How to: add users in python
How-To: How to: add users in python
Created by element. Last modified on 2000/02/13.
HOW-TO: Add zope users in Python (external methods)
For a long time I was baffled about how to get users data that I was storing in an external method into the zope "acl_users" folder.
There were plenty of how-to's on adding users and user folders in DTML, but I was already storing all my data using python
'Products'.
I wanted to use zope's built-in user manager to handle authentication instead of building my own. I tried the Generic User Folder,
but everytime, it locked me out of the folder I installed it in.
After a bit of researching and troubleshooting I found the task of coding this into my product to be very simple.
The first part of the code is found in the primary __init__ method of the primary class. When the product is installed into a folder,
this method is called.
My Product is folderish, meaning that it contains other objects. I had to create the acl_users folder inside it.
self.manage_addUserFolder(curr_folder)
In the main __init__ method, python also creates a folder called users, which I am using to allow storage and restricted access to
authenticated users. In this folder we can create the users folder automatically by enabling the create a userfolder variable.
manage_addFolder's syntax looks like this: manage_addFolder('FolderID','FolderTitle','createAUserFolder','createAPublicInterface')
self.manage_addFolder('Users','Users',1,1)
I also wanted to create a local role for my users, and make them a member of that role.
self._addRole('MyUser')
The second part of the code is found in the primaryclass.addUser method. This code is called after a user fills out a
createUserForm. This is where the actual user gets created.
self.Users.acl_users._addUser(username,password,confirmpass,['MyUser'],0)
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/element/addingUsersInPython?pp=1 [05/07/2000 11:15:53]
How-To Change your Default DTMLs
How-To: How-To Change your Default DTMLs
Created by kslee. Last modified on 1999/11/23.
Warning, this How-To is written by non-native English writer, hence may contain errors might lead your Zope to fatal results.
Why You may change the Default DTML Method and Document
While developing sites, I found myself frequently modifying the default DTMLs like this:
default DTML
<dtml-var standard_html_header>
-->
after modification
<dtml-var "my_site_html_header(_, _, x=y)">
Yes, this might be becuase I did bad site design. But I guess the flexiblity to configure the default DTML contens is Good
ThingTM. Furthurmore, when I change standard_html_header/footer into some more complex error-abunduant forms, even
tracebacks look far more ugly.
So How You Can Change the Default DTMLs?
In short, you should edit the Zope source. If you do not have the access to the your Zope source code, you can not but ask the
superuser and wait.
In the Your_Zope_Directory/lib/python/OFS directory, there is DTMLMethod.py and DTMLDocument.py. You will edit both of
them.
Open DTMLMethod.py with your favorite editor.
Find "def addDTMLMethod". It's at the end of the file. For my Zope installation(it's version 2.0.1 Linux binary install), it's at Line
383
Now the source of addDTMLMethod:
def addDTMLMethod(self, id, title='', file='', REQUEST=None, submit=None):
"""Add a DTML Method object with the contents of file. If
'file' is empty, default document text is used.
"""
if type(file) is not type(''): file=file.read()
if not file: file=default_dm_html
<--You'll change this line.
ob=DTMLMethod(file, __name__=id)
ob.title=title
id=self._setObject(id, ob)
if REQUEST is not None:
try: u=self.DestinationURL()
except: u=REQUEST['URL1']
if submit==" Add and Edit ": u="%s/%s" % (u,quote(id))
REQUEST.RESPONSE.redirect(u+'/manage_main')
return ''
Change the above indicated line as the following:
if not file:
if hasattr(self, 'default_dm_html') and self.default_dm_html:
file=self.default_dm_html.read()
else: file=default_dm_html
So you inserted two more lines.
Now edit the DTMLDocument.py in the same way. It's addDTMLDocument method, which is corresponding addDTMLMehod
method of DTMLDocument.py, is at line 193 (in my case).
Just do the same except that now "default_dm_html" is "default_dd_html".
And now restart your Zope.
http://www.zope.org/Members/kslee/default_document_patch?pp=1 (1 of 2) [05/07/2000 11:15:57]
How-To Change your Default DTMLs
How You Can abuse the New Flexiblity
Suppose you have the follwing folder structure:
/foo/babo1/babo2
imsi1/imsi2
bar/blabl/blabl
If you want to change the both of the defualt DTMLs in the foo branch create default_dm_html and default_dd_html in the foo
folder. default_dm_html and default_dd_html can be DTML Document or DTML Method. That does not matter.
If you need different default_dm_html in the babo1 folder, just create anoter default_dm_html.
What if you want the newly created DTML Document under imsi1 branch has the origianl default contents?
Just create a 'default_dd_html' property in the imsi1 folder. It might be string or boolean or integer. Actually anything is OK if it is
'False': i.e., zero-lenth string, boolean set to false, integer set to 0 ... ...
Then you can have original defaut DTML Documents under imsi1 folder.
What if you create non-false default_dm(or dd)_html property?
You will see a traceback saying that type of property does not have read() method.
Actually, there are several ways to make you never see that sort of tracebacks, but they require more codes. So I do not want make
more complex this probably-most-frequently-used addDTMLMethod(or Document) method.
Lastly ...
I hope this kludge be help to other Zopistas and someday those wonderfull DC people put this or simillar code into the official
Zope source base.
I suppose those Zope Hosting Providers can utilize this in various way, though they may already have their own ways.
Last but least, do not forget to put an appropriate attribution in your default_xx_htmls.
p.s. I have retired from the Zope list. Simply 100+ mails per day is over my capacity to handle. Hence please send any comments
and questions to my email address. Complaints will be redirected to /dev/null. You are sufficiently warned.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/kslee/default_document_patch?pp=1 (2 of 2) [05/07/2000 11:15:57]
How-To: Secrets of working with the tree tag
How-To: How-To: Secrets of working with the tree
tag
Created by cybertad. Last modified on 1999/09/16.
Introduction
The DTML tree tag is a very cool feature of Zope. However, too many of it's coolest features are under-documented.
Under-documented? Well, basically, as with most things in the Zope world (and that is rapidly changing!), the documentation is
there, you just had to search all over to find it, and here are the places to go:
●
Zope Documentation Project (ZDP)
●
DTML User Guide (this goes directly to the tree stuff)
●
Zope and Zope-Dev mailing list archives
●
How-To Documents
After trying to find information on some of the different usages of the tree tag, and just about going bald at my own hands, I have
compiled my findings in this one document.
What you will not find here are the basics. These are pretty well documented in the FAQs or the Users Guide. What you will find
are the things that are NOT very well documented. Displaying only certain objects or subobjects, using the tree tag in conjunction
with Z SQLMethods, and other obscure, but useful, stuff. By the way, this document assumes Zope 2 and uses the
<dtml->...</dtml> syntax.
As with any documentation efforts by mere mortals such as
myself, I can't take responsibilty if what I write is wrong. That
said, please, if you know of any changes that need to be made to
this document, either with respect to it's accuracy or
completeness, please let me know!
Displaying only certain objects/subobjects.
For those people not used to the Python way of doing things (which is something that you will pick up quickly, like it or not), the
idea of trying to get the tree tag to display only specified objects can be somewhat maddening. Until now.
The branches_expr attribute of the tree tag works just like the
expr attribute of any other tag (i.e. if, elif, etc.). Looking at it like
that, it makes sense that, since all you want to do is pass the
objectValues method a valid Python list, you could do this
'<dtml-tree branches_expr="objectValues(['Folder', 'DTML Document'])">'
to get a tree of only Folder and DTML Document subobjects. What you are passing in the list is the metatype of the Product.
Remember, each Product defines it's own metatype, or "information about the type of product it is". If you want to show more
types, just add them to the list. Run the following code to see what metatypes you have access to in objectValues in the current
folder.
<dtml-in "currentFolderName.objectValues()" sort=meta_type> <dtml-var meta_type><BR>
</dtml-in>
Keep folder names from being links
This is a common problem. When creating a tree of objects, you will want most of them to be clickable. In other words, if you
have a tree of documents, you will want to be able to click on the documents link and go directly to it. However, if you have a tree
http://www.zope.org/Members/cybertad/how_to/working_with_tree?pp=1 (1 of 2) [05/07/2000 11:16:00]
How-To: Secrets of working with the tree tag
of documents AND folders, you will still want to be able to click on the document link, but NOT on the Folder itself. In the case
of Folders, you will just want to click on the +/- sign to the left of it to collapse or expand it. If you were to click on the Folder
name, it would take you to the index_html document in that folder and therefore would not display the next branch of the tree.
To remedy this situation, we will test to see if the object we are displaying is a Folder or not. If it is a Folder object, we will only
display the name. If it is anything else, we will display it's name as a link. Pretty simple stuff.
The following code will return a tree of all subobjects in the folder currentFolderName and display them sorted by id. It will
display Folder objects with a +/- to the left of them, but not as links themselves. Anything that is not a Folder will be clickable.
'<dtml-tree currentFolderName branches_expr="objectValues()" sort=id> <dtml-if "meta_type=='Folder'"> <dtml-var title_or_id>
<dtml-else> <A HREF="<dtml-var tree-item-url>"><dtml-var title_or_id></A> </dtml-if> </dtml-tree>'
Display object's icon in Tree
It is very easy to dispay the icon that is associated with the meta_type of the object. Simply add output, within an HTML Image
tag the variable icon. Don't forget to add BORDER="0" to the HTML Image tag, otherwise the icon will display with a large
border around it (if it is inside a link).
'<dtml-tree currentFolderName branches_expr="objectValues()" sort=id> <dtml-if "meta_type=='Folder'"> <IMG
SRC="<dtml-var icon>" BORDER=0> <dtml-var title_or_id> <dtml-else> <A HREF="<dtml-var tree-item-url>"> <IMG
SRC="<dtml-var icon>" BORDER=0> <dtml-var title_or_id></A> </dtml-if> </dtml-tree>'
Selectively display objects
Ocassionally you might want certain objects to be displayed in your tree, such as the document listing the tree, but not want it to
be a link. This is pretty simple.
First of all, we create a variable called thisDocument and assign
it the value of the id of the current DTML Document.
'<dtml-call "REQUEST.set(thisDocument, id)">'
The next thing to do is test your new variable against each iteration of the tree tag. If the id of the current iteration matches the
value of the variable you set, then this code will not display that object as a link.
'<dtml-tree currentFolderName branches_expr="objectValues()" sort=id> <dtml-if "_.getitem(thisDocument,1) !=
_.getitem(id,1)"> <IMG SRC="<dtml-var icon>" BORDER="0"> <A HREF="<dtml-var tree-item-url>"><dtml-var
title_or_id></A> </dtml-if> </dtml-tree>'
This will not work with DTML Methods, as DTML Methods take on the id of the Folder they are in or the DTML Document that
is calling them. Due to the coolness of Aquisition, this can work to your advantage. Something I have done is create a DTML
Method that contains all of the above code and use it to do a site map. I include it in my standard_html_header method, and, due to
Aquisition, the thisDocument variable that gets set is the id of the calling DTML Document.
more to come...
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cybertad/how_to/working_with_tree?pp=1 (2 of 2) [05/07/2000 11:16:00]
How-To: Zope and Allaire ColdFusion Studio
How-To: How-To: Zope and Allaire ColdFusion
Studio
Created by cybertad. Last modified on 1999/10/17.
Introduction
Sometimes it's nice to work with DTML code outside of the Zope Management screens. This text is not a generic overview of how
to do this since there are so many ways.
Instead, this is a description of how to work with DTML code within Allaire's ColdFusion Studio.
It has come to my attention that only CF Studio version 4.0.1 works via FTP with Zope. If you are a registered user of CF Studio
then you can request a CD with the upgrade on it from 4.0. If you are using a lesser version of CF Studio, such as 3.x, the FTP
connection into Zope should work fine, but please let me know if it doesn't or if you need to do something different to get it to
work.
For CF Studio 4.0.1 users, your FTP configuration screen should look like this ...
DTML Syntax Highlighting
CF Studio doesn't include actual syntax highlighting for DTML code, but it's quite simple to emulate it. This works with both 1.x
and 2.x DTML syntax, however, since the 2.x syntax is closer to straight HTML, it works better.
Follow these steps to make sure .dtml files (that's the extension I chose for storing DTML code in the file system) are associated
with the HTML syntax highlighting in CF Studio.
Open up CF Studio and press F8 (or go to Settings under the Options menu) to bring up the settings dialog. Go to the Color
Coding tab and highlight the HTML scheme. Press the Edit Extensions button and enter dtml; at the end of the string.
This way, when you open a DTML document it will apply the HTML color coding to it. It's not as easy to write a DTML parser
for color coding within Studio like it is with, say, emacs, and I don't think that Allaire will really want to support DTML as Zope
is such a threat to it's upcoming Spectra product.
FTP into Zope
Zope (running on top of ZServer) contains the ability to work with it's objects via standard FTP. To setup FTP within CF Studio to
work with Zope, simply use the same methods you would use when setting up FTP access to any other FTP server. The
username/password combo is the same you would use to login to the Zope Web Management interface. One caveat when using
FTP to work with Zope, is that Zope, by default, uses the non-standard FTP port 8021. Other than that, it's business as usual.
Conclusion
Basically, working with DTML in CF Studio is pretty straight forward. I like using Studio much better than the Web Management
interface when dealing with lots of code. I think you might like it better too!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cybertad/how_to/homesite?pp=1 [05/07/2000 11:16:02]
HowTo Store Files Externally
How-To: HowTo Store Files Externally
Created by sabaini. Last modified on 2000/02/01.
How to store data externally
Update: theres now jfarr's excellent LocalFS Product. Take a look at it if you want to store files outside the ZODB.
Sometime you dont want a file's contents to get stored inside ZODB -- say, if other processes need to operate on them, or because
they're just too big. You'd rather store a reference to the file in the ZODB and put the contents somewhere else.
I had one such situation. Here's what I did. Disclaimer: I'm not very good at python, and a Zope newbie in general. I dont make
any claims as to the functionality of that code, let alone beauty and simplicity. Please take care.
And feel free to point out any errors to me.
The Situation
I decided upon the following:
●
References will be hold in a ZClass 'myFileClass' (along with some other bookkeeping data)
●
File data will get stored in one central directory, the Repository
●
●
●
Filenames in the Repository will be composed of the PATH_INFO of the request that created them and the orig. filename
(I hoped to more easily spot lost files later this way)
A 'myFile' object will have an attribute 'filename' where the physical files' name is stored. (The Repository is systemwide
constant).
I just care for uploading files. If a 'myFile' object gets deleted the physical file stays. I do a garbage collect sort of thing
on it later on. I think that should work fine with Zopes' undo mechanism.
Solution
Heres the external methods I created (in fact mostly stole from Zope source).
--------- snip --------import string
import os
"""
read a files contents from a <form enctype="multipart/form-data">
save it somewhere
i heavily ripped of zope file mgt src for this,
especially lib/python/OFS/Image.py
i'm sure you can also find other interesting
things there.
-- sabaini@niil.at
"""
StringType=type('')
ZClassPathInfo = 'manage_addProduct_myFileProduct_myFileClass_factory_myFileClass_add'
Repository = '/tmp' #where all the files get stored
def read_data(file):
"""Read binary data."""
"""see also orig. OFS/Image.py _read_data for this"""
if type(file) is StringType:
size=len(file)
http://www.zope.org/Members/sabaini/externalfiles-howto?pp=1 (1 of 4) [05/07/2000 11:16:06]
HowTo Store Files Externally
return file, size
seek=file.seek
read=file.read
seek(0,2)
size=end=file.tell()
seek(0)
return read(size), size
def cookFilename(REQUEST):
"""generate new filename from PATH_INFO"""
filename=string.replace(REQUEST['PATH_INFO'], '/', '_')
filename=string.replace(
filename,
ZClassPathInfo,
'')
return filename+cookId(REQUEST['file'])
def cookId(file):
"""generate id from orig.filename, """
origfilename=file.filename
id=origfilename[max(string.rfind(origfilename, '/'),
string.rfind(origfilename, '\\'),
string.rfind(origfilename, ':'),
)+1:]
return id
def writefile(fn, data):
"""check name conflicts, change filename, """
"""write data in Repository"""
cantwriteExc = 'Cannot write: '
c=0
while 1:
try:
os.stat('%s/%s' % (Repository, fn))
except OSError:
break
else:
c=c+1
fn='%s_%s' % (id, c)
try:
f=open('%s/%s' % (Repository, fn), 'w')
except IOError:
raise cantwriteExc, fn
else:
try:
f.write(data)
except:
raise cantwriteExc, fn
else:
f.close()
return fn
def addf(file, REQUEST):
"""Replace the contents of the document with the text in file."""
if type(file) is not type(''):
data, size = read_data(file)
id = cookId(file)
filename = cookFilename(REQUEST)
# change filename in case of nameconflict
filename=writefile(filename, data)
REQUEST.set('id', id)
REQUEST.set('filename', filename)
REQUEST.set('size', size)
return
--------- snip --------And heres the Dtml I used for the myFileClass_add method (the one that gets called by the addForm when you create an instance
of the ZClass). You have to create the external methods on the same level as that method.
--------- snip ---------
http://www.zope.org/Members/sabaini/externalfiles-howto?pp=1 (2 of 4) [05/07/2000 11:16:06]
HowTo Store Files Externally
<HTML>
<HEAD><TITLE>Add myFileClass</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<!--#comment--> We add the new object by calling the class in
a with tag. Not only does this get the thing
added, it adds the new thing's attributes to
the DTML name space, so we can call methods
to initialize the object.
<!--#/comment-->
<!--#comment-->
You can ad code that modifies the new instance here.
For example, if you have a property sheet that you want to update
from form values, you can call it here:
<!--#/comment-->
<dtml-with "myFileClass.createInObjectManager(cookId(REQUEST['file']), REQUEST)">
<dtml-call "REQUEST.set('createdOn', '1900/01/01')">
<dtml-call "addf(file, REQUEST)">
<dtml-comment>
replace "Properties" below with whatever your stylesheet
is called
</dtml-comment>
<dtml-call "propertysheets.Properties.manage_editProperties(REQUEST)">
</dtml-with>
<!--#comment--> Now we need to return something. We do this via
a redirect so that the URL is correct.
Unfortunately, the way we do this depends on
whether we live in a product or in a class.
If we live in a product, we need to use DestinationURL
to decide where to go. If we live in a class,
DestinationURL won't be available, so we use URL2.
<!--#/comment-->
<!--#if DestinationURL-->
<!--#call "RESPONSE.redirect(
DestinationURL+'/manage_workspace')"-->
<!--#else-->
<!--#call "RESPONSE.redirect(
URL2+'/manage_workspace')"-->
<!--#/if-->
</body></html>
</pre>
<p>
--------- snip ---------
Epilog
I tested this setup with files of various sizes (up to 200Mb) and it
worked all right (Sun Solaris).
One problem with file upload via html-form
is of course you dont get any "upload progress" or somesuch. With that
200Mb file I waited for 20min., with no feedback whatsoever...
Feel free to direct any questions/comments/abuse to me.
http://www.zope.org/Members/sabaini/externalfiles-howto?pp=1 (3 of 4) [05/07/2000 11:16:06]
HowTo Store Files Externally
© 2000,
Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/sabaini/externalfiles-howto?pp=1 (4 of 4) [05/07/2000 11:16:06]
Including the contents of an external HTML page
How-To: Including the contents of an external
HTML page
Created by cba. Last modified on 1999/09/15.
From the Zope mailing list:
...it's real easy to do with httplib in an External Method. Something like:
import httplib
def GetLinuxToday():
http = httplib.HTTP('linuxtoday.com')
http.putrequest('GET', '/backend/lthead.inc')
http.putheader('Accept', 'text/html') # we can handle text/html
http.putheader('Accept', 'text/plain') # we can handle text/plain
http.endheaders()
httpcode, httpmsg, headers = http.getreply() # get response headers
if httpcode != 200:
raise "Could not get document" # HTTP error
doc = http.getfile()
data = doc.read() # read file
doc.close()
return data
Didn't test this inside Zope, but it works standalone. Just put it in a file in Zope's extensions directory (eg., c:\zope\extensions),
add an External Method, enter GetLinuxToday as id and function name, and call it up in a document:
<!--#var "GetLinuxToday()"-->
or:
<!--#var GetLinuxToday-->
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cba/Get_External?pp=1 [05/07/2000 11:16:08]
Inheriting from ZCatalog as a Container
How-To: Inheriting from ZCatalog as a Container
Created by tseaver. Last modified on 1999/11/03.
Abstract
When using CatalogAware ZClasses, it is convenient to have the "root" container double as the catalog (which would otherwise be
a singleton object in the root).
Steps
1. Create a product, e.g. "FooProduct".
2. Within FooProduct, add a ZClass, "FooContainer", with base classes (in order):
❍
ObjectManager
❍
ZCatalog
3. Within FooContainer, add a DTMLMethod, "Catalog", with the following body:
<dtml-return "this()">
4. Also within FooContainer, add a ZClass, "Foo", with base class CatalogAware.
5. On the Foo_factory page, click the "Change" button (working around "No resource found" bug).
6. On the FooContainer.Permissions tab, add "Add Foos" to the list of permissions.
Notes
●
●
The "Catalog" method in the ZCatalog-derived container hijacks the default catalog name used by the
CatalogAware-derived ZClasses, causing them to register with the derived catalog.
In DTML, this catalog cannot be accessed using the standard "Catalog( criteria='value'...)" syntax. Instead, use:
<dtml-with Catalog>
<dtml-call "searchResults( criteria='value'...)">
</dtml-with>
Credits
Shamelessly imported from Martijn Pieters' posting to the Zope list, "ZCatalog, CatalogAware, and ZClasses."
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/tseaver/inherit_ZCatalog?pp=1 [05/07/2000 11:16:10]
Inserting Data using Z SQL Methods
How-To: Inserting Data using Z SQL Methods
Created by jshell. Last modified on 1999/09/17.
How to Insert Data using Z SQL Methods
Inserting data
Inserting data into a relational database from Zope is as easy as any other Z SQL Method. You can do just about any SQL statement
allowed by the target database in an SQL Method, and that includes INSERT and UPDATE statements. So to insert data, all you need to to
do is write an SQL Method with one (or more) INSERT statements. Well, it can get a bit more interesting than that.
Arguments and Type Checking (SQLVAR)
Arguments are the values passed into SQL Methods, either from the request or in an expression (Python) call. Z SQL Methods have an
input widget for specifying arguments. The usage of this widget is defined more fully in the Z SQL Methods guide. What's important here
thought is a degree of type safety -- inserting the correct type of value into its column (not inserting a string where an integer is expected,
for instance). Zope with Z SQL Methods provides three ways of trying to aid type safety with the database:
1. Zope input types.
❍
The ZPublisher component of Zope handles the marshling of input passed in through the web (URL's, Forms) into
Python objects. For example, <input type="text" name="age:int" size="3"> gets submitted to the
target method as an integer, and an error gets raised at a fairly low level of Zope if, for example, the value of the field is
the string 'twelve'. See the Zope documentation for more details.
2. Argument list input types.
❍
Z SQL Methods argument list allows a similar type/marshalling mechanism as above. If the argument list contains
'age:int' and a string with the value of '12' is submitted as age into the SQL Method, it will get converted into an
integer with the value of 12 in the SQL Method. See the Z SQL Methods documentation for more details.
3. SQLVAR DTML Tag.
❍
Z SQL Methods implement some extra DTML tags to aid in writing correct (and safe!) SQL. SQLVAR is the one most
likely to be used heavily in INSERT statements. SQLVAR operates similarly to the regular DTML VAR tag in that in
inserts values into the SQL Method, but it has some tag attributes targeted at SQL level type safety, and dealing with
inserting NULL values if desired. For the most part, this is the most preferred way with type checking and safety with Z
SQL Methods. See the Z SQL Methods documentation for more details. Some examples will be given below.
An example!
So how about a simple example eh? Let's say we have a table, customers, with the following schema:
column
customer_id
name
planet
age
type
varchar(12)
varchar(40)
varchar(40)
integer
nulls?
no
no
yes
yes
Customers table schema
and we need to insert a new customer into it. Planet and Age allow nulls, and are (we assume) optional. We add a new Z SQL Method
called sqlInsertCustomer, setup like the following:
Id
Title
Arguments
sqlInsertCustomer
Insert a customer
customer_id name planet="" age=""
http://www.zope.org/Members/jshell/ZSQLMethods-InsertingData?pp=1 (1 of 3) [05/07/2000 11:16:15]
Inserting Data using Z SQL Methods
Query
INSERT INTO customers (
customer_id, name, planet, age
) VALUES (
<dtml-sqlvar customer_id type=nb>,
<dtml-sqlvar name type=nb>,
<dtml-sqlvar planet type=nb optional>,
<dtml-sqlvar age type=int optional>
)
Notice the use of sqlvar. The sqlvar tag always needs a type specified. The allowable types are "string", "nb", "int", and "float". "String"
and "nb" are both string types that do two important things:
1. Proper escaping (or SQL Quoting). This means that any single quotes in the value being inserted are properly escaped. This
minimizes the liklihood of someone hijacking your SQL by passing in a string like:
"foo'); DELETE FROM customers;"
which could cause possible nastiness (the inserted string would end at foo). The sqlvar string and nb types would turn this into:
"foo''); DELETE FROM customers;"
which is safe. (Two single quotes in SQL is like the string \' in Python).
2. The difference between string and nb is an important feature. NB stands for "non-blank", and it means a non-blank string is to be
inserted. Many databases by default don't allow empty strings. If NB is used as the type and a blank string is passed, Z SQL
Methods raises an error saying a non-blank string was expected.
Where this is most useful is with the sqlvar attribute optional. With a sqlvar of type 'nb' and 'optional', if a blank string is
encountered, the SQL Value NULL is inserted instead. The table below shows the results of passing an empty string to sqlvar
with various options.
Effects of passing an empty string to sqlvar
sqlvar options
type=string
type=string
optional
type=nb
type=nb optional
result
''
''
raises exception!
NULL
So why did we not specify age as age:int in the arguments list? Because we want an empty string (or missing value) to mean that age
wasn't passed into the method (ie, a field not filled out in a form) and we want a NULL to be inserted in this case instead of an error being
raised that a string was being submitted instead of an integer.
Putting this SQL Method to use
So how can we make use of this method now? We will need an HTML form (a DTML Document or Method), and an action for that form
(another DTML method that calls the SQL Method and returns a message). Very simply, we can create a form like the following (in a
DTML Document)
Id
Title
Source
AddCustomer
Add a Customer
<dtml-var standard_html_header>
<h2><dtml-var title_or_id></h2>
<form action="dtPerformAddCustomer">
<table>
<tbody>
<tr>
<th align="left">Customer Id</th>
<td><input type="text" name="customer_id"></td>
</tr>
<tr>
<th align="left">Name</th>
<td><input type="text" name="name"></td>
</tr>
<tr>
<th align="left"><em>Planet</em></th>
<td><input type="text" name="planet"></td>
http://www.zope.org/Members/jshell/ZSQLMethods-InsertingData?pp=1 (2 of 3) [05/07/2000 11:16:15]
Inserting Data using Z SQL Methods
</tr>
<tr>
<th align="left"><em>Age</em></th>
<td><input type="text" name="age" size="3"></td>
</tr>
</tbody>
</table>
<input type="submit" value="Add Customer">
</form>
</table>
</form>
<dtml-var standard_html_footer>
This is a form whose action is dtPerformAddCustomer. dtPerformAddCustomer will be a DTML Method that will call the Z SQL Method
created above (sqlInsertCustomer).
Id
Title
Source
dtPerformAddCustomer
Add Customer Action
<dtml-var standard_html_header>
<dtml-call sqlInsertCustomer>
<h2>Customer <dtml-var name="name"> was added.</h2>
<dtml-var standard_html_footer>
This is a very simple action. Naturally more could be done here (like checking to make sure customer_id and name were submitted). Or
even checking for customer_id and name and if they're there, calling sqlInsertCustomer and returning to the AddCustomer form with a
message. Even better would be to have a list of customers (from a select query) on the AddCustomer page so that it's easy to see that a
new customer has been added.
SQL Methods, like most methods, have an arguments list. When a web form is submitted, the values of the fields are marshalled into an
object called the REQUEST (See the Zope documentation for more information on the REQUEST). The target of the form (usually a
DTML Method) uses the REQUEST to present its information and call/render other objects.
When a SQL Method is called in the above fashion (<dtml-call sqlInsertCustomer>, the same as <dtml-call
name="sqlInsertCustomer">), the REQUEST is used to pass in variables defined in the arguments list. Consult the ZSQL
Methods guides for more information.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jshell/ZSQLMethods-InsertingData?pp=1 (3 of 3) [05/07/2000 11:16:15]
Installing Zope under BSDI 4.x
How-To: Installing Zope under BSDI 4.x
Created by BitDancer. Last modified on 1999/11/08.
Apparently between BSDI 3.x and BSDI 4.x the way shared libraries are done was improved. Python as of 1.5.2 is not aware of
this change. This means that the compilation of the Zope dynamic modules fails. The fix is simple, and has been reported to the
Python newsgroup, so hopefully this HOWTO will be obsolete in the near future.
To compile Zope under BSDI 4.x, you have to get your Python installation done right. After that Zope compiles without a hitch.
Not being a Configure guru, I'm not sure where the neccessary changes go to make the fix correctly. So I'll just tell you how I did
it:
1. Run configure with the --with-threads option.
2. Make sure you have edited Modules/Setup to uncomment the line specifying that modules be built for dynamic loading.
3. Try to make Python. It fails.
4. Edit Modules/Makefile and change the following three lines to appear as indicated here::
LDSHARED=
CCSHARED=
LINKFORSHARED=
gcc -shared
-fpic
-Xlinker -export-dynamic
5. Make python. It should work, and the corrected version of the Makefile should get copied to the [pythonlib]/config
directory.
At this point, Zope should build correctly.
Note that there is a bug somewhere (probably in BSDI) that causes the Python make test to hang in the signal test. This does
not appear to impact Zope.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/BitDancer/bsdi4_x_install?pp=1 [05/07/2000 11:16:17]
Zope Installation for Windows 95
How-To: Installing Zope under Windows 95
Created by adil.h. Last modified on 2000/05/18.
This is a quick installation guide for beginners and refers to Zope version 2.1.6
Download the Windows installation program for Zope.
Double-click the downloaded file to begin installation on your computer.
The installation program will step you through a series of screens or dialogs. Just follow the on-screen prompts.
First, you'll be asked to provide a site name. This merely identifies, or gives a name to, the installation. It's easiest to accept
the default name provided (in this case "WebSite"). Press the Next button to continue:
Next, you'll be asked to choose a location in which to store the Zope program files. Again, it's easiest to accept the default:
C:\Program Files\WebSite
http://www.zope.org/Members/adil.h/win95_install?pp=1 (1 of 4) [05/07/2000 11:16:31]
Zope Installation for Windows 95
The next dialog screen will prompt you to provide a username and password. Be sure to remember your choice here as you'll
need both your username and password when you start working with the Zope Management interface. The password you
type will be "masked" i.e. hidden with an asterisk (*) character.
Click Next to continue the installation. Once installation is complete, you'll be returned to the main Windows Desktop. Zope
does not create a program shortcut. The next section explains how to start Zope.
http://www.zope.org/Members/adil.h/win95_install?pp=1 (2 of 4) [05/07/2000 11:16:31]
Zope Installation for Windows 95
Starting Zope
Find the installation folder for Zope. If you accepted the default installation path or location, this will be: C:\Program
Files\WebSite\
Inside this folder you'll find a file:
●
start.bat or
●
start
Double-click the file to start the ZServer. The ZServer is a web server that enables you to explore the Zope Management
interface locally from your own computer.
After double-clicking the start.bat file, a DOS window appears:
Wait a few moments and a series of lines will appear in the DOS Window. One of the lines will state:
ZServer Medusa (V1.13.4.1) Started at...
Look for the lines
Hostname: <name here>
Port: 8080
<name here> is individual to each computer. In the example below, the hostname is called MAIN
Start your web browser. In the browser address or location bar, type:
http://<hostname here>:8080
Replace <hostname here> with the actual hostname of your computer. For example, if the hostname is MAIN, type:
http://main:8080
If successful, your browser should present you with a "Welcome to Zope" page. One of the links on the page takes you to
the Zope Management screen. The first time you select this link, you'll be prompted for your username and password.
Each time you want to access the Zope Management Interface, you'll need to make sure the ZServer is running first (i.e.
leave the DOS Window open and running).
When you want to close or shut down the ZServer, click on the Control Panel folder in the left-hand navigation pane of the
Zope Management Interface.
http://www.zope.org/Members/adil.h/win95_install?pp=1 (3 of 4) [05/07/2000 11:16:31]
Zope Installation for Windows 95
In the right-hand pane or workspace, you'll find a button marked Shutdown. Click this to end the ZServer session. Then
simply close the DOS window.
Where Next?
A good place to start is by reading the Zope Guides. These explain the various components that make up Zope. The
Administrators guide explains some of the concepts and terminology relating to Zope. The Content Managers guide contains
a tutorial that explores the Zope Management Interface. Good luck!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/adil.h/win95_install?pp=1 (4 of 4) [05/07/2000 11:16:31]
Installing and Upgrading Zope 2.X
How-To: Installing and Upgrading Zope 2.X
Created by mcdonc. Last modified on 2000/01/17.
Overview
This document is intended for two groups of people:
The Installer You've just got Zope, and you want to install it "the right way". The INSTALL.txt and other documentation has
got you confused and you'd like a little more hand-holding.
The Upgrader You've got Zope 2.X running on your system and a newer version of Zope has been released that fixes a serious
bug or adds a new feature you'd like to use. You don't want to lose what's in your current Zope install.
If you're not upgrading an existing Zope, follow only those steps which have to do with installation and skip the others.
Assumptions
●
You're running Zope on a UNIX variant.
If you're using Win32 to run Zope, translate UNIX-ish instructions where necessary.
Some quick hints for Windows users: "cp" is the UNIX file copy command, equivalent to the DOS "copy" command which is also equivalent to
right-click dragging a file from one folder to another in Windows Explorer. "pwd" prints the current working directory and is the equivalent of
DOS "cd". "ls" shows a directory listing and is the equivalent of DOS "dir". "chmod", "chgrp", and "chown" set permissions, group ownership,
and user ownership respectively on files and directories within UNIX. Under Windows NT, you may be able to perform roughly equivalent
operations by right-clicking a file or directory and performing actions within the Security tab. Under Windows 9X, you have no such option
because you cannot secure Zope on Windows 9X, and you should probably switch to NT or a UNIX variant if you intend on making your Zope
site public. The UNIX "mkdir" command is equivalent to the DOS "md" command which is equivalent to creating a new Folder in Windows
Explorer.
●
You want to install Zope as a "normal" user, to start Zope as the root user, and to run Zope as the "nobody" user.
Let's examine how Zope works when it runs:
❍
When Zope is started as the root user, it will run, for security reasons, as the "nobody" user.
❍
When Zope is started as a normal user it will run under the context of that user.
We are going to install Zope as a "normal" user. This is a security risk at at the tradeoff of convenience. We do this so we can easily manage
Zope files without logging in as root or otherwise switching accounts.
We are going to start Zope as the root user. We could have chosen to start Zope as a "normal" user, but there's one important restriction to
starting Zope under a normal user account. If you want to use Zope's ZServer HTTP, FTP, or monitor servers to serve content on TCP ports
beneath 1024 (such as 80, 21, and 89), you'll need to start Zope as root, no matter how you install it. If you start it while logged on as a normal
user, it will fail with an error. This restriction is lifted when you're not serving Zope content via ZServer's HTTP daemon and you're not using
ZServer's FTP or monitor functionality because ZServer doesn't need to bind to any TCP ports under this configuration. Such would be the case if
you're serving Zope content via Apache only, for instance. It is also possible to start Zope as a normal user if ZServer doesn't try to bind to ports
beneath 1024. We are going to make life simple for ourselves by assuming that we may want ZServer to be able to bind to ports beneath 1024
someday, therefore we're going to start Zope as root. This is also a security risk at the tradeoff of convenience.
We are going to run Zope as the "nobody" user. After Zope starts as root, it will switch user context to "nobody". The "nobody" user is common
across many UNIX variants, and it represents a user who has no power whatsoever on the system other than those which are explicitly assigned to
it by an admin. Know that Zope in an extreme cicrumstance -- most likely through sloppy Zope end-user programming on your system -- is
capable of exposing files on the filesystem to external users. Under this extreme circumstance, you'll want to make sure you've taken the
"belt-and-suspenders" approach of not giving signigicant access to the account under which Zope runs. We achieve this goal by running Zope as
the "nobody" user.
●
You can "get root" on the UNIX box on which you're installing Zope.
We're assuming you're running Zope on a machine for which you have root privilege. If you're using Zope on a hosting provider's system, you
likely have no choice but to install and run Zope as the user under which the hosting provider established your account. This is fine. You're a little
more vulnerable if you make a configuration mistake or a programming error because external users who can gain access to the filesystem
through sloppy Zope programming might be able to read other files owned by your account. This just means you'll need to be more careful while
writing Zope applications. If you're running Zope on a hosting provider, you can skip following steps having to do with installation. Instead,
unpack Zope under your lone user account, and follow the install directions in the file doc/INSTALL.txt contained within the new tarball straight
away. After untarring and installing, to upgrade an older Zope, peform the steps in this document that have to do with upgrading.
●
You're now running a Zope version equal to or later than 2.0.0.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (1 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
This HOWTO does not cover upgrading Zope 1.X installations to 2.X.
●
You're upgrading to another 2.X version.
Though this example shows us upgrading a Zope 2.1.1 instance to Zope 2.1.2, you shouldn't take this to mean that the same steps wouldn't work
for upgrading between other minor 2.X versions. The same steps should apply to upgrading from Zope 2.0.0 to Zope 2.1.2 or in all likelihood to
an as-yet-mythical Zope 2.2.5.
●
Your current Zope top-level installation directory (the directory where the "start" and "stop" scripts live) is $ZOPEHOME.
Subsitute the actual path to your Zope installation where you see $ZOPEHOME in this document.
●
You're running the Apache webserver to front-end Zope via PCGI.
The steps that deal with reconfiguration of Apache settings will be marked as optional. If you're not using Apache to front-end Zope, you can
safely skip these steps.
Getting Started
OK, ready? Here we go:
1. Record your installed Products.
Visit your currently-installed Zope's Control_Panel through the Zope management interface, and select Product Management. You'll get a screen
which lists currently-installed Products and their version numbers. Print this screen out or transcribe its contents onto a piece of paper.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (2 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
The Product Management screen
Now that you've got your list of Products, cross the following off the list:
ExternalMethod
MIMETools
MailHost
OFSP
ZCatalog
ZGadflyDA
ZSQLMethods
These are core Zope products. They come with all releases of Zope and you needn't worry about replacing them in a new Zope install because
they're automagically installed into every version of Zope. Products that you haven't crossed off your list are either core products that haven't
been released before this writing or are products that you've installed yourself. For your "new" Zope to work as your old one did, we'll later need
to reinstall any non-core products, so hang on to this list.
2. Review your External Methods.
Let's go to our current Zope directory and list the files there. My existing Zope installation is in "/home/mcdonc/Zope2". The directory which
holds External Methods (*.py files) within any Zope installation is named "Extensions". I'm going to go look in my
"/home/mcdonc/Zope2/Extensions" directory to see what I've got in there:
[mcdonc@mcdonc Zope2]$ cd /home/mcdonc/Zope2/Extensions/
[mcdonc@mcdonc Extensions]$ ls
imglib.py imglib.pyc
[mcdonc@mcdonc Extensions]$
Looks like I've got at least one External Method in there, so I'll write myself a little note that I need to copy this over to my new install when the
time comes. This is a subtle editorial clue that you should do the same. If you don't have an Extensions directory, don't worry about it, it just
means you don't have any External Methods.
3. Download the latest version of Zope.
You may download Zope source and binary releases here. If you're running Linux, I suggest installing the Linux binary release, but you may
install from source too without much trouble. If you're running Solaris, I suggest using the binary release as many of the libraries and utilities that
are required to build Zope are likely missing from your environment. If you're running Win32, you should almost certainly download the binary
release. If you're running anything else, you'll need the source release.
For the purposes of this HOWTO, and because a majority of folks don't want to install from source or lack the tools to do so, we'll assume you're
installing from the binary release.
(NOTE for Linux users: Jeff Rush has packaged a set of Zope RPMs that might make installation slightly less painful. As I haven't kept up with
their status, I can't speak much about them, but you can go get them here. This document does not cover how to install Zope via RPM.)
4. Make and secure a Zope "holding" directory.
(NOTE for Win32 users: You can skip this step, but you should ultimately secure your Zope directory. If your system supports NTFS filesystem
security, you may wish to go change permissions on the Zope directory and the files contained within. I won't explain that here, it's left as an
exercise for the reader :->)
You'll notice that within this HOWTO, the Zope I want to upgrade from is located within my home directory (/home/mcdonc). This can be a very
appropriate place for a holding directory. In this HOWTO, though, we want our holding directory to be "/usr/local/zope".
We're going to want to do maintenance on Zope after it's installed. To make this less painful, we're going to want to allow our "normal" user
account access to the directory. The "nobody" account will also need to traverse this directory, as this is the account under which Zope will run.
Thus we're going to create a directory that is accessible and writable by our "normal" account and by the "nobody" user, but it will be otherwise
inaccessible.
(NOTE for Solaris/SVR4 users: Solaris and other SVR4-derived UNIX variants probably won't have a "normal" user group if you haven't created
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (3 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
one. You should create a group (using "groupadd") with the same name as your "normal" user and make this group the primary group of your
"normal" user in /etc/passwd before going on.)
For where I've chosen to put the holding directory, I'll need to "su" to root to make the directory, set its owner and group, and change its
permissions. Let's go do it:
[mcdonc@mcdonc mcdonc]$ cd /usr/local
[mcdonc@mcdonc local]$ su
Password:
[root@mcdonc local]# mkdir zope
[root@mcdonc local]# chown nobody zope
[root@mcdonc local]# chgrp mcdonc zope
[root@mcdonc local]# ls -ald zope*
drwxrwxr-x
2 nobody
mcdonc
1024 Jan 16 19:02 zope
[root@mcdonc local]# chmod 770 zope
[root@mcdonc local]# ls -ald zope*
drwxrwx--2 nobody
mcdonc
1024 Jan 16 19:02 zope
[root@mcdonc local]#
There, I did it. I've got a holding directory named "/usr/local/zope" that's owned by the "nobody" user. It's also group-owned by the "mcdonc"
group, whose only member is my user account "mcdonc". It's otherwise inaccessible to other users of the system except root. This is exactly what
we want. We've made sure that the only users who can mess with our holding directory and any other files or directories we put into the holding
directory are "nobody", "mcdonc" and the root user.
All this futzing with security is important because we're going to start Zope as the root user. When Zope is started as root, it executes
programs kept in the Zope directory as the root user before it switches user context to the nobody user. If these program files are modifiable
by arbitrary users, you could are compromising the security of your system. We've limited our risk by allowing only three trusted users to access
the Zope holding directory and anything kept within it. Giving arbitrary write access to the holding directory or the Zope directory and files
within is a Bad Idea.
5. Unpack the newly-downloaded Zope in to the holding directory.
(NOTE for Win32 users: you can ignore this step for the most part, just double-click on the downloaded Zope executable and make sure to put the
new Zope in a *different* directory than the existing one when going through the GUI install.)
Zope UNIX releases come as gzipped "tarballs" that need to be expanded before installation can occur. One of the differences between the Zope
installation process and that of a great many other UNIX programs is that the "build" directory of Zope is also the "install" directory of Zope.
This means that wherever you put Zope to begin with after you "un-gzip" and "un-tar" it will be where it expects to live for the rest of its useful
life. Another way to think of this is that Zope installs "in-place" without copying its built install files to another place on the filesystem. So let's
say I've downloaded the Zope binary release to my home directory. I'm going to switch from root to my normal user account and list my home
directory:
[root@mcdonc local]# exit
exit
[mcdonc@mcdonc local$ cd /home/mcdonc
[mcdonc@mcdonc mcdonc]$ ls -ald Zope*
-rw-rw-r-1 mcdonc
mcdonc
827392 Jan
Zope-2.1.2-linux2-x86.tgz
drwxrwxr-x 13 mcdonc
mcdonc
1024 Jan
9 19:48
8 16:47 Zope2
We can see that I have an existing Zope2 directory in my home directory. That's my existing Zope-2.1.1 installation, which I'd like to upgrade to
2.1.2. We can also see that I've downloaded the Zope-2.1.2 binary release for Linux ("Zope-2.1.2-linux2-x86.tgz").
If you're installing Zope on your own workstation or on a computer to which you have root access, you may have unpacked and installed a
previous Zope installation while logged in as the root user. I'm going to ask you to rethink that decision this time around. There is no explicit
reason to install Zope as root. Now, I know that as of this writing that the binary install docs explicitly say that you should install Zope as the root
user if you want to front-end Zope with Apache or another webserver. I've personally found this to be untrue, and this advice seems to cause a lot
of grief due to the way UNIX file permissions work. For the purpose of this HOWTO, we're going to install Zope under a normal user account.
We're going to ungzip and untar the Zope-2.1.2 release while we're logged in under our "normal" user account. This is important because if we
ungzip and untar the Zope tarball as root, its files will be owned by the user id of the owners of the files on the Digital Creations "build"
machine. Unless you work at Digital Creations, and you're unpacking this installation back on to the "build" machine, this is clearly not what you
want and might cause you some grief. (NOTE: Thanks to Patrick Phalen for the tar explanation here).
Since I've already downloaded my new Zope tarball, I'm going to ungzip and untar it as "mcdonc" while within the "zope" directory, which will
make all of its files owned by the "mcdonc" user.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (4 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
[root@mcdonc mcdonc]$ cd /usr/local/zope
[mcdonc@mcdonc zope]$ gunzip /home/mcdonc/Zope-2.1.2-linux2-x86.tgz
[mcdonc@mcdonc zope]$ tar xvf /home/mcdonc/Zope-2.1.2-linux2-x86.tar
Zope-2.1.2-linux2-x86/
Zope-2.1.2-linux2-x86/LICENSE.txt
Zope-2.1.2-linux2-x86/README.txt
<.. snip lots of other files ..>
[mcdonc@mcdonc zope]$ ls -ald Zope*
drwxr-xr-x
3 mcdonc
mcdonc
Zope-2.1.2-linux2-x86
[mcdonc@mcdonc zope]$
1024 Jan
9 21:41
Good news! I've got a Zope directory with all the files I need in it in /usr/local/zope/Zope-2.1.2-linux2-x86 that's owned by the "mcdonc" user. So
far so good.
6. Rename and secure your Zope directory.
(NOTE for Win32 users: skip this step.)
We've now got a Zope directory. But egads is the directory name "/usr/local/zope/Zope-2.1.2-linux2-x86" ugly! I don't want to have to type that
every time I want to specify that directory, as I'll be visiting it a lot. Remember that wherever you unpack Zope will be the place that it's going to
live for the rest of its useful life. Additionally, after the Zope "./install" script has been run, that instance of Zope depends heavily on the
directory's name to do little things like...... start. Renaming your Zope directory after an install is not an easy task.
We want to get it right the first time, and as we have the opportunity to "start fresh", let's not make it hard on ourselves. I'm going to change the
name of the directory to something a little less painful. Since I'm still logged on as "mcdonc", and "mcdonc" has write permissions to the newly
created directory, I can just go ahead and rename the directory without switching user contexts:
[mcdonc@mcdonc zope]$
/usr/local/zope
[mcdonc@mcdonc zope]$
drwxr-xr-x 10 mcdonc
Zope-2.1.2-linux2-x86
[mcdonc@mcdonc zope]$
[mcdonc@mcdonc zope]$
total 3
drwxrwxr-x
3 mcdonc
drwxr-xr-x 15 root
drwxr-xr-x 10 mcdonc
[mcdonc@mcdonc zope]$
pwd
ls -ald Zope*
mcdonc
1024 Jan
4 15:42
mv Zope-2.1.2-linux2-x86 2-1-2
ls -al
nobody
root
mcdonc
1024 Jan
1024 Jan
1024 Jan
9 21:41 .
9 20:56 ..
4 15:42 2-1-2
Now I've renamed my brand new shiny (but uninstalled) Zope 2.1.2 directory to "2-1-2". This is much easier to remember and type.
I'm also going to secure this directory in the same way we secured the Zope holding directory. I'm going to change the owner of 2-1-2 to
"nobody", then I'm going to chmod the 2-1-2 directory so only "nobody", root, and "mcdonc" can get in there. I'll need to "su" to root to do this.
Here goes:
[mcdonc@mcdonc zope]$ su
Password:
[root@mcdonc zope]# chown nobody 2-1-2
[root@mcdonc zope]# chmod 770 2-1-2
[root@mcdonc zope]# ls -al
total 3
drwxrwx--3 nobody
mcdonc
1024 Jan 16 19:13 .
drwxr-xr-x 15 root
root
1024 Jan 16 19:02 ..
drwxrwx--- 10 nobody
mcdonc
1024 Jan 4 15:42 2-1-2
[root@mcdonc zope]#
Looks good to me. Let's keep going.
7. Install the new Zope.
(NOTE for Win32 users: you can safely skip this step as the unpack routine in the GUI installer also installs Zope under Win32.)
After you've succesfully unpacked Zope into a directory, it's time to install it. In our case, we're going to descend into the new
/usr/local/zope/2-1-2 directory, switch our user context to our "normal" user account and start the install process. The following shows the binary
install process. The source install process is a little different, and you should read doc/INSTALL.txt for more info if you're installing from source.
Let's go do it:
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (5 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
[root@mcdonc zope]# exit
exit
[mcdonc@mcdonc zope]$ pwd
/usr/local/zope
[mcdonc@mcdonc zope]$ ls
2-1-2
[mcdonc@mcdonc zope]$ cd 2-1-2
[mcdonc@mcdonc 2-1-2]$ ls
LICENSE.txt README.txt ZServer
utilities
var z2.py zpasswd.py
[mcdonc@mcdonc 2-1-2]$ ./install
bin
doc
inst
install
lib
pcgi
-----------------------------------------------------------------------------Compiling python modules
----------------------------------------------------------------------------------------------------------------------------------------------------------creating default access file
Note:
The super user name and password are 'superuser'
and 'VjLk2UV8'.
You can change the superuser name and password with the
zpasswd script. To find out more, type:
/usr/local/zope/2-1-2/bin/python zpasswd.py
chmod 0600 /usr/local/zope/2-1-2/access
chmod 0711 /usr/local/zope/2-1-2/var
-----------------------------------------------------------------------------setting dir permissions
-----------------------------------------------------------------------------creating default database
chmod 0600 /usr/local/zope/2-1-2/var/Data.fs
-----------------------------------------------------------------------------Writing the pcgi resource file (ie cgi script),
/usr/local/zope/2-1-2/Zope.cgi
chmod 0755 /usr/local/zope/2-1-2/Zope.cgi
-----------------------------------------------------------------------------Creating start script, start
chmod 0711 /usr/local/zope/2-1-2/start
-----------------------------------------------------------------------------Creating stop script, stop
chmod 0711 /usr/local/zope/2-1-2/stop
-----------------------------------------------------------------------------Done!
[mcdonc@mcdonc 2-1-2]$
Success! I've now got a presumably working copy of Zope installed on my new system in the /usr/local/zope/2-1-2 directory. One thing I do
probably want to write down is the superuser password that is given to me during the install process. We're going to change this password later,
but it's wise to record the installation-generated password.
Let's go check out who owns our Zope files now:
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (6 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
[mcdonc@mcdonc
total 65
drwxrwx--- 10
drwxrwx--3
-rw-r--r-1
-rw-r--r-1
drwxrwxr-x
4
-rwxr-xr-x
1
-rw------1
drwxrwxr-x
2
drwxrwxr-x
3
drwxrwxr-x
2
-rwxr-xr-x
1
drwxrwxr-x
4
drwxrwxr-x
7
-rwx--x--x
1
-rwx--x--x
1
drwxrwxr-x
2
drwxrwxr-x
2
-rw-r--r-1
-rw-rw-r-1
-rw-r--r-1
-rw-rw-r-1
[mcdonc@mcdonc
2-1-2]$ ls -al
nobody
nobody
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
2-1-2]$
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
mcdonc
1024
1024
3479
987
1024
483
43
1024
1024
1024
225
1024
1024
156
54
1024
1024
17925
10717
8806
5310
Jan
Jan
Mar
Apr
Jan
Jan
Jan
Jan
Jan
Jan
Jul
Jan
Jan
Jan
Jan
Jan
Jan
Oct
Jan
Oct
Jan
16
16
9
29
16
16
16
4
4
16
23
4
16
16
16
16
16
22
16
28
16
19:22
19:19
1999
1999
19:20
19:22
19:22
15:42
15:42
19:21
13:48
15:42
19:22
19:22
19:22
19:22
19:22
13:33
19:22
13:54
19:22
.
..
LICENSE.txt
README.txt
ZServer
Zope.cgi
access
bin
doc
inst
install
lib
pcgi
start
stop
utilities
var
z2.py
z2.pyc
zpasswd.py
zpasswd.pyc
Fantastic, this is exactly what we want. The "mcdonc" user owns all these files. Let's move along.
8. Change "access" file permissions.
(NOTE for Win32 users: you can skip this step.)
For Zope to start properly, we'll need to let the nobody user read the "access" file. This file contains the encrypted superuser password. We'll also
make sure that our "normal" user account can read and write the file while we're at it to make maintenance easier. We'll need to use the su,
chown, and chmod commands to do this. Let's go:
[mcdonc@mcdonc 2-1-2]$ su
Password:
[root@mcdonc 2-1-2]# chown nobody access
[root@mcdonc 2-1-2]# chmod 660 access
[root@mcdonc 2-1-2]# ls -al access
-rw-rw---1 nobody
mcdonc
43 Jan 16 19:22 access
[root@mcdonc 2-1-2]#
That'll do it.
9. Change the superuser password.
Zope assigns a random password to the superuser account during installation for security reasons. I'm not really confident that I'll remember the
Zope-provided superuser password 'VjLk2UV8' that I received during the install process. Therefore, I'm going to go change it. You should
change yours as well. We'll use the zpasswd utility to do this. Unfortunately, if the only Python interpreter we've got installed on our system is the
one that came with Zope, we need to do some rather obscure futzing around with the PYTHONPATH environment variable before we can use the
zpasswd utility. I'm also going to change user context back to my normal login before changing the password, just for fun. Bear with me:
[root@mcdonc 2-1-2]# exit
exit
[mcdonc@mcdonc 2-1-2]$
PYTHONPATH=/usr/local/zope/2-1-2/lib/python:/usr/local/zope/2-1-2/lib/python1.5
[mcdonc@mcdonc 2-1-2]$ export PYTHONPATH
[mcdonc@mcdonc 2-1-2]$ bin/python zpasswd.py --username=superuser
--password=mycoolpassword access
Could not find platform independent libraries
Could not find platform dependent libraries
Consider setting $PYTHONHOME to [:]
[mcdonc@mcdonc 2-1-2]$ more access
superuser:{SHA}GU5Jq70HskQ5FKQGGsOaqZ8LDtM=
[mcdonc@mcdonc 2-1-2]$
Ignore those "Could not find platform independent library" errors. Yes, I know it's ugly, but it works. You've successfully changed your superuser
password to "mycoolpassword". Note that if you're upgrading, you'll probably want to change your superuser password to the one you're using in
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (7 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
your "old" Zope install.
10. Permit the "nobody" user to access the ZODB files.
(NOTE for Win32 users: skip this step.)
Since we plan on running Zope as the "nobody" user, we'll need to go in and change some specific file permissions to allow "nobody" to access
the Zope Object Database (ZODB) files and some related files. Specifically, we'll need to change permissions on the "var" directory in the
top-level Zope dir as well as the files contained therein. What we want to do is allow the "nobody" user to have write permission to the var
directory as well as all the files contained within it.
We'll need to su to root and issue the chown command to do this. Let's go do it:
[mcdonc@mcdonc 2-1-2]$ su
Password:
[root@mcdonc 2-1-2]# chown -R nobody var
[root@mcdonc 2-1-2]# ls -al var
total 154
drwxrwxr-x
2 nobody
mcdonc
1024
drwxrwx--- 10 nobody
mcdonc
1024
-rw------1 nobody
mcdonc
76750
-rw-r--r-1 nobody
mcdonc
76750
[root@mcdonc 2-1-2]#
Jan
Jan
Jan
Jun
16 19:22 .
16 19:22 ..
16 19:22 Data.fs
23 1999 Data.fs.in
Perfect. We've let "nobody" read and write to our ZODB files (the files named Data*) as well as some others that will prove necessary. We've
still let our "normal" user mcdonc get some access to these files, which is important too. It's important to note that if we did not take this step,
we would have been presented with this unceremonious error message upon attempting to start Zope as root:
Traceback (innermost last):
File "/usr/local/zope/2-1-2/z2.py", line 574, in ?
pf = open(PID_FILE, 'w')
IOError: [Errno 13] Permission denied: '/usr/local/zope/2-1-2/var/Z2.pid'
By changing our var directory permissions so "nobody" could write to it as well as the files contained within it, we've ensured that we'll be able to
start Zope successfully as root. We're in good shape.
11. Stop your "old" Zope.
(NOTE for Win32 users: You are probably going to stop your running Zope instance by shutting it down from the Control Panel Services applet.)
Stop the running Zope processes by using the stop script in $ZOPEHOME. Let's take a look at me doing this on my Zope installation:
[root@mcdonc 2-1-2]# exit
exit
[mcdonc@mcdonc 2-1-2]$ cd ~
[mcdonc@mcdonc mcdonc]$ pwd
/home/mcdonc
[mcdonc@mcdonc mcdonc]$ ls -ald Zope*
drwxrwxr-x 13 mcdonc
mcdonc
1024 Jan
[mcdonc@mcdonc mcdonc]$ cd Zope2
[mcdonc@mcdonc Zope2]$ ./stop
[mcdonc@mcdonc Zope2]$
8 16:47 Zope2
Looks like stopping the running Zope process worked. You usually know that the stop process works when you don't see an error come back.
If you do see an error come back, it's possible that your existing Zope isn't actually running or is running under a different user account than the
one I've shown. If this is the case, "su" to root, run the ./stop shell script again, and see if the error goes away. If it still persists, you'll need to
"kill" the Python processes that Zope is running under. To find the process to kill, do a "ps -aux|grep python" (Linux) or "ps -ef|grep python"
(Solaris), and then issue the kill command against each of these processes' process ids -- eg. "kill 30241 30242")
12. Making sure the new Zope actually works.
(NOTE for Win32 users: You'll probably start your new instance of Zope from the Control Panel Services applet.)
Before we blindly go copying all our data to the new Zope installation, we'd like to know whether it actually runs. We'll need to "su" to root to
start Zope successfully, then we'll need to issue the ./start command. Let's go do it:
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (8 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
[mcdonc@mcdonc Zope2]$ cd /usr/local/zope/2-1-2
[mcdonc@mcdonc 2-1-2]$ su
Password:
[root@mcdonc 2-1-2]# ./start
-----2000-01-10T04:05:32 PROBLEM(100) ZServer Computing default hostname
-----2000-01-10T04:05:32 INFO(0) ZServer Medusa (V1.13) started at Sun Jan
23:05:32 2000
Hostname: localhost
Port:8080
9
-----2000-01-10T04:05:32 INFO(0) ZServer FTP server started at Sun Jan 9
23:05:32
2000
Authorizer:None
Hostname: mcdonc.dyndns.org
Port: 8021
-----2000-01-10T04:05:33 INFO(0) ZServer PCGI Server started at Sun Jan 9
23:05:33
2000
Unix socket: /usr/local/zope/2-1-2/var/pcgi.soc
-----2000-01-10T04:05:33 INFO(0) ZServer Monitor Server (V1.5) started on port
8099
This is a good sign. Zope has started successfully! Congratulations! By the way, Ignore that message about a "PROBLEM(100)", it's
unfortunately normal. If you get other "PROBLEM" messages, they may be important, however, so keep an eye out.
Let's check our freshly-started Zope out through a web browser by visiting http://localhost:8080.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (9 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
Zope Welcome screen
Looks like we're in business!
Let's try the management screen by visiting http://localhost:8080/manage: It's going to prompt us for a username and password when
we hit it. We should use the username "superuser" and the password "mycoolpassword" to gain access here.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (10 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
Zope Management Screen
Looks like it worked! Note that if you can't gain access with the superuser username/password combination, chances are you've not properly
changed the superuser password with the zpasswd utility. If this is the case, try the Zope-install-supplied superuser password. If it still doesn't
work, try to change the password with the zpasswd.py utility. If all else fails, you can edit the "access" file by hand. When editing the file, change
the field on the right-hand-side of the colon. Replace the {SHA}hasdhajsdhjasdh gobbeldygook with a password and restart Zope. You
should be able to gain access with the superuser username and the cleartext password you've typed.
13. Stop the "new" Zope.
(NOTE for Win32 users: Control Panel Services Applet will probably stop the new Zope).
Stop the "new" Zope copy by pressing CTRL-C in the terminal window that you've started it in. You'll see something like:
Traceback (innermost last):
File "/usr/local/zope/2-1-2/z2.py", line 583, in ?
asyncore.loop()
File "/usr/local/zope/2-1-2/ZServer/medusa/asyncore.py", line 107, in
loop
poll_fun (timeout)
File "/usr/local/zope/2-1-2/ZServer/medusa/asyncore.py", line 55, in
poll
(r,w,e) = select.select (r,w,e, timeout)
KeyboardInterrupt
Traceback (innermost last):
File "/usr/local/zope/2-1-2/z2.py", line 433, in ?
zdaemon.run(sys.argv, os.path.join(INSTANCE_HOME, Zpid))
File "/usr/local/zope/2-1-2/lib/python/zdaemon.py", line 208, in run
p,s = os.waitpid(pid, 0)
KeyboardInterrupt
[root@mcdonc 2-1-2]#
Now we're back at the shell prompt. If you're installing Zope for the first time, congratulations, you've got Zope successfully installed!
14. Making the new Zope start automatically on bootup.
(NOTE for Win32 users: You can make your Zope start automatically by choosing "Automatic" in the Control Panel Services applet under NT for
your new Zope instance).
If we're going to be using Zope pretty regularly, we'll want to have Zope start when the computer boots. I'm not going to go in to editing "rc"
scripts in this HOWTO, you'll need to read a little about startup commands in pertinent materials. Good sources for "rc" script editing and
placement are available on the Internet and in books about UNIX/Linux. Here's a sample rc script that seems to work well for me on Red Hat
Linux:
#! /bin/sh
#
# processname: zope2
# Source function library.
. /etc/rc.d/init.d/functions
# Get config.
. /etc/sysconfig/network
# Check that networking is up.
if [ ${NETWORKING} = "no" ]
then
exit 0
fi
# See how we were called.
case "$1" in
start)
echo -n "Starting Zope2 service: "
daemon /usr/local/zope/2-1-2/start
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (11 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
;;
stop)
echo -n "Stopping Zope2 service: "
/usr/local/zope/2-1-2/stop
;;
restart|reload)
$0 stop
$0 start
;;
*)
echo "Usage: zope {start|stop|restart}"
exit 1
esac
exit 0
15. Moving the "old" Zope data over to the new installation.
Since we've successfully configured a new copy of Zope, our next task is to move the data over from our "old" Zope instance. Remember, for the
purposes of this HOWTO, our "old" copy of Zope is contained in "/home/mcdonc/Zope2" and we want to move that copy's data from there to our
new, shiny Zope in "/var/local/zope/2-1-2".
You'll be surprised how easy this really is. All we have to do is copy the files "Data.fs", "Data.fs.lock", "Data.fs.tmp", and "Data.fs.old"
(possibly) from our old Zope's "var" directory to our new Zope's "var" directory. Then we'll need to make sure our permissions are set properly
on the copied files. Let's go do that while we're logged in as root:
(NOTE: IMPORTANT!! Make sure the "new" Zope and the "old" Zope are stopped before trying this!! You may corrupt the database
otherwise.)
[root@mcdonc 2-1-2]# cd /usr/local/zope/2-1-2/var
[root@mcdonc var]# cp /home/mcdonc/Zope2/var/Data.fs* .
cp: overwrite `./Data.fs'? y
cp: overwrite `./Data.fs.in'? y
cp: overwrite `./Data.fs.lock'? y
cp: overwrite `./Data.fs.tmp'? y
[root@mcdonc var]# ls -al Data*
-rw------1 nobody
mcdonc
20485935 Jan 16 20:46 Data.fs
-rw-r--r-1 nobody
mcdonc
76750 Jan 16 20:46 Data.fs.in
-rw-rw-r-1 root
root
272343 Jan 16 20:46 Data.fs.index
-rw-rw-r-1 root
root
5 Jan 16 20:47 Data.fs.lock
-rw------1 root
root
1314527 Jan 16 20:47 Data.fs.old
-rw-rw-r-1 root
root
89700 Jan 16 20:47 Data.fs.tmp
[root@mcdonc var]# chown nobody Data*
[root@mcdonc var]# ls -al Data*
-rw------1 nobody
mcdonc
20485935 Jan 16 20:46 Data.fs
-rw-r--r-1 nobody
mcdonc
76750 Jan 16 20:46 Data.fs.in
-rw-rw-r-1 nobody
root
272343 Jan 16 20:46 Data.fs.index
-rw-rw-r-1 nobody
root
5 Jan 16 20:47 Data.fs.lock
-rw------1 nobody
root
1314527 Jan 16 20:47 Data.fs.old
-rw-rw-r-1 nobody
root
89700 Jan 16 20:47 Data.fs.tmp
[root@mcdonc var]#
That's it for copying the old database over to the new install.
16. Installing old Products in the new Zope.
Remember when we checked our "old" Zope to see what Products were installed in it? Well, whip out that list. You've crossed off "core"
products from the list and the rest you'll need to reapply to the "new" Zope installation. You'll either need to copy the Product directories out of
the old Zope installation or go to Zope.org and download their latest versions. We're going to opt to download the latest versions of the Products
from the Zope.org website. Products come as gzipped "tarballs" that need to be ungzipped and untarred while you're in the "new" Zope home
directory. We'll go through the reapplication of one Product, ZFormulator, which we've downloaded from Zope.org and placed in our home
directory. We'll do this logged on under our "normal" user account.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (12 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
[root@mcdonc var]# exit
exit
[mcdonc@mcdonc 2-1-2]$ pwd
/usr/local/zope/2-1-2
[mcdonc@mcdonc 2-1-2]$ ls /home/mcdonc/zform*
/home/mcdonc/zformulator-0.1.tar.gz
[mcdonc@mcdonc 2-1-2]$ gunzip /home/mcdonc/zformulator-0.1.tar.gz
[mcdonc@mcdonc 2-1-2]$ tar xvf /home/mcdonc/zformulator-0.1.tar
import/zf_demo.zexp
lib/python/Products/ZFormulator/
lib/python/Products/ZFormulator/BasicField.gif
<.. snip lots more files ..>
[mcdonc@mcdonc 2-1-2]$
We untarred the Product while we were in the /usr/local/zope/2-1-2 directory so it would be applied to our new installation.
Repeat this procedure for each Product you need to apply to your new Zope installation.
17. Copy over old External Methods to the new Zope install.
Earlier, we checked to see if we had any External Methods in our Extensions directory in our old Zope installation. I did, so I'm going to go copy
them to the Extensions directory of my new Zope installation. Note that out-of-the-box, Zope does not create an Extensions directory upon initial
installation. We'll need to create this directory for our new installation.
[mcdonc@mcdonc 2-1-2]$ ls
LICENSE.txt ZServer
access doc
inst
lib
start utilities
z2.py
zpasswd.py
README.txt
Zope.cgi bin
import install pcgi stop
var
z2.pyc
zpasswd.pyc
[mcdonc@mcdonc 2-1-2]$ pwd
/usr/local/zope/2-1-2
[mcdonc@mcdonc 2-1-2]$ mkdir Extensions
[mcdonc@mcdonc 2-1-2]$ cd Extensions
[mcdonc@mcdonc Extensions]$ cp /home/mcdonc/Zope2/Extensions/* .
[mcdonc@mcdonc Extensions]$ ls -al
total 5
drwxrwxr-x
2 mcdonc
mcdonc
1024 Jan 16 21:04 .
drwxrwx--- 12 nobody
mcdonc
1024 Jan 16 21:04 ..
-rw-rw-r-1 mcdonc
mcdonc
620 Jan 16 21:04 imglib.py
-rw-rw-r-1 mcdonc
mcdonc
1093 Jan 16 21:04 imglib.pyc
[mcdonc@mcdonc Extensions]$
Good enough. They're moved over.
18. Retesting our new Zope with the old data.
We're going to retest Zope to see if the data transfer went "OK". All we're going to do is start it as root and browse the management screen to see
if all looks well.
[mcdonc@mcdonc Extensions]$ cd /usr/local/zope/2-1-2
[mcdonc@mcdonc 2-1-2]$ su
Password:
[root@mcdonc 2-1-2]# pwd
/usr/local/zope/2-1-2
[root@mcdonc 2-1-2]# ./start
-----2000-01-17T02:13:59 PROBLEM(100) ZServer Computing default hostname
-----2000-01-17T02:13:59 INFO(0) ZServer Medusa (V1.13) started at Sun Jan 16
21:13:59 2000
Hostname: localhost
Port:8080
-----2000-01-17T02:13:59 INFO(0) ZServer FTP server started at Sun Jan 16
21:13:59
2000
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (13 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
Authorizer:None
Hostname: mcdonc.dyndns.org
Port: 8021
-----2000-01-17T02:13:59 INFO(0) ZServer PCGI Server started at Sun Jan 16
21:13:59
2000
Unix socket: /usr/local/zope/2-1-2/var/pcgi.soc
-----2000-01-17T02:13:59 INFO(0) ZServer Monitor Server (V1.5) started on port
8099
Now let's go to our browser and look at it:
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (14 of 15) [05/07/2000 11:17:08]
Installing and Upgrading Zope 2.X
Retesting our new Zope.
Looks good to me. I didn't have much in my old Zope 2.1.1, just a single ZFormulator form. You'll probably have a lot more stuff. What's
important however is that I can now see the object named "testform", which existed in my "old" Zope instance. I think we're successful.
19. Changing our Apache rewrite rule to point to our new Zope.cgi
If you're using Zope frontended by Apache via PCGI, you'll need to change your Apache's httpd.conf rewrite rule. I'm going to make the
assumption that you know where the file is. Edit it and change the line that starts "RewriteRule" to point to the "new" Zope instance's Zope.cgi.
Note that both the "before" and "after" lines should be entered a single continuous line in your editor, even if they break across multiple lines on
your browser.
Before:
RewriteRule ^/Zope/(.*) /home/mcdonc/Zope2/Zope.cgi/$1
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
After:
RewriteRule ^/Zope/(.*) /usr/local/zope/2-1-2/Zope.cgi/$1
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
That should allow you to access your new Zope instance from Apache. If you're installing for the first time, see the file doc/WEBSERVER.txt for
more information about setting up Apache to work with Zope.
Conclusions
Well, that was boatloads of fun, wasn't it? If you're still having problems installing or upgrading Zope or getting it to work with Apache, here are some
resources for you to check out:
●
Zope Mailing Lists: http://www.zope.org/Resources/ MailingLists
●
Zope HOWTOs: http://www.zope.org/Documentatio n/How-To
That's it. Good luck!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mcdonc/HowTos/zopeinstall/ZOPE-INSTALL-HOWTO?pp=1 (15 of 15) [05/07/2000 11:17:08]
Integrating LoginManager and SMB
How-To: Integrating LoginManager and SMB
Created by tseaver. Last modified on 2000/05/09.
Products like NTUserFolder jcNTUserFolder and smbUserFolder allow a Zope site to perform user authentication using an
NT/Samba domain controller. However, these products are not compatible with the newer authentication protocols required by the
PortalToolkit and the Tracker.
Here is how to fix that:
1. Install the prerequisite software:
❍
the PAM_SMB library
❍
the smbvalid Python module
❍
the LoginManager Zope product
2. In the place of the standard acl_users folder, drop in a LoginManager object, selecting the GenericUserSource.
3. Inside the GenericUserSource, drop an ExternalMethod, smbUserAuthenticate, with the following body::
def smbUserAuthenticate( self, username, password, smb_pdc, smb_bdc, smb_domain ):
"""
Authenticate against SMB, using the smbvalid module.
"""
import smbvalid
return smbvalid.check( username, password, smb_pdc, smb_bdc, smb_domain )
4. Add properties to the UserSource::
smb_domain
smb_primary_dc:
smb_backup_dc:
the SMB domain name
the PDC for the SMB domain
the BDC for the SMB domain
5. Add a Python method to the UserSource::
Id:
userAuthenticate
Parameter list: self, REQUEST, username, password
Body:
return self.smbUserAuthenticate( username
, password
, self.smb_primary_dc
, self.smb_backup_dc
, self.smb_domain
)
6. Add Python methods to fill out the other slots in the GenericUserSource interface (you may need to tweak the return
values as appropriate to your environment)::
Id:
userExists
Parameter list: self, REQUEST, username
Body:
return 1
Id:
userDomains
Parameter list: self, REQUEST, username
Body:
return []
Id:
userRoles
Parameter list: self, REQUEST, username
Body:
return []
http://www.zope.org/Members/tseaver/LoginManager_and_SMB?pp=1 (1 of 2) [05/07/2000 11:17:12]
Integrating LoginManager and SMB
7. Tweak the ZClass which the UserSource uses to represent users on its Class tab (for PTK, select DemoPortal
LoginMember).
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/tseaver/LoginManager_and_SMB?pp=1 (2 of 2) [05/07/2000 11:17:12]
Linking Domino and Zope (a work in process!)
How-To: Linking Domino and Zope (a work in
process!)
Created by cba. Last modified on 1999/09/15.
This is a work in process! I am adding info as I learn (i.e. slowly!)
System Requirements
My environment (for this topic) is Windows 95, Domino 5.0, and Python 1.5.2 with Win32 extensions. Now that Domino is being
beta tested on Linux, perhaps a Python/Domino guru could figure out if it's possible to do this in the Linux environment; that
person is not me.
Step 1: Talking to Domino using Python
The following text was copied directly from a posting I found on DejaNews.
If you're working on a Win32 platform, you could use the Pythonwin COM extensions to access Notes functionality. The exposed
object model is very similar to what you can access from LotusScript directly.
First of all, use the COM browser that comes with the win32com module to view the available classes, methods, and properties.
You will have to create a NOTESSESSION object to initialize the Notes API before accessing any other functionality. To access
these classes easily, you should probably generate a Python module encapsulating all of these classes per the instructions. The file
you are looking for is in the Notes installation directory... notes32.tlb.
To generate the module, run (for example):
c:\> python \python\win32com\client\makepy.py -o \python\notes.py
c:\notes\notes32.tlb
Note: All of the above should be on one line!
Once you have a generated module, just put it somewhere on your Pythonpath and import it:
import notes
You can then create a NotesSession object and open a database fairly easily. Example:
import notes
"""open your PAB, display it's title, then display each view's title and list of
columns."""
session = notes.NOTESSESSION()
view = notes.NOTESVIEW
notesdb = session.GETDATABASE("", "names.nsf")
print notesdb.TITLE
viewarray = notesdb.VIEWS
for v in viewarray[1:]:
print v.Name
for c in v.COLUMNS[1:]:
print "
", c.Title
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cba/Domino_and_Zope?pp=1 [05/07/2000 11:17:15]
Local Roles and the Owner Role
How-To: Local Roles and the Owner Role
Created by anthony. Last modified on 1999/11/21.
(Note: much of the content in this how-to is derived from postings by Brian Lloyd to the Zope list)
Local Roles
As well as the normal roles that you assign to a user, it's possible to give a user local roles. These are roles that are only valid in
the context of the object. So if I assign the local role Editor to the user fredbert in the folder
/documentation/userguide, this role will only apply to objects in the folder /documentation/userguide.
Defining Local Roles
Local roles are defined in the manage_listLocalRoles screen - this can be accessed by clicking on the Security tab, then
clicking on the link in the opening paragraph.
To add a local role for a user, click on the user in the left list, and the role in the right hand list.
Say you give the user fredbert the Owner role in a folder - fredbert now has all the access of the owner role, but only in that
folder (and in subfolders, of course).
Important note Regardless of the permissions you give a local role, if you grant them the Change Permissions permission, you're
effectively granting them the Manager role in that folder (since they can just edit the permissions in that folder to boost their own
role)
The Owner local role
One special local role is the Owner local role. This is automatically granted to the user that created the object, but can also be
reassigned to another user using the manage_listLocalRoles page. This allows you to create a folder for a particular user,
which only they can edit, without having to create a special role per user and per folder.
This allows you to very easily create 'home directories' for people, in a way similar to the zope.org website.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anthony/owner_role?pp=1 [05/07/2000 11:17:17]
Looping in DTML
How-To: Looping in DTML
Created by tone. Last modified on 1999/11/27.
Looping in DTML
This uses the Zope 2 syntax of <dtml-in "..."> ... </dtml-in>. The Zope 1.10 syntax will also work (<!--#in "..."--> .. <!--#/in-->)
The DTML loop
Example using pre-cooked values
<dtml-in "16,21,3,49">
item# <dtml-var sequence-index>,<dtml-var sequence-number> = <dtml-var sequence-item>
</dtml-in>
and it's output
item#
item#
item#
item#
0,1
1,2
2,3
3,4
=
=
=
=
16
21
3
49
Example using ranged values (only available in Zope 2)
You can use the _.range() function to specify a range of values to iterate over (you're limited to 1000 items). _.range(10) will
iterate over the values 0..9. _.range() works in the same way as the python function range(), in that you can also specify start, stop
and increment values. e.g.
<dtml-in "_.range(15, 33, 4)">
item# <dtml-var sequence-index>,<dtml-var sequence-number> = <dtml-var sequence-item>
</dtml-in>
and it's output
item#
item#
item#
item#
item#
0,1
1,2
2,3
3,4
4,5
=
=
=
=
=
15
19
23
27
31
Some Real Work - a database lookup
Assume you have a ZSQL method called get_users in your acquisition tree that doesn't take a parameter. Something like
"SELECT username, password, roles, domains FROM users". Then this code will show all the elements in the table;
<dtml-in "get_users()">
Username:<dtml-var username>, Password:<dtml-var password>, Roles:<dtml-var role>,
Domains:<dtml-var domains><br>
</dtml-in>
If you have another ZSQL method get_user_by_username that uses a parameter, username, with the following code: SELECT
username, password, roles, domains WHERE username = <dtml-sqlvar username type=string> then this will get the table rows
with a particular username
<dtml-in "get_user_by_username(username='tone')">
Username:<dtml-var username>, Password:<dtml-var password>, Roles:<dtml-var roles>,
http://www.zope.org/Members/tone/looping?pp=1 (1 of 2) [05/07/2000 11:17:20]
Looping in DTML
Domains:<dtml-var domains><br>
</dtml-in>
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/tone/looping?pp=1 (2 of 2) [05/07/2000 11:17:20]
Make an Index with Internal Lettered Links
How-To: Make an Index with Internal Lettered
Links
Created by toyota. Last modified on 1999/09/15.
I looked at the huge list of Zopistas and thought, "This is going to get out of hand soon. This is a very long list."
Then I thought of making internal links like index pages often have:
a | b | c ...
---------------a
ape
apple
b
banana
boy
c
camel
car
coyote
I decided to give it a try. This is what I came up with:
1. create an External Method
title: bsort
function: bsort
module: buckets
2. create a DTML Method:
id: make_index
file: make_index.dtml
3. use the DTML Method in a DTML Document:
id: index_html
file: index.dtml
(for testing purposes, I added a 'keys' list property to this document)
Here is everything zipped up: make-index.zip
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/toyota/make-index?pp=1 [05/07/2000 11:17:22]
Make your life easier with INSTANCE_HOME
How-To: Make your life easier with
INSTANCE_HOME
Created by 4am. Last modified on 2000/06/29.
A default Zope installation places your site data within the same subdirectory as the Zope software itself, and encourages you to
add Products to the same directory as the ones which came with Zope.
It doesn't have to be this way, though. You can easily place all of your site's data, and the Products it uses, in a separate location.
Why would I want to do that?
●
you can keep "standard" Zope Products separate from ones you have installed. You can make some Products and
Extensions shared, and some site- specific.
●
you can run multiple, independent Zope sites from the same installation of Zope.
●
you can keep multiple versions of Zope around, and easily change the version used by a site.
●
you can upgrade Zope by just installing the new version.
How does it work?
Zope looks for an environment variable called INSTANCE_HOME. If it is found, its value is the location of var, import, and
access. It can also have a custom_zodb.py module.
Both the INSTANCE_HOME location and the Zope installation can have Products and Extensions. The
INSTANCE_HOME is searched first.
So what do I do?
1. Create a directory for your site
2. Place the var and Extensions directories and the access file for the site in the site directory. If you are starting
from scratch, you can make empty directories and create access with the zpasswd.py utility.
3. Create an import directory and a Products directory. Install or copy products you use that are not distributed with
Zope into this Products.
4. (*nix only) If you want to share some Products among all sites, but still keep them independent of your Zope installation,
you can create a Shared_Products directory for them somewhere, then symlink them into the Zope Products
directory.
5. If you follow Jim Cain's excellent howto, you can skip the following steps.
6. Copy start and stop to the site directory.
7. (*nix only) Edit start to look something like the following (the lines defining and exporting variables are the important
ones)::
#! /bin/sh
reldir=`dirname $0`
INSTANCE_HOME=`cd $reldir; pwd`
export INSTANCE_HOME
PYTHONHOME=/usr/local/Zope2.1.6
export PYTHONHOME
exec /usr/bin/python \
$PYTHONHOME/z2.py \
-D -p /home/httpd/cgi-bin/Zope.cgi "$@"
http://www.zope.org/Members/4am/instancehome?pp=1 (1 of 2) [05/07/2000 11:17:25]
Make your life easier with INSTANCE_HOME
8. (Win32 only) Edit start.bat using Notepad or your favorite text editor to look something like the following (using
your own file paths, of course)::
"C:\Zope\bin\python.exe" "C:\Zope\z2.py" -D %1 %2 %3 %4 %5 %6 %7 %8 %9
INSTANCE_HOME="C:\MyWebSite"
9. Edit stop so that the path it uses leads to the site's var, not the Zope installation.
What now?
Use the start and stop scripts in the site directory to control that site. You can install a new version of Zope simply by
replacing your current installation. You can install different versions of Zope in different directories, and switch among them by
changing PYTHONHOME in your start script.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/4am/instancehome?pp=1 (2 of 2) [05/07/2000 11:17:25]
Making A First Zope Website
How-To: Making A First Zope Website
Created by hgebel. Last modified on 1999/09/27.
by Harry Henry Gebel
This How-To describes the steps I went through to install Zope and create my first one page Zope web site. I used the source tarball on Linux, so if you use
another operating system the installation instructions may not apply to you. I already had Python 1.5.2 installed, if you don't you will have to install it to run
Zope. This How-To assumes you already know at least as much HTML as I do (ie. what the common tags mean.) The HTML 4.0 specification is a good
reference if you need to know what a tag means. If you have any corrections, suggestions or answers to questions I ask in the How-To please email me.
Installing Zope
After downloading the source tarball I installed Zope with the following commands (replace new_passwd with the password you want and change directory
names to whatever would be most appropriate on your system):
[hgebel@localhost hgebel]$ cd /usr/local
[hgebel@localhost local]$ su
[root@localhost local]# tar xvzf /home/hgebel/Zope-2.0.1-src.tgz
[root@localhost local]# mv Zope-2.0.1-src Zope
[root@localhost local]# chown -R nobody:nobody Zope
[root@localhost local]# cd Zope
[root@localhost Zope]# su nobody
[nobody@localhost Zope]$ python -O w_pcgi.py
[nobody@localhost Zope]$ python zpasswd.pyo -p new_passwd access
[nobody@localhost Zope$ exit
[root@localhost Zope]# python z2.pyo
[root@localhost Zope]# exit
This performed the following steps:
1. Became root and extracted the archive into /usr/local/Zope
2. Made nobody the owner of all the files in the installation (ZServer will run as user nobody as it is set up by default.)
3. Became nobody in order to keep file ownerships correct.
4. Compiled Zope with pcgi. This will allow me to use Zope with Apache in the future. If you have no plans to run with anything except ZServer than
you can compile with python -O wo_pcgi.py instead. The -O option to python causes python to make the .pyo optimized python bytecode. Can
anybody tell me if this actually makes a difference?
5. Changed the password for the Zope superuser.
6. Became root again and started the ZServer. The ZServer will run as user nobody even if you start it while logged in as root. If you want to run on
the standard http port (80) you will have to start as root because 80 is a reserved port. If you wanted to run on the default ZServer port (8080) then
you could start it while still logged in as nobody. Either way the server will run as nobody for security reasons.
7. Became a normal user again.
Logging on for the first time.
Now that Zope is running you can access it by pointing your browser to http://127.0.0.1:8080 or http://localhost:8080. You should see the following
screen:
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (1 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
Visit the link labeled Visit the management screen. You will be prompted to enter a User ID and a password. Enter superuser as the USER ID and enter
the password you created above as the password. You will then see the following screen:
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (2 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
You may want to bookmark this screen so that you can immediately go to it in the future. The first thing we will do is create a new user for ourselves. Select
the acl_users folder on the left hand frame, then press the Add button and fill out the following form:
The domain field allows you to restrict where you will be able to log into Zope from. For example, if you specified *.inet.net then no one would be able to
log on as you unless they were located on the inet.net network, even if they knew you user ID and password. Roles define what permissions a user is
allowed, by selecting Manager as your role you will be able to manage anything in the Zope database. You can add new roles and give each role exactly the
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (3 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
permissions necessary to allow people with that role to perform the actions they need to perform to manage their area of the Zope database. (However, roles
will not be covered further in this How-To.) When you are done filling out the form press the Add button to add this user to the database.
Now we will try to log in using our new user ID. Close all your browser windows and exit your browser, then reload it. This should cause the browser to
forget to log in as the Zope superuser. Now visit http://127.0.0.1:8080/manage and you will again be prompted to type in you user ID and password. This
time type the new user ID and you should be able to log in successfully. (If you cannot you may have forgotten to press the Add button after filling out the
above form, try logging back in as superuser and repeating the last step.)
First look at DTML
Now, select the top folder in the left-hand pane of the management screen to return to the root folder. You will see that the root folder contains seven objects.
An object is similar to a file except that it is located in the Zope database instead in an individual file in the computer's filesystem, and it is able to be
acquired by lower levels of the database (more on this later.) There are different types of objects to represent different types of data. The type of the object is
represented by the icon to the left of object's ID in the folder contents list. The Control_Panel object allows you to perform some basic maintenance of the
Zope database and program, and allows you to shutdown or restart the Zope process. The QuickStart object is a folder, it is like a directory in the filesystem
and holds other objects much like a directory is a file that holds other files. The acl_users object is a user folder and holds user IDs and passwords.
index_html, standard_error_message, standard_html_header, and standard_html_footer are DTML Methods. A DTML Method holds DTML which is
similar to the HTML used to design web pages and is in fact used by Zope to generate the HTML which will be sent to people's browsers. Select
standard_error_message to get your first look at DTML.
The Id field of this form gives the name of this object. It cannot have spaces but may have underscores, capital and lower case letters, a period and numerals.
Windows users note that case is significant in Zope, Standard_Error_Message would be considered a completely different object than
standard_error_message. This rule applies even if you are running Zope on an operating system such as Windows that uses a filesystem where case is not
significant; this is because Zope does not store object as individual files in the filesystem, but rather as object in it's own database. You cannot change the Id
on this form, but you can change it be selecting the object in the folder contents list and pressing the Rename button. However, it is not a good idea to
change names unnecessarily because other objects may be referring to them by their old names.
The Title field contains the objects title, which is used by some objects to effect how they are rendered into HTML. For example, a DTML Method's title
would be used as the title of the document when someone viewed the page that was rendered from that DTML method. An image object's title would be used
to provide the ALT tag seen by non-graphical browsers. An objects title can also be used to provide you with a reminder of that object's functions if the Id is
not as self-explanatory as you would like it to be.
The large text entry field holds the actual DTML source code of the method. You can enter the source code directly into this box, you can cut and paste it
from a text editor, you can use the Upload tab at the top of the form to upload it from a file on your filesystem, or you can use FTP to upload it to the Zope
database. The last option is especially useful if you use an editor like Emacs that allows you to edit files with FTP. The Tips section of the Zope site has tips
on using Zope with Emacs and XEmacs.
Here are the contents of standard_error_message:
<!--#var standard_html_header-->
<!--#if error_message-->
<!--#var error_message-->
<!--#else-->
<TABLE BORDER="0" WIDTH="100%">
<TR VALIGN="TOP">
<TD WIDTH="10%" ALIGN="CENTER">
<IMG SRC="<!--#var BASE1-->/p_/ZButton" ALT="Zope">
</TD>
<TD WIDTH="90%">
<H2>Zope Error</H2>
<P>Zope has encountered an error while publishing this resource.
</P>
<P>
<STRONG>Error Type: <!--#var error_type--></STRONG><BR>
<STRONG>Error Value: <!--#var error_value--></STRONG><BR>
</P>
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (4 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
<HR NOSHADE>
<P>Troubleshooting Suggestions</P>
<UL>
<!--#if "error_type in ('KeyError','NameError')"-->
<LI>This resource may be trying to reference a
nonexistent object or variable <STRONG><!--#var error_value--></STRONG>.</LI>
<!--#/if-->
<LI>The URL may be incorrect.</LI>
<LI>The parameters passed to this resource may be incorrect.</LI>
<LI>A resource that this resource relies on may be encountering an error.</LI>
</UL>
<P>For more detailed information about the error, please
refer to the HTML source for this page.
</P>
<P>If the error persists please contact the site maintainer.
Thank you for your patience.
</P>
</TD></TR>
</TABLE>
<!--#comment-->
Here, events like logging and other actions may also be performed, such as
sending mail automatically to the administrator.
<!--#/comment-->
<!--#/if-->
<!--#var standard_html_footer-->
Notice that it very closely resembles HTML. The actual DTML is contained in the tags opening with <!--# and closing with -->. Anything not enclosed in
one of these tags will be copied exactly into the page that is generated from the DTML. In other words, it so closely resembles HTML because it is HTML.
DTML tags may also be opened with <dtml- and closed with a greater-than sign. The following two tags are equivalent:
<!--#var some_dtml_method-->
<dtml-var some_dtml_method>
Let's look at the DTML tags used in standard__error_message one at a time.
The var tag evaluates an object or expression and inserts the result into the rendered page. How the result is rendered is dependent on what type of object is
being evaluated. The following tag:
<!--#var standard_html_header-->
evaluates the DTML method standard_html_header. When a DTML method is evaluated the resulting HTML is inserted into the current page. The
methods standard_html_header and standard_html_footer contain the tags used to begin and end an HTML document. By using these two tags you not
only save yourself a lot of typing, you also make it easy to propagate changes in appearance throughout your site. (More on that in a little while.)
When an image object is evaluated an IMG tag is inserted into the HTML document. If you have an image object called logo, with a width of 100, a height
of 50 and a title of "Our Company Logo" the following tag:
<dtml-var logo>
would insert the following tag into the HTML document:
<img src="http://127.0.0.1:8080/logo" width="100" height="50" alt="Our Company Logo">
When an expression is evaluated the result of the expression is converted to a string and inserted in the document. Expressions are enclosed in quotes and
must be valid python expressions. The following line of DTML:
<P>4 + 5 = <dtml-var "4 + 5">
would appear in the generated HTML as:
<P>4 + 5 = 9
The if tag is used for conditional insertion. The variable or expression referred to is evaluated, and if it is true the DTML following the tag is rendered. If it is
not true and if there is an else tag following the if tag than the DTML following the else tag will be rendered. Here is a trivial example:
<dtml-if "2 < 3">
<P>God is in heaven and all is right with the world.
<dtml-else>
<P>The law is inside out, the world is upside down.
</dtml-if>
<P>This will be rendered in either case
If, in fact, two is lower than three, than this will be rendered as:
<P>God is in heaven and all is right with the world.
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (5 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
<P>This will be rendered in either case
But if it turns out that three is lower than two then it will be rendered as:
<P>The law is inside out, the world is upside down.
<P>This will be rendered in either case
Note that the last line, because it comes after the closing tag, is rendered unconditionally. The end tag can be expressed as either </dtml-if> or as <!--#/if-->.
Here is what the DTML reference manual has to say about true and false conditions: All Zope objects are either true or false. Numeric values are true if they
are non-zero and false if they are zero. Objects that are sequences of objects, like search results, are true if the sequences are non-empty and false
otherwise. Most other objects are true.
The comment tag is used to mark comments in the DTML code. Anything between a comment tag and the closing (</dtml-comment> or
<!--#/comment-->) tag will not be rendered at all in the resulting HTML.
There is much more to DTML, please read the Zope Document Template Markup Language Reference for more information.
Making a new page
Okay, let's make a new page. The first thing we will do is to make a new folder to put it in. This is not necessary, you can have as many documents as you
want in a folder. However, by making a new folder you will keep the root folder from getting cluttered, and we will also be able to see an example of
acquisition.
Select the root folder in the left hand pane of the management screen. Find the drop down list box labeled Available Objects. Pull it down and make the
selection marked Folder.
A form will come up for you to fill in with information about the folder you wish to create. The Id is the name by which the folder will be referred to. A
folder is an object that contains other objects. If a folder with an Id ThatsSomeFolder contained an object with an Id SomePig.jpg that object could be
referenced by pointing your browser to http://127.0.0.1:8080/ThatsSomeFolder/SomePig.jpg. If you do not specify which object in the folder you wish to
reference (ie. http://127.0.0.1:8080/ThatsSomeFolder) than the object whose Id is index_html. This similar to to way some servers provide the index.html
file when you point to a directory name. (But note that that is an underscore and not a period between index and html.)
The Title can be used to help you remember the folders purpose if you give it an obscure Id.
The Create public interface checkbox is used to denote whether this folder will be created with it's own index_html object. If you do not select this
checkbox than the folder will not have it's own index_html object but will instead have to acquire it from the root folder. The way acquisition works is that
if an object is requested and no object with that Id exists in the folder, Zope will start searching backwards through the folder tree until it finds an object with
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (6 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
that Id. Since there is an object with the Id index_html in the root folder, that is the object that will be found if the new folder does not have it's own
index_html object.
The Create user folder checkbox is used to create a user folder in the new folder. A user folder can be used to create User Ids that are only valid for the new
folder and it's subfolders.
Give the new folder the Id FirstFolder, the Title My First Folder, select the Create public interface checkbox and unselect the Create user folder
checkbox. When you have finished filling out the form press the Add button to create the new folder.
You should see the new folder listed in the the folder contents list. If it is not listed your browser is showing you a cached copy of the folder contents list;
make sure your browser is set to compare documents to the cache every time a document is visited and not "once per session" or "never."
Select the new folder and it's contents list should appear. Let's add an image to the folder Select image from the dropdown list.
Id is the name by which this object will be referred. Title is the string that will appear in the image's ALT tag. Image is the file on your filesystem which will
be uploaded to Zope.
Give the image the Id FirstImage, the title First Image, and use the browse button to select any image on your filesystem that is of a type your browser can
view. Press the Add button and the image will be uploaded to Zope.
Note that we did not include an extension in the Id. Because the period has a special meaning to python, Using Ids with periods in them is a little more
complicated than using Ids without periods. For more on using Id's with periods see the DTML section of the ZDP's Zope FAQ.
Now, select index_html. This object is a DTML Document, for the purposes of this How-To a DTML Document and DTML method are equivalent. Here is
the DTML code for index_html:
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (7 of 8) [05/07/2000 11:18:06]
Making A First Zope Website
<!--#var standard_html_header-->
<h2><!--#var title_or_id--></h2>
<p>
This is the <!--#var id--> Document.
</p>
<!--#var standard_html_footer-->
The standard_html_header and standard_html_footer objects are DTML methods that contain the that contain the HTML headers and footers. Because
FirstFolder does not contain objects with these Ids they are acquired from the root folder. The id variable is a property that evaluates to a string containing
the Id of the current document. The title_or_id variable is a property that evaluates to a string containing the title of the current document unless the title is
empty, in which case it evaluates to the current document's Id. Select the View tap located at the top of the right hand pane to view the page that will be
generated from this DTML.
Now push your browsers back button to return to the DTML entry form and change the document's title to My First Document, then change the document's
code to read as follows:
<!--#var standard_html_header-->
<P>This is my first DTML document.
<P><dtml-var FirstImage>
<!--#var standard_html_footer-->
By selecting the Change button at the bottom of the form you will update the Zope database with index_html's new contents.
Now point your browser to http://127.0.0.1/FirstFolder and you should see something like this:
Congratulations! You've made you're first Zope website.
I know this ends abruptly, but I'm not sure what the best area to cover next is, not least because I am just learning Zope myself. I would appreciate any
feedback on what needs improvement in this document and where it should go next.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/hgebel/HowTo/FirstZope?pp=1 (8 of 8) [05/07/2000 11:18:06]
Making Folder Directory Listings
How-To: Making Folder Directory Listings
Created by gwachob. Last modified on 2000/04/13.
One of the first things I wanted to do when putting together a small zope server was to have an index_html file which
automatically listed all objects in a folder (presumably of a certain type). This would create essentially a "directory" page that
would keep itself automatically in-sync. I can drop new objects in the folder and voila, they are instantly listed.
It turns out that its a good challenge for a new DTML programmer. So, here's how i do it, listing only File and DTML Document
Objects:
<dtml-in "PARENTS[0].objectValues(['File','DTML Document'])" sort=title>
<dtml-unless "_.string.find(_['id'],'index_html')==0">
<li>
<a href="<dtml-var URL1>/<dtml-var id>">
<dtml-var title>
<small>
(<dtml-var bobobase_modification_time fmt="%m/%d/%Y">)
</small>
</a>
<small>
[<a href="<dtml-var URL1>/<dtml-var id>/manage_main">Edit</a>]
</small>
</dtml-unless>
</dtml-in>
This piece of code presents a plainly-formatted list indented with <li> tags. It also allows you to directly edit each object in the
folder.Here's example output:
●
File 1 (01/02/2000) [Edit]
●
File 2 (01/04/2000) [Edit]
●
File 3 (01/09/2000) [Edit]
The most interesting part is the first line:
<dtml-in "PARENTS[0].objectValues(['File','DTML Document'])" sort=title>
PARENTS[0] refers to the folder containing the DTML Document this snippet is taken from.
objectValues is a method on folderish objects which returns a list of objects contained by the folder whose meta-types match one
of the list elements of the parameter to the method. Here, I select File and DTML Document meta-types. Inside the dtml-in
element, the id and title references are relative to each objects enumerated by the objectValues method call. So, for each DTML
Document and File object inside the folder, the title and id are printed out here. You may want to retrieve other attributes,
depending on the types of objects contained in the folder.
You can easily come up with a different layout and do something fancier. I was asked about how to insert the last modified date of
each file into the listing, and thats what the bobobase_modification_date is doing in there. Why bobobase_modification_date and
not just date or modification_date? I'm sure its a legacy of the Bobo product from which Zope is derived. However, its really quite
confusing not to use an intuitive name...
One problem with the approach of including the edit links the way I do here is that when you click on the edit links, you are sent
to a management view page which may not have the same look and feel as your page containing this listing. You may want to
write your own editing views and link to them with this code instead of to the manage_main view.
One more caveat-- there is a test to see if the id begins with index_html. This is a workaround-- I was trying to avoid the
index_html file from being listed, but for some reason, no string I could find would match with the id of index_html using a ==
operator. Perhaps this is a bug?
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/gwachob/directorylisting?pp=1 [05/07/2000 11:18:09]
Managing http/https URL Links
How-To: Managing http/https URL Links
Created by rbickers. Last modified on 2000/07/03.
The Problem
Many Web sites use a combination of SSL and non-SSL servers to handle requests. The problem is managing which URL links
need to be prefixed with https and which need to be prefixed by http. You could use absolute URLs everywhere (which you need
to use anyway when switching from non-SSL to SSL, or vice versa), but that gets cumbersome and the benefits of relative URLs
are lost. Some argue that it's easy enough to just run everything under SSL, but why waste the resources where encryption is
unimportant? This document explains one of probably many ways to handle this problem.
A Simple Solution
Zope provides a handy method called absolute_url() that returns the URL that can be used in the HREF attribute of an HTML link.
The problem is that absolute_url() always returns an http prefix if the current request was using a non-SSL server, and an https
prefix if the current request was using an SSL server.
A simple solution starts with setting a boolean property called SSL in the Zope root folder. Setting this on will make the site
default to http. Setting it off will make the site default to https. You can then set the same boolean property in different Zope
folders based on whether or not you would like links to objects within the folder to use SSL or not. Acquisition is a wonderful
thing, so you only need to set the property on folders that you want to be different than its parent folder. You can also set the SSL
property on individual documents if you desire.
In order to make use of the new property, there are a few options: 1) create an external method, 2) apply patches to modify
absolute_url() in Zope 2.1.6, or 3) install the SSLAbsoluteURL product for Zope 2.2.
ulink() External Method
One way to make use of this new SSL property is to create a Python file named ulink.py in your Zope Extensions folder with the
following content:
from string import replace
def ulink(self, RESPONSE=None):
"""Return an absolute url to the object using the built in
absolute_url method, except that if the boolean property
SSL exists and is on, https will be used instead of http."""
url=self.absolute_url()
if hasattr(self, 'SSL') and self.SSL:
url=replace(url, "http://", "https://", 1)
else:
url=replace(url, "https://", "http://", 1)
return url
Then create a new External Method in the Zope root folder. Give it an ID of ulink, a function name of ulink, and a Python module
file of ulink. You now have a new (and improved) version of absolute_url(), sort of. You can't use the alternate ways of calling
absolute_url() which would be a nice thing to be able to do.
Now all you need to do is create links using the new method. For example, if you want to provide a link for the URL
http://www.logicetc.com/Services/Support/Zope, use the following HTML code:
<A HREF="<dtml-var "Services.Support.Zope.ulink()">">Zope Support</A>
Of course if you're using this link on a document, for example, inside /Services/Support, you could use the following as well:
<A HREF="<dtml-var "Zope.ulink()">">Zope Support</A>
Now if you ever decide you want everything under /Services to use SSL, simply set a boolean property called SSL in the Services
folder. Any link, anywhere on your site, to anything under the Services folder, will magically begin to use https.
http://www.zope.org/Members/rbickers/http_https_ulinks?pp=1 (1 of 2) [05/07/2000 11:18:12]
Managing http/https URL Links
Apply Patches to Modify Zope
Note: These patches only work with Zope 2.1.6
It would be more convenient if absolute_url() recognized this new SSL property by default. If you apply this patch in the
lib/python/OFS directory, it will! The patch does not require you to use the new property, so it won't change Zope's behavior if
you apply it and don't have the SSL property set. Don't forget to restart Zope after applying it.
SiteAccess is a popular Zope product that modifies Zope's absolute_url(), so if you're using SiteAccess, you can apply this patch
to make use of the SSL property. I don't use SiteAccess, so this hasn't been well tested.
Keep in mind that there may be other products that modify or replace the behavior of absolute_url(). If you install such a product,
these patches may become useless.
Once you've applied one or both of the above patches, you create links using absolute_url(). For example, if you want to provide a
link for the URL http://www.logicetc.com/Services/Support/Zope, use the following HTML code:
<A HREF="<dtml-var "Services.Support.Zope" url>">Zope Support</A>
Of course if you're using this link on a document, for example, inside /Services/Support, you could use the following as well:
<A HREF="<dtml-var "Zope" url>">Zope Support</A>
or this:
<A HREF="&dtml.url-Zope;">Zope Support</A>
Note that there doesn't appear to be a way to handle references to sub items using the entity syntax. For example, if you want to
reference Services.Support.Zope, you'll have to stick with the conventional dtml syntax, but if you just want to reference Zope, you
can use the entity syntax.
Now, just like if you were using the external method, if you ever decide you want everything under /Services to use SSL, simply
set a boolean property called SSL in the Services folder. Any link, anywhere on your site, to anything under the Services folder,
will magically begin to use https.
SSLAbsoluteURL Product
There is now an option for users of Zope 2.2 to install the SSLAbsoluteURL product, which will modify the behavior of
absolute_url(). The modified absolute_url() calls the original absolute_url() and then changes the http/https prefix accordingly.
Usage is the same as described in the section above titled Apply Patches to Modify Zope.
Comments/Questions
If you have any questions or comments, please let me know at rbickers@logicetc.com. Unless someone comes up with a better
solution, I'll be presenting this to Digital Creations and the maintainer of SiteAccess for inclusion into their products.
Enjoy!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rbickers/http_https_ulinks?pp=1 (2 of 2) [05/07/2000 11:18:12]
Minor Nitpicks and Quibbles
How-To: Minor Nitpicks and Quibbles
Created by Gregor. Last modified on 2000/01/03.
As a newcomer to Zope, I see a number of things in the interface that should be fixed, but doing so requires editing the source.
Now, I know that the Zope Mozilla project to build a better interface was announced, but I was too impatient to wait for that.
The first of these is from our friend, the "Powered by Zope" button. The problem is that it opens www.zope.org in the same frame
as the button, rather than the entire browser. I have the button in a navbar frame, so you can imagine why trapping Zope.org in my
frame just won't work.
To fix it, go into the lib/python/OFS subdirectory of your Zope installation and open the file "Application.py". Find the line that
reads:
def ZopeAttributionButton(self):
Three lines below that is a section that reads
<a href="http://www.zope.org/Credits">
Just before the >, hit space and type the text target="_top". Save and close the file. You should now have something like
this:
<a href="http://www.zope.org/Credits" target="_top">
The second problem is the Zope document editing interface. To put it bluntly, no wrapping sucks. ^_^
In the same directory as the file above, find and open "documentEdit.dtml". Close to the middle of the file is a line that reads
<TEXTAREA NAME="data:text" WRAP="Off"
Change the word "Off" to "Soft" and save.
Voìla! Both items are now fixed. Ah, the joy of Open Source Software. Have fun.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Gregor/misc_nitpicks?pp=1 [05/07/2000 11:18:14]
Mix ZCatalog/ZSearch with Apache
How-To: Mix ZCatalog/ZSearch with Apache
Created by tseaver. Last modified on 1999/11/07.
Problem
●
CatalogAware objects store relative URLs in their Catalog.
E.g., object Baz in folder Bar in folder Foo catalogs itself in Foo.Catalog as:
Foo/Bar/Baz
●
The ZSearch interface presents these URLs without modification, which leads to long, cyclic URLs when the ZSearch
documents are not direct siblings of their catalog:
<dtml-comment> within Foo/Bar/search_results </dtml-comment>
<dtml-in Catalog> -- only match is "Baz"
<dtml-let itemPath="getpath( data_record_id_ )">
<A HREF="&dtml-itemPath;">...</A>
</dtml-let>
</dtml-in>
becomes (when the user clicks the link):
http://my.site:8080/Foo/Bar/Foo/Bar/Baz -- ICK!!!
First Solution
●
Prepend a "/" to the HREF URL:
<dtml-comment> within Foo/Bar/search_results </dtml-comment>
<dtml-in Catalog> -- only match is "Baz"
<dtml-let itemPath="getpath( data_record_id_ )">
<A HREF="/&dtml-itemPath;">...</A>
</dtml-let>
</dtml-in>
will "click through" to:
http://my.site:8080/Foo/Bar/Baz.
Apache Complications
●
The first solution works fine under "plain" ZServer installations, but breaks when using Apache's rewrite mechanism and
PCGI -- the rewritten URL of the source document does not carry through to the HREF.
E.g.:
<dtml-comment> now rewriting "http://my.site/Zope"
--> "http://mySite:8080"
<dtml-comment> within Foo/Bar/search_results </dtml-comment>
<dtml-let itemPath="getpath( data_record_id_ )">
<A HREF="/&dtml-itemPath;">...</A>
</dtml-let>
</dtml-in>
becomes (when the user clicks the link):
http://my.site/Foo/Bar/Baz -- OOPS!
No "Zope", so 404!
http://www.zope.org/Members/tseaver/catalog_under_apache?pp=1 (1 of 2) [05/07/2000 11:18:17]
Mix ZCatalog/ZSearch with Apache
Better Solution
●
Prepend a SCRIPT_NAME + "/" to the HREF URL:
<dtml-comment> now rewriting "http://my.site/Zope"
--> "http://mySite:8080"
<dtml-in Catalog> -- only match is "Baz"
<dtml-let itemPath="getpath( data_record_id_ )">
<A HREF="&dtml-SCRIPT_NAME;/&dtml-itemPath;">...</A>
</dtml-let>
</dtml-in>
will "click through" to:
http://my.site/Zope/Foo/Bar/Baz.
This technique works equally well under "plain" ZServer (where SCRIPT_NAME is the empty string) and under
Apache+PCGI.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/tseaver/catalog_under_apache?pp=1 (2 of 2) [05/07/2000 11:18:17]
Multi Environment Database Access
How-To: Multi Environment Database Access
Created by peracles. Last modified on 1999/11/26.
Alright first How-To here so bear with me.
In a recent project I created an online realtime reporting tool for our Customer Service Reps. It accesses an Oracle database using
the ZODBCDA.
In a couple of short days I had a functional search and display interface up and running using SQLMethods and DTMLMethods.
Zope being a fast fix for viewing the necessary data was a godsend, but we have three environments. (Dev,QA,Prod). How could
this tool be used by developers, the QA department, and the CSRs?
Enter Accuisition:
+-Zope root
+-Oracle
| |_Get_This_Query (SQLMethod)
| |_Get_That_Query (SQLMethod)
| |_Get_Some_Other_Data (SQLMethod)
| |
| +-Reports
|
|_Display_This_Data (DTML Method)
|
|_Display_That_Data (DTML Method)
|
|_Display_Some_Other_Data (DTML Method)
|
+-db
+-dev
| |_ODBC_Database_Connection
|
+-qa
| |_ODBC_Database_Connection
|
+-prod
|_ODBC_Database_Connection
Using this structure allows for acquiring your database connections.
http://www.myzope.com/db/dev/Oracle/Reports/Display_Some_Data
http://www.myzope.com/db/qa/Oracle/Reports/Display_Some_Data
http://www.myzope.com/db/prod/Oracle/Reports/Display_Some_Data
would pull data from three differnent environments.
The only trouble is that you must use
http://www.myzope.com/db/prod/Oracle/manage
to edit or change SQLMethods otherwise it cannot find the database connection(s).
I hope this is usefull, maybe it is more of a tip that a How-To though.
Happy Zoping
-Karl
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/peracles/db_howto?pp=1 [05/07/2000 11:18:19]
Output in columns
How-To: Output in columns
Created by drewp. Last modified on 2000/05/04.
If the result of a <dtml-in> is many short items, you might like to output them in columns like this:
hw01: 2 lab04: 100 p4: None quiz06: 10
hw02: 2 lab06: 100 p5: None quiz07: 1
hw03: 2 lab07: 100 p6: None quiz08:
hw04: 2 lab08: 100 p7: None quiz09:
hw05: 5 lab09: 100 p8: None quiz10: 6
hw06: 2 lab10: 100 quiz01: 100 quiz11: None
hw07: 2 mt1: 51 quiz02: 6 quiz12: None
lab01: 100 p1: None quiz03: 10
lab02: 100 p2: None quiz04: 10
lab03: 100 p3: None quiz05: 10
Here's the DTML that made that output. Without knowing the user's browser size, there probably isn't a good way to automatically
determine the right number of columns on the fly.
<table><tr><td valign=top>
<dtml-in getactiveassignments sort=name>
<dtml-var name>: <dtml-var "_[name]"><br>
<dtml-if "not (_['sequence-index']+1)%10">
</td><td valign=top>
</dtml-if>
</dtml-in>
</tr></table>
You could use a similar trick to make sequential items go across-then-down, instead of down-then-across like I did here. Wrap
each item in <td>...</td> and have a conditional <tr>..</tr> to break the rows at the right places.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/drewp/multiple_columns?pp=1 [05/07/2000 11:18:21]
Overlib and Zope
How-To: Overlib and Zope
Created by Ioan. Last modified on 2000/04/26.
Use properties to control doc of your
links with overlib.
Overlib is a JavaScript library created to enhance websites with small popup information boxes to help visitors around your
website. Load overlib.js on your standard_html_header:
<DIV ID="overDiv" STYLE="position:absolute; visibility:hide; z-index: 1;"></DIV>
<SCRIPT LANGUAGE="JavaScript" SRC="/js/overlib.js"></SCRIPT>
Then add a properties let's say 'caption' on your objects (folders and DTML documents).
In SimpleSiteMap made by djay you may try to add :
<dtml-if caption>
onMouseOver="overlib('<dtml-var caption>')" onMouseOut="nd()"
</dtml-if>
inside the link.
That is! After you create your SimpleSiteMap you add that js code.
(Tip: I put a link to home and map in my standard_html_header)
Look at www.Immortalitysystems.net. Is not mine, but is nice.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Ioan/Overlib?pp=1 [05/07/2000 11:18:23]
Pass parameters implicitly
How-To: Pass parameters implicitly
Created by paulabrams. Last modified on 2000/06/28.
Its easy to pass parameters between pages explicitly.
somepage_html?keywords=parrot
What if you wanted to call the page implicitly, without the keyword?
somepage_html?parrot
Here is a very simple way to pass one parameter implicitly::
<dtml-if "_.has_key('keywords')">
<dtml-comment> passed explicitly </dtml-comment>
<dtml-elif QUERY_STRING>
<dtml-comment> passed implicitly </dtml-comment>
<dtml-call "REQUEST.set('keywords', QUERY_STRING)">
<dtml-else>
<dtml-comment> safety </dtml-comment>
<dtml-call "REQUEST.set('keywords', '')">
</dtml-if>
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/paulabrams/howto_querystring?pp=1 [05/07/2000 11:18:25]
Passing parameters to a dtml method
How-To: Passing parameters to a dtml method
Created by teyc. Last modified on 2000/03/24.
Introduction
This how-to takes you through creating a dtml-method which takes a parameter, and calling it.
DTML Method
Create a dtml method with:
●
id: dtmlStudent
Code:
Student Name: <dtml-var student_name>
Calling the method
Create a dtml-method with:
●
id: dtmlDisplayStudent
●
Title: How To Example
Code:
<dtml-var standard_html_header>
<dtml-var "dtmlStudent(_.None, _, student_name='Harry')">
<dtml-var standard_html_footer>
Yes, the bit with ...var dtmlStudent(_.None, _, ... looks ugly and complicated, and you may not even find it necessary to use every
time. For example, you can get away with dtmlStudent(student_name='Harry). I found using the canonical form consistently better
and as your programming skills develop, you find that the syntax above will save you a lot of grief.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howtoDtmlCall?pp=1 [05/07/2000 11:18:27]
Postmortem Debugging In Zope
How-To: Postmortem Debugging In Zope
Created by mcdonc. Last modified on 2000/04/05.
This is going to be a quick and lame HowTo because I'm strapped for time.
But in order to do postmortem debugging in Zope, follow the steps in Michel Pelletier's HowTo named "The Debugger Is Your
Friend".
Except, instead of using the ZPublisher facility for running tests, use the following:
>>> import Zope
>>> Zope.debug('/path/to/the/object?your_query_string', u='username', pm=1)
.... output will happen
>>> import pdb
>>> pdb.pm()
You will be placed at the point where the exception occured in the code. You can't step through code from here, but you can
examine the values of the code where the error occured. Note the usefulness of the "u=" parameter which lets you set your user to
any user id without needing a through-the-web authentication.
To do normal debugging, instead of the "pm=1" argument, provide an argument to Zope.debug of "d=1". This will allow you to
step through code ala Michel's HowTo.
Sorry about the extreme lameness of this HowTo.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mcdonc/HowTos/pm-debug?pp=1 [05/07/2000 11:18:29]
Product author's guide to Zope 2.2+ security
How-To: Product author's guide to Zope 2.2+
security
Created by Brian. Last modified on 2000/06/29.
Product author's guide to Zope 2.2+ security
Overview
Zope 2.2 introduced a number of changes to the underlying Zope security infrastructure. These changes were designed to address
the Server Side Trojan issue as well as to make the implementation of the security model more coherent and maintainable. While
most of the changes are transparent to Zope product developers, there are certain changes to the post-2.2 security model that
product authors must be aware of, and it is possible that some products may need to be updated for deployment in a 2.2+
environment.
The two main changes that may affect Zope product authors are the change in the default accessibility rules for objects that are not
explicitly protected and the correct way to perform "manual" authorization checks.
Accessibility rules
In versions of Zope prior to 2.2, access to objects was granted or denied by a Zope security policy that was somewhat lenient - the
intent of this leniency was to make things a bit easier for product authors. The old security policy took the position that objects
that were not specifically protected by permissions or the explicit association of an __roles__ attribute were accessible if the
parent of the object was accessible. This policy put the responsibility for making sure that sensitive objects and methods were
protected on the object author.
It is now clear, however, that this default policy is flawed. People make mistakes after all, and having a default policy that allows
access leads to security holes. Under the old policy it was too easy to forget to protect an object or method in a product (or in Zope
itself). Worse, it was not obvious that there was a problem under the old policy - unless you went specifically looking for it, you'd
never realize it was there.
The new Zope security policy in 2.2 by default denies access to objects that are not explicitly protected. This forces product
authors (and Zope core developers) to take explicit action to make object or methods accessible. It also has much better
consequences when an author forgets to protect a needed method - you get a bug report instead of a security hole since the
unprotected object will throw an Unauthorized error when users try to access it.
Updating products for 2.2
It is inevitable that there are some products out there that depend on the default behavior of the old Zope security policy - they
have objects or methods that are not explicitly associated with a permission and do not have explicit __roles__ assigned. Under
the old model this was not a problem, but in Zope 2.2 and above these unprotected objects will cause authorization prompts when
users attempt to access them from the Web, DTML or other Zope objects that enforce the security policy.
The best way to ensure that your products will work in Zope 2.2 is to ensure that all of the methods of your objects that should be
accessible from the Web or DTML are associated with an appropriate permission. Take the following example class from a
"Spam" product:
class Spam(OFS.SimpleItem.Item, RoleManager):
"""A Spam object"""
meta_type='spam'
manage=manage_main=HTMLFile('manageSpam', globals())
icon='misc_/Spam/Spam_icon'
__ac_permissions__=(
('View management screens', ('manage','manage_main') ),
('Change Spam objects',
('manage_changeSpam',)
),
('Use Spam objects',
('', 'taste',)
),
)
def __init__(self):
self.year=1950
http://www.zope.org/Documentation/How-To/ProductAuthorUpdateGuide?pp=1 (1 of 4) [05/07/2000 11:18:34]
Product author's guide to Zope 2.2+ security
def manage_changeSpam(self, year=1950, REQUEST):
"""Change the vintage of this spam"""
self.year=year
return REQUEST.RESPONSE.redirect(REQUEST['URL1']+'/manage_main')
def taste(self):
"""Taste the spam"""
return 'yeccch...'
def color(self):
"""Return the color of the spam"""
if self.year > 1990:
return 'pinkish'
return 'greenish'
Note the __ac_permissions__ structure of the Spam class. We see that the management screens are protected by the View
management screens permission, the mutator method is protected by Change Spam objects and general usage is
protected by Use Spam objects. But notice that the color method is not explicitly protected. Under the old security policy
this would not have been a problem, but under the new policy anyone that uses:
<dtml-with MySpam>
<dtml-var color>
</dtml-with>
in a web page will find that they get an authorization prompt under Zope 2.2. The solution is to add color to the Use Spam
objects permission declaration in __ac_permissions__.
This is by far the "preferred" way to handle this. In rarer cases a product author may need to make explicit protection declarations
instead of associating an object or method with a permission. For example, in rare cases you may have a method that should
always be accessible. To achieve this, the author needs to associate an __roles__ with the object equal to None. If the object is a
method, you can't set an __roles__ attribute on it directly, so you define methodName__roles__ = None in the class where
the method is defined. For example, to make the color method always accessible in our example above, we would use:
def taste(self):
"""Taste the spam"""
return 'yeccch...'
# make color always accessible
color__roles__=None
def color(self):
"""Return the color of the spam"""
if self.year > 1990:
return 'pinkish'
return 'greenish'
In other cases, you may want to make sure that an object or method is always inaccessible. The easiest way to do this is to give the
object a name beginning with an underscore, for example _color. Sometimes this is not possible though, so an alternate way to
make an object inaccessible from the Zope security policy is to give it an __roles__ declaration equal to an empty list:
def taste(self):
"""Taste the spam"""
return 'yeccch...'
# make color never accessible, except to internal
# Python code.
color__roles__=[]
def color(self):
"""Return the color of the spam"""
if self.year > 1990:
return 'pinkish'
return 'greenish'
Finally, there may be cases where it is not possible or practical to associate an object with permissions or to make these role
declarations. This could happen for example with an External Method that returns some kind of dynamically created objects with
http://www.zope.org/Documentation/How-To/ProductAuthorUpdateGuide?pp=1 (2 of 4) [05/07/2000 11:18:34]
Product author's guide to Zope 2.2+ security
very dynamic attributes. In cases like this you can use a special declaration in the object which will tell the security policy to allow
access to unprotected items in this particular object. For instance, lets say you have an External Method that returns instances of a
Thing class that you have created. You want to be able to call the External Method from DTML and iterate over the 'Thing's
returned to make a report. Your Thing class is not a Zope class of any kind, so it doesn't know about permissions or anything else
about the Zope security model. To be able to access the methods and attributes of your 'Thing's from DTML you will need to add
the special declaration to the Thing class for the security model to allow you access to those unprotected methods:
class Thing:
# This tells the security policy that its ok to use attributes
# of this object even though they aren't explicitly protected
__allow_access_to_unprotected_subobjects__=1
def name(self):
return self._name
Product authors should be very careful about using this special declaration as all subobjects not explicitly protected will now be
accessible. Note when you add this security assertion that all unprotected attributes in the base classes of the object will be
affected as well.
Product authors should be aware that the SimpleItem.Item class (that is used as a common base class for most Zope objects) has
this security assertion. This is why "dynamic" attributes such as properties continue to be accessible as in pre-2.2 versions of Zope.
This means that product authors must continue to be careful about protecting potentially dangerous methods in their classes.
Your first reaction might be (as mine was) "well, doesn't that just put us right back where we were before?". Not quite. What has
been done is a first step to changing the policy to deny- by-default rather than allow-by-default. Having the assertion in the Item
class has the effect of:
●
●
allowing access to properties and some other kinds of attributes that are not currently explicitly protected, needed for
backward compatibility
DISallowing access to certain other things that the old security rules would have allowed - for example under the old
rules alone it was possible to get to the func_globals and other attributes of methods that you really shouldn't have access
to. We had to handle that with special cases, which was painful and error prone (and only worked for problems that you
knew about).
The new policy with the security assertion allows us to keep access to properties and things we need access to for backward
compatibility, but also has the effect of protecting things like method attributes and other (possibly unknown) bits that should be
off limits (a method would need a security assertion of its own for those things to be accessible).
While this is not totally perfect and still requires you to be careful about protecting attributes of base classes, it is better than it was
before and a first step on the road to where we want to be that shouldn't cause too much angst among users and product
developers.
Manual authorization checking
A second area where changes to the Zope security model could affect product developers is in the area of "manual" authorization
checks. In most cases, Zope handles authorization chores in a way transparent to product developers. In certain cases however,
product authors want to make manual authorization checks.
An example of this is in the file lib/python/OFS/Folder.py in the Zope core. Folder objects have options on their add forms to
create a UserFolder and an index_html document automatically when the Folder is created if the user so desires. The
constructor for Folder objects (manage_addFolder, in lib/python/OFS/Folder.py) has a problem. The constructor method itself is
covered by the Add Folders permission, so standard Zope security manages access to that. But once the method is called, the
constructor needs to find out if the user submitting the form is actually allowed to add UserFolders and DTMLDocuments if the
user elected to have these created automatically.
Under the old security model, the code in manage_addFolder had to know how to get the current user from the web request
and then try to see if the user had the right permissions to perform the operation:
try: user=REQUEST['AUTHENTICATED_USER']
except: user=None
if createUserF:
if (user is not None) and not (
user.has_permission('Add User Folders', self)):
raise 'Unauthorized', (
'You are not authorized to add User Folders.'
http://www.zope.org/Documentation/How-To/ProductAuthorUpdateGuide?pp=1 (3 of 4) [05/07/2000 11:18:34]
Product author's guide to Zope 2.2+ security
)
The problem of course, is that these hard-coded checks will be hard to maintain if the security policy changes (and we have seen
that it does from time to time). The new security architecture in Zope 2.2 provides a SecurityManager object that can be used
to perform manual authorization checks without product code having to know too much about security policy. The
AccessControl module exposes a function getSecurityManager that will return a SecurityManager object that can
be used to do manual checks. For example, Folder.py now uses:
from AccessControl import getSecurityManager
checkPermission=getSecurityManager().checkPermission
if createUserF:
if not checkPermission('Add User Folders', ob):
raise 'Unauthorized', (
'You are not authorized to add User Folders.'
)
Now you only need to get a SecurityManager and call the checkPermission method on it, passing the name of the
permission to check and object that you are checking. The SecurityManager handles the details.
Summary
Many product authors may not be affected at all by the security policy changes in Zope 2.2. If you have a product where all
methods and subobjects that should be accessible are protected by permissions and you don't do any manual authorization checks,
you are probably not affected by the changes. For those products that are affected, you should use the above guidelines to update
your products for Zope 2.2.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/ProductAuthorUpdateGuide?pp=1 (4 of 4) [05/07/2000 11:18:34]
Programming ZopeLDAP
How-To: Programming ZopeLDAP
Created by jshell. Last modified on 2000/06/19.
1. Getting an Entry Object and its attributes
The best way to get an Entry object is by using an LDAP Filter Method. LDAP Filter Methods return a sequence of Entry objects,
so they can be used with any sort of looping structure (ie: the dtml-in tag, Python for loops). For example, if you had an
LDAP Filter Method named getEntriesWithSN with the attribute 'sn' (the common LDAP attribute for a persons Surname),
you could use it in DTML like this:
<dtml-in expr="getEntriesWithSN(sn='shell')">
Surname: <dtml-var name="sn"><br />
Common name: <dtml-var name="cn"><br /><br />
</dtml-in>
Or in Python like this:
entries = getEntriesWithSN(sn='shell')
for entry in entries:
print "Surname: %s" % entry.sn
print "Common name: %s" % entry.cn
Entry objects have the method get(attr) which can also be used to retrieve attributes based on a name lookup.
1.1 Notes about attributes and entry objects
By default, LDAP always returns its attributes as sequences (Python lists), even if there is always one value. ZopeLDAP Entry
objects use a special class AttrWrap when returning attributes accessed through normal __getattr__ (the a.b syntax). AttrWrap
behaves and acts like a normal Python list with the exception that when printed as a string (ie, with dtml-var or Python str()
or %s), it prints the results as a comma seperated list. When the Entry get() method is used, the attribute is returned the same way
the PythonLDAP returned it.
2. Setting attribute values
ZopeLDAP tries to hide as much of general LDAP programming as possible by making the Entry objects act as Pythonic/Zopish
as possible. ZopeLDAP "fakes" transactions to try to insure at least some integrity with the state of other objects in the ZODB and
any ZRDB (SQL Methods) usage. The transaction support is not perfect, but it does work well enough to get most jobs done.
2.1 Setting permissions
ZopeLDAP Entry objects have always been programmable, and as of ZopeLDAP 1.0b3 they (finally!) play nice with the Zope
security machinery (you can protect the methods used to modify and create new entry objects by setting the right permissions).
These permissions are:
Manage Entry Information
Protects the methods used to add/edit attributes.
Create New Entry Objects
Protects the methods used to add new subentries in the directory to an entry object.
These permissions can be set anywhere in Zope, and should best be set in the folder containing any methods that would make calls
to these objects and/or any folders containing the LDAP Filter Methods.
2.2 Setting values in Python
This applies to disk-based Python, such as External Methods or Python based products. Or anywhere that security is less of a
concern and certain methods are not protected.
The main method that can be used in Python is _set(attr, value). This method exists on all Entry objects, it comes from
the base class GenericEntry which all writable Entry objects inherit from. A simple task might be adding/changing an attribute
names 'zoperoles' which might be a sequence of Zope roles a Person object might have stored in the LDAP Directory. To set the
attributes for all users with the surname of shell (using the example LDAP Filter method above), it could look like this:
http://www.zope.org/Members/jshell/programmingZopeLDAP?pp=1 (1 of 2) [05/07/2000 11:18:38]
Programming ZopeLDAP
shells = getEntriesWithSN(sn='shell')
for shellPerson in shells:
shellPerson._set('zoperoles', ['Editor', 'Member'])
The legal values passed into _set are:
attr
String, name of the attribute to set.
value
String or Python list of values for this attribute.
2.3 Setting values with DTML Scripting
This applies to through-the-web Python methods as well, and can be used in disk based Python code as well.
The main methods useful for setting values are manage_addAttribute(id, values), manage_changeAttributes(**kw), and
manage_editAttributes(REQUEST). They all fundamentally do the same thing, but have slightly different signatures and target
behaviors.
manage_addAttribute(id, values)
Add a new value (or replace existing value) for the attribute 'id' with the values passed in. values should be a string or a
Python list.
manage_changeAttributes(**kw)
Uses keyword parameters to set attributes. For example: entry.manage_changeAttributes(sn='Shell',
zoperoles=['Editor','Member']). If the attribute name doesn't exist, it gets added.
manage_editAttributes(REQUEST)
Useful for changing a lot of attributes with values from a web form. Will NOT add new attributes for names that it finds,
will only change attributes that the entry object already has.
To do the above example in the Python code using DTML Scripting, it would look like this:
<dtml-in expr="getEntriesWithSN(sn='shell')">
<dtml-call expr="manage_changeAttributes(zoperoles=['Editor','Member'])">
</dtml-in>
3. Adding new entries
Will be covered in the future as a new little buglet has just been detected in 1.0b3. (more of an annoyance really.
manage_newEntry() doesn't return the entry object, making it hard to get ahold of).
4. Other tricks with Entry objects
Entry objects contain built in support for the Zope tree-tag protocol. They can be used relatively easily with the Zope tree tag. If
you have an LDAP Filter Method that is guaranteed to only return ONE Entry object, you can make a tree of all of its subentries
like so:
<dtml-in expr="getUniqueObjectWithUID(uid='somethingUniqueHere')">
<dtml-tree sequence-item><dtml-var name="dn"></dtml-tree>
</dtml-with>
Entry objects also support objectValues(), objectIds(), and objectItems(). None of these methods are filterable
(unlike traditional ObjectManager objects). They return objects, strings, or string/object pairs from the Entries sub-entries in the
tree.
Subentries are also traversable through __getitem__ using their RDN (Relative Distinguished Name). This could look like (in
Python):
entry['ou=digicool']['uid=jshell']
This would navigate down two levels to the uid=jshell entry contained in the ou=digicool entry.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jshell/programmingZopeLDAP?pp=1 (2 of 2) [05/07/2000 11:18:38]
Recovering Corrupted Data.fs ZODB files
How-To: Recovering Corrupted Data.fs ZODB
files
Created by itamar. Last modified on 2000/05/02.
Warning: This can trash your Zope installation. Only work on copies of your Data.fs, and make sure you have backups!
Every once in a while you'll corrupt your Zope Data.fs file, and Zope won't start. Since the Data.fs tacks transactions on to the
end of the Data.fs file, recovering the Data.fs is most likely as easy as deleting the last transaction or three from the Data.fs.
Here's how to do it, using Ty Sarna's cool tranalyzer:
1. Make a backup of the Data.fs file!!!
2. Download Ty Sarna's tranalyzer.py from http://www.zope.org/Members/tsarna/Tranalyzer
3. Tranalyzer prints out the transactions in the Data.fs and their location in the file. So we'll run tranalyzer.py on the Data.fs
and see what we get:
[itamar@moriarity itamar]$ python tranalyzer.py Data.fs | tail -2000
Lets say the last result we got was:
TID: 331F256472BAFF7 @ 6191877 obs 1 len 7352 (status 'p') By
"/gl/harvest/balsamico//index.htm/manage_addProperty"
OID: 10160 len 7234
superuser
TID: 331F2566736A2E6 @ 6199245 obs 1 len 8983 (status 'p') By
"/gl/harvest/balsamico//cooking.htm/manage_addProperty"
OID: 10161 len 8863
superuser
This means the last transaction started at byte 6199245 and added 8983 bytes to the Data.fs. So lets get rid of
it and see if Zope starts working again. We'll truncate Data.fs so it's only 6199244 bytes long - that is, 6199245 - 1
bytes. That's because the 6199245th byte is already the beginning of the new transaction.
4. We'll use Python, of course, to do this:
Python 1.5.2 (#1, Feb 1 2000, 16:32:16) [GCC egcs-2.91.66 19990314/Linux (egcson linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> f = open("Data.fs", "ab")
>>> # we'll truncate Data.fs so it's length is 6199244 - that's 1 byte less
... # then where the next transaction starts.
...
>>> f.truncate(6199244)
>>> f.close()
That's it! If it still doesn't work, we'll get rid of the next to last transaction, till it starts working.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/itamar/CorruptedZODB?pp=1 [05/07/2000 11:18:40]
Removing ZClass Instances Programmatically
How-To: Removing ZClass Instances
Programmatically
Created by Mammux. Last modified on 2000/02/25.
This is really simple, but it complements the information in How-To: Adding ZClass Instances Programmatically nicely and even
though this function is mentioned once in the documentation, it's not documented.
To delete an instance, do something like this (if this is in a DTML method and the Id you want to delete is in the string delthisid):
<dtml-call "manage_delObjects(REQUEST['delthisid'])">
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Mammux/removeZClasses?pp=1 [05/07/2000 11:18:43]
Rewriting rules for Roxen and Zope.
How-To: Rewriting rules for Roxen and Zope.
Created by Bill. Last modified on 2000/01/05.
After looking at Roxen, I thought I would try it out with one of my sites. As per Magnus' How-To, I set it up.
That was the easy part. I have been running all of my zope sites through Zserver. Made it much easier on me (I still recommend
this for newcomers just playing with it). However, there were issues I had to face when 'converting' to PCGI. This document
attempts to expose these, along with how I got around it.
Installation
I followed the directions in the above referenced How-To and set it up so that zope was the root server. I then wanted all requests
to go through Zope initially, with Roxen picking up the rxml, etc. So, I dug into the Redirector Module.
Getting Zope.cgi out of the URL
This was the difficult part.
The redirector module is like unto Apache's mod_rewrite. One advantage is that you can change the rules to your enjoyment w/o
restarting your server. Made it much easier to pin down. I eventually did the following (these steps are in reverse order than what I
did, this is easier ... trust me):
When converting from ZServer, there is no Zope.cgi in the URLs in the Data.fs db. This was an issue. I eventually discovered that
if I put:
SCRIPT_NAME=
as the last line in the Zope.cgi file, I would not have Zope.cgi in the URL anywhere.
The ReWrite Rules
I originally thought I wanted everything to be rewritten to zope. Turns out some of the features in Roxen require urls, go figure :-).
After using Roxen and Zope now for a while, I noticed some problems. Namely, my rules didn't always work! Sometimes, they
would work until the server restarted.
So, now I have two methods. The first is the preferred method (according to me :-)
In the Roxen config interface, go to the redirect module fo rthe server you are working on. The go to the redirect lines screen. In
the textbox, enter the following:
Preferred ReWrite Ruleset
^/([A-Z])(.*) /Zope.cgi/$1$2
^/([a-z])(.*) /Zope.cgi/$1$2
^/([0-9])(.*) /Zope.cgi/$1$2
What They Do
Well, they should be obvious to anyone who uses regex, but for the rest of the world, what is happening is that anything that starts
with an alphabetical character, or a numeral, will be rewritten to use zope. Note: I haven't tried the numeral match yet, so let me
know if it doesn't work for you.
Why this ruleset? When I first tried it, everything went to zope. Well, if you want to use some of the cool stuff that Roxen can do,
you don't want to do that.
http://www.zope.org/Members/Bill/Documentation/RoxenZope?pp=1 (1 of 2) [05/07/2000 11:18:46]
Rewriting rules for Roxen and Zope.
Roxen Internal Mountpoint Configuration
Various tags/modules in Roxen mount the filespace in various places. Here is a list of the ones that I have found so far.
●
Config-tabs
●
gtext
●
Explicit Clock
●
Graphical Counter
●
Maybe more (email me if you find them )
What I did was to change their mountpoint to start with an underscore. Seems to work fine so far. Note that gtext doesn't have it's
own mountpoint config option. This is because it uses the server's internal mount of _internal.
For some modules, such as IMHO, this URL would actually be seen by the user. The ones in the list so far are not normally seen
by the user. For those that would be, you may be able to set up a special Redirect rule and it may work. Note, if you do, indeed,
elect to do so, the rule MUST come before the rewrites to have a chance. As you will see below, this is the method I previously
used. You will note it is this method that eventually failed for me, ymmv.
Alternate Method
The following rules dont require any additional configuration of Roxen, but may not work that well. They are here as an option, as
well as historical reasons (ie. to keep people from trying them, having them fail, and wonder why :)
Alternate Rewrite Rules
●
/_internal/ /_internal/
●
/configtabs/ /configtabs/
●
^(.*) /Zope.cgi$1
The following rules also work:
●
^/_internal/(.*) /_internal/$1
●
^/configtabs/ /configtabs/
●
^/(.*) /Zope.cgi/$1
"But you changed the SCRIPT_NAME, why is the
Zope.cgi line still there?"
I knew you were going to ask that. Zope stuff still has to be routed through the Zope.cgi file. The SCRIPT_NAME only changes
the building of self-referential URLs ... like the Zope Management screens.
Optional Methods for Related Stuff
So, what about if you want your Zope URLs to have Zope (or Objects) in the URL and not have to rewrite Roxens internal
references (there may be more than what I have found so far)? Simple.
First, you want the CGI module's mountpoint to be whatever it is for your zope urls, such as: /Objects/ and change the rewrite rule
above to include /Objects/ as such:
^/Objects(.*) /Zope.cgi$1
and it should work. I have not tried this out yet, ymmv.
Questions/Comments? Let me know.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Bill/Documentation/RoxenZope?pp=1 (2 of 2) [05/07/2000 11:18:46]
Roxen Challenger and Zope
How-To: Roxen Challenger and Zope
Created by magnus. Last modified on 1999/11/06.
This document explains how to get the webserver Roxen Challanger and Zope to work together.
See this for Roxen server specifications.
Prerequisites:
This is the packages and setup that I use:
●
Redhat 6.1
●
roxen-1.3.121-10mdk.i586.rpm
●
Zope-2.0.1-src.tgz
●
python-1.5.2-7.i386.rpm
●
python-devel-1.5.2-7.i386.rpm
●
python-docs-1.5.2-7.i386.rpm
●
python-tools-1.5.2-7.i386.rpm
Both Roxen and Zope are available for many many more platforms.
Installation
1. Install all python rpm packages:
$ rpm -i filename
2. Install the Roxen Challanger rpm.
$ rpm -i filename
3. If apache or anything else is running on port 80, stop it now.
4. Start Roxen Challenger:
$ /etc/rc.d/init.d/roxen start
Roxen is now up'n'running. Try to access it on https://localhost:13121
This is the configuration interface for Roxen. Login.
5. If you want to serve several websites from this machines IP-number, do the following:
1. Click on the virtual server that is already created for you.
2. Click on the "More options" icon.
3. Click on the "Add module" and choose the "IP-Less virtual hosting" module. Click on save.
Else, enter the "Server variables" section and add a port and ip-adress in "Listen ports".
6. Create a new virtual server in Roxen called Zope.
7. In the "Server variables" section, set "Server URL" to the DNS name that you want to use for this website.
8. Add and delete modules until you get the following:
Status and debug info
Server variables
CGI executable support
PATH_INFO support
Content types
Graphics text
Main RXML parser
Index files only
9. Set "Index files" in the "Index files only" module to "Zope.cgi".
10. Unpack Zope in some directory. I am using /home/roxen/
http://www.zope.org/Members/magnus/Roxen?pp=1 (1 of 3) [05/07/2000 11:18:49]
Roxen Challenger and Zope
$
$
$
$
$
$
$
$
$
su cd /home/roxen
tar zxvf /path/Zope-2.0.1-src.tgz
mv Zope-2.0.1-src Zope-2.0.1
chmod -R nobody:nobody Zope-2.0.1
su - nobody
cd Zope-2.0.1
python w_pcgi.py
chmod 755 Zope.cgi
Zope is now compiled. Remeber or change (zpasswd.py) the userid and password that was created for you.
11. Go back to Roxens configuration interface and the "CGI executable support copy 0" module:
You should change it to look like this:
Builtin variables (security, comments etc.)
Allow listing of cgi-bin directory : Yes
Allow symlinks : Yes
CGI-bin path : /
CGI-script extensions : cgi
Extra environment variables : Empty
Handle *.cgi : Yes
Limits...
Log CGI errors to... : main log file
Parse RXML in CGI-scripts : Yes
Pass environment variables : No
Provide the tag : Yes
Raw user info : Yes
Run scripts as :
Run user scripts as owner : Yes
Search path : /home/roxen/Zope-2.0.1/
Send decoded password : No
Set the supplementary group access list : Yes
Treat non-executable files as ordinary files : Yes
Warn for CGIs executing as root : Yes
12. Start Zope. I have a simple initscript that runs /home/roxen/Zope-2.0.1/start
For now, do the following:
$ su $ /home/roxen/Zope-2.0.1/start
13. Try to access the site using the name you set in "Server variables".
14. Tada! You sould now see the "Welcome to Zope" page. Login.
15. Now you should be able to use Roxens modules in Zope too. Try to add this in a Zope DTML-method or something:
<gtext>This is a test!</gtext>
View the DTML-method. "This is a test" should be rendered as a gif image using Roxens "Graphical Text" module.
There are more Roxen modules that you probably can make greate use of; obox, tablify, diagram etc. Add them from
Roxens configuration interface and try them out!
Next step
Now, you probably don't want to have "Zope.cgi" in the URL. Read Bill's "How-To: Have your Zope and Roxen too.", and your
problems will be solved.
Troubleshooting
Question: I can't login to Zope!
The rpm that I use put all logfiles in /etc/roxen/logs/
$ tail -f /etc/roxen/logs/debug/etcroxenconfigurations.1
Try to login to Zope. If you get this output from Roxens log
http://www.zope.org/Members/magnus/Roxen?pp=1 (2 of 3) [05/07/2000 11:18:49]
Roxen Challenger and Zope
CGI: Failed to get user information for
Make sure that the "Raw user info : Yes" is correct in the "CGI executable support copy 0" module.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/magnus/Roxen?pp=1 (3 of 3) [05/07/2000 11:18:49]
Running Zope off CD ROM
How-To: Running Zope off CD ROM
Created by jens. Last modified on 2000/03/28.
This HowTo describes how to make a "Demo" Zope installation that can be burned onto a CD ROM and runs from the CD itself
without installing anything onto the hard drive.
I tested this using the Zope 2.1.2 source distribution and the windows precompiled binaries. It is guaranteed to work with
2.1.3/2.1.4 as well. If you use something like the RPMs found on some Linux distribution CDs your mileage may vary.
●
●
Take either an existing Zope installation tree or create a new one and create whatever demo app you wish to have in it.
Once you're done you should pack the database. This is not a "must" but do you want users of your demo to see a huge
"undo" list when they browse the "undo" page?
●
Shut down Zope
●
In the Zope root directory create a file with the name "custom_zodb.py" and the following contents:
import Globals, ZODB.FileStorage, ZODB.DemoStorage
name='%s/Data.fs' % Globals.data_dir
base=ZODB.FileStorage.FileStorage(name, read_only=1)
Storage=ZODB.DemoStorage.DemoStorage("Demo (%s)" % name, base)
●
●
●
Modify the "start" script in the Zope root folder so that z2.py is called with the "-r" flag, which coincidentally means
"read-only".
Test this setup by running the modified start script. You will see that all log messages are now going to the command line
from where you ran it. This also means that while the demo runs you need to leave this window open.
As the last step, transfer the Zope install tree onto CD using the CD burning program of your choice. Make sure you
enable long filenames by selecting Joliet extensions.
Under Windows you can use the dreaded CD autorun to have the server start automatically after the CD has been inserted. For this
to work you need to create a file named "autorun.inf" in the CD root directory that contains something like...
[autorun]
OPEN=<path_to_my_start_script>
In my case it also pointed to an icon file which will show up in the Explorer window:
[autorun]
ICON=\CD.ICO
OPEN=RunDemo.BAT
Since startup off CD takes longer than off the hard drive I "embellished" my setup by calling a .bat file from autorun.inf (see
above).
This batch file opened a DOS command line to start the server and popped up a web page with info:
@Echo Off
start begin.htm
Echo:
Echo:
Echo:
Echo After the server is loaded please open your preferred web browser and
Echo go to "http://localhost:8080" or double-click on the "Begin.htm"
Echo file on your CD.
Echo:
Echo:
Echo:
Echo:
Echo Please be patient, initialization may take a few minutes.....
"windows\bin\python.exe" "windows\z2.py" -r
http://www.zope.org/Members/jens/docs/zope_on_cdrom?pp=1 (1 of 2) [05/07/2000 11:18:52]
Running Zope off CD ROM
Make sure to change paths to reflect your particular setup.
UPDATE: Maurice Davis provided some welcome feedback on improving this setup for running under Windows. He added one
line to RunDemo.BAT just before the line that calls python:
set INSTANCE_HOME=.
In addition, his autorun feature did not work with this setup. He ended up using a freeware autorun.exe. This can be dropped into
the CDs root directory, but make sure to edit the autorun.inf file to reflect your specific autorun's requirements.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jens/docs/zope_on_cdrom?pp=1 (2 of 2) [05/07/2000 11:18:52]
Running Zope on BeOS
How-To: Running Zope on BeOS
Created by rbickers. Last modified on 2000/03/02.
Overview
This document describes setting up and using Zope (http://www.zope.org/) on BeOS. I wouldn't run this on BeOS to do any real
serving, especially with BeOS's minimal select() support (see below).
It should also be noted that NetPositive doesn't behave with the Zope management interface for whatever reason. However, Opera
does just fine.
Requirements
This has only been tested on BeOS Intel (4.5.2) with Zope 2.1.2 and without PCGI, so if you're running anything else, you may
have problems. PCGI doesn't compile and I haven't tried to get it to since my use of Zope on BeOS is only for testing Zope
products during development. Since Zope 2.x requires Python 1.5.2, you'll need to have Python installed. However, as of this
writing, the binary distribution of Python for BeOS has a broken select(), so you'll need to either compile Python from the sources
or download my binaries.
Compiling Python 1.5.2
Python compiles cleanly on BeOS. However, in the 1.5.2 release (and in the BeOS binary distribution available on the BeWare
site) there is a problem with Modules/selectmodule.c which needs to be patched. You can download the patch, place it in
Modules/ and apply it as follows:
$ patch < selectmodule.c.patch
Note that revision 2.30 and later from the Python CVS repository includes the patch.
After applying the patch, follow the directions in BeOS/README carefully to complete the installation.
Zope also requires FCNTL.py which can be created by running h2py.py in the Tools/scripts directory as follows:
$ python h2py.py /boot/develop/headers/posix/fcntl.h
Then copy the resulting FCNTL.py file to /boot/home/config/lib/python1.5/.
The problem with select() on BeOS and a workaround for Zope
BeOS has limited support for select(). Very limited. It only supports sockets (not files or pipes or anything else), and it only works
for "reads", which means you can only check a socket to see if there is incoming data, not outgoing. Unfortunately, Zope uses
select() for reads and writes to sockets in ZServer/medusa/asyncore.py as well as a pipe in ZServer/medusa/select_trigger.py.
There is support in select_trigger.py to use a loopback socket instead of the pipe. So with a small patch to get BeOS to use the
non-posix code, the lack of pipe support is no longer a problem.
However, I haven't found a clean fix for the lack of "write" support, and since poll() isn't supported at all in BeOS, we can't use
that. So instead of a clean fix, there's a dirty, but usable, workaround. Since select() will never tell Zope there's something to send,
we have to bypass select() for any outgoing data and just attempt to send every time. Fortunately, attempting to send if there really
isn't anything to send will simply be ignored by Zope. Another problem is that anything waiting to be sent will not go out until
either the select() timeout is reached (set to 30.0 seconds by default) or there is something else to read. To avoid this, we can set
the timeout to some ridiculously low number, like 0.1 seconds. That's a lot of unnecessary work every 0.1 seconds, but with Pulse
and other tools the load isn't noticable. Changing it to 0.0 seconds did put a strain on the CPU, so don't do it.
It's rumored that Be will provide more complete support for select() in an upcoming release, so all of these issues will go away and
Zope should compile without modifications. Until then, however, this is a reasonable solution for using BeOS for developing
Zope products. If for some reason you're using BeOS as a server, I would wait for a better select().
http://www.zope.org/Members/rbickers/Zope_on_BeOS?pp=1 (1 of 2) [05/07/2000 11:18:55]
Running Zope on BeOS
Compiling Zope
With better select() support, Zope would compile on BeOS without modifications. However, not living in a perfect world, we're
left with this workaround.
To compile Zope you need to have the Python sources and you need to modify /boot/home/config/lib/python1.5/config/Makefile.
Change LDSHARED to point to BeOS/linkmodule in your Python source tree, and change LINKCC to point to BeOS/ linkcc in
your python source tree. By default they're relative paths that don't work when compiling Zope.
There are three files that need to be patched. Copy select_trigger.py.patch and asyncore.py.patch to ZServer/medusa/ and apply
them as follows:
$ patch < select_trigger.py.patch
$ patch < asyncore.py.patch
Copy z2.py.patch to the top level source directory and apply it as follows:
$ patch < z2.py.patch
These patches are tiny, and serve only as the select() workaround.
From the top level source directory, run 'python wo_pcgi' to compile Zope.
You should now be able to modify 'start' as you wish and run Zope.
Binaries
For those that would rather not tinker so that they may be productive instead, I've made available a binary Python 1.5.2 (includes
the select() patch and FCNTL.py), as well as a precompiled Zope 2.1.2 source tree with the above modifications. You can unzip
Python in your /boot/home/config directory. You can unzip Zope anywhere you like, modify 'start' as you wish, and have at it.
Comments/Questions
If you have any questions or comments, especially better ways to work around the select() problems, please let me know at
rbickers@logicetc.com.
Enjoy!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rbickers/Zope_on_BeOS?pp=1 (2 of 2) [05/07/2000 11:18:55]
Sending email attachments (external method)
How-To: Sending email attachments (external method)
Created by rossl. Last modified on 2000/04/11.
I'm working with Piers Lauder's Survey product. One user
wanted output in excel, so I decided to email it. Here's
a snippet of code from a class in a python external method which
illustrates:
1. use of base64 encoding
2. use of boundaries to construct a multipart mime message
3. tricking excel by using a .xls extension on a tab delimited file!
It does NOT use a zope mailhost (code snitched from somewhere on the python
starship!). Obviously, you'll need to hack this code
to fit your application, but it works in production here and
office97 excel seems happy. Note that the boundary string
(bstring) is arbitrary but must be consistent within each
mail message.
==snippet of a python class======
import string, smtplib, base64
fromaddr = 'zope@your.server.au'
mailserver = 'your.smtp.server.au'
def email(self):
'''
email surveyoutput to useremail
self.res contains tab delimited data
first line is column names
subsequent lines are survey data
this is part of a class which sets up lots of
things at initialisation like self.surveyoutput etc
'''
dashes = '----'
errlog = []
res = base64.encodestring(self.surveyoutput)
bstring = dashes + base64.encodestring(self.useremail)[:-1] # ? trailing
return !!!
mailbody = """To: %s
From: %s
Subject: Survey dump for excel importing
X-Mailer: zope on sin, smptplib via dumpsurveytosdf.py - ross lazarus
X-Accept-Language: en
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="%s"
This is a multi-part message in MIME format.
If you can read this, you need a new email reader
--%s
Attached is a tab delimited dump of the %s survey.
It should be possible to read this into excel by opening it.
If necessary, save the file somewhere and try opening it in a spreadsheet.
Yours,
the zopebot
--%s
Content-Type: application/msexcel; name="%s.xls"
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename="%s.xls"
%s
--%s-""" %
(self.useremail,self.fromaddr,bstring,bstring,self.surveyId,bstring,self.surveyId,self.surveyId,res,bstring)
server = smtplib.SMTP(self.mailserver)
errdict= server.sendmail(from_addr = self.fromaddr,to_addrs=[self.useremail,],
msg=mailbody)
server.quit()
if len(errdict) != 0:
errlog.append('Not all addresses accepted')
errlog.append('Failed addresses:')
errlog.append(str(errdict.keys()))
return errlog
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rossl/emailattach?pp=1 [05/07/2000 11:18:58]
Serve PHP/Perl within Zope
How-To: Serve PHP/Perl within Zope
Created by Mamey. Last modified on 2000/03/28.
As you migrate your site to Zope, you may want to embed a previously created PHP or Perl directory tree within Zope. Or perhaps
you found a nifty web application that is coded in PHP/Perl and want to serve it within your Zope site.
This differs from the intent of the Serving Zope and other content from Apache How-To written by Michel Pelletier. In that
How-To, you are using Apache RewriteRules to bypass Zope and serve the PHP/Perl tree directly to the user. This How-To deals
with the case where you want to serve that tree within Zope so you can achieve a consistent look to your site. That is, you want to
wrap the PHP/Perl output with <standard_html_header> or whatever other dtml code you want.
The solution presented here uses three Zope resources: (1) the SiteAccess product by Evan Simpson of 4AM Productions Inc., (2)
the tip presented by Loren Stafford in Using ZClient to Access Another Server and (3) Pelletier's How-To.
You'll also need a web server that can serve Zope as well as the PHP/Perl tree. We use Apache, and that is what this How-To will
use as an example.
So let's begin. We have our wonderful Zope site located at www.mysite.com. Within our site, we want to embed a
PHP_Nifty_Tree. So let's create a folder "PHPContent" so users can go to www.mysite.com/PHPContent.
Install the SiteAccess product in your Zope source tree. And after restarting Zope, go to the PHPContent folder and add a SiteRoot
object with the path set to "/PHPContent".
We now want to attach an AccessRule to the folder that will transparently redirect users to the PHP tree. The solution offered here
is based on the assumption that the PHP tree will have relative links to other php scripts within it. However, we want the user to
always end up within the index_html object, so we'll strip any requests for those relative links from REQUEST.path and pass the
original request as a REQUEST variable to index_html.
Begin, by placing the following script, DirectToPHPContent.py, within the Extensions folder of your Zope source tree:
from string import join
def DirectToPHPContent( self ):
if self.REQUEST.path:
# Build up a list of what is contained in self.REQUEST.path since
# you are going to redirect the user to /PHPContent/index_html. Start
# by adding the way you can access the PHP tree outside of Zope
originalRequestPath = ['http://www.mysite.com/internalPathToPHPtree']
# is the user asking for a php script?
if self.REQUEST.path[0][-5:] == '.php3':
# pop paths of REQUEST.path until its empty
while self.REQUEST.path:
# Remember the path components before you pop them
originalRequestPath.append( self.REQUEST.path[-1] )
self.REQUEST.path.pop()
# Create a full url to pass to index_html
phpScript = join( originalRequestPath, '/' )
# Is there a query string being passed to the script also?
if self.REQUEST.environ['QUERY_STRING']:
phpScript = phpScript + '?' + self.REQUEST.environ['QUERY_STRING']
# self.REQUEST.path is now empty so the user is going to be
# directed to /PHPContent/index_html. That method needs to
# know what the original request is, so pass it a REQUEST
# variable
self.REQUEST.set('phpScript', phpScript)
# you also may want to redirect image requests to the PHP tree
elif self.REQUEST.path[0][-3:] in ['gif', 'png']:
while self.REQUEST.path:
originalRequestPath.append(self.REQUEST.path[-1])
self.REQUEST.path.pop()
http://www.zope.org/Members/Mamey/PHP?pp=1 (1 of 2) [05/07/2000 11:19:01]
Serve PHP/Perl within Zope
# This is an image, so give it to a DTMLMethod that will
# just serve up images
self.REQUEST.path.append('ServeImageInPHPTree')
self.REQUEST.set('imagePath', join(originalRequestPath, '/'))
Now add an ExternalMethod object to /PHPContent that points to the script above, let's be redundant and call it
DirectToPHPContent. To finish, use the "Set Access Rule" method and give it the DirectToPHPContent id.
So what have we done so far? Any requests to /PHPContent/whatever/script.php3 are going to be redirected to
/PHPContent/index_html. In addition, any requests to /PHPContent/whatever/image.gif will also be redirected to the method
ServeImageInPHPTree that we still have to write. Before we do that, let's implement Stafford's tip within
/PHPContent/index_html.
We begin by adding another ExternalMethod object, ServePHPContent. Simplifying Stafford's tip somewhat,
ServePHPContent.py in the Extensions folder will be of the form:
from ZPublisher import Client
def ServePHPContent(url="http://www.mysite.com/internalPathToPHPtree", username=None,
password=None):
return Client.call(url,username,password)[1]
We can now write within /PHPContent/index_html the following code:
<standard_html_header>
.
.
.
<dtml-if "REQUEST.has_key('phpScript')">
<dtml-var "ServePHPContent(REQUEST['phpScript'])">
</dtml-if>
.
.
.
<standard_html_footer>
To finish off, we add the ServeImageInPHPTree DTMLMethod with the single line:
<dtml-var "ServePHPContent(REQUEST['imagePath'])">
You will probably also want to read Pelletier's Apache How-To to learn how the calls to
http://www.mysite.com/internalPathToPHPtree made by Zope can be correctly passed to the PHP tree and not to Zope!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Mamey/PHP?pp=1 (2 of 2) [05/07/2000 11:19:01]
Serve an external files directly
How-To: Serve an external files directly
Created by LiDongfeng. Last modified on 1999/12/06.
I have a product which generatea graphics dynamically using an external program, the image files are written to the system's
temporary directory. I want to serve the images directly to the user without adding an Image object in the ZODB. This avoid one
annoying problem, i.e., dynamically adding objects often causes transaction conflict if more than one user is using the application.
The following python code support reading of an arbitrary PNG file from the server's local disk:
def get_external_image(self, filename, REQUEST, RESPONSE):
"""
Read and return an external file with appropriate response set.
Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ if not
os.path.exists(filename): return ""
fptr=open(filename, "rb") data=fptr.read() size=len(data) fptr.close()
RESPONSE.setHeader("Content-Type", "image/png") RESPONSE.setHeader("Content-Length", size)
return data
In order to include an external image in your DTML document/method, just use the following idiom:
<IMG SRC="get_external_image?filename=<dtml-var imgname url_quote>">
Other types of image/HTML file can be served similarly, just modify the content type code in the python function. You can
generalize the function to allow more content types.
This function can be used in a product, or as an external method(copy it to your Extensions directory under the Zope installation
root.)
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/LiDongfeng/external_image?pp=1 [05/07/2000 11:19:03]
Serving Zope and other content from Apache
How-To: Serving Zope and other content from
Apache
Created by michel. Last modified on 1999/09/15.
Serving Zope and other content from Apache
Note: This How-To assumes you have a working Zope and Apache system.
Zope works with a number of other programs, including web servers like Apache. Zope can serve it's own content through it's own
web server, or Apache can communicate with it over a certain protocol called PCGI. Using Zope and Apache with PCGI, you can
serve some of your sites content from Apache and some from Zope. This also gives you the very flexible and secure Apache to
work with as your front end.
The HTTP server that comes with Zope is designed to be used in a production in environment, but there is little chance of Zope's
HTTP server ever becoming as incredibly configurable and module as Apache. While Apache might be slower than using Zope's
HTTP Server, it is often useful to use Apache as the front end.
One particular usefulness is to serve many different content systems (like Zope) from the same site and server. For example, you
want:
http://www.zope.org/
to run Zope, but:
http://www.zope.org/mailman/listinfo/zope
to call the mailman cgi scripts (written in Python, but not zope components).
Further, you'd like:
http://www.zope.org/pipermail/
to point to the pipermail archives, which are files on the fileystem. Here is an example of the important lines from an Apache
config file to do these things. Keep in mind that these lines have to jive with your existing Apache config file and filesystem. If
you just blindly copy these lines into your http.conf file, it will never work for you. You have to understand how to configure a
working Apache to understand this:
<Directory "/home/mailman/cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
ScriptAlias /mailman/ "/home/mailman/cgi-bin/"
Alias /pipermail/ "/home/mailman/archives/public/"
RewriteEngine On
RewriteLog logs/rewrite_log
RewriteLogLevel 0
RewriteRule
RewriteRule
RewriteRule
RewriteRule
^/mailman - <a href="#l">[l]</a>
^/pipermail - <a href="#l">[l]</a>
^/cgi-bin - <a href="#l">[l]</a>
^/icons - <a href="#l">[l]</a>
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^/(.*) /usr/local/zope/Zope.cgi/$1
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
Note that the last rewrite rule of this example must be all on one line.
Apache evaluates RewriteRules in the order in which they are defined in the config file. In our example, RewriteRule
http://www.zope.org/Members/michel/HowTos/ApacheRewriting?pp=1 (1 of 2) [05/07/2000 11:19:06]
Serving Zope and other content from Apache
^/mailman - [l] will be evaluated first. This rule says "if the request begins with /mailman then do absolutely nothing".
The l flag means that no further rules should be processed and the request should proceed with this rule succeeding to rewrite
the request.
Since the rewrite rule succeeded, but did nothing, Apache continues the request starting with /mailman and thus matches the
ScriptAlias line which matches /mailman/ to a script directory, which Apache understands means to execute a CGI script.
The rule RewriteRule ^/pipermail - [l] drops Apache down to the Alias that maps /pipermail/ to a directory
on the filesystem.
Apache's RewriteEngine is very powerful, and is documented fully on the Apache home page.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/michel/HowTos/ApacheRewriting?pp=1 (2 of 2) [05/07/2000 11:19:06]
Session data stored in URL
How-To: Session data stored in URL
Created by Duncan. Last modified on 1999/12/09.
Using SQLSession with SiteAccess
This how-to describes how to use Anthony Baxter's SQLSession product in conjunction with 4am's SiteAccess product to store a
session id in the URL without the need to set any cookies.
To do this, I found it necessary to modify the SQLSession 0.2.2 product. These changes may be included in a future version of
SQLSession, otherwise they are described at the end of this document.
The examples below are all in a top level folder called Test accessed via the URL http://localhost:8080/Test,
obviously your own URL will be something different. The session id is appended to the above URL, and any subsidiary items
follow on at the end of the URL. In this example:
http://localhost:8080/Test calls the method dtNewSession which validates access and creates a new session id as
necessary.
http://localhost:8080/Test/doUtkMJ8$xua70jE7kM0Gg returns index_html in the Test folder
http://localhost:8080/Test/doUtkMJ8$xua70jE7kM0Gg/fields.html returns a document fields.html in the
Test folder.
Any attempt to use an invalid session id (or no session id) in the URL will throw a "Session Error" exception. For example
http://localhost:8080/Test/fields.html throws an error.
Instructions
First install the two products SQLSession and SiteAccess.
Create a SQL Session object. Set up a database as described in the SQLSession documentation. (Anyone who is foolish enough to
try to use a Microsoft Access database should note that it doesn't like fields called 'value' so you will need to change the queries
appropriately, I called the column 'val' in the sqlSetValue query and in the sqlGetValue query retrieved it with 'val AS [value]' so
that SQLSession would still find the right field.).
Create an access rule called 'dtProcessSession' with the following DTML and set it as the access rule for the folder using 'Set
Access Rule' on the list of Available Objects for the folder:
<dtml-comment>
This method is called by __before_traverse__ to handle a session id
in the URL path. If the path terminates here, there is no session id
and the request is redirected to the dtNewSession method.
Otherwise the next element in the path is taken to be the session id
and stored in the variable SessionID.
http://phoenix:8080/Test/nkLV8XSNaLbjxKRhQqRytQ/aMethod
should be rewritten to
http://phoenix:8080/Test/aMethod
and 'SessionId' set to 'nkLV8XSNaLbjxKRhQqRytQ' in the REQUEST variables.
When writing this kind of Access Rule, it is useful to remember the following:
REQUEST.path is the stack of Ids yet to be traversed.
REQUEST.steps is the list of Ids already traversed.
You can manipulate the path stack with append, insert, pop, and similar list operations.
You should not manipulate 'steps', instead using REQUEST.setURL(path=...) to alter the
apparent traversal history and URL generation.
</dtml-comment>
<dtml-if "_.len(REQUEST.path) == 0">
No session id
<dtml-call "REQUEST.set('SessionID', '')">
<dtml-call "REQUEST.path.insert(0, 'dtNewSession')">
<dtml-else>
Don't intercept management requests
<dtml-unless "REQUEST.path[0][:6]=='manage' or REQUEST.path[-1][:6]=='manage'">
The next path segment is a session id, save it and remove it from the path
<dtml-call "REQUEST.set('SessionID', REQUEST.path.pop())">
Add it back into the logical path
<dtml-call "REQUEST.setURL(path=REQUEST.steps+[SessionID])">
</dtml-unless>
</dtml-if>
Insert the following line into standard_html_header:
http://www.zope.org/Members/Duncan/SessionURLs?pp=1 (1 of 3) [05/07/2000 11:19:10]
Session data stored in URL
<dtml-if SessionID>
<dtml-var "Session(session_id=SessionID, noCookie=1, validSession=1)">
</dtml-if>
Create a DTML method called dtNewSession to handle accesses to the folder where no session has been specified. For example:
<dtml-if "REQUEST.form.has_key('username')">
<dtml-let sessionName="Session.newSession(REQUEST, noCookie=1)">
<dtml-call "RESPONSE.redirect(URL1 + '/' + sessionName)">
<dtml-call "SESSION.set('yournamehere',username)">
</dtml-let>
<dtml-else><dtml-var standard_html_header>
Please identify yourself.
<form action="&dtml-URL1;" method="POST">
Your name: <input type="text" name="username">
<input type="submit" value="submit">
</form>
<dtml-var standard_html_footer>
</dtml-if>
This version of dtNewSession asks the user to enter their name and stores the name they entered in a session variable. You could
simplify it by just doing the redirect so that any access to the folder automatically picks up a new session, or you could add
additional verification or look up an old session id previously used by that user.
Finally, add index_html and any other methods where you want to use the session. Here is a short example index_html (which
requires Zope 2.1) and an associated method that lets you play around with session variables.
<dtml-var standard_html_header>
<h2><dtml-var document_title></h2>
<p>
<dtml-with SESSION>
<dtml-if "SESSION.has_key('yournamehere')">
Welcome <dtml-var "SESSION['yournamehere']"><br>
<dtml-with SESSION>
Your name is <em>still</em> <dtml-var yournamehere><br>
The xyzzy variable has the value '&dtml.missing-xyzzy;'<br>
</dtml-with>
<dtml-else>Who are you?</dtml-if><br>
</dtml-with>
</p>
<A href="fields.html">Play with variables.</A>
<dtml-var standard_html_footer>
And the associated method fields.html is:
<dtml-var standard_html_header>
<dtml-if setfield>
<dtml-if "setfield.value">
<dtml-call "SESSION.set(setfield.name, setfield.value)">
<dtml-else>
<dtml-call "SESSION.delete(setfield.name)">
</dtml-if>
</dtml-if>
<table border=1>
<tr><th colspan=2>Session Variables</th></tr>
<dtml-in "SESSION.items()">
<tr><td valign=top><dtml-var sequence-key></td>
<td><pre><dtml-var "`_['sequence-item']`" html_quote></pre></td>
</tr>
</dtml-in>
</table>
<form action="fields.html" method="POST">
<table><tr>
<td>Field:</td>
<td><input type="text" name="setfield.name:record:ignore_empty"></td>
</tr>
<tr>
<td>Value:</td>
<td><textarea name="setfield.value:record"></textarea></td>
</tr>
</table>
<INPUT TYPE="hidden" NAME="setfield.name:record:default" VALUE="AField">
<input type="submit" value="Submit">
</form>
<A href="">Back to index.</A>
<dtml-var standard_html_footer>
http://www.zope.org/Members/Duncan/SessionURLs?pp=1 (2 of 3) [05/07/2000 11:19:10]
Session data stored in URL
Changes to SQLSession.py
In class SessionObj just before the definition of __setitem__, I added the following:
# Allow SESSION.fred as well as SESSION['fred']
def __getattr__(self, key):
return self.__getitem__(key)
This change lets you access session items as attributes of the SESSION object (as in the index_html example). If you don't like
this change leave it out and modify index_html accordingly.
In class SQLSession, change the definition of __call__:
def __call__(self, REQUEST=None, session_id=None, noCookie=None, validSession=None):
""" magic bits here. """
#print "called"
if REQUEST is None:
try:
REQUEST=self.REQUEST
except:
raise "Session Error", "can't find REQUEST, even in self!"
response = REQUEST['RESPONSE']
if session_id is not None:
if not REQUEST.cookies.has_key(self.cookie_name):
sessionName = self.newSession(REQUEST, session_id, noCookie=noCookie)
sessionName = REQUEST.cookies[self.cookie_name]
if sessionName != session_id:
sessionName = self.newSession(REQUEST, session_id, noCookie=noCookie)
r = self.sqlSessionLookup(session=sessionName)
if len(r) != 1:
if validSession: # validSession means we don't autocreate a session.
raise "Session Error", "Invalid session specified"
else:
sessionName = self.newSession(REQUEST, noCookie=noCookie)
else:
pass # session is fine as-is.
else:
if not REQUEST.cookies.has_key(self.cookie_name):
sessionName = self.newSession(REQUEST)
else:
if ( REQUEST.has_key('_force_new_session') and
REQUEST['_force_new_session'] ):
sessionName = self.newSession(REQUEST)
else:
sessionName = REQUEST.cookies[self.cookie_name]
r = self.sqlSessionLookup(session=sessionName)
if len(r) != 1:
sessionName = self.newSession(REQUEST)
else:
pass # session is fine as-is.
self.buildSessionObj(REQUEST, sessionName)
return '<!-- session is "%s" -->'%sessionName
and the definitions of newSession and makeSession:
def newSession(self, REQUEST, sessionName=None, noCookie=None):
response = REQUEST['RESPONSE']
if sessionName is None:
sessionName = self.makeSessionName(REQUEST)
r = self.sqlSessionCreate(session=sessionName)
#response.expireCookie(self.cookie_name, path='/')
if not noCookie:
response.setCookie(self.cookie_name, sessionName, path='/')
REQUEST.cookies[self.cookie_name] = sessionName
self.buildSessionObj(REQUEST, sessionName)
return sessionName
def makeSessionName(self, REQUEST):
""" create a new unique string to name the session. feel free to
fiddle with this. """
import md5, time, base64
m = md5.new()
m.update('happy happy happy boing plonk splut')
m.update(str(time.time()))
m.update(str(REQUEST))
return string.replace(base64.encodestring(m.digest())[:-3], '/', '$')
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Duncan/SessionURLs?pp=1 (3 of 3) [05/07/2000 11:19:10]
Setting Up & Using "Pseudo-Images"
How-To: Setting Up & Using "Pseudo-Images"
Created by kslee. Last modified on 1999/09/03.
Setting Up & Using "Pseudo-Images"
Pseudo-Image?
Uh, .. well, this is a some what exaggerated name. Just let me explain what I mean.
Zope's Image object is really wonderful. It is. But if your server box is a low end machine or your site has enormous traffic, the fact that the
whole data of your graphic files are spit by Zope process might get painful. Expecially when the size of your graphic file is large and it is
unlikely to be changed before long.
In this situation, a static(=non Zope) url to the graphic file and a DTML Document(or Method if you prefer) which contains just that url will be
better, at least I guessed so. The exaggerated name "Pseudo-Image" means this DTML Document.
Nothing special? Yes. Remember all the my writings are for a newbie by a newbie sort.
How to set up many Pseudo-Images at once?
Suppose the whole www.my.site is a Zope site except www.my.site/some/where, which is a static one(=served by Apache not Zope) and located
in your server at /some/where/in/the/filesystem. Many set up their sites so that www.my.site is served by a web server and Zope serves only
www.my.site/Zope. But I like otherwise. See Setting up a Static "Folder" for how to do this(but this usual(?) practice is OK. for using
pseudo-images)
Do the following, then you have lots of pseudo-images at www.my.site/where .
1. Create "where" folder under the root folder of Zope.
2. Create a python file pseudo_images.py(it's at the end of this writing) in your Zope extension directory and create an External Method
p_i_import in the root folder of Zope or the "where" folder.
3. Type in this url your browser's location window:
"http://www.my.site/where/p_i_import?fsdir=/some/where/in/the/filesystem&st_url=/some/where"
If you serve the static part through www.my.site and Zope page at www.my.site/Zope,the above url will be
"http://www.my.site/Zope/where/p_i_import?fsdir=/some/where/in/the/filesystem&st_url=/some/where"
4. Then as many DTML Documents as the graphic files in /some/where/in/the/filesystem will be created in "where" folder.
If you have a.gif in /some/where/in/the/filesystem whose width is 22 pixel and height is 50, the corresponding DTML Document is like this:
id: a_gif
title: a.gif
content: <img src="/some/where/a.gif" width="22" height="50">
gif's, jpeg's, png's are supported Image type. So the p_i_import external method insert width and height attribute automatically. For unsupported
types, no width height. i.e., for no.tiff:
id: no_tiff
title: no.tiff
content: <img src="/some/where/no.tiff">
If there is subdirectories under /some/where/in/the/filesystem, subfolders /where/subdirectoyname and the DTML Document corresponding the
graphic files in that subdirectories will be created.
How to Use Pseudo-Images?
If you want use the pseudo-image, a_gif at /aZope/folder/subfolder, type into your DMethod of DDocument this DTML "<!--#var
"where.a_gif.raw"-->". REMEMBER to attach '.raw'! Otherwise where.a_gif will be html-quoted and your browser show not the image
/some/where/a.gif but the string '<img src="/some/where/a.gif" width="22" height="50">.
If you want to set the width and height different or add/delete attributes, just edit a_gif or a copy of a_gif and use it, i.e.,
id: deformed_a_gif
title: Actually id & title do not matter.
content : <img src="/some/where/a.gif" width="11" height="100" border="4" alt="Deformed a.gif">
If you want the visitor's browsers use images from other server(hence to lower the burden of your server), just suck the other's graphic files into
/some/where/in/the/filesystem using wget or other tools and type into your browser the url,
http://www.zope.org/Members/kslee/pseudo_image?pp=1 (1 of 3) [05/07/2000 11:19:14]
Setting Up & Using "Pseudo-Images"
"http://www.my.site/where/p_i_import?fsdir=/some/where/in/the/filesystem&st_url=http://www.other.site/directory/where/the/pirated/images/in"
instead the previous one. In this case, you don't need to Set up a Static "Foler". But you can be sued by the other site :-(.
The pseudo_images.py file
Even if you do not know python, compare this pseudo_images.py with Brian Lloyd's fsimport.py which I plagiarised. Python is extremely
readable. I hope the difference between fsimport.py and pseudo_images.py show you the python-illiterate how to utilize other people's external
methods.
******
pseudo_images.py
******
"""psuedo_images setting up utility.
Shameless Plagiarism of Brian Lloyd's fsimport.py"""
import sys, os, string
from Globals import MessageDialog
from OFS.Image import Image
def p_i_import(self, fsdir, st_url, REQUEST=None):
"""Import a filesystem directory as a new folder object into
the current folder. """
if not os.path.isdir(fsdir):
raise ValueError, 'Path does not exist'
rcimport(fsdir, self, st_url)
if REQUEST is not None:
return MessageDialog(title='Pseudo Images created.',
message="""Pseudo Images created from the
server directory %s.<br>
Their static(none Zope but under Zope's) url is
%s.
""" % (fsdir, st_url),
action='%s/manage_main' % REQUEST['URL1']
)
def rcimport(fsdir, obdir, st_url):
temp = """<img src="%s/%s" width="%s" height="%s">"""
temp4unsupported = """<img src="%s/%s">"""
filenames=os.listdir(fsdir)
for name in filenames:
pathname=os.path.join(fsdir, name)
if name[0] in ('.', '#', '_'):
continue
if name[-1] in ('~',):
continue
elif os.path.isfile(pathname):
if _isimg(pathname):
file=open(pathname, 'rb')
data=file.read()
file.close()
new_name = string.join(string.split(name, '.'), '_')
iobj = Image(new_name, '', data)
obdir.manage_addDTMLDocument(new_name, name, temp %
(st_url, name, iobj.width, iobj.height) )
elif _is_unsupported_img(pathname):
new_name = string.join(string.split(name, '.'), '_')
obdir.manage_addDTMLDocument(new_name, name, temp4unsupported %
(st_url, name) )
else: continue
elif os.path.isdir(pathname):
obdir.manage_addFolder(name)
rcimport(pathname, getattr(obdir, name), st_url)
else:
__traceback_info__=(fsdir, obdir, name)
raise ValueError, name
unsupported_imgs={
'ief': 'image/ief',
'jpe': 'image/jpeg',
'pgm': 'image/x-portable-graymap',
'pnm': 'image/x-portable-anymap',
'ppm': 'image/x-portable-pixmap',
'ras': 'image/x-cmu-raster',
http://www.zope.org/Members/kslee/pseudo_image?pp=1 (2 of 3) [05/07/2000 11:19:14]
Setting Up & Using "Pseudo-Images"
'rgb': 'image/x-rgb',
'tif': 'image/tiff',
'tiff': 'image/tiff',
'xbm': 'image/x-xbitmap',
'xpm': 'image/x-xpixmap',
'xwd': 'image/x-xwindowdump'
}
def _isimg(path):
ext=string.lower(string.split(path, '.')[-1])
return ext in ('gif', 'jpg', 'png', 'bmp', 'jpeg')
def _is_unsupported_img(path):
ext=string.lower(string.split(path, '.')[-1])
return ext in unsupported_imgs.keys()¡¡
******
kslee, the bonest-head in the Zopeland.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/kslee/pseudo_image?pp=1 (3 of 3) [05/07/2000 11:19:14]
Setting the MIME Type of a DTML Method
How-To: Setting the MIME Type of a DTML
Method
Created by anser. Last modified on 2000/04/12.
Although you may have read examples of the form
<dtml-call "RESPONSE.setHeader('Content-Type', 'application/x-whatever')">
for controlling your Zope MIME types, there is a much simpler way: just put the Content-type: line by itself at the very top
of your Method, then follow it with a blank line before any other text or DTML markup.
For example, if you create a DTML method called meth1 and enter the following text into the editor box:
Content-type: text/plain
How plain I feel today!
and then View it, you will see a page of plain text instead of formatted HTML. The Content-type line will not show up because it
was obeyed rather than displayed!
The same thing goes for more complicated or esoteric MIME types, like a Shoutcast/MP3 stream:
Content-type: audio/x-scpls
[playlist]
NumberOfEntries=1
File1=http://radio.grassyhill.org:8810/
Of course the MIME type itself, as well as the envelope content that follows it, can all be programmatically generated in DTML
rather than hard coded into your method's text space. All that matters is that the first 1..N lines emitted by your method, before the
first blank line, be in the HTTP header form - including but not limited to MIME type - and they will be obeyed.
Content-Type: text/xml
Expires: Tue, 11 Apr 2000 01:21:00 GMT
X-Pillow-Fight: tonight
<document title="Oh ok">
<status>Working...</status>
</document>
Note that this does NOT work for DTML Documents, only for DTML Methods. However, if you begin a DTML Document with
one or more header lines, then include that Document at the very top of a DTML Method via <dtml-var>, the effect will be the
same.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anser/howto_dtml_mimetype?pp=1 [05/07/2000 11:19:16]
Setting up a static "folder"
How-To: Setting up a static "folder"
Created by kslee. Last modified on 1999/09/15.
Setting up a static "folder"
Occasionally, you may want to add an static "folder" in the Zope site. By "static 'folder'", I mean serving the files directly from the
filesystem, not from Zope Database(Data.bbb or Data.fs in the var directory). Here is the low-tech solution for this unusual (you'll
lost almost all the benefit of the Zope) situation.
Situation.
My site is www.my.site, and it served from Zope via pcgi & Apache. But www.my.site/some/where/ has almost never changing
contents(icons, buttons, etc). I want them served by Apache, not by Zope. For I guess serving non-changing simple files via Zope
is unnecessary waste and my machine is not a strong one.
Solution.
The part of httpd.conf which rules my site is following(The lines ending with "\" are continued on the following line and appear in
the original file on a single line as usual, and x: are line numbers not a part of httpd.conf file.):
1:
2:
<VirtualHost www.my.stie>
... ...
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^/(.*) /home/me/public_html/cgi-bin/Zope.cgi/$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,L]
</VirtualHost>
Insert "RewriteRule ^/some/where/(.*) /some/where/in/the/filesystem/$1 [L]" between line 1 & line 2, i.e.,
1:
A:
2:
<VirtualHost www.my.stie>
... ...
RewriteEngine on
RewriteRule ^/some/where/(.*) /some/where/in/the/filesystem/$1 [L]
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^/(.*) /home/me/public_html/cgi-bin/Zope.cgi/$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,L]
</VirtualHost>
That's it. So simple? Yes! Now www.my.site/some/where is directly served by Apache from the server's directory
/some/where/in/the/filesystem, while all other www.my.site come from Zope(via pcgi & Apache).
This is the newbie's solution for other newbies. I tested this on Red Hat 6.0(localized by a Korean team) with Apache 1.3.6. It just
works & well. But Red Hat 5.2's Apache did strange things with mod_rewrite(I had to build Apache from the source. That was so
hard a task for this novice). So your luck may vary.
You can insert as many lines like line A between line 1 & line 2. Explaining why this works in English is far heavier task than
doing it for me. Check Apache documentation for mod_rewrite module by Engelschall.
If you want more.
If you want to utilize acquisition, instead of hard-coding the url to the static files, just create a DTML Method whose contents is
that url and use it. Unless you're serving lots of such files, maintenance will be almost non-issue.
Also, you might want to read Setting up & Using Pseudo-Images, if you find useful this "Setting up a 'static folder'" by a novice
like me.
When to use?
1. There were some discussion on the slashdotting a Zope site(Bruce Perens' technocrat.net). The webmaster decided to drop out
many graphic buttons, since serving them through Zope increases the agony of the machine. But I suspect that invoking one more
RewriteRule is far cheaper than serving via Zope and graphic buttons delight eyes.
http://www.zope.org/Members/kslee/static_folder?pp=1 (1 of 2) [05/07/2000 11:19:19]
Setting up a static "folder"
2. You' are migrating from the old world to the Zopeland. But you and/or your customer likes the dynamic features of Zope a lot.
So the http://www.some.one is simply a Zope site. But for various reasons, you and/or your customers' boss wants some folders to
be served not by Zope.
kslee, the bonest-head in the Zopeland.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/kslee/static_folder?pp=1 (2 of 2) [05/07/2000 11:19:19]
Simple But Powerful DTML Tips
How-To: Simple But Powerful DTML Tips
Created by admin. Last modified on 1999/10/14.
Here is a list of tips which I learned while creating the ActiveImage Product ZClass:
Allow your ZClass to contain other ZOPE objects.
On the "Add ZClass" page when creating the ZClass, add the "Object Manager" Base Class.
Set and echo a variable using
<!--#call "REQUEST.set('varName', 'varValue')"-->
<!--#var varName-->
Create truly random id numbers.
<!--#call "REQUEST.set('randomID', _.random.random()*1000000000)"-->
<!--#call "REQUEST.set('randomID',_.str(_.int(randomID)))"-->
Upload a new image (Used inside a "with" tag).
<!--#call "manage_addImage('myImage',REQUEST['myImage'],'')"-->
Upload to an existing image (Used inside a "with" tag)..
<!--#call "myImage.manage_upload(REQUEST['myImage'])"-->
Using the "lines" property - looping with in and sequence-item.
The "sequence-item" represents the value of the current line. The following prints all lines.
<!--#in myLinesProperty-->
<!--#var sequence-item-->
<!--#/in-->
Substring search of a variable, using the _[] format.
When using the "sequence-item" in a statement such as an If statement, you will need to refer to it as
_['sequence-item']:
<!--#if "_.string.find(StringToSearch,SubString)" -->
Example: <!--#if "_.string.find(PATH_INFO,_['sequence-item'])" -->
Using the .tag function to access the auto-generated img tag.
Provide the attributes you'd like to change as follows:
<!--#var "on.tag(border=0, alt='text', width='')"-->
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/admin/QuickTips?pp=1 [05/07/2000 11:19:21]
Simple Database/form interaction
How-To: Simple Database/form interaction
Created by Amos. Last modified on 1999/09/15.
Here's a quick recipe from Michel Pelletier for hooking a HTML input form to a database.
You will need a database, and the apropriate Zope Database
Adapter.
1. Create a Database Connection and type in a valid connection string which connects you to your database.
2. Create a DTML method input with your form in it, with the form action pointing to another DTML Method output
ie:
. Let's say one of your form elements is named name ie:
.
3. Create a ZSQL Method called insertName with one argument, name and containing SQL code that inserts this value
into the database. something like::
INSERT INTO my_table_with_one_column_called_name
name VALUES (<!--#sqlvar name-->)
4. Now create the output DTML method to contain::
<!--#standard_html_header-->
<!--#call insertName-->
Thanks for the input!
<!--#standard_html_footer-->
5. Now, when a user goes to input and fills in the form, the form will action to output, which will call the ZSQL
method inserName which will insert the name into the database, and then the output DTML method will say
something nice, and that's it.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/SQLInsert?pp=1 [05/07/2000 11:19:23]
Soft References
How-To: Soft References
Created by jwm. Last modified on 2000/05/11.
A soft reference is basically a text name of some object or property that gets stored in some other property. They're enormously
useful for all sorts of things; for example, I have various ZClass based product that are designed to handle documents with various
predefined attributes, and it includes a property with the name of the default cascading style sheet to use with it.
To use the style sheet, I need to add a item inside the head of the document, so that the browser can grab the style sheet. Normally
you'd add smoe dtml like this into the href of the link:
<dtml-var "style_sheet.absolute_url()">
In our case, the name of our stylesheet is stored in some string, so we need some way of derefencing that to access the
absolute_url() method of the real style sheet object.
One way to dereference our string is to use _.getitem() [r1]:
<dtml-var "_.getitem(style_sheet_pointer).absolute_url()">
_.getitem() is acquisition aware so style_sheet_pointer can be defined anywhere down the tree, as can the object
being refered to. This usage of _.getitem() turns out to be eqivalent to this [r2]:
<dtml-var "_.getattr(this(),style_sheet_pointer).absolute_url()">
Use _.getattr() when you know the object that your pointer property is in is outside the acquisition tree that you're working
from.
[r1] Thanks to Dr. Ross Lazarus <rossl@med.usyd.edu.au> for this method.
[r2] Thanks to Chris Withers <chrisw@nipltd.com>
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jwm/soft_references?pp=1 [05/07/2000 11:19:25]
some neat tricks with dtml-tree
How-To: Some Neat Tricks with dtml-tree
Created by anthony. Last modified on 2000/02/15.
A warning
First, a warning. This How-To is incomplete. I'd hoped to get it finished quite a while ago, but other things intervened. Some of
the things already covered here (such as SQL-based trees) come up so often in the Zope lists that it seemed useful to put it online,
even in it's current state. If you'd like to volunteer content for one of the sections here, let me know. The major part that's not
finished is the ZCatalog based trees.
an additional note: I've built trees based on all of the different methods below. They all work, but might require some fiddling. For
an example of the ZCatalog based trees, look at the help functions on the www.ekit.com site (although to get a meaningful amount
in the help, you'll need to create a free account).
introduction
DTML's tree construct is one of those extraordinarily powerful and extraordinarily hard to use corners of Zope. Debugging the
tree tag can be an exercise in pain and suffering. Here's a collection of neat tricks I've found for working with dtml-tree.
hopefully you will have already read secrets of working with the tree tag.
Generating trees from SQL Methods or
PythonMethods
Rather than just using the ZODB for the information to put in a tree, you can use almost any source of hierarchical data in a tree.
I've built trees based on SQL Methods, External Methods, PythonMethods, and ZCatalogs. Each will be covered in turn here (with
a simple example of each.)
Some general notes
When using branches_expr, you'll need to provide an initial set of values - do this by wrapping the dtml-tree inside a
dtml-let block that sets the values.
SQL Methods
This is a fairly common question - how do I make a tree that's populated from an SQL table? Normally, this will be from a table
that has a column specifying id, and a column specifying parentid.
The schema
This table is a very simple one - a real table would have integrity constraints, indexes on parentid, and the like.
CREATE TABLE myobjects (
objectid
INTEGER PRIMARY KEY,
parentid
INTEGER,
http://www.zope.org/Members/anthony/tree-coding-tricks?pp=1 (1 of 5) [05/07/2000 11:19:36]
some neat tricks with dtml-tree
objectname VARCHAR
)
INSERT INTO myobjects ( objectid,
VALUES ( 1, 0, 'topA' )
INSERT INTO myobjects ( objectid,
VALUES ( 2, 1, 'secondA' )
INSERT INTO myobjects ( objectid,
VALUES ( 3, 1, 'secondB' )
INSERT INTO myobjects ( objectid,
VALUES ( 4, 1, 'secondC' )
... insert more values here ...
parentid, objectname )
parentid, objectname )
parentid, objectname )
parentid, objectname )
In this schema, an object ID is an integer. The 'root' of the tree of objects is the 'magic' object ID '0'
The SQL query
We now make an SQL method called child_lookup, which takes a single argument parentid. The body of the SQL method
should be
SELECT objectid, objectname
FROM myobjects
WHERE
<dtml-sqltest parentid type=int>
Test this a few times. Confirm that it does what you expect - that is, when given an object ID, it will return the objects that are
direct children of that object.
Putting it together - the DTML Method
<dtml-let objectid="0">
<dtml-tree id=objectid branches_expr="child_lookup(parentid=objectid)">
<b><dtml-var objectname></b> <dtml-var objectid>
</dtml-tree>
</dtml-let>
So what's going on here? The core of this here is the branches_expr code - the tree tag will call this code, then call it recursively
for each result. The dtml-let at the start sets up an initial value for the objectid - otherwise the first time the branches_expr code is
called we'll get an NameError for objectid. The only other thing to note is the id=objectid. This is needed to make the tree expand
and collapse correctly. By default, dtml-tree looks for an attribute called 'tpId' to use when storing the state of the tree (which bits
are expanded and which are not). Since our SQL results don't have a 'tpId' attribute, it will pick up the 'tpId' of the dtml document
that this is part of (or the folder.) This will result in much strangeness - your tree will not work properly.
ExternalMethods/PythonMethods
A simple example
notes to self tree doesn't set sequence-item - also doesn't support 'mapping'. can't be used from pythonmethods, then.. can't create
classes in PMs (?)
class _dummy:
def __init__(self, name):
self.name=name
def child_lookup(parent):
c = []
for i in range(0,5):
c.append(_dummy("%s.%d"%(parent, i)))
return c
http://www.zope.org/Members/anthony/tree-coding-tricks?pp=1 (2 of 5) [05/07/2000 11:19:36]
some neat tricks with dtml-tree
The DTML Method
<dtml-let name="0">
<dtml-tree id=name branches_expr="child_lookup(parent=name)">
<b><dtml-var name></b>
</dtml-tree>
</dtml-let>
This, with a couple of nodes expanded, looks something like:
This tree can be expanded forever, if you so choose - 0.1.3.1.2.3.2.2.2.2.2.2.2.0.0.0 (almost makes me feel nostalgic for SNMP...)
ZCatalogs
We've got these neato ZCatalog things in Zope - they allow you to index objects by attributes, and do searches on them - so why
aren't we using them to build trees? It should be faster than walking through the ZODB each time.
Some notes to start with: first of all, the objects that you're indexing should have some notion of a 'parent id'. And this should be
unique across the catalog, or else you're going to end up with all sorts of strangeness. Secondly, it's a good idea to make sure that
all the attributes that you want to access inside the <dtml-tree></dtml-tree> tags are stored in the metadata of the Catalog.
... still to do this bit ...
For an example of an application that uses this, look at www.ekit.com's help system. Note that the help available to the public user
is not large - you'd be better off creating an account and then viewing the help there.
http://www.zope.org/Members/anthony/tree-coding-tricks?pp=1 (3 of 5) [05/07/2000 11:19:36]
some neat tricks with dtml-tree
Using a ZCatalog directly
All objects that are indexed should have an attribute that uniquely identifies them, and a parent attribute. You could use
'absolute_url'? (check... blah)
Using a ZCatalog via a PythonMethod
This allows you a lot more flexibility - you can massage the arguments to the ZCatalog SearchResults, or call a different method
entirely. For instance, you might call uniqueValuesFor when at the top level of the tree to produce a listing of major tree sections.
Debugging generated trees.
It can be somewhat problematic to debug a generated tree. One neat trick is to use a series of nested 'dtml-in' statements.
For a dtml-tree set up like
<dtml-tree branches_expr="somefunc(parent=id, b=b, c=c)">
<dtml-var id> - <dtml-var title>
</dtml-tree>
you can do something like
<UL>
<dtml-in "somefunc(parent=id, b=b, c=c)">
<LI> <dtml-var id> - <dtml-var title>
<UL>
<dtml-in "somefunc(parent=id, b=b, c=c)">
<LI> <dtml-var id> - <dtml-var title>
<UL>
<dtml-in "somefunc(parent=id, b=b, c=c)">
<LI> <dtml-var id> - <dtml-var title>
</dtml-in>
</UL>
</dtml-in>
</UL>
</dtml-in>
</UL>
this will show the dtml-tree expanded out to three levels deep, using the HTML <UL> tag to show the levels.
Use this to test that the code you're using works correctly to generate the tree.
Common problems
tree won't expand, or expands in a strange way
Check that whatever 'id' is set to (by default, it's 'tpId') is actually set inside the tree. Check also that it's different for sibling nodes
of a tree (e.g. that all top level items have a different value). This can bite you when working with tree results generated from a
ZCatalog - make sure that the ZCatalog MetaData table includes whatever you use as a tpId.
expand_all=1 won't work.
expand_all is supposed to tell the tree tag to expand all branches of a tree. This would allow you to confirm that the tree you're
viewing works appropriately. A bug in the tree tag prevents this from working when branches_expr is set, but branches is
not. In any case, make sure your tree will not recurse infinitely if someone (for example a loop in the objects.)
http://www.zope.org/Members/anthony/tree-coding-tricks?pp=1 (4 of 5) [05/07/2000 11:19:36]
some neat tricks with dtml-tree
tree 'id' stuff behaves very strangely with
ZCatalog based trees
As far as I can see, this is a really ugly tree bug. The tree tag, when rendering itself, pushes itself on top of the namespace when
rendering the tree body (between <tree> and </tree>). This is unnecessary, and since the tree object acquires, anything inside the
tree body (like, f'r instance, 'id') will get set to the id around the tree, not the current object. I have a patch for this that's been
posted to zope-dev, it's also in the Collector. I'll dig it out and put it on www.zope.org - or mail me and ask for it.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anthony/tree-coding-tricks?pp=1 (5 of 5) [05/07/2000 11:19:36]
Starting and Stopping Multiple Zope Instances
How-To: Starting and Stopping Multiple Zope
Instances
Created by jec. Last modified on 2000/02/04.
This document explains the custom scripts I created for starting and stopping Zope on my Red Hat Linux system. I currently run
two separate instances of Zope, each with its own database and Zope.cgi file. Take a look at my Zope/Apache HOWTO for more
on my configuration.
If there is anything here upon which I could improve, please let me know.
System rc Script
Red Hat puts these in /etc/rc.d/, with the runlevels at /etc/rc.d/rc?.d/* and the actual scripts at /etc/rc.d/init.d/*. My script is a
standard script that can be created from any of the other examples in that directory, or with the skeleton script provided.
Put links in the appropriate places in /etc/rc.d/rc?.d/*. Red Hat systems provide a command called chkconfig that can read an
appropriately written script and create/destroy the links. First, create the script:
/etc/rc.d/init.d/zope
#!/bin/bash
# zope
#
# chkconfig: 345 90 10
# description: Starts and stops the Zope instances
# processname: z2.py
# Source function library.
. /etc/rc.d/init.d/functions
# Get config.
. /etc/sysconfig/network
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 1
[ -x /usr/local/scripts/zstart ] || exit 1
[ -x /usr/local/scripts/zstop ] || exit 1
# See how we were called.
case "$1" in
start)
echo -n "Starting Zope instances: "
if /usr/local/scripts/zstart; then
success zope startup
else
failure zope startup
fi
echo
;;
stop)
echo -n "Stopping Zope instances: "
if /usr/local/scripts/zstop /etc/zope.conf; then
success zope shutdown
else
failure zope shutdown
fi
echo
;;
status)
status z2.py
exit $?
http://www.zope.org/Members/jec/startstop_howto_html?pp=1 (1 of 5) [05/07/2000 11:19:41]
Starting and Stopping Multiple Zope Instances
;;
restart)
$0 stop
sleep 2
$0 start
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
exit 1
esac
exit 0
Then, create the links. On Red Hat, chkconfig looks for the chkconfig: comment in the script and creates links as appropriate.
When I do this:
chkconfig --add zope
it creates the following links for me:
●
/etc/rc.d/rc0.d/K10zope
●
/etc/rc.d/rc1.d/K10zope
●
/etc/rc.d/rc2.d/K10zope
●
/etc/rc.d/rc3.d/S90zope
●
/etc/rc.d/rc4.d/S90zope
●
/etc/rc.d/rc5.d/S90zope
●
/etc/rc.d/rc6.d/K10zope
According to the standard way of doing things, I'm not sure that all of these links are necessary, but that's what chkconfig creates
anyway.
Zope Start and Stop Scripts
I created two scripts, zstart and zstop, that handle the starting and stopping of Zope. You can put these anywhere in your
filesystem that makes sense to you. I have them in /usr/local/scripts.
zstart
#!/bin/bash
#
# Set default environment
#
ENV_FILE=/etc/zope.d/env
INSTANCES_FILE=/etc/zope.d/instances
ZOPE_USER=nobody
ZOPE_BASE=/opt/Zope
CGIBIN_BASE=/home/httpd/cgi-bin
INSTANCE_BASE=/home/httpd/zope
PS="ps wax"
PYTHON=/usr/local/bin/python
#
# process command line options
#
while getopts "c:" opt
do
case "$opt" in
i)
if [ -r "$OPTARG" ]
then
INSTANCES_FILE=$OPTARG
http://www.zope.org/Members/jec/startstop_howto_html?pp=1 (2 of 5) [05/07/2000 11:19:41]
Starting and Stopping Multiple Zope Instances
else
echo "$0: cannot read $OPTARG"
exit 1
fi
;;
e)
if [ -r "$OPTARG" ]
then
ENV_FILE=$OPTARG
else
echo "$0: cannot read $OPTARG"
exit 1
fi
;;
?)
echo "Usage: $0 [-i instances-file] [-e environment-file]"
exit 3
;;
esac
done
#
# Read environment file
#
. "$ENV_FILE"
#
# is_running instance_name
#
is_running ()
{
if $PS | grep -v grep | grep "z2\.py.*$1" > /dev/null 2>&1
then
echo yes
else
echo no
fi
}
#
# start_instance instance_name ftp_port n_threads use_manager
#
start_instance ()
{
if [ "$4" = "0" ]
then
manager=
else
manager="-Z $INSTANCE_BASE/$1/manager.pid"
fi
su $ZOPE_USER -c \
"$PYTHON $ZOPE_BASE/z2.py \
-p $CGIBIN_BASE/$1/Zope.cgi \
-w '' -m '' -f $2 -t $3 $manager\
INSTANCE_HOME=$INSTANCE_BASE/$1 \
>> $INSTANCE_BASE/$1/z2.log 2>&1 &"
}
############################################################
#
# Main program
#
awk 'substr($1, 0, 1) != "#" {print $0}' $INSTANCES_FILE | (
while read line; do
set $line
if [ "`is_running $1`" = "yes" ]
http://www.zope.org/Members/jec/startstop_howto_html?pp=1 (3 of 5) [05/07/2000 11:19:41]
Starting and Stopping Multiple Zope Instances
then
echo "$0: Warning: instance $1 already running"
else
start_instance $1 $2 $3 $4
fi
done)
zstop
#!/bin/bash
#
# Set default environment
#
ENV_FILE=/etc/zope.d/env
INSTANCES_FILE=/etc/zope.d/instances
ZOPE_USER=nobody
ZOPE_BASE=/usr/local/Zope
CGIBIN_BASE=/home/httpd/cgi-bin
INSTANCE_BASE=/home/httpd/zope
PS="ps wax"
PYTHON=/usr/local/bin/python
#
# process command line options
#
while getopts "c:" opt
do
case "$opt" in
i)
if [ -r "$OPTARG" ]
then
INSTANCES_FILE=$OPTARG
else
echo "$0: cannot read $OPTARG"
exit 1
fi
;;
e)
if [ -r "$OPTARG" ]
then
ENV_FILE=$OPTARG
else
echo "$0: cannot read $OPTARG"
exit 1
fi
;;
?)
echo "Usage: $0 [-i instances-file] [-e environment-file]"
exit 3
;;
esac
done
#
# Read environment file
#
. "$ENV_FILE"
#
# stop_instance instance_name use_manager
#
stop_instance ()
{
if [ "$2" = "0" ]
then
http://www.zope.org/Members/jec/startstop_howto_html?pp=1 (4 of 5) [05/07/2000 11:19:41]
Starting and Stopping Multiple Zope Instances
pidfile=pcgi.pid
else
pidfile=manager.pid
fi
kill `cat "$INSTANCE_BASE/$1/var/Z2.pid"` # > /dev/null 2>&1
}
############################################################
#
# Main program
#
awk 'substr($1, 0, 1) != "#" {print $0}' $INSTANCES_FILE | (
while read line; do
set $line
stop_instance $1
done)
Configuration
By default, the zstart and zstop scripts look for two configuration files in the /etc/zope.d directory: env and instances. The env file
overrides environment variables that are set within the scripts. The instances file provides the scripts with information about what
Zope instances to start or stop.
env
# /etc/zope.d/env
#
# Set environment variables that differ from default
ZOPE_USER=www
ZOPE_BASE=/usr/local/Zope
instances
# /etc/zope.d/instances
#
# instance_name ftp_port n_threads use_manager
#
zope0
8022
2
0
site3
8023
2
1
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jec/startstop_howto_html?pp=1 (5 of 5) [05/07/2000 11:19:41]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
How-To: Step-by-step installation of Zope (2.1.7)
on SuSE Linux 6.3 and 6.4
Created by maryniak. Last modified on 2000/06/26.
This is a detailed step-by-step description of the procedure I used to
install Zope 2.1.7 under SuSE Linux 6.3 and 6.4. Included are:
o Setup with existing web server (ic. Apache) in /opt/zope
o Apache rewrite rules for any static content and virtual hosts
o Start/Kill rc script according to SuSE Linux standards
Should you decide to use it somehow (of course at your own risk ;-),
I'd welcome any thoughts, comments and bug fixes you might have.
I have tried to do my best at testing the various pieces of software
and config setup's, and studied all docu to the best of my knowledge,
but real Zope and SuSE Linux experts should have a final say on this.
* Background/rationale.
Some time ago I asked if anybody got Zope running on SuSE Linux
and what setup was chosen and why (ZServer or Apache, and if Apache,
FastCGI, PCGI, Proxy, ...). I received very helpful e-mail from
Piotr and from Jeff, whose Red Hat rpm's and notes on:
http://starship.python.net/crew/jrush/Zope/Zope216.html
are also very informative. Furthermore, I studied the various howto's
and other docu about SuSE on the Zope site, http://www.zope.org/
With all this info, however, I wasn't really satisfied, because:
a) I didn't like the setup in many cases from a security point of view
(I'm not a security expert, but I do know, it's generally not a good
idea to run a service as root).
b) Jeff's RPM's proved to be too Red Hat specific and failed to install
properly on SuSE Linux 6.3 and 6.4 (in my case anyway), v2.1.6 gave:
an 'rpm -i Zope-2.1.6-1.i386.rpm Zope-pcgi-2.1.6-1.i386.rpm' outputs:
error: failed dependencies:
python-devel is needed by Zope-2.1.6-1
/sbin/chkconfig is needed by Zope-2.1.6-1
Note that SuSE does not have a separate 'python-devel' package and
that /sbin/chkconfig does not exist in SuSE, it is Red Hat specific...
Imho it's Linux distro idiosyncracies like this that may be a serious
problem to desktop end user acceptance of Linux.
You can force an install with 'rpm -i --nodeps', but, eh, my 32 Mb SuSE
Linux 6.4 system at home gave:
execution of script failed
Segmentation fault
Anyways, Zope was extracted, which allowed me to study Jeff's setup.
It's unfortunate that Jeff's rpm's did not work on SuSE because he has
done a remarkable job (including a lot of Zope bug fixes!).
c) The Zope howto's, and there are several, are sometimes a little
outdated, incomplete or conflicting.
So, I realized that the lazy rpm days were over and decided to dive into
it -- deeply as it turned out ;-)
First I decided on the setup to use, because:
a) we already have a lot of "static" html pages, which are to be gradually
zope-ed, but not right away
b) thus, zope should not become / (and the static pages /static) right
away, because a lot of people have bookmarked the old url's (solved by
using Zope on an internal virtual host, migrating pages, and making
the internal zope host the external)
c) on some pages we want to use ssl (which works fine with apache but
not with Zope ZServer).
d) and have apache as primary server, so the logging (visitor statistics)
can be kept unified.
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (1 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
It would seem, judging from Guy Davis' (recent) notes:
http://www.zope.org/Members/guy_davis/install_routes/
that Apache+Zope via PCGI was the best way (at least for the relative
novice): "... My Solution - After a couple of days and with much help,
I was able to get Zope working with Apache through PCGI. All the other
methods had drawbacks or didn't work for me, so I'd have to recommend
PCGI for Apache users. ...".
So please find my...
===========================================================================
Installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4, step-by-step
Eric Maryniak <e.maryniak@pobox.com>, 2000-06-16.
Home page: http://pobox.com/~e.maryniak/
This is a detailed step-by-step description of the procedure that I used
to install Zope 2.1.7 under SuSE Linux 6.3 and 6.4. Included are:
o Setup with existing web server (ic. Apache) in /opt/zope
o Apache rewrite rules for any static content and virtual hosts
o Start/Kill rc script according to SuSE Linux standards
To install Zope 2.1.7 (dated 2000-06-15) on SuSE Linux 6.3 or 6.4 with
the existing Apache web server (httpd) using Persistent CGI (PCGI),
log in as root or become root ('su - root') and follow the steps below.
I have tried to setup Zope with security in mind, but since this is not
my field of expertise I welcome feedback there!
Notes:
- The 'bash' shell is assumed.
- The Unix shell prompt for root is '# ' and '$ ' for normal users.
- I use here-documents (those 'cat << ...' thingies) frequently in the
copy & pastable Unix commands below. You may want to familiarize
yourself with this rather neat (ba)sh feature first ('man bash' ;-)
Important last note:
- Shortly after I downloaded Zope 2.1.7, which was basically 2.1.6 with
security fixes, the Zope maintainers pulled version 2.1.7 in favor of
a so-called "hot fix" product, please see:
http://www.zope.org/Products/Zope/Hotfix_06_16_2000/security_alert/
so please read "2.1.6" in the rest of this document for "2.1.7" and
apply this hot fix after you have concluded all the steps below.
Step 1/8. Verify python 1.5.2 is installed
==========================================
First verify that your are running Python version 1.5.2:
# rpm -q python
You should get something like 'python-1.5.2-97'.
Alternatively, simply run Python ('python').
Somewhere in the welcome blurp, the version number is hidden :-)
Note that, unlike Red Hat, there is no 'python-devel' SuSE package.
Step 2/8. Extract the Zope source
=================================
Important: see the "Important last note" above about the version 2.1.6
and 2.1.7 issue.
Download the Zope 2.1.7 source from:
http://www.zope.org/Products/Zope/2.1.7/
and store it in directory:
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (2 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
/usr/src/packages/SOURCES/
This document describes the installation and configuration of Zope
version 2.1.7 (dated 2000-06-15), but it may also work for newer
(and perhaps older) versions.
Be sure to download the _source_ tarball (Zope-x.y.z-src.tgz), not a
binary distribution for some specific operating system.
Extract the Zope 2.1.7 source (a gnuzipped tarball) and rename the Zope
directory to /opt/zope:
# cd /opt
# tar zxvf /usr/src/packages/SOURCES/Zope-2.1.7-src.tgz
# mv Zope-2.1.7-src /opt/zope
Note that '/opt/zope' is also the directory to unpack any additional Zope
products (zproducts) in, that you might be installing later.
Enter the Zope directory and perhaps have a look at the Zope documentation.
You may notice that the files have a funny ownership, but you may safely
leave that for now (it is fixed in a later step).
# cd /opt/zope
# less README.txt doc/INSTALL.txt doc/*.txt
Step 3/8. Build Zope (PCGI with existing web server setup)
==========================================================
Build Zope to use PCGI with an existing web server (Apache in our case),
hence use the 'w_pcgi.py' script:
# cd /opt/zope
# python w_pcgi.py
Write down the generated Zope superuser password!
The user account info is stored in file '/opt/zope/access' which must
have file permission 0600 (but we will come to security settings in the
next steps).
You can always change the Zope administrator (superuser) password later
on the command line (this is explained in section "Zope administration").
Step 4/8. Fix file permissions and ownerships of the Zope tree
==============================================================
After unpacking, the Zope tree (/opt/zope) has a few peculiarities.
The ownership is uid 506 and group id is 'users'. Furthermore, some
directories are group writable. The building process did not fix this.
Bring the files more in line with other SuSE program directories, which
are often root:root and 0755 for directories c.q. 0644 for files at most.
Make the 'var' directory (/opt/zope/var) writable for wwwrun:nogroup,
which is the uid:gid the Apache web server runs as, so Zope.cgi (called
by httpd) can write logging info, program pid's and the Zope database
itself (Data.fs) there. The 'access' file is also fixed:
# chown -Rh root:root /opt/zope
# chmod -R og-w /opt/zope
# chown -R wwwrun:nogroup /opt/zope/var
# chmod 0600 /opt/zope/access
# chown wwwrun:nogroup /opt/zope/access
Next, change the 'start' script (save a backup first), to make sure the Zope
server is started as user wwwrun:nogroup, just like the Apache web server
(option '-u' is used for this). Also remove the debug option ('-D').
Be sure to fully copy and paste the cat command, ie. upto and including
the end-of-script (_EOS) marker or just start an editor and copy the stuff
between 'cat << ...' and '_EOS':
# mv /opt/zope/start /opt/zope/start.orig
# cat <<'______EOS' > /opt/zope/start
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (3 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
#! /bin/sh
reldir=`dirname $0`
PYTHONHOME=`cd $reldir; pwd`
export PYTHONHOME
exec /usr/bin/python $PYTHONHOME/z2.py -u wwwrun "$@"
______EOS
# chown root:root /opt/zope/start
# chmod 0711 /opt/zope/start
Create a symlink from /var/log/zope to /opt/zope/var, so you can use a
'tail -f /var/log/zope/*.log /var/log/httpd*log /var/log/httpd/*log' to
monitor zope and web log files:
# ln -s /opt/zope/var /var/log/zope
Finally, verify that there are no setuid or setgid files (always a safe
precaution):
# find /opt/zope \
-type f \( -perm -04000 -o -perm -02000 \) -exec ls -lg {} \;
Step 5/8. Create a cgi-bin directory for Zope.cgi
=================================================
Create a special cgi-bin directory for Zope.cgi, because the default
cgi-bin directory does not allow cgi scripts to execute ('Options None'
in /etc/httpd/httpd.conf of SuSE Linux 6.3 and 6.4).
Instead of adding an 'Options +ExecCGI' to the generic cgi-bin directory
and thus possibly creating a security hole, we therefore create a special
zope-cgi-bin directory (and add 'ExecCGI' later, when we edit the Apache
config file).
Use the same ownership (root:root) and permission (0755) as the other
cgi-bin directory:
# mkdir /usr/local/httpd/zope-cgi-bin
# chown root:root /usr/local/httpd/zope-cgi-bin
# chmod 0755 /usr/local/httpd/zope-cgi-bin
For the <VirtualHost> virtual.yourdomain.tld, do
(note: a base directory '/home/www/<virtual-host>' is assumed in the rest
of this document for all your virtual hosts, if any):
# mkdir /home/www/virtual.yourdomain.tld/zope-cgi-bin
# chown root:root /home/www/virtual.yourdomain.tld/zope-cgi-bin
# chmod 0755 /home/www/virtual.yourdomain.tld/zope-cgi-bin
Copy Zope.cgi to the specially created zope cgi-bin directory:
# cp -p /opt/zope/Zope.cgi /usr/local/httpd/zope-cgi-bin/.
For the <VirtualHost> 'virtual.yourdomain.tld', do:
# cp -p /opt/zope/Zope.cgi /home/www/virtual.yourdomain.tld/zope-cgi-bin/.
Make sure Zope.cgi has the same ownership (root:root) and permission (0755)
as the cgi scripts in /usr/local/httpd/cgi-bin/.
Step 6/8. Configure Apache for Zope with PCGI
=============================================
Now that Zope is built and installed, we have to configure the Apache web
server (httpd) so that Zope can be used (via PCGI). Furthermore, we want to
make sure Zope is started after a system reboot.
We already copied Zope.cgi to the cgi-bin directory.
As always, begin with making a copy of the Apache config file in case you
need to rollback:
# cp -p /etc/httpd/httpd.conf /etc/httpd/httpd.conf.bak
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (4 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
First add an entry for the zope-cgi-bin directory we created above.
Either do this globally, or for specific virtual hosts (in our case for
the <VirtualHost> 'virtual.yourdomain.tld').
In the Apache web server configuration file (/etc/httpd/httpd.conf) locate
'ScriptAlias /cgi-bin/' and '<Directory "/usr/local/httpd/cgi-bin">' and
add the zope-cgi-bin entries (add the '==> Add this' piece).
===> For the global configuration:
[---- Begin file (fragment): /etc/httpd/httpd.conf ----]
# ... Notes about ScriptAlias ...
ScriptAlias /cgi-bin/ "/usr/local/httpd/cgi-bin/"
# ... The regular cgi-bin directory:
<Directory "/usr/local/httpd/cgi-bin">
# ... with very restrictive 'Options None' ...
</Directory>
# ==> Add this:
# Zope.cgi is located in it's own zope-cgi-bin, because ExecCGI
# permission is needed, which is not (by default) enabled in SuSE
# Linux 6.3 and 6.4 for the regular cgi-bin directory.
ScriptAlias /zope-cgi-bin/ "/usr/local/httpd/zope-cgi-bin/"
<Directory "/usr/local/httpd/zope-cgi-bin">
AllowOverride None
Options None
Options +ExecCGI
Order allow,deny
Allow from all
</Directory>
# ==> End: zope-cgi-bin <==
[------ End file (fragment): /etc/httpd/httpd.conf ----]
===> For the <VirtualHost> 'virtual.yourdomain.tld' configuration:
[---- Begin file (fragment): /etc/httpd/httpd.conf ----]
# ... Notes about ScriptAlias ...
ScriptAlias /cgi-bin/ "/home/www/virtual.yourdomain.tld/cgi-bin/"
# ... The regular cgi-bin directory:
<Directory "/home/www/virtual.yourdomain.tld/cgi-bin">
# ... with very restrictive 'Options None' ...
</Directory>
# ==> Add this:
# Zope.cgi is located in it's own zope-cgi-bin, because ExecCGI
# permission is needed, which is not (by default) enabled in SuSE
# Linux 6.3 and 6.4 for the regular cgi-bin directory.
ScriptAlias /zope-cgi-bin/ "/home/www/virtual.yourdomain.tld/zope-cgi-bin/"
<Directory "/home/www/virtual.yourdomain.tld/zope-cgi-bin">
AllowOverride None
Options None
Options +ExecCGI
Order allow,deny
Allow from all
</Directory>
# ==> End: zope-cgi-bin <==
[------ End file (fragment): /etc/httpd/httpd.conf ----]
Next configure Apache to pass authentication headers to Zope.
Add these lines to the _bottom_ of the Apache web server configuration file
(/etc/httpd/httpd.conf) and/or to the _bottom_ of every virtual host you
might have (enclosed in <VirtualHost>) and that you want to use Zope for
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (5 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
(in our case for the <VirtualHost> 'virtual.yourdomain.tld').
Note that mod_rewrite must be loaded (default it is in SuSE Linux 6.3/6.4).
===> For the global configuration:
[---- Begin file (fragment): /etc/httpd/httpd.conf ----]
# Zope PCGI.
# Final rewrite rules to ensure that '/' is served by Zope.
# These must be the _last_ lines in the Apache httpd.conf file, or,
# if that is appropiate, at the bottom of every <VirtualHost> you
# wish to use Zope for. The order of the rules is significant!
# To facilitate the migration of static content (*.html files in
# /usr/local/httpd/htdocs) into Zope, it is served as /static/.
RewriteEngine on
# Remove the next 2 rules if migration of static content is done or
# if you start with Zope from scratch and do not have html files yet.
RewriteRule ^/static/(.*) /usr/local/httpd/htdocs/$1 [l]
RewriteRule ^/cgi-bin/(.*) /usr/local/httpd/cgi-bin/$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
# Ensure the documentroot '/' is served by Zope and Apache passes
# authentication headers to Zope as well. Zope.cgi is located in it's
# own /zope-cgi-bin/ directory for security reasons ('Options +ExecCGI').
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) /usr/local/httpd/zope-cgi-bin/Zope.cgi$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
[------ End file (fragment): /etc/httpd/httpd.conf ----]
===> For the <VirtualHost> 'virtual.yourdomain.tld' configuration:
[---- Begin file (fragment): /etc/httpd/httpd.conf ----]
# Zope PCGI with at the of <VirtualHost> 'virtual.yourdomain.tld.
# Final rewrite rules to ensure that '/' is served by Zope.
# These must be the _last_ lines in the Apache httpd.conf file, or,
# if that is appropiate, at the bottom of every <VirtualHost> you
# wish to use Zope for. The order of the rules is significant!
# To facilitate the migration of static content (*.html files in
# /home/www/virtual.yourdomain.tld/htdocs) into Zope, it is served as /static/.
RewriteEngine on
# Remove the next 2 rules if migration of static content is done or
# if you start with Zope from scratch and do not have html files yet.
RewriteRule ^/static/(.*) /home/www/virtual.yourdomain.tld/htdocs/$1 [l]
RewriteRule ^/cgi-bin/(.*) /home/www/virtual.yourdomain.tld/cgi-bin/$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
# Ensure the documentroot '/' is served by Zope and Apache passes
# authentication headers to Zope as well. Zope.cgi is located in it's
# own /zope-cgi-bin/ directory for security reasons ('Options +ExecCGI').
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) /home/www/virtual.yourdomain.tld/zope-cgi-bin/Zope.cgi$1 \
[e=HTTP_CGI_AUTHORIZATION:%1,t=application/x-httpd-cgi,l]
[------ End file (fragment): /etc/httpd/httpd.conf ----]
Notes:
a) The order of the rules is important and allows you to keep static html
files (default in directory entry <Directory "/usr/local/httpd/htdocs">)
and have them served as:
http://www.yourhost.tld/static/*
which allows a gradual zope-ing of your static content into Zope served
content. In this setup Zope serves the root (http://www.yourhost.tld/).
If, however, you start from scratch and do not have old static content,
remove RewriteRule's '^/static/(.*)' and '^/cgi-bin/(.*)'.
b) Note the intentional omission of trailing slashes in the last rule.
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (6 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
If you use '^/(.*)' and 'Zope.cgi/$1' instead of '^(.*)' and 'Zope.cgi$1',
respectively, you would have trouble deleting objects in the root folder.
Furthermore, all html served by Zope would have an extra slash at the
beginning ('//index.html' etc.), as can be verified by inspecting the log
file '/opt/zope/var/Z2.log'.
c) Replace '/usr/local/httpd/htdocs/' and/or '/usr/local/httpd/cgi-bin/'
with appropiate locations if you changed SuSE's defaults and/or added
virtual hosts for which you use Zope as well.
d) If your Apache server does not support line splitting with '\', then
every Rewrite* statement must be on one (1) line.
e) The last character in some RewriteRule's ('[l]', ',l]') is the letter l,
not the digit 1.
You may restart Apache at this moment, just to verify that the httpd.conf
is parsed ok, but realize that Zope is not running yet! If you want to be
on the safe side, goto the next step.
If you do want to restart Apache, keep a sharp lookout on the log files
with 'tail -f' in a separate window:
# tail -f /var/log/httpd.access_log /var/log/httpd/*
and then restart the Apache server:
# rcapache restart
Undo the changes (using the backup '/etc/httpd/httpd.conf.bak') if strange
things happen.
Step 7/8. Create start/stop Zope rc script and verify Zope
==========================================================
Using the rc skeleton script, /sbin/init.d/skeleton, as a starting point,
create the Zope rc script, listed next.
Be sure to fully copy and paste the cat command, ie. upto and including
the end-of-script (_EOS) marker or just start an editor and copy the stuff
between 'cat << ...' and '_EOS':
# cat <<'______EOS' > /sbin/init.d/zope
#! /bin/sh
# Copyright (c) 1995-2000 SuSE GmbH Nuernberg, Germany.
#
# Author: Eric Maryniak <e.maryniak@pobox.com>, 2000-06-16.
#
# /sbin/init.d/zope
#
# and symbolic links:
#
# /usr/sbin/rczope
# /sbin/init.d/rc2.d/K20apache
# /sbin/init.d/rc2.d/S20apache
# /sbin/init.d/rc3.d/K20apache
# /sbin/init.d/rc3.d/S20apache
#
# Zope rc start/stop script for SuSE Linux 6.3 and 6.4.
# The Zope base directory is assumed to be: /opt/zope
# More information about Zope at the Zope web site:
#
# http://www.zope.org/
#
. /etc/rc.config
# Determine the base and follow a runlevel link name.
base=${0##*/}
link=${base#*[SK][0-9][0-9]}
# Force execution if not called by a runlevel directory.
test $link = $base && START_ZOPE=yes
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (7 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
test "$START_ZOPE" = yes || exit 0
# The echo return value for success (defined in /etc/rc.config).
return=$rc_done
# The Zope base directory.
zope_base=/opt/zope
# The main Zope process id (Z2 pid) file.
# This should be /var/run under SuSE Linux.
# However, because the z2.py process is running as wwwrun and
# Z2.pid has ownership wwwrun, z2.py cannot write to /var/run.
# So we keep Zope's default (./var).
zope_pid=$zope_base/var/Z2.pid
# The Zope access file.
zope_access=$zope_base/access
# The Apache config file (if any).
apache_config=/etc/httpd/httpd.conf
case "$1" in
start)
## Start Zope with the Zope start script '/opt/zope/start',
## unless it is already running (checked with 'checkproc').
## If this fails, the echo return value is set appropriately.
echo -n "Starting service Zope (z2.py):"
checkproc -f $zope_pid /usr/bin/python 1>/dev/null 2>/dev/null
if [ $? -eq 0 ]; then
# Zope is still running. Do not start a second instance.
return=$rc_failed
else
# Zope is not running (anymore). It is safe to try to start.
( /opt/zope/start 2>/dev/null & ) || return=$rc_failed
fi
echo -e "$return"
;;
stop)
## Stop Zope with the Zope stop script '/opt/zope/stop'.
## If this fails, the echo return value is set appropriately.
echo -n "Shutting down service Zope (z2.py):"
/opt/zope/stop 2>/dev/null || return=$rc_failed
echo -e "$return"
;;
restart)
## Stop Zope and if that goes ok, start Zope.
## In either case, the echo return value is set appropriately.
$0 stop && $0 start || return=$rc_failed
;;
reload)
## Implement a reload as a restart, because the Zope scripts
## and Zope server do not seem to accept a SIGHUP signal to
## reread a (changed) configuration.
$0 stop && $0 start || return=$rc_failed
;;
status)
## Check Zope status with checkproc(8).
## If the process is running, checkproc exits with code 0.
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (8 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
echo -n "Checking for service Zope (z2.py): "
checkproc -f $zope_pid /usr/bin/python 1>/dev/null 2>/dev/null
[ $? -eq 0 ] && echo OK || echo No process
;;
probe)
## Probe for the necessity of a start, restart or reload and
## give out the argument which is required for the applicable
## action (if any). The idea is that you can always safely do:
## rczope `rczope probe`
## If there is nothing to do, or if there is an error condition,
## output will go to stderr, so the command will still work.
# Check Zope status with checkproc(8) and save the result.
checkproc -f $zope_pid /usr/bin/python 1>/dev/null 2>/dev/null
[ $? -eq 0 ] && zope_runs=1 || zope_runs=0
# Zope needs a restart if the access file has been changed.
# Thus, test if the access file is newer (-nt) than the pid file.
# First test if the access file is present and if Zope is running.
# Finally, also suggest a restart if the Apache configuration file,
# if present, is newer (ie. has changed) than the pid file.
# This is a conservative approach: most of the time a Zope restart
# is probably not necessary, but the RewriteCond-ition(s) and/or
# RewriteRule-(s) for Zope may have changed.
if [ ! -e $zope_access ]; then
echo "error: no access file ($zope_access); unable to probe." \
> /dev/stderr
elif [ ! -e $zope_pid ]; then
echo start
elif [ $zope_runs -eq 0 ]; then
echo start
elif [ $zope_access -nt $zope_pid ]; then
echo restart
elif [ -e $apache_config ]; then
if [ $apache_config -nt $zope_pid ]; then
echo restart
fi
fi
;;
*)
echo "Usage: $0 {start|stop|restart|reload|status|probe}" \
> /dev/stderr
exit 1
;;
esac
# Inform the caller verbosely and set an exit status as well.
test "$return" = "$rc_done" || exit 1
exit 0
______EOS
Next, fix permissions and ownerships:
# chmod 0744 /sbin/init.d/zope
# chown root:root /sbin/init.d/zope
And create symbolic links.
Also note the handy '/usr/sbin/rczope', which is SuSE specific.
You have an rc<service> for all services (rcapache, rcmysql, rcsmb, etc.).
I use it often, if not exclusively.
Number 20 is somewhat arbitrary, but Apache has 20 as well, so Zope will
be started shortly after Apache (/sbin/init.d/apache).
# cd /sbin/init.d/rc2.d && ln -s ../zope K20zope; \
cd /sbin/init.d/rc2.d && ln -s ../zope S20zope; \
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (9 of 11) [05/07/2000 11:19:51]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
cd /sbin/init.d/rc3.d && ln -s ../zope K20zope; \
cd /sbin/init.d/rc3.d && ln -s ../zope S20zope
# cd /usr/sbin && ln -s ../../sbin/init.d/zope rczope
I wonder why SuSE uses these relative links (../) so extensively; it has
probably to do with mount points when installing a new system (mount on
/mnt, so /sbin is /mnt/sbin).
Anyways, finish up with making /etc/rc.config (and thus YaST!) Zope aware
(do a grep first, to prevent multiple entries).
Be sure to fully copy and paste the grep/cat command, ie. upto and including
the end-of-script (_EOS) marker or just start an editor and copy the stuff
between 'grep ...' and '_EOS':
# grep -q START_ZOPE /etc/rc.config || cat <<'______EOS' >> /etc/rc.config
#
# Should Zope be started at bootup? (yes/no)
#
# Zope is the leading Open Source web application server.
# Zope enables teams to collaborate in the creation and management of
# dynamic web-based business applications such as intranets and portals.
# Zope makes it easy to build features such as site search, news,
# personalization, and e-commerce into your web applications.
# (Quoted from www.zope.org on 2000-06-14).
#
START_ZOPE=yes
______EOS
Now we can restart Apache and start Zope:
# rcapache restart
# rczope start
Simultaneously, keep a sharp lookout on the log files:
# tail -f /var/log/httpd.access_log /var/log/httpd/* /var/log/zope/*.log
Start a browser and see if you get the Zope welcome page!
If strange things happen, rollback to backupped configuration files or undo
changes.
Step 8/8. Zope administration
=============================
Once Zope is started, you can administer Zope with a locally started
frames-capable web browser (such as Netscape) from this URL:
http://localhost/manage
You may get a warning:
Alert!: Access without authorization denied -- retrying
But then the prompt:
Username for 'Zope' at server 'localhost': superuser
Password: XXXXXXXX
Alternatively, you can administer Zope from a non-local host, provided
the host is a member of the domain (*.yourdomain.tld) to which the access is
restricted.
Changing the Zope adminstrator password.
The Zope administrator account is used, amongst other things, to create
Zope users. One of the first things to do is change the password and add
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (10 of 11) [05/07/2000 11:19:52]
Step-by-step installation of Zope (2.1.7) on SuSE Linux 6.3 and 6.4
domain restriction. You can change the Zope administrator ('superuser')
password from the command line with:
# /usr/bin/python /opt/zope/zpasswd.py /opt/zope/access
Use at least 8 characters for a password, of which at least two (2) should
be non-alfanumerical, and SHA encrypting:
[==== Begin dialog: /opt/zope/zpasswd.py (password change) ====]
Username: superuser
Password:
Vefify password:
Please choose a format from:
SHA - SHA-1 hashed password
CRYPT - UNIX-style crypt password
CLEARTEXT - no protection.
Encoding: sha
Domain restrictions: *.yourdomain.tld
[====== End dialog: /opt/zope/zpasswd.py (password change) ====]
You can now enter use the management screen to add users, etc.
The access file, /opt/zope/access, must be 0600 and wwwrun:nogroup, ie.
the same ownership as the web server (httpd), that, in our case, calls
the Zope.cgi wrapper.
After a password change, you need to restart Zope, as an 'rczope probe'
will tell you:
# rczope probe
restart
Shortcuts for restarting Zope and Apache are:
# rczope restart
# rcapache restart
Note that 'rczope' is a symlink to '/sbin/init.d/zope'.
Apart from the usual 'start', 'stop' and 'restart' signals, a status and
probing signal is also supported:
# rczope status
# rczope probe
The idea of a probe is that is it always safe to:
# rczope `rczope probe`
Installing additional Zope products (zproducts) is simply done by unpacking
them into the Zope base directory:
/opt/zope
Final trivia
============
After you have moved the static content (html files) into Zope, remove
the RewriteRule's '^/static/(.*)' and '^/cgi-bin/(.*)' in the Apache
config file (/etc/httpd/httpd.conf) and restart Apache.
Remove the html files themselves as well after verifying that they have
been incorporated into Zope succesfully.
Don't remove the SuSE online help: best to put that in a virtual host.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/maryniak/zope-on-suse64-howto?pp=1 (11 of 11) [05/07/2000 11:19:52]
SuSE 6.3 Daemon How-To 0.1
How-To: SuSE 6.3 Daemon How-To 0.1
Created by jules. Last modified on 2000/01/30.
This works well on my SuSE 6.3 system and applies to you if you run ZServer.
Robert asked:
| Whats the best way to Automatically Start Zope Server on bootup??
|
| I have SuSE 6.2 system. I am having some trouble (newbie, sorry)
| deciding what the best way to start it up automatically. Docs say
| boot.local, but it stops the process.
|
| Do I run it as daemon? If so how? In background?? Not sure.
Yes, you do run it as a daemon and need to do a few tweaks to your system.
First, login as root. cd to /sbin/init.d/ and copy skeleton to, say, zope or whatever you want to call the script file.
Edit the zope file with your favorite text editor. Change all instances of START_FOO to START_ZOPE and the informational
messages that say "start/stopping foo" to something meantingful to you.
Then in the start) and stop) sections in the file, uncomment (remove the "#" on that line) the startproc and killproc lines.
Depending on how you've got your system setup, you'll need to figure out what goes here. If you're running ZServer so you'll want
to remove the *proc entries and put the path to Zope's start and stop scripts where skeleton has the example /sbin/foo.
In the reload) section, uncomment the "$0 stop && $0 start || return=$rc_failed" line.
The status) section is the same as you've done to start) and stop). I don't think probe) applies here so leave it as it is.
Save this file and exit the editor. Test it by calling
/sbin/init.d/zope start
/sbin/init.d/zope stop; and
/sbin/init.d/zope status
to see if it all works to your liking.
Once that's working, the next step is to create a symbolic link from the zope file you've created to one of the runlevel directories.
I'd put mine in rc2.d as that's a logical place to start services such as Zope.
cd to /sbin/init.d/rc2.d/ and type
ln -s ../zope S22zope
and then
ln -s ../zope K22zope
The numbers after the S and K set the startup order -- the highest number is the last daemon to start.
These files will be called when your system stops and starts providing an environment variable has been defined.
So, the final step is to define that variable. cd to /etc and edit rc.config. Make a backup if you're new to manually tweaking this
file as if you mess it up, your system may not boot!
Scroll down and somewhere just after all the START_* entries, put
#
# Start Zope Daemon? (yes/no)
#
START_ZOPE=yes
If you want to disable Zope when your system starts or stops, you can now use yast to toggle the Yes or No entry.
If you upgrade SuSE to another release, be sure to keep a copy of the zope shell file and /etc/rc.config -- just in case. I don't think
the move from 6.2 -> 6.3 messes anything up but you never know. If yast replaces the files, you'll need to edit them again.
http://www.zope.org/Members/jules/SuSE-6.3_html?pp=1 (1 of 2) [05/07/2000 11:19:57]
SuSE 6.3 Daemon How-To 0.1
Probably the worst case is yast might strip out your edits to rc.config but this is unlikely.
This may work with other versions of Linux but I'm not really intimate with too many other distributions. A copy of my init script
is here. Be sure to modify the path to your local version of Zope.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jules/SuSE-6.3_html?pp=1 (2 of 2) [05/07/2000 11:19:57]
The (old) Product API Tutorial
How-To: The (old) Product API Tutorial
Created by Zen. Last modified on 1999/10/25.
About This Document
This document is the pre-Zope 2.0 Product API Tutorial as written by Amos. The only thing that has so far been changed is the
'comments to' line, as it will most likely be superceeded by forthcomming documentation from Digital Creations. I'm told that this
documentation is still relevant, although there may be 'newer' and 'preferred' ways of doing things. An example product that
demonstrates the newer aproaches can be found at http://www.zope.org/Members/gtk/Boring.
I will try to annotate this document as I learn them, although if someone more knowledgable wants to take over maintenance of it
be my guest. This documentation has been copied here to ensure that it remains if/when the old Zope site at
http://www.zope.org:8080/ is decomissioned. It will be removed when better documentation becomes available.
Approaching the Zope Product API
Extending Zope is quite simple compared to many other environments. Since Zope is based on the clear, elegant Python scripting
language, Zope extensions are quick to develop and clear to maintain.
However, since they can leverage the full power of the Zope framework, writing a Zope Product is more complicated than the
extremely simple extensibility of External Methods. Fortunately the step up in complexity is much less than the dramatic increase
in functionality.
In this tutorial we are going to build a poll Product.
About this tutorial
Version 0.2
Changes
0.2
Cleaned up some language. Added discussion of requirements analysis. Added HTML version of the tutorial.
0.1
Initial version, there may be some inaccuracies.
The included Python and DTML files show some of the steps in the development of a sample poll Product. The Poll.tar.gz
file is the completed Product. To install the completed Product ungzip and untar it inside your Zope directory. Then shutdown and
restart Zope to start using the sample poll Product.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (1 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
What you need to know before you
begin
Before you read this tutorial you should have a basic understanding of how Zope publishes Python objects. You should, of course,
know Python reasonably well. If you've used object publishing before, perfect. If not, you might want to read an introduction to
object publishing before you begin, because the Zope Product API is just a special case of object publishing programming. Finally
you should have a reasonable familiarity with Zope itself. You should understand how to do things like create Folders and
Documents, and use DTML to call methods, and set properties. The Zope Manager's Guide is a good place to learn how to use
Zope.
Defining and analyzing your task
Writing a Zope Product, creating a website, building a database, all these activities are tasks which are done to solve specific
problems. If you want to do a good job building a solution, you must understand your problem first. In the next section we'll talk
about different approaches to solving problems in Zope. However, its a good idea to start with a description of the problem.
Here's a short problem description and analysis for our poll project.
Requirements
Customers manage their areas of a Zope site and want to provide information services. In this case, a customer wants to collect
feedback from users regarding a certain topic. That is, they want to conduct a poll.
A poll contains ballot with question with one or more answers. The ballot is the HTML presentation of the question, answers, and
control elements. Users can select only one answer for a poll. Users can either vote in the poll or view the current results or both.
The results at any time are displayed by default as text using an attractive out-of-the-box format. The appearance of the ballot
should be customizable, and the ballot should be able to be embedded in other documents.
Finally, a poll should allow voting by only a select group of users that have the correct credentials. That is, should be able to be
private.
Analysis
The Poll appears to have actors of Customer, User, Privileged User. The Customer actor is someone able to add and manage Zope
content. The User actor is someone with the Anonymous role that interacts with Zope. A Privileged User appears to be a
non-Anonymous Zope User. This means that the Poll should expose some permissions for operations such as voting and
presenting results, allowing the permissions to include various roles.
The Poll has a number of crisp abstractions. A Poll represents all the state and behavior needed to accomplish the task. A Ballot
appears to be Document with some DTML scripting in it. A Question might not be a separate object. Answers might be separate
objects that leverage the normal Zope Folder interface to manage them.
Is the Product API right for the
project?
After you have described and thought about your pojects, the next step is to evaluate whether your idea would work well as a
Product, or if it could be better implemented some other way. In our case we would like to build a poll product like you see on
Slashdot and many other sites.
We could probably build it with SQL Methods and DTML without too much difficulty. Though this would require mucking
around with the RDBMS whenever you wanted to create a new poll or modify an existing poll.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (2 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
We could probably build a simple polling facility just using DTML, Documents and maybe properties. Again, this approach
would probably involve more administration hassles than we would like.
Using a Zope Product we could provide easy manageabiliy to Zope users who want to create their own polls. Using a Product also
gives us fine control over access controls, and gives us room to refine and develop our poll over a period of time.
So we have a marginal case for turning our idea into a Product. OK, good enough let's get going.
Getting started in plain old Python
Now that we have decided to harness the full power of the Zope Framework to create a reusable Zope Product, the first thing to do
is to write a prototype in Python. This prototype will try to capture the essential algorithms of our product. It will also allow us to
test the guts of the product and make sure they are sound before we start confusing ourselves with exotic Zopisms.
Here's a first cut at what a poll class might look like:
class Poll:
"A multiple choice poll."
def __init__(self,question,choices):
self.question=question
self.choices=choices
self.votes={}
for choice in range(len(choices)):
self.votes[choice]=0
def vote(self,choice):
"vote for a choice"
self.votes[choice]=self.votes[choice]+1
def total_votes(self):
"total number of votes cast"
total=0
for v in self.votes.values():
total=total+v
return total
def votes_for(self,choice):
"number of votes cast for a given choice"
return self.votes[choice]
Basically our Poll object keeps track of what is going on in a dictionary that maps choices to numbers of votes. The vote
method actually casts a vote. votes is a dictionary keyed the the choice index which keeps track of the number of votes for each
choice. The total_votes and votes_for methods just report statistics about the poll.
Of note is the fact that we haven't made any effort to keep people from voting more than once. There are a number of ways we
could handle this, for example we could somehow record the identity of everyone who voted. Then when someone tries to vote we
could look them up in our list of people who have voted. This approach has some problems since it is often difficult to reasonably
identify someone. Let's leave this problem unresolved for now.
Testing our initial implementation in
Python
An original purpose of Zope was to hide the publishing details and allow Python developers to think in terms of Python. Thus, at
this stage let's go into Python and test what we have.
OK, so does our first attempt at a poll work? Let's exercise it a little:
def test():
p=Poll("What's for breakfast?",["spam","eggs","toast"])
p.vote(0) # vote for spam
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (3 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
p.vote(2) # vote for toast
p.vote(0) # vote for spam
print p.total_votes() #returns 3
print p.votes_for(0) #returns 2
You can find this code in the included Poll1.py example file. Run it and see for yourself that it works.
At first glance it seems pretty reasonable. Though if you try to vote for a non-existent choice you get an KeyError. Also there's
nothing forcing you to initialize your poll class with reasonable arguments. Well that's OK for now. But we may want to add some
error handling later. Zope provides a number of error handling features, like automated user notification of errors, and transaction
handling to abort transactions when uncaught exceptions are raised.
Adding a user interface
OK, now we have a semi-functional poll, let's add a user interface so that people can actually use it through the web. The normal
Zopish way to do this is to use DocumentTemplate. A product can choose to use a full fledged Document object, or a plain old
DocumentTemplate object. Let's start simple by defining a DocumentTemplate.HTMLFile to display the poll. Let's create a
file called poll2.dtml:
<p><!--#var question--></p>
<form action="vote">
<!--#in choices-->
<input type="radio" name="choice:int"
value="<!--#var sequence-index-->">
<!--#var sequence-item--><br>
<!--#/in-->
<input type="submit" value="Vote">
</form>
Then create a class attribute for template:
index_html=DocumentTemplate.HTMLFile("poll2.dtml")
OK, looking petty good. Now let's test it:
def test2():
p=Poll("What's for breakfast?",["spam","eggs","toast"])
print p.index_html(p)
You can see the complete program in the Poll2.py example file. Run it and see for yourself that it works.
What this test function does is create a Poll instance and display it using its template. When Zope publishes an object it will
publish an object's index_html method if no other method is specified. So by naming our template index_html we ensure
that it is the default view of our object.
Additionally, when Zope publishes a template it will pass the template's object and the REQUEST as arguments to the template.
So by calling our poll's template with itself as an argument we are emulating to a certain extent what Zope will do when it
publishes our object.
If these methods seem strange to you, go back read up on Zope's object publishing system. You may also want to read some
introductions to the Zope ORB and its conventions for object publishing. The basic idea of publishing an object is that a template
is used to provide a skeleton of how an object should be displayed. Then when an object is published, its data is used to fill in the
template. The Document Template User's Guide gives more details about templates.
Here's what we get when our Poll object is displayed:
<p>What's for breakfast?</p>
<form action="vote">
<input type="radio" name="choice:int" value="0"> spam<br>
<input type="radio" name="choice:int" value="1"> eggs<br>
<input type="radio" name="choice:int" value="2"> toast<br>
<input type="submit" value="Vote">
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (4 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
</form>
This seems more of less right. We have the question followed by a form which allows you to select one of the choices. The form
calls the poll's vote method, with the choice argument set. So far so good.
You've got a publishable object
At this point you've developed a pretty simple publishable Python object. If you are not interested in the Zope framework, you
could at this point start publishing your poll objects with the Zope ORB and be done with it. Of course our poll still doesn't do a
lot of things like return a response when you vote, or handle persistence. However, we do in fact have a publishable Zope
object--pretty easy isn't it.
Now we can start publishing our poll object. If you're an experienced Zope user, this is old news to you. You may wish to skip
ahead to the next section. If not welcome to publishable objects.
The core element in Zope is the object publishing ORB. The publisher lives in the ZPublisher.Publisher package. Zope
runs the publisher for you over the web converting URLs into object calls. After our poll object is plugged into Zope it will be
published normally, by being referenced in a Zope URL. This is exactly how other Zope objects like Folders are published.
Testing the poll with the debugger
You can run the Zope publisher from the commandline to test objects before they are integrated into Zope.
The Zope debugger (formerly bobo.py) is located in the ZPublisher.Test module. It allows you to activate the publishing
ORB from the command line. To test our Poll class we need to create an instance of that class and publish it. Create a new file
poll_test.py:
import Poll2
p=Poll2.Poll("What's for breakfast?",["spam","eggs","toast"])
You now should have three example files, Poll1.py our original class, Poll2.py which contains the latest Poll class
definition, and pole_test.py which imports Poll and creates a Poll instance.
Now you can publish the poll object by issuing this command from within the directory where your two poll files are located.:
<Zope directory>/bin/python <Zope
directory>/lib/python/Zpublisher/Test.py poll_test p
Note, this command should be one complete line.
refers to your Zope directory, for example, /usr/local/Zope. This command will fire up Zope's copy of Python and run the
ZPublisher.Test module with poll_test as the published module and p as the published object. (If you are using the
source rather than the binary distribution of Zope you should use your own copy of Python, rather than Zope's to run the
debugger.)
Here's what you should get back when you run the debugger:
Status: 200 OK
Content-Length: 332
Content-Type: text/html
<html><head>
<base href="http://127.0.0.1/poll_test/p/">
</head>
<p>What's for breakfast?</p>
<form action="vote">
<input type="radio" name="choice:int" value="0"> spam<br>
<input type="radio" name="choice:int" value="1"> eggs<br>
<input type="radio" name="choice:int" value="2"> toast<br>
<input type="submit" value="Vote">
</form>
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (5 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
This is letting you know that ZPublisher will successfully publish your poll object. In fact, this is exactly what Zope will send
back to your browser if you were able to publish your poll object through the web right now.
We can see that we're getting a status of 200 which is right for most conditions. Also the HTML looks pretty much like what we
defined in our template. The main difference is that Zope has inserted a base href for us.
So, now let's test what will happen when you vote by submitting the form. We can do this by telling the debugger to publish the
voting method like so:
<Zope directory>/bin/python <Zope
directory>/lib/python/Zpublisher/Test.py poll_test
p/vote?choice:int=0
Note, this command should be one complete line.
This tells the debugger to publish the poll's vote method with an argument of choice=0. So we are simulating what would
happen if the poll was published on the web and we clicked the spam radio button on the poll's form and clicked the submit
button.
This is the result we get:
Status: 204 No Content
Which is understandable considering that the vote method doesn't return anything.
So far so good. Our work with the debugger is done for now, but we may have good reason to come back to it later. The debugger
can do some pretty powerful things including allow you to run the publishing process through the standard Python debugger
which can come in pretty handy. Also, right now we are debugging an object which is not installed in Zope. We can however,
debug objects inside Zope. The only difference is to tell the debugger that we want to operate on object inside Zope's Main
module, rather our own module.
How are you doing?
If you are lost at this point you should read up on the Zope ORB and come back to this tutorial later when you have a better
feeling for how Zope object publishing works.
If you're doing fine, get yourself a snack and then let's continue.
Going from publishable object to Zope
Product
There is a long way between having a publishable object and having a Zope product. You have to attend to a number of things in
order to turn your publishable object into a functioning Product:
Create a Product package
All Zope products need to be structured as Python packages. There are a number of details you will need to attend to in
order to make your package fulfill all the Product requirements. Most of these requirements are met by including various
attributes in your package's __init__.py file.
Adhere to the Product API
The Zope Framework gives a managed environment providing a number of facilities for Products. The more Zope
conventions you adhere to, the more manageable your Product will be in Zope. For example Zope requires specific
handling of object creation, object access controls, object meta data, etcetera. You will fulfill these requirements by
inheriting from Zope Product classes, implementing appropriate methods and setting appropriate class attributes.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (6 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
Product packaging
A Zope product has many attributes, but one of the most essential, is that it lives as a Python package inside the
/lib/python/Products directory. Zope interprets all packages in this directory as Products, and tries to install them at
startup.
To create a package you need to create a directory inside the lib/python/Products directory to hold your files. So we
create a directory called Poll. Inside our package directory we need at least two files: an __init__.py file which lets Python
know that our directory is a package, and additional Python files. In our case we will probably only have two Python files,
__init__.py, and Poll.py.
Our package directory will hold other files too, for example, DTML files, and an icon GIF file. You may want to include other
files in your package, too such as README files.
You also may choose to locate some support Python files in the shared package. The shared package is a central repository of
utility modules located in lib/python/Shared. To use the shared package create a sub-package inside it with the name of
your organization. This measure is to help minimize the risk of package name collisions. So for example you could create a
package KingSuperInc inside the shared package. Then you could import that package like so:
import Shared.KingSuperInc
This way you can access to your shared package from within your product Python files. You may even want to create
sub-packages inside your shared package.
Beefing up the Poll class with
inheritance
Now that we've got a basic publishable poll class, we're ready to start dressing up our poll in Zope finery. Zope can provide our
classes with many services but let's start with the basics. One of the most common ways to give your Zope product abilities is to
use standard Zope mix-in classes. Here's how we'll define our class for starters:
class Poll(
Acquisition.Implicit,
Persistent,
AccessControl.Role.RoleManager
OFS.SimpleItem.Item,
):
"A Poll product"
...
This is a lot of inheritance. Let's look at what we're buying.
Acquisiton.Implicit provides our poll with the ability to acquire attributes from it container objects. This probably won't
be vitally important to our poll object, but it might come in handy. More importantly, acquisition is central to the Zope way. Even
if we don't plan on using acquisition too much, we shouldn't assume that attributes of the poll object won't need to use it. This is
less important for non-Folderish objects, but take my word for it--if you're writing a product, inherit from
Acquisition.Implicit.
Persistent gives your Product the ability to have its state transparently stored and retrieved in Zope's object database. By
inheriting from the class your Product will also participate in transactions, and all the other benefits of Zope's storage system.
Products must inherit from this class. Using persistence requires a small amount of care and discipline. Read up on Bobo POS to
find out more.
AccessControl.Role.RoleManager allows your class to use Zope's permissions machinery. By inheriting from
RoleManager we allow our poll to be managed normally. For example, we will be able to have a Security tab in our poll
management screen to set and modify permissions.
OFS.SimpleItem.Item is a basic Zope base class for non-Folderish Products. This class gives your Product the ability to be
managed normally via the Zope management interface. By inheriting from the class your Product gains the ability to be cut and
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (7 of 18) [05/07/2000 11:20:13]
The (old) Product API Tutorial
pasted, and to work with management tabs. It also gives you some standard methods including manage which is the standard
management URL for Zope products. You also get the title_or_id, title_and_id, and this DTML utility methods.
Finally this class gives your Product basic tree tag support.
This collection of base classes provides any simple Zope Product with a good place to start. You may want to add to or change
from this list of base classes later when you have a better understanding of Zope's internals. You may also want to override some
of the methods of these base classes as you gain more knowledge.
How Product creation works
Now that our poll class is starting to get closer to a working Product, we need to add some methods in order to allow Zope to
manage it as a Product. By inheriting from OFS.SimpleItem.Item we gain a couple management methods, notably manage,
however, will still need to provide other methods.
One of the defining characteristics of Zope Products are that they can be added to Zope Folders. To allow your Product to be
created in this way you need to provide some management methods.
The normal way to accomplish Product creation is to provide two methods for each product. The first method displays a Product
creation form to collect pertinent data such as the Product id, title, and various parameters. The second method is called by
the creation form and it actually creates the Product object and installs it in its parent Folder.
If you are paying close attention, you may notice that these methods can't be methods of the Product's class, since they are needed
before the Product object is created. In fact, these two creation methods need to be methods of the Zope Folder class.
To facilitate the Product creation process, Zope installs special Product creation methods in the Zope Folder class when it starts
up. So to allow your Product to be created in Zope, you will have to write Folder methods.
Product creation form
OK, so how will we handle actually creating poll objects and installing them in Folders? It's relatively simple. Let's start with the
poll creation form defined in pollAdd.dtml:
<html>
<head><title>Add Poll</title></head>
<body bgcolor="#FFFFFF">
<h2>Add Poll</h2>
<form action="manage_addPoll" method="POST">
<table cellspacing="2">
<tr>
<th align="LEFT" valign="TOP">Id</th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="id" size="50">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP"><em>Title</em></th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="title" size="50">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Poll Question</th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="question" size="50">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Poll Answers<br>(one per
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (8 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
line)</th>
<td align="LEFT" valign="TOP">
<textarea name="choices:lines" cols="50"
rows="10"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><br><input type="SUBMIT" value="Add"></td>
</tr>
</table>
</form>
</body>
</html>
What does this form do? It collects information needed to create a poll. We recognize the question and answers parameters,
but what are the id and title for?
Zope Products are connected to each other in an object hierarchy. Each Folder has a number of sub-objects which are bound to its
attributes. So for example a Folder might contain a Document with an id index_html. This means that the Folder's
index_html attribute is bound to the folder, and that the Document's id attribute is index_html. It's pretty simple actually.
Another important thing about an object's id is that its id is its web address. For example, if our example Folder has a URL of
/myFolder then, the URL of the index_html Document inside it would be /myFolder/index_html. In fact all Zope
URLs consist of concatenations of Zope object ids.
An object's title is an optional string property which is used to give it more description than is possible with an id.
There are a few more interesting things about our poll creation form. If you look closely you will notice that the name of the
answers textarea is choices:lines this is a specially encoded name which tells the Zope ORB to convert the contents of to a
list of strings rather than one large string with line breaks. To find out more about form data marshalling and coercion, see the
Zope ORB documentation.
Finally we note that the action of the poll creation form is manage_addPoll. This is the name of the poll installation method
which we will describe next.
Product installation method
The job of the installation method is to create an instance of the Product and install it in the parent Folder. As we mentioned
before, this method will be installed in the Folder class by Zope.
Here's how we define the product installation method for our poll:
def addPoll(self,id,title,question,answers,REQUEST=None):
"""Create a poll and install it in its parent Folder.
The argument 'self' will be bound to the parent Folder.
"""
poll=Poll(id, title, question, answers)
self._setObject(id, poll)
if REQUEST is not None:
return self.manage_main(self,REQUEST)
This method creates a poll and then installs it with the Folder's _setObject method. The reason it tests for the REQUEST is
allow the method to forgo returning a management screen if it is not called from the web.
Next we will see how we need to specially indicate the object creation form and the object installation method in our Product's
package so that they are recognized by Zope.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (9 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
Making creation methods available to
Zope
Now that we have a creation form and an installation method defined in Poll.py it's time to add them to __init__.py so that
Zope can easily find them when it inspects our package.
You must define these methods in a special methods dictionary in your Product's __init__.py file. Here's how we do this for
our poll Product:
methods={
'manage_addPollForm': Poll.addPollForm,
'manage_addPoll': Poll.addPoll,
}
So what we've done is let Zope know about two special methods of our Poll module, addPollForm which is the creation form,
and addPoll which is the installation method. By listing them in the methods dictionary, we are telling Zope to install them as
methods of normal Zope Folders at startup time. These methods will be by bound to Folders with the names we give them in the
methods dictionary. So, for example, Folder.manage_addPollForm is Poll.addPollForm. You may wonder why
we rename these methods. By convention creation methods (and other management related methods) begin with the prefix
manage_.
By now we have done much of the work of making our poll class available to Zope as a Product.
Are you still with us?
Right now the process of turning your simple publishable object into a full fledged Zope Product is probably starting to seem a bit
tedious. It's true, it is a bit tedious. Perhaps in the future there will be a stream lined process.
Take heart. You're most of the way there. Most of the rest of the work is fairly straightforward. Soon you'll bask in the glory of
seeing your Product appear the the Zope add list.
Get another snack and when you're ready let's continue Productifying our poll.
Product editing
Next on our list of features to add to our poll is the ability to edit its attributes through the web. This process closely mirrors the
process of creating and installing the poll. It will be accomplished with two methods: an editing form which displays the current
information and a method which processes the form input and changes the poll object. These two components will closely mirror
the poll adding and installation methods, with the exception that they will be methods of the Poll class itself, rather than the
Zope Folder class.
Product editing form
Our poll editing form is quite similar to the poll creation form. In fact, it's easiest just to make a copy of the adding form and go
through and make small changes to create the editing form. Here's what it looks like:
<html>
<head><title>Edit Poll</title></head>
<body bgcolor="#FFFFFF">
<!--#var manage_tabs-->
<h2>Edit Poll</h2>
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (10 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
<form action="manage_edit" method="POST">
<table cellspacing="2">
<tr>
<th align="LEFT" valign="TOP"><em>Title</em></th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="title" size="50"
value="<!--#var title-->">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Poll Question</th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="question" size="50"
value="<!--#var question-->">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Poll Answers<br>(one per
line)</th>
<td align="LEFT" valign="TOP">
<textarea name="choices:lines">
<!--#in answers-->
<!--#var sequence-item fmt="html-quote"-->
<!--#/in-->
</textarea>
</td>
</tr>
<tr>
<td></td>
<td><br><input type="SUBMIT" value="Edit"></td>
</tr>
</table>
</form>
</body>
</html>
There are a couple things to notice here. For one we've included a manage_tabs variable at the top to create the characteristic
Zope tabbed management interface. We've also changed the form's action to point to manage_edit which is the name of editing
method that will we discuss next. Also we do not allow editing the the poll's id. This should be handled by the standard Zope
rename facility. Finally, we've included value attributes for the form inputs to supply the current values. In the case of the
answers textarea, we've dumped all the answers into the textarea with an in loop.
Product editing method
To allow editing of the poll's attributes, we need to have a method to accept the editing form's input. By convention such a method
is called manage_edit though you need not always name your editing method this. In fact complex products will have a
number of editing methods with different names.
The editing method isn't very complex. It simply updates the poll's attributes and returns a confirmation message to the user
indicating that their update was successful. it is a good idea to provide the user with a confirmation message when they perform a
management task, unless the results of their action is readily visible in some other way.
Here's what our poll's editing method looks like:
def manage_edit(self,title,question,choices,REQUEST=None):
"edit the poll's characteristics"
self.title=title
self.question=question
self.choices=[]
for choice in choices:
if choice:
self.choices.append(choice)
if len(self.choices) < 2:
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (11 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
raise ValueError, "You must supply at least two valid
choices."
if REQUEST is not None:
return MessageDialog(
title='Edited',
message='<strong>%s</strong> has been edited.' %
self.id,
action ='./manage_main',
)
Since this is a published method it needs to have a doc string. The method replaces the title and question attributes. For the choices
it makes sure to only add non blank choices to the poll.
The method returns a confirmation message using the MessageDialog facility, which is a standard Zope document for
notifying users. The title and message of the MessageDialog inform the user of what has happened. The action of the
message dialog is the URL that the user will be taken to when they click OK after reading the confirmation. Since manage_main
is the default management screen for Zope Products we have chosen that as the URL to display following a confirmation.
The method tests to see if the REQUEST exists before sending a confirmation message since, this will indicate whether the
method has been called through the web or not. In cases where the method is called in DTML or External Methods, etcetera, it is
not necessary to return an HTML confirmation.
OK, so now our poll is looking pretty complete. Congratulations! At this point there is very little logic that we need to add to it.
The majority of what's left to do are niggling Zope details which we need to nail down.
Defining Zope permissions
Permissions are at the heart of Zope's access control system. All Products need to define their use in terms of permissions.
Permissions are what allows users to define who gets to access objects through the web and through DTML. Permissions describe
activities, and they are bound to roles through the management interface. Roles in turn are bound to users.
A permission consists of a number of methods along with default roles settings. To define permissions the Product class should
have an __ac_permissions__ class attribute which is a tuple of tuples. The individual permissions are defined by a tuple of
three items: the permission name, a tuple of methods bound to the permission, and an optional tuple of roles.
Here's how we initially define permissions for our poll Product:
__ac_permissions__=(
('View management screens',('manage_tabs','manage_main')),
('Change permissions',('manage_access',)),
('Change Polls',('manage_edit',),('Manager',)),
('Vote',('vote',),('Anonymous','Manager')),
('View',('','total_votes','votes_for')),
)
You should not assign the same method to more than one permission. Also you should probably only count on Manager and
Anonymous roles being present when you set your default roles. Finally you should try to define as much of your Product's
permissions with the same names as those of existing Products. This way your Product can profitably inherit permission settings.
Categorizing methods into permissions is a tricky business.
In our case we have chosen to use three existing permissions: View management screens, View and Change
permissions. We have added only two new permissions, Vote and Change Polls.
In order for your Product's permissions to work well with acquisition, you should export your Product's unique permissions to the
Folder class, so that they can be set on Folders and acquired easily. To do this you need to define a second set of
__ac_permissions__ in your Product's __init__.py file. So in our Poll package's __init__.py file we define the
following:
__ac_permissions__=(
('Add Polls',('manage_addPollForm','manage_addPoll')),
('Change Polls', ()),
('Vote', ()),
)
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (12 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
As the example shows, you needn't fully define permission that are already defined in your Product's class. In other words, since
Change Polls is already defined in our poll class, we only need to add its name here.
Also, you may wish to added permissions in the __init__.py file to cover the creation methods that you define there. We have
done this by defining Add Polls here. Why didn't we define Add Polls in our Poll class? Because the Add Polls
permission doesn't actually cover methods of the Poll class, it covers methods of the Folder class. Remember that Zope installs
the product creation methods in its Folder class.
Whew, we've got permissions mostly nailed. Now on the the next detail.
Defining management tabs
All Products are manageable through the web. This is the Zope way. The standard convention for Product management is to
provide a series of tabbed management screens. These tabs are visible on the top of the right frame in the Zope management
interface. As we have seen you create these tabs with the manage_tabs method in the DTML of the management screens. You
can inherit the manage_tabs method, but you need to define the tabs in order for the method to do its work.
To provide tabs for your Product you need to define them with the manage_options class attribute. Here are the tabs that we
decide to define for our poll Product:
manage_options=(
{'label':'Properties','action':'manage_main'},
{'label':'View','action':''},
{'label':'Security','action':'manage_access'},
)
This tuple of dictionaries defines a couple management tabs. The label item defines the tab's name. The action item defines
the method that the tab calls. Note that a blank method is the same as calling index_html. You can optionally provide a
target item to indicate the frame target.
Defining management tabs is not always straightforward. In general you should group like management functions together onto
one management screen. You can also choose to include more standard management tabs than we have chosen to include like
Undo and Find. You're best bet is to study how existing Products work and try to steal good ideas from them.
If you're paying very close attention you will notice that Zope automatically adds a Help tab for you.
Specifying Product meta-type
An object's meta-type is a string which describes what sort of Product it is. A meta-type is just a name; it has nothing to do
with Python meta-classes. All Products need to have different meta-types. The meta-type of Folder objects is Folder, the
meta-type of Documents is Document. You get the idea. A meta-type is the name of a Zope Product as displayed in the
management interface, and it is needed to create a Zope Product.
To define a meta-type for your object you need to include a meta_types tuple in your Product's __init__.py file. Here's
how we will do this for our poll Product:
meta_types=(
{'name': 'Poll',
'action': 'manage_addPollForm'
},
)
So basically what we have is a tuple of addable objects with their names and the object creation form method name in dictionaries.
Note that your Product can define more than one meta-type, if it defines more than one type of addable object.
You will also need to define you Product's meta-type in its class as a class attribute. For example this is how we indicate our poll's
meta-type:
class Poll:
...
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (13 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
meta_type='Poll'
...
The Product's meta-type appears in the management interface in the list of objects which can be added. The meta-type is also used
in a number of Zope methods (such as objectValues) when querying an object about its sub-objects.
OK, that was pretty easy. Only a few more details and we're done.
Product icons
Products are identified in the management interface by their icons. Icons are 16 pixel by 16 pixel GIFs with transparent
backgrounds. By convention Folderish products have icons that looks somewhat like a folder.
Product icons are defined in the Product's __init__.py file with the misc_ mapping.
Zope defines a special top-level object, misc_, which has a sub-object for each installed Product. Each Product sub-object in
misc_ has sub-objects for each of the keys defined in the misc_ dictionary in the Product's __init__.py. This provides a
mechanism for creating Product-dependent, instance-independent objects that are web accessible. By convention, misc_ only
holds Product icons.
Here's our poll's misc_ definition:
misc_={
'poll': ImageFile('poll.gif',globals()),
}
It only defines one thing--the poll's icon. For your Product's icons you should use 'ImageFile's.
Objects defined in a Product's misc_ mapping are made available through the web at /misc_//.
To allow Zope to find this icon you need to specify its URL in your Product's class. So here's how we indicate our poll's icon:
class Poll:
...
icon='misc_/Poll/poll'
...
We're done for now
Hooray! We've now wrapped up our poll Product. It was a fair amount of work, but hopefully it helped you learn a little more
about how Zope functions under the hood.
Now that we have our Product fleshed out let's install it and test it out. You should not break for a snack now, the anticipation is
too great at this point.
Installing the poll
To install you Product simply create a directory inside lib/python/Products and place your files in it. So for our poll we
will create a directory lib/python/Products/Poll and inside we will put these files:
__init__.py
misc poll product data.
Poll.py
the poll class definition.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (14 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
poll.dtml
the display method.
pollAdd.dtml
the creation form.
pollEdit.dtml
the editing form.
poll.gif
the icon file.
Later we may want to add additional files to the distribution like a README file and a version file.
To make the Product accessible to Zope you now need to shutdown and restart Zope. The best way to do this is via the Zope
Control Panel.
Testing the poll
Now let's see if our Product works. If the Product took, you should see Poll listed in the pop-up menu of items to add on the
management screen. You can also check things out under the Zope Control Panel. In the Control Panel there is a sub-object called
Products. If your Product failed to load you should be able to see a traceback there which will give you information about what
went wrong loading your Product.
If a Product fails to load, existing instances of the Product will be unusable and will appear broken.
Edit the Product. Shutdown and restart Zope. Repeat as necessary until the Product loads.
If you really mess things up you can cause Zope to fail to start. In these cases you'll need to read the traceback and figure out what
you did wrong.
Once the poll actually loads correctly into Zope we can test it further by creating a poll object, editing it and using it.
Refining the poll
As soon as you have a semi-functional Product it's time to start refining. In the case of our Poll product the main things we need to
work on are user interface issues. We have a simple method for displaying a Poll's form, but no feedback mechanism. Another
thing we would like to be able to do is to embed a poll in another document. Here's a plan to deal with these issues.
●
Factor the user interface into a couple different components
●
Provide a default view and operation for the Poll
By factoring the UI, we allow access to different poll component to be embedded in Documents. By providing a default view and
operation we allow simple use of a poll without any other Documents.
Let's factor the voting form and results display into two class attributes, form and results. Next we provide a default poll
view, index_html which calls these components. We also need to make sure that we define the appropriate permissions for
these different user interface components.
Here's the poll form:
<p><!--#var question--></p>
<form action="<!--#var id-->/vote" method="post">
<!--#in choices-->
<input type="radio" name="choice:int"
value="<!--#var sequence-index-->"> <!--#var
sequence-item--><br>
<!--#/in-->
<!--#if action-->
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (15 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
<input type="hidden" name="action" value="<!--#var action-->">
<!--#/if-->
<input type="submit" value="Vote">
</form>
It's basically our original poll user interface. It is not a complete HTML document.
Here's the poll results template:
<!--#if total_votes-->
<p>
<!--#in choices-->
<!--#var sequence-item-->: <!--#var
expr="percent_for(_['sequence-index'])" fmt="%2d"--> %<br>
<!--#/in-->
</p>
<p>Total number of votes cast: <!--#var total_votes--></p>
<!--#else-->
<em>No votes have been cast yet.</em>
<!--#/if-->
Again this is an HTML fragment. It uses a new method we've added called percent_for to display the percentage of votes for
each choice.
Finally we can redo the main poll interface template now:
<html>
<head><title>Poll:<!--#var title_or_id--></title></head>
<body>
<h1>Poll: <!--#var title_or_id--></h1>
<h2>Vote</h2>
<!--#var form-->
<h2>Results</h2>
<!--#var results-->
</body>
</html>
This template presents a complete way to use the poll. It allows you to vote, and it displays the results.
To finish up the user interface changes we need to modify the vote method a little:
def vote(self,choice,action=None,REQUEST=None):
"""Vote for a choice
'choice' is an integer which represents a choice.
'action' is an optional URL which the user will
be taken to after they vote."""
# this is done like this in order to allow Zope's
# persistence system notice that we're updating
# a mutable attribute.
votes=self.votes
votes[choice]=votes[choice]+1
self.votes=votes
if action is None:
action=''
if REQUEST is not None:
return MessageDialog(
title='Vote Accepted',
message='Your vote has been accepted.',
action =action,
)
What we have here is a moderately flexible way of responding to a user when they vote. We display a standard Zope message
indicating that their vote was accepted, and then we send them to a URL. If you call the vote method with an action URL you
can set the return page, if not, you are sent to the poll's default view after you vote.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (16 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
We can always do better
Well, this is a reasonable solution, but we could do much better. For example, it would be nice to make the Poll Folderish, and
provide form, results, and index_html not as fixed class attributes, but as default Documents inside the Poll. Then users
could modify the look and feel of a Poll and even add more methods.
There are many other things that might be nice to do as well. Like we might want to provide more hooks for different voting
controls. For example, we could keep track of users and make sure they only vote once. We could limit anonymous voters by IP
address.
We could also expand our concept of poll to include more than just multiple choice polls. For example, we could allow write in
votes, voting for multiple choices at once, and weighted votes--ie allowing you to vote for or against choices.
We might want to have some concept of poll closure. Managers should be able to close a poll to further voting. Alos, polls could
have some time limit, so they close automatically after a week for example.
We could add graphs showing voting trends and results. In fact we could make simple ones by stretching GIFs in proportion to the
numbers of votes.
OK, we're not going to do these things right now, but you might want to try some of these ideas on your in order to learn more.
Since we chose to create a Zope Product, all these goals are easily within our reach, and if we plan it right, we should be able to
roll out our poll Product now, and add more features to it later.
Congratulations
If you made it this far, congratulations! You've slogged through a long discussion. At this point you might want to re-read the
example poll code and maybe the final poll Product code, now that you understand some of the ideas behind it better.
The rest of our discussion will cover issues that arise as you get more comfortable with writing Zope products and prepare to share
them with other people.
Now might be a good time for another snack, or maybe a actual meal--you've earned it.
Control Panel Product features
There are a couple interesting tricks you can do with the Zope Control Panel. Besides showing your Product's traceback if it's
broken, the Control Panel will show your Product's version information and a README file.
To include version information with your Product create a version.txt file at the top level of your Product's package
directory. Include one line of text in the file to name your Product's version. For example we might want to include something like
this:
Poll 0.1.0
To indicate this is an early version of our poll Product.
Additionally if you include a README file with your Product, it will be made available through the Control Panel. You need to
make sure to name your README file README.txt and to include it at the top level of your Product's package directory. Zope
will interpret your README file as structured text. To find out more about structured text visit the Zope site's documentation
area. Suffice it to say that your README file should look OK if you use normal text and include blank lines in between
paragraphs.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (17 of 18) [05/07/2000 11:20:14]
The (old) Product API Tutorial
Distributing Products
The standard way to distribute a Zope product is in a gzipped tar archive. You should arrange you tarball so that it can be untarred
from within the Zope directory. For example assuming we have developed our poll Product in Zope and that it is currently in
lib/python/Products/Poll, we can create an archive for our poll product by first cding to the Zope directory, and then
issuing this command:
tar cvfz Poll.tar.gz lib/python/Products/Poll
If your Product relies on resources in the Shared package you will also need to include those packages in your tarball.
If you reasonably document your Product with doc strings, Zope gives you reasonable online help via the help tab. It's really worth
it to make the effort to document your Product.
In order for your Product's methods to show up in the online help, you need to have both a doc string for the method and assign it
to a permission.
Also don't forget the version.txt and the README.txt files that we mentioned earlier. Providing these files helps others
who want to use your Product quite a bit.
Upgrading and evolving Products
Issues can occur when you change your Product class and then reload objects that were created with an earlier version of the class.
The simplest way to handle these sorts of situations is to provide class attributes as defaults for added attributes. For example if
the latest version of your Product expects a improved_spam attribute while earlier versions only sported spam attributes, you
may wish to define improved_spam class attribute in your new class so your old object won't break when they run under your
new class. Another solution is to use the standard Python pickling hook __setstate__, however, this is in general more error
prone and complex.
While you are developing a Product you won't have to worry too much about these details, since you can always delete old
instances that break with new class definitions. However, once you release your Product and other people start using it, then you
need to start planning for the eventuality of upgrading.
Another nasty problem that can occur when unpickling objects is what to do when you change the name of your Product's class.
This should be avoided by not renaming your Products class, unless you wish to lose all stored instances.
We're done
OK, that's it for now. Now you should be ready to build your own Products and distribute them.
Go for it!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/Zen/howto/ProductAPITutorial?pp=1 (18 of 18) [05/07/2000 11:20:14]
The DTML Name Space How-To
How-To: The DTML Name Space How-To
Created by michel. Last modified on 1999/09/15.
Zope How-To: DTML Name Spaces
Introduction
This is an advanced DTML How-To, for a gentle introduction to DTML, see the DTML Manual
Like Python, the language Zope is written in, DTML has a dynamic model of name spaces. A name space is a difficult concept
to explain, therefore we will use some simple examples.
In DTML, a name space is a group of related names. Name spaces can be empty, or contain any number of objects. When in the
context of a name space, you can access any of it's members by name. Name spaces are collections, meaning that when the
collection goes away, all reference to the objects in it go away also. When the context of a name space is left or no longer
applicable, that name spaces objects and names no longer make sense in the context of the DTML code and can be reassigned.
The Stack
There can be many name spaces in DTML. These name spaces are arranged in a stack. Name spaces are pushed and popped on
to and off of the name space stack during the execution of DTML. At any time in DTML, the sum of all the name spaces on the
stack is available through the name space. Therefore, if a deeper name space defines contains the object ADeeperThought, the
name spaces will be queried, starting at the top of the stack, until ADeeperThough is found. If no object is found, an error is
raised. A deeper name space object can be overridden with a higher name space.
Example: The In Tag
A common sight in DTML is iterating over the contents of a Folder. Create a folder called MyFolder and a DTML Method
called MyMethod containing the following:
<dtml-in objectValues>
<dtml-var id>
</dtml-in>
Before entering the block, id was bound to MyFolder. When the block is entered, a new name space is popped onto the name
space stack. The new name space contains the name space of the sequence-item in the tag. Because new name spaces
override old ones, the new 'sequence-item's id attribute is printed in our example instead of MyFolder.
DTML tags push and pop name spaces onto the stack. Some DTML tags, like and , do not change the name space at all, but , and
modify the name space stack in their own way.
The tag pushes the current sequence-item onto the name space stack and then pops it off the stack when the end of the tag is
reached. In each iteration through the sequence objectValues, the id of each item in the sequence is rendered.
Example: The With Tag
The tag allows you to explicitly push a certain object's name space onto the top of the name space stack. When the tag is closed,
the object's name space is then popped off the stack. The With tag works well in conjunction with the In Tag and shows off how
many name spaces can really get involved. In a DTML Method, push the name space of an object called Catalog into the stack
and iterate over its searchResults attribute:
<dtml-with Catalog>
<dtml-in searchResults>
<dtml-var id>
</dtml-in>
</dtml-with>
searchResults is an method of Catalog. It returns a sequence of result rows. Thus, in this code, first the name space of
http://www.zope.org/Members/michel/HowTos/NameSpaceHow-To?pp=1 (1 of 4) [05/07/2000 11:20:19]
The DTML Name Space How-To
Catalog is pushed on the stack, then for each sequence-item in the sequence returned by searchResults the items
name space is pushed onto the stack, used to look up id, and then popped off. After the sequence is iterated through, the tag
closes, and 'Catalog's name space is pushed off the stack.
Example: The Let Tag
The Let tag let's you initialize and push a new name space onto the name space stack. The arguments to the Let Tag are evaluated
and placed into the top most name space. For example:
<dtml-let x=5 y=7>
<dtml-var x><dtml-var y>
</dtml-let>
The let tag creates a new name space, enters the integers x and y into the new name space, and pushes the name space onto the
stack.
The arguments to let can be called by name or by expression. See The Let Tag How-To for details.
Example: The _ object
The DTML name space stack is bound to the object _. _ is a mapping object, meaning the name space stack can be searched for
attr with "_[attr]". This, in python speak, is identical to calling __getitem__() on the _ name space stack.
The _ object can be used to create new name spaces by calling it:
<dtml-with "_(name='bob', relation='uncle')">
<dtml-var name> is your <dtml-var relation>
</dtml-with>
This calls the _ object and creates a name space continuing the strings name and relation. This new name space is then
pushed onto the name space stack by the tag.
Info: Default content of _ in a Zope request
When a request comes into Zope, ZPublisher is kind enough to push a couple of name spaces onto the stack for us. For example,
REQUEST and RESPONSE are objects that represent the request and the response of this particular name space context. Because,
for example, PARENTS is a member of REQUEST, PARENTS is immediately available in the name space. In a DTML Method:
<dtml-in PARENTS>
<dtml-var id>
</dtml-in>
would be valid, because ZPublisher pushes the name space of REQUEST onto the name space stack before any DTML is
executed. It would be redundant to say:
<dtml-with REQUEST>
<dtml-in PARENTS>
<dtml-var id>
</dtml-in>
</dtml-with>
Acquisition
Acquisition offers a twist to our name space theory. In strict DTML, the name space stack defines the context in which the DTML
executes. Zope, however, provides another name space called Acquisition. These two name spaces together define the complete
context of a Zope request.
For example, Create a catalog in the root folder called Catalog. In a sub-folder, create a DTML Method called Search
containing:
http://www.zope.org/Members/michel/HowTos/NameSpaceHow-To?pp=1 (2 of 4) [05/07/2000 11:20:19]
The DTML Name Space How-To
<dtml-with Catalog>
<dtml-in searchResults>
<dtml-var "getpath(data_record_id_)">
</dtml-in>
</dtml-with>
The DTML Method started out with it's containers name space stack. Because the root folder is in the acquisition path of
the DTML Method, Catalog was acquired from the root folder and it's name space was pushed onto the top of the name space
stack.
Acquisition paths are built during a Zope request. As the request traverses Zope objects looking recursively for its final
destination it builds the acquisition path from the root folder down to the object being requested. If had a DTML Document name
'MyDocument in a Folder named MyFolder which was in the root folder, your Zope would look like this:
/
MyFolder
MyDocument
If the request is for /MyFolder/MyMethod then the acquisition name space stack contains three object:
top> MyDocument MyFolder bot> / (the root folder)
Thus, the acquisition path of the MyDocument object object is /MyFolder/MyDocument.
The acquisition path is used to build the DTML name space for a DTML object. When DTML execution begins, the DTML name
space is pre initialized with the name space of the REQUEST object and that of the DTML object itself. Because the DTML
object's name space includes the acquisition path of MyFolder and the root folder, they are also by default in the DTML name
space stack.
In the following scenarios, lets illustrate the name spaces.
Create a Folder in the root folder called One Folder. Create another in the same location called TwoFolder Inside OneFolder
create a DTML Document called OneDocument and inside TwoFolder create a DTML Method called OneMethod:
/
OneFolder
OneDocument
TwoFolder
OneMethod
If the user were to request /OneFolder/OneDocument, the DTML name space stack would contain:
1 The name space of REQUEST (TOP)
REQUEST contains all objects marshaled by ZPublisher as arguments to the request. For example, if you had a form
element named textbox, textbox would be defined in this name space, and equal to the value of the submitted form
element.
2 The name space of OneDocument.
An object's name space is always included in the dtml name space.
3 The acquired name space of OneFolder
OneFolder is acquired because it is 'OneDocument's container. The name space of OneFolder is all of the contents
of OneFolder, including OneDocument and any other objects that happen to be be in OneFolder. Thus, an object's
siblings are in the DTML name space.
4 The acquired name space of the root folder / (BOTTOM)
By definition of Acquisition, the root folder is in every acquire path. Therefore, the root folder servers two purposes, as
the root of the object hierarchy, and as the root of the acquisition hierarchy. Any items placed in the root folder are
acquirable by any object in Zope. The root folder's name space comes in handy as a global name space, but this
concept should not be abused.
http://www.zope.org/Members/michel/HowTos/NameSpaceHow-To?pp=1 (3 of 4) [05/07/2000 11:20:19]
The DTML Name Space How-To
If the user were to request /TwoFolder/OneMethod it would call the DTML Method OneMethod which has the following
name space:
1 The name space of REQUEST (TOP)
2 The name space of TwoFolder
3 The acquired name space of the root folder / (BOTTOM)
DTML Methods don't have their own name space. From the perspective of OneMethod, all it's attributes and name spaces are
defined by TwoFolder and REQUEST.
Limiting Name Spaces
All these name spaces upon name spaces can start to get confusing. When you don't need that much stuff being passed around, or
if you just want one name space on the stack and nothing else.
You can ensure that there is only one name space on the stack by using the With Tag with it's only option:
<dtml-with Catalog only>
<dtml-in searchResults>
<dtml-var meta_type>
</dtml-in>
</dtml-with>
In this code, 'Catalog's name space is the only name space on the name space stack.
Acquisition can be similarly disabled. All Zope objects that can acquire have the attribute aq_explicit which represents the
name space of the object, without implicit acquisition:
<dtml-with aq_explicit>
<dtml-var PropertyX>
</dtml-with>
If this were in a DTML Document, and that document did not have the property PropertyX this code would fail. If it were a
DTML Method and the containing Folder did not have PropertyX, it would fail. It would not matter that PropertyX was defined
higher up in the acquisition path, in the case of aq_explicit, acquisition does not work unless it is called explicitly.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/michel/HowTos/NameSpaceHow-To?pp=1 (4 of 4) [05/07/2000 11:20:19]
Zope
How-To: The Debugger Is Your Friend
Created by michel. Last modified on 2000/03/12.
The Debugger Is Your Friend
Jim Fulton is fond of the expression The debugger is your friend. And although debugging Zope can sometimes be a
bit painful, once you are used to it the debugger truly is your friend, and will save you years off your life.
Zope has been designed to work in an integrated way with the Python debugger (pdb). In order to really be able to use this
document, you must know how to use pdb. pdb is pretty simple as command line debuggers go, and anyone familiar with other
popular command line debuggers (like gdb) will feel right at home in pdb. If you are more familiar with the graphical,
bells-and-whistles type debugger, I suggest you read the pdb documentation in the standard Python module documentation on the
Python website.
For the purposes of this document, I will refer to the debugger as the Zope debugger, even though most of it is actually the
Python debugger. This is not to dis the python people, it's just a handy convention. Whenever I refer to just the debugger that is
not in the context of debugging Zope, I will say pdb.
To debug Zope, you must first shut down the Zope process. It is not possible to debug Zope and run it at the same time, as the
debugger stops the entire process dead in its tracks. Starting up the debugger will by default start Zope in single threaded mode. It
is not possible to run the debugger and Zope in multi-threaded mode at the same time. This is normally not an issue unless you are
doing some pretty complex low level stuff, which you shouldn't be messing with anyway if you have to read this, right?
For most Zope developer's purposes, the debugger is needed to debug some sort of application level programming error. A
common scenario is when developing a new third party Product for Zope. Products extend Zope's functionality but they also
present the same kind of debugging problems that are commonly found in any programming environment. It is useful to have an
existing debugging infrastructure to help you jump immediatly to your new object and debug it and play with it directly in pdb.
The Zope debugger lets you do this.
It is also useful to actually debug Zope itself. For example, if you discover some sort of obscure bug in Zope's object request
broker (ZPublisher) it would be useful to tell Zope to stop at a certain point in ZPublisher and bring up the debugger. The Zope
debugger also lets you do this.
In reality, the Zope part of the Zope debugger is actually just a handy way to start up Zope with some pre-configured break points
and to tell the debugger where in Zope you want to start debugging. I'll stop talking now and give some examples:
Remember, for this example to work, you MUST shut down Zope. Debugging Zope starts in Zope's lib/python directory. lib
is a sub-directory of Zope's top level directory. Go to the lib/python directory and fire up Python 1.5.2:
Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import Zope, ZPublisher
>>>
Here we have run the python interpreter (which is where using the debugger takes place) and imported two modules, Zope and
ZPublisher. If Python complains about an ImportError and not being able to find either module, then you are probably in the
wrong directory, or you have not compiled Zope properly.
The Zope module is the main Zope application. Now, in most cases, the term application when used in Zope means some
sort of application developed on top of Zope, but in this case, we mean Zope itself is the application. The ZPublisher module is the
Zope ORB (object request broker) and is needed to run the debugger.
If trying to import these modules results in a cannot lock database error, then another Zope process is using your
database. Shut that Zope down if you want to debug it. If you get error about cannot open file such-and-such then
you do not have proper permission to read or write one of Zope's files. Get the right permission and try again.
At this point, if Python did not complain, then all is well and you are on your way to debugging Zope. First, let's try something
really neat. Zope is, as you should know, completely protocol agnostic. You do not need a webserver to actually call and use
Zope, you can do it right here from this python prompt! To illustrate this, let's call your Zope site and ask it for the very root level
URL. If your Zope website's URL is http://www.mysite.org/, then the very root level URL is '' (or /). Calling this object
with the debugger is as easy as, and just like calling it through the web. The debugger will set up all kinds of fake environment to
make Zope not know the difference between you calling it here with the debugger and accessing your top level root object. The
debugger, for example, will insert valid debugging values for CGI 1.1 specification environment variables. For SERVER_URL for
http://www.zope.org/Members/michel/HowTos/TheDebuggerIsYourFriend?pp=1 (1 of 4) [05/07/2000 11:20:24]
Zope
example, it will insert the string http://127.0.0.1/::
Here is an exmaple of how to call Zope directly:
>>> ZPublisher.Zope('') Status: 200 OK X-Powered-By: Zope (www.zope.org), Python (www.python.org) Content-Length: 1238
Content-Type: text/html
... blah blah...
>>>
If you look closely, you will see that the content returned is exactly that which is returned when you call your root level object
through HTTP, including all the HTTP headers.
Keep in mind that calling Zope this way does NOT involve ZServer. No ports are opened, the ZServer code is not even imported.
In fact, this is just an interpreter front end to the same application code the ZServer does call. This is a useful way to think
about your perspective here. ZServer uses an interface to push requests into the Zope application. This application normally
is a group of threads all grouped around a central ZServer thread. The ZServer thread looks around, picks a free Zope
application thread, and hands it an incomming request, whether it be from HTTP, FTP or the monitor protocol. We'll call this
thread a request handler. Calling it in this way is just like you being ZServer, picking one request handler to handle your
request. In this case however, there is only one request handler, and that's the interactive interpreter.
This means that using the Zope debugger in this way is not very usefull to debug possible thread related issues. That is more tricky
realm of computer science all together.
Because you are only using one request handler does not mean that code within that request handler couldn't itself fire up some
threads of it's own. If you are using threads in this way, you also have to be a bit more clever.
So now let's suppose that you have an object in your Zope's root level folder called aFoo. aFoo is an instance of your Foo class
(I'm being very hypothetical here, later on we will use a more concrete example with ZCatalog). You can call your aFoo instance
with the debugger like so:
>>> ZPublisher.Zope('aFoo')
(or you could say /aFoo or /aFoo/ etc..., the rules are the same as HTTP). This is a handy way to test out your objects without
ever leaving python.
Now, let's get to actually debugging your object. Debugging, typically, involves running your program up to a point where you
think it's failing, and then inspecting the state of your variables and objects. The easy part is the actual inspection, the hard part is
getting your program to stop at the right point. For example, if you suspect your logic is failing right at the one hundredth iteration
in a large for loop, then you need to cleverly construct a breakpoint to stop your program right before this iteration.
The actual techniques of doing things like this, conditional breakpoints and other advanced bebugging, is beyond the scope of this
document. Many debugger come with documentation, but few come with an explanation of the Zen of debugging. Your best bet
is to play with the debugger as much as possible, and to even use it to help you actively engineer your software.
So, for the sake our example, let's say that your aFoo object is defined in a Zope Product called FooProduct, and is located in
the lib/python/Products/FooProduct directory. The class that defines the aFoo instance is also called aFoo, and is
defined in the aFoo.py module in your Product. Therefore, from Zope's perspective, your aFoo's classes fully qualified name is
Products.FooProduct.aFoo.aFoo. All Zope objects have this kind of fully qualified name. For example, the ZCatalog
class can be found in 'Products.ZCatalog.ZCatalog.ZCatalog (The redundancy is because the Product, Module, and class are all
named ZCatalog).
Now, your aFoo class defines a method called fooMethod that is definatly not doing the right thing for you. Here is a possible
definition of your class and method:
class aFoo:
def fooMethod(self, N):
""" return the sum from 1 to N """
sum = 0
for x in range(N):
sum = sum + x
return sum
This method can be called with N=5 through the web by visiting http://www.mysite.org/aFoo/fooMethod?N=5. This
http://www.zope.org/Members/michel/HowTos/TheDebuggerIsYourFriend?pp=1 (2 of 4) [05/07/2000 11:20:24]
Zope
will make Zope return the value 15, renderered into an HTML document. There's actually nothing wrong with this method to
debug, but for the sake of argument let's suppose that returning the sumation of N is not what we wanted, and therefore we want to
debug this method.
Well then, let's fire up the debugger! This is done in a very similar way to how we called Zope through the python interpreter
before, except that we introduce one new argument to the call to Zope:
>>> ZPublisher.Zope('/aFoo/fooMethod?N=5', d=1)
* Type "s<cr>c<cr>" to jump to beginning of real publishing process.
* Then type c<cr> to jump to the beginning of the URL traversal
algorithm.
* Then type c<cr> to jump to published object call.
> <string>(0)?()
pdb>
Here, we are calling Zope from the interpreter, just like before, but there are two differences. First, we are specificaly calling our
method (fooMethod) with an argument, and second, we have provided a new argument to the Zope call, d=1. The d argument,
when true, causes Zope to fire up in the python debugger, pdb. Notice how the python prompt changed from >>> to pdb>. This
indicates that we are in the debuggger.
When you first fire up the debugger, Zope gives you a helpfull message that tells you how to get to your object. To understand this
message, it's useful to know how we have set Zope up to be debugged. When Zope fires up in debugger mode, there are three
breakpoints set for you automatically (if you don't know what a breakpoint is, you need to read the python debugger
documentation.
The first breakpoint stops the program at the point that ZPublisher (the Zope ORB) tries to publish the application module (in this
case, the application module is Zope). The second breakpoint stops the program right before ZPublisher tries to traverse down the
provided URL path (in this case, /aFoo/fooMethod). The third breakpoint will stop the program right before ZPublisher calls
the object it finds that matches the URL path (in this case, the aFoo object).
So, the little blurb that comes up and tells you some keys to press is telling you these things in a terse way. Hitting s will 's'tep you
into the debugger, and hitting c will 'c'ontinue the execution of the program until it hits a breakpoint.
Note however that none of these breakpoints will stop the program at fooMethod. To stop the debugger right there, you need to
tell the debugger to set a new breakpoint. Why a new breakpoint? Because Zope will stop you before it traverse your objects path,
it will stop you before it calls the object, but if you want to stop it exactly at some point in your code, then you have to be explicit.
Sometimes the first three breakpoints are convienent, but often you need to set your own special break point to get you exactly
where you want to go.
You could, of course, continue to step your way down into the guts from where you are, but that could be a lot of code depending
on what your doing.
This is done quite easily. First, you must import your Python module (in this case, a Zope Product called aFoo, note the above
discussion on fully qualified names).
pdb> import Products pdb> b Products.FooProduct.aFoo.aFoo.fooMethod Breakpoint 5 at C:\Program
Files\WebSite\lib\python\Products\FooProduct\aFoo.py:42 pdb>
First, we import Products. Since your module is a Zope Products, it can be found in the Products Module. Next, we set a new
breakpoint with the 'b'reak debugger command (pdb allows you to use single letter commands, but you could have also used the
entire word break). The breakpoint we set is Products.FooProduct.aFoo.aFoo.fooMethod. After setting this
breakpoint, the debugger will respond that it found the method in question in a certain file, on a certain line (in this case, the
ficticious line 42) and return you to the debugger.
Whew! Lots of stuff to take in there in a few short paragraphs, but you are now on your way to debugging your method with style.
Now, we want to get to our fooMethod so we can start debugging it. But along the way, we must first 'c'ontinue through the
various breakpoints that Zope has set for us. Although this may seem like a bit of a burden, it's actually quite good to get a feel for
how Zope works internally by getting down the rythum that Zope uses to publish your object. In these next examples, my inline
comments will begin with '#". Obviously, you won't see these comments when you are debugging. So let's debug:
pdb> s
# 's'tep into the actual debuging
> <string>(1)?()
# this is pdb's response to being stepped into, ignore it
pdb> c
http://www.zope.org/Members/michel/HowTos/TheDebuggerIsYourFriend?pp=1 (3 of 4) [05/07/2000 11:20:24]
Zope
# now, let's 'c'ontinue onto the next breakpoint
> C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(112)publish()
-> def publish(request, module_name, after_list, debug=0,
# pdb has stopped at the first breakpoint, which is the point where
# ZPubisher tries to publish the application module.
pdb> c
# continuing onto the next breakpoint we get...
> C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(101)call_object()
-> def call_object(object, args, request):
# Here, ZPublisher (which is now publishing the application) has
# found your object and is about to call it. Let's jump out here
# and discuss this point...
#
#
#
#
#
'Calling' your object consists of applying the arguments supplied by
ZPublisher against the object. Here, you can see how ZPublisher is
passing three arguments into this process. The first argument is
'object' and is the actual object you want to call. This can be
verified by 'p'rinting the object::
pdb> p object
<aFoo instance at 00AFE410>
#
#
#
#
#
#
#
#
Neat! So here you can inspect your object (with the 'p'rint
command) and even play with it a bit, but we're not there yet!
The next argument is 'args'. This is a tuple of arguments that
ZPublisher will apply against your object call.
The final argument is 'request'. This is the 'request object' and
will eventually be transformed in to the DTML usable object 'REQUEST'.
pdb> c
# Now let's continue, which breakpoint is next?
Yours!
> C:\Program Files\WebSite\lib\python\Products\FooProduct\aFoo.py(42)fooMethod()
-> def fooMethod(self, N)
# and now we are here, at YOUR method. How can we be sure, well,
# let's tell the debugger to show us where we are in the code:
pdb> l
41
42
43->
44
45
46
def fooMethod(self, N):
""" return the sum from 1 to N """
sum = 0
for x in range(N):
sum = sum + x
return sum
And that's it, really. From here, with a little knowledge of the python debugger, you should be able to do any kind of debugging
task that is needed.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/michel/HowTos/TheDebuggerIsYourFriend?pp=1 (4 of 4) [05/07/2000 11:20:24]
The Let Tag How-To
How-To: The Let Tag How-To
Created by michel. Last modified on 2000/03/17.
Zope How-To: The Let Tag
Usage
The let tag is powerful DTML name space manipulator. it is often used in complex DTML code to improve clarity by removing
lots of redundant expression constructs. The let tag operates by pushing a new, empty name space onto the DTML name space
stack, and then populating that name space with zero or more objects that are the results of the tags arguments. For example:
<dtml-let name="'bob'"
relation="'uncle'">
<dtml-var name> is your <dtml-var relation>
</dtml-let>
name and relation in this example are objects that are bound to the two strings bob and uncle. A Let tag argument can
come in two forms, either by name or by expression. Expression arguments are quoted between "double quotes", and using an
object by name is not enclosed in quotes. For example:
<dtml-with UncleBob>
<dtml-let nephew=Nephew
assertion="('I, '+name+', am your uncle!')"
me=nephew>
<dtml-var id> asserts: <b><dtml-var assertion></b><br>
to <dtml-var "nephew.name"><br>
</dtml-let>
</dtml-with>
This code block takes your uncle object, and makes it print an interesting assertion. Notice how nephew is assigned to
'UncleBob's sub-object, Nephew; then me is assigned to nephew. This shows that items defined in the let tag can use an object
defined before it in the tag. The Let tag can also save you alot of complex expression typing and extracting:
<dtml-let my_list="[0, 1, 2, 3, 4, 5, 6, 7 ,8 ,9]">
<dtml-in my_list>
<dtml-let x=sequence-item>
<dtml-in my_list>
<dtml-let y=sequence-item>
<dtml-in my_list>
<dtml-let z=sequence-item>
<dtml-var "x*y*z">
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-in>
</dtml-let>
This is some more text.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/michel/HowTos/LetTagHow-To?pp=1 [05/07/2000 11:20:26]
The Multiple Selection How-To
How-To: The Multiple Selection How-To
Created by limi. Last modified on 1999/12/16.
As it is not documented anywhere (at least not yet :), but is an incredibly useful function, I thought I would make my first how-to.
And since it was fixed to work as it should in Zope 2.1.1, I saw no reason not to document it.
This is actually How-to number 100 totally, no less. (yay! :)
I write this with the absolute newbie in mind, as I am one of them myself.
Anyway, on to the interesting details.
I will exemplify the use of the multiple selection data type by using a real-world example, and try to stay away from unnecessary
Zope lingo. As a newbie, I know how frustrating Zope's documentation can be, but this concept actually IS very simple.
On my site I have a section where different types of software is located. Some of this software is multiplatform, and to avoid
entering the OS details every time I add a software package, I made a list of the required OSes, and made a multiple selection list,
so I can select more than one OS for an application.
In my case it is appropriate to let the list be accessible from all of my folders, so I put it in the root folder, like this:
1. Access the root folder
2. Click the "Properties" tab
3. Add a property of type "lines" (do not fill in anything yet). I call my property "platform_choices".
(Note that you can use the datatype "tokens" too, but I use "lines" in this example.)
4. Add the selections in the appearing text box, one on each line, like this:
Windows
Macintosh
Linux
FreeBSD
Amiga
etc...
5. Press "Save changes".
OK, now you have a list that is accessible from anywhere in the object hierarchy. The next part is to make the relevant property
that will be tied to your file, ZClass or any other object/class.
For my own part, I made a class that has the possibility to have more than one OS selected. Here's how:
1. Enter the properties tab of the object you want to have the selection list available for. (Or the property sheet if it's a ZClass.)
2. Add a property of the type "multiple selection". I call mine "file_platform", and as a value you put the name of the list you want
to get your selections from. In my case, "platform_choices".
3. Now a multiple selection list containing the OSes you listed in the "platform_choices" will appear among the properties. If you
instead want a pulldown menu with only one selectable choice, you just add a property of the type "selection" instead of "multiple
selection".
The multiple selection property opens for many useful applications, especially in combination with ZClasses. Just fool around a
bit with the concept, and you will get the hang of it.
Happy Zoping!
Alexander.
PS: any comments/suggestions are welcome at alexander@mp3.no.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/limi/multipleselection-howto?pp=1 [05/07/2000 11:20:29]
The With Tag How-To
How-To: The With Tag How-To
Created by michel. Last modified on 1999/09/15.
Zope How-To: The With Tag
Usage
This is an advanced DTML How-To, for a gentle introduction to DTML, see the Manual
The With tag pushes the name space of it's argument to the top of the DTML name space stack. For example, if you had
an object called MyObject with an id attribute:
<dtml-with MyObject>
<dtml-var id>
</dtml-with>
Would print the id of MyObject. The With tag is useful when dealing with object hierarchies. Because the with tag is a container
tag, it can contain arbitrarily nested with tags. If you had an object X, with a sub-object Y which in turn had a sub object Z you
could access Z with:
<dtml-with X>
<dtml-with Y>
<dtml-with Z>
<dtml-var id>
</dtml-with>
</dtml-with>
</dtml-with>
This is equivalent to:
<dtml-var "X.Y.Z.id">
Using the with tag, it is possible to manipulate the name space that is pushed onto the name space stack. Zope's name space object
_ can be called with keyword arguments assigned to expressions. This returns the name space object with a new name space
pushed on top, containing the results of the arguments:
<dtml-with "_(name="bob", relation="uncle">">
<dtml-var name> is your <dtml-var relation>
</dtml-with>
When the With tag exits, it's name space is popped off the name space stack.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/michel/HowTos/WithTagHow-To?pp=1 [05/07/2000 11:20:31]
Thread Safety in Zope 2
How-To: Thread Safety in Zope 2
Created by Amos. Last modified on 1999/09/15.
1. How can you tell if a product is thread safe?
If a Zope product defines persistent objects and those objects don't use any mutable gloal or shared variables (such as
class attributes or default arguments), then the product is thread safe! Pay close attention to this. It could let you off the
hook! :)
A key feature of Zope 2 (ZODB 3) is that (a copy of) a persistent object is never accessed by more than one thread
(unless the programmer goes way out of thier way). Each thread uses it's own database connection(s). Each connection
has it's own copies of persistent objects. This feature provides a major simplification for application developers.
If a product does make use of mutable shared data, then the product must take steps to assure that they are used safely.
Some key examples of shared data include:
❍
Variables defined in modules (globals),
❍
Variables defined in classes,
❍
Default arguments to Python functions.
Note that a mutable global variable can be thread safe without special care in some cases. For example, thanks to the
methods append and pop, lists can be used as thread-safe stacks and queues without any extra locks, because they are
protected by the global interpreter lock.
2. What can I do if my product is not thread safe?
I can think of two choices:
1. Make it thread safe,
2. Tell my "customers" that it can only be used in Zope applications with a single application thread.
3. How do I achiev thread safety for my objects?
If they are persistent objects, you don't need to. If they are non-persistent objects that are only referenced by persistent
objects, you also don't need to worry.
This is a big topic, but in many cases, simple approaches can be used.
First, you can assure that only one thread can call operations on your objects at one time and that data are accessed only
through operations. If your objects are of extension types, then the Python global interpreter lock protects operations on
them (unless they release the lock). If your objects are written in Python, you can protect them by mixing in the
Synchronized class from the Sync module.
The Synchronized class provides a per-instance lock that gets automatically acquired and released on entry to and exit
from methods on your objects.
Second, you should, if possible, provide a thread-safe API for your objects. In a thread-safe API, the operations are
self-contained. The meaning of an operation depends on the history of operation calls. An example of a non-thread-safe
API is the regex API. Regex objects have a group method whose results depend on results of previous search and
match calls.
If you can't change the API for an object to make it thread safe, then you can often create a wrapper for the object that is
thread safe (see for example, lib/python/ts_regex.py) or make sure that you only make thread safe calls. In many places in
Zope, we make sure we only make thread safe calls on regex objects by storing references to the thread-safe methods
rather than to compiled regular expressions themselves.
If the approach sketched above doesn't work for you, then you need to put on your thinking cap and get up to speed on
thread programming.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/ThreadSafety?pp=1 [05/07/2000 11:20:33]
Upgrading a Zope Installation
How-To: Upgrading a Zope Installation
Created by tone. Last modified on 1999/09/15.
Upgrading a Zope Installation
This can be relatively painless, but beware of item #5!
This assumes that you've downloaded a new binary distribution. If you've got a source distribution, the only real difference is that
the Python files you'll have installed
Make sure you start the Zope distribution up as the user that owns the var directory, otherwise you'll get errors mentioning
Z2.pid, ie
Traceback (innermost last):
File "/usr/local/src/Zope-2.0.0b5-src/z2.py", line 494, in ?
pf = open(PID_FILE, 'w')
IOError: [Errno 13] Permission denied:
'/usr/local/src/Zope-2.0.0b5-src/var/Z2.pid'
1. copy all your .tar and .tgz files that you've accumulated into the top-level of the new Zope distribution.
2. untar and install all of them (tar zxf or tar xf for .gz and .tar extensions respectively). Some of them may need to be
compiled (such as the ZMySQL distribution)
3. create an Extensions directory (and while you're at it, make an import one too)
4. copy all the files in your old Extensions directory into the new one, ie for unix; cp ../old_dist/Extensions/* Extensions
5. if you're using a binary distn., copy all the Python files you've accumulated in ../old_dist/lib/python1.5 into the new
lib/python1.5 directory (if you're using a source distn this isn't necessary, as your Python files are in a location that the
Zope system knows about).
6. create a backup directory for the Data.fs files that came with the new distribution; mkdir new_distn/var/Data_old
7. mv new_dist/var/Data.* new_dist/var/Data_old
8. cp ../old_dist/var/Data.* var/
9. use zpasswd to create a superuser id/pw of your choice
10. stop the old distn. cd to the old_distn directory and ./stop
11. cd to the new distribution and start it with ./start .
(5) is the real pain - you'll only find the .py files you've missed when you get a traceback.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/tone/upgrade?pp=1 [05/07/2000 11:20:35]
Use Direct Traversal of SQL Methods
How-To: Use Direct Traversal of SQL Methods
Created by mcdonc. Last modified on 2000/02/21.
Consider for a moment just how hideous the following URL is:
http://www.zope.org/dtShow?MEMBER=mcdonc&TIME=3am
There's no way you're going to tell your mom that URL over the phone. You're gonna have to email it to her, or in the worst case, drive over to her house and type it in for her if
you want her to visit it. After you're done messing around on the computer, she's going to make you hang up that picture frame you've been promising to hang up for her for the
last three weeks. Thus, most of the time, we'd like to avoid URLs such as these. Mom Avoidance.
Many times we construct web applications that need to present data to a viewer based on information gleaned from the URL. Such is the case with my fictional hideous example.
Most of the time, such an application will be reliant on the "query string" (the data which follows the "?" in a URL). In this case, the query string is:
MEMBER=mcdonc&TIME=3am
Which is HTTP-speak that indicates that MEMBER is mcdonc and TIME is 3am ("&" and "=" are special separator characters in a query string). Different systems deal with
query strings differently. Zope in particular takes query string argument keys and values and stuffs them in to the REQUEST namespace. Thus, when we give the query string
"MEMBER=mcdonc&TIME=3am" to the dtShow method (as we're doing with our hideous URL), Zope takes that to mean "call the dtShow method, but whoa, hold up there
buddy, before you do that, execute REQUEST['MEMBER']='mcdonc' and REQUEST['TIME']='3am'."
The REQUEST namespace is a dictionary (an associative array or hash for all you Perl people) with all kinds of gory stuff in it. What makes this a good place to stuff things,
however, is that DTML relies on REQUEST pretty heavily and looks inside REQUEST as a default namespace. Our query string arguments and values just get tacked on to the
end of the REQUEST dictionary, making them easy for DTML to find without any weird qualifications.
This also makes a query string a natural place to stuff values for arguments to SQL Methods. SQL Methods generally rely on some sort of argument in order to return useful data.
When you establish a SQL Method, you type a SQL query into the little query box such as:
SELECT PROPER_PLACE
FROM
PLACES
WHERE
MEMBER = <dtml-sqlvar MEMBER type=string>
AND
TIME = <dtml-sqlvar TIME type=string>
In the "Arguments" box, you'd type something like:
MEMBER TIME
This indicates that the SQL Method should expect to receive at least two arguments when it's called, and those arguments should be named "MEMBER" and "TIME". Now, we
can call a SQL Method from DTML with arguments. It's easy. Watch:
<dtml-in expr="sqlShowProperPlace(MEMBER='mcdonc', TIME='3am')">
Proper place: <dtml-var PROPER_PLACE>
</dtml-in>
Now, let's assume that I have a sqlShowProperPlace SQL Method that is hooked up to a database that has a PLACES table. The PLACES table looks like this:
MEMBER
-----mcdonc
jim
TIME
---3am
7pm
PROPER_PLACE
-----------Bed
Wrestling practice
The sqlShowProperPlace method SQL query looks like the one shown above ("SELECT PROPER_PLACE FROM PLACES WHERE...").
Calling the DTML listed above (<dtml-in expr="sqlShowProperPlace....) will produce, when called, the following:
Proper place: Bed
Works like a champ. But obviously we can't have people type DTML all the time to get a result to a database query. But we can do the equivalent by presenting them a URL that
looks like this:
http://www.zope.org/dtShow?MEMBER=mcdonc&TIME=3am
While in "dtShow", we put the following DTML:
<dtml-in expr="sqlShowProperPlace(MEMBER=MEMBER, TIME=TIME)">
Proper place: <dtml-var PROPER_PLACE>
</dtml-in>
When our illustrious users click on the URL with the query string in it, they'll see:
Proper place: Bed
If I had the URL http://www.zope.org/dtShow?MEMBER=jim&TIME=7pm, however, they would see:
Proper place: Wrestling practice
So we've constructed a general-purpose combination of a DTML Method and a SQL Method that can return different results to the viewer based on arguments inside a URL query
string. This, by and large, is how large sites like Deja.com instruct their system to do a lookup based on values you type in to form fields or based on values in the query strings of
URLs that you click on. Consider:
http://www.deja.com/[ST_rn=ps]/qs.xp?ST=PS&svcclass=dnyr&QRY=zope&defaultOp=AND&DBS=1&OP=dnquery.xp
&LNG=ALL&subjects=&groups=&authors=&fromdate=&todate=&showsort=score&maxhits=25
Egads! Try telling THAT one to your mom over the phone. You'll be over there for weeks, taking out the garbage, mowing the lawn, washing the dog. As you see, it gets
http://www.zope.org/Members/mcdonc/HowTos/direct_traversal?pp=1 (1 of 2) [05/07/2000 11:20:39]
Use Direct Traversal of SQL Methods
complicated. Our piddling little URL has nothing on the big boys. They know nothing of Mom Avoidance. But we do, so let's try this a different way.
Let's construct a different DTML Method to display our results. We'll call it dtShowTraverse:
Proper place: <dtml-var PROPER_PLACE>
Yup. That's it.
Now, with the magic of Zope URL traversal on a SQL Method and Mom Avoidance, we will provide our users with a different URL to click on:
http://www.zope.org/sqlShowProperPlace/MEMBER/mcdonc/TIME/3am/dtShowTraverse
OK, it's still a mouthful. But you don't have any of those symbols like "&" and "=" and "?" to say to Mom. This is a plus. It's hard enough telling her the difference between the
slash and the backslash. I can't help you much with the capitalization issues.
But, anyway, when called (surprise, surprise) our URL will return:
Proper place: Bed
What happened here is that we called the sqlShowProperPlace SQL Method directly. When we do this, it interprets the remainder of the URL as key-value pairs that correspond to
its arguments. It traverses the URL, looking for key-value pairs (MEMBER=mcdonc, TIME=3am in this case), and when it has enough of them to fill its "Arguments" list up, it
stops. It then calls the method you specify in the rest of the URL (dtShowTraverse) to display the results.
Note that this only works when your SQL method's result set is restricted to a single row.
You need to change nothing whatsoever about your SQL Method for this to work. Notably, you do NOT need to click on "enable simple direct traversal" in the Advanced section
of the SQL Method. "Simple direct traversal" can be used only when the SQL method's "Arguments" box has only one argument, while what I describe in this document can be
used when the SQL Method has any number of defined arguments. You can mix up the order of the arguments in the URL if you'd like too. Have fun!
Thanks to Martijn Pieters for an explanation.
For more information, also see:
The Z SQL Method User's Guide (somewhat dated now, especially with regards to URL traversal)
Simple Database/form interaction HowTo
Using ZSQL Methods With Acquisition HowTo
Accessing a ZSQL Method from an External Method
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/mcdonc/HowTos/direct_traversal?pp=1 (2 of 2) [05/07/2000 11:20:39]
Use Rsync to Backup Zope
How-To: Use Rsync to Backup Zope
Created by jrush. Last modified on 1999/10/15.
Because Zope appends new data to the end of its OODB, and because rsync only transmits the differences between two files when
copying, using rsync to back up Zope is very efficient. The only time it slows down is when you pack your OODB and it has to
start over. Drop this file into your Cron directory, and it will keep backups for the last seven days, using a minimum of disk
copying. It can also be used efficiently over dialup lines, on a perhaps weekly basis.
#!/bin/sh
########################################
# File: /etc/cron.daily/zbackup.cron
#
# Backup Zope Database Daily
########################################
#
# rsync arguments:
# -q
::= Quiet operation, for cron logs
# -u
::= Update only, don't overwrite newer files
# -t
::= Preserve file timestamps
# -p
::= Preserve file permissions
# -o
::= Preserve file owner
# -g
::= Preserve file group
# -z
::= Compress during transfer
# -e ssh
::= Use the ssh utility to secure the link
#
ARCHTOP="/archive/zope/"
DOW=`date +%A`
ARCHDIR="${ARCHTOP}${DOW}"
#
# Insure Our Day-of-Week Directory Exists
[ -d ${ARCHDIR} ] || mkdir ${ARCHDIR} || {
echo "Could Not Create Day-of-Week Directory: ${ARCHDIR}" ; exit 1
}
#
/usr/bin/rsync -q -u -t -p -o -g /var/zope/var/Data.fs ${ARCHDIR}
#
ln -sf ${ARCHDIR} ${ARCHTOP}Current
#
exit 0
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jrush/howto_rsync_zope?pp=1 [05/07/2000 11:20:41]
Use SQL and dtml to create drop down boxes and check boxes
How-To: Use SQL and dtml to create drop down
boxes and check boxes
Created by teyc. Last modified on 2000/04/11.
Introduction
You can combine sql and dtml methods to create simple shortcuts to generate listboxes and check boxes automatically from sql
tables.
Requirements
For the demo to run, you will require the Gadfly database adapter. This already comes with Zope, so you should have no trouble
running this unless you have deliberately removed Gadfly.
Instructions
Download this file first.
formelements.zexp
and place it in YOUR_ZOPE_DIR/import directory. The default installation of Zope does not create this directory so you will
have to do this yourself if you haven't already.
I have a bunch of dtml and sql methods in the file you just downloaded. Go to the root folder and click on the Import/Export tab
and import formelements.zexp
Then just go to your server's address
http://localhost/formelements/
Have fun.
Chui Tey
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/teyc/howtoSQLFormElements?pp=1 [05/07/2000 11:20:42]
User Defined Roles and Proxies
How-To: User Defined Roles and Proxies
Created by lvogel. Last modified on 2000/03/07.
This HOWTO is to give a brief explanation of what a user defined role is, why you would want to set one up, and how to go about creating one. I also briefly
touch setting up a proxy on Zope objects.
User Defined Roles
A user-defined role is a role given to all users of Zope, from developers and project managers to your anonyous end user. Everyone has a role, with specific
rights and access permissions to Zope objects. These roles determine what permissions to what objects your specific user is allowed. These roles can be
defined in the Security page of your folders, where you can create your user defined roles and assign them permissions.
For example, when you first access the Zope.org website, you access it as an Anonymous user. Why are you anonymous? Well, nobody knows who you are.
However, when you log into the website and become authenticated, you become a member of Zope.org, with certain rights and privileges granted to you that
weren't granted to you previously as the anonymous character you were. Those new privileges are defined in the user defined role assigned to your login id
Confused? So am I. Let's take a look at the root folder. Click on your top folder and go to the Security tab.
(These images are shown from the Control Panel. Unless you know something I don't, I wouldn't define anything like that here.)
We have four roles defined at the top level of our system: Anonymous, Manager, Model, and Owner. Each one of these roles has different permissions for all
of the products installed on our Zope setup. Anonymous was the role you were previously using as an unidentified, unauthenticated user. Once you were
authenticated, the role assigned to your login id now inherits the permissions granted to your proxy. For example, if I logged in as Lucas an Lucas was given
the proxy of Manager, I would be able to access and do whatever the Manager is allowed to do, as displayed above.
If you set a UDR at the root level of your directory, the role will be available in all of its child directories. You can create specific roles for specific directories
or give proxies different permissions in different directories.
Setting up a user defined role
1. Go to the folder you want the UDR to be created at, such as the root level(as shown in the Control Panel in the picture above) or a directory of your choice.
http://www.zope.org/Members/lvogel/proxy?pp=1 (1 of 2) [05/07/2000 11:21:00]
User Defined Roles and Proxies
2. Scroll down to the bottom of your screen. You will see a textbox at the bottom inviting you to create a User Defined Role:
3. Enter the name of the UDR you wish to use(if you can't think of one, call him Billy. It's such a nice name). Press the 'Add Role' button.
4. Presto! a UDR entitled 'Billy' has been created.
5. Assign your newfound role some privileges. You will have to know what you want your role to have permissions to do.
6. Assign to a user that authenticates. Note that you can assign multiple roles to a user.For the default acl_users folder all you have to do is change the user
properties.
The Proxy
A proxy is the same thing as a user-defined role, but different. Instead of assigning a user-defined role to a person, you assign it to a Zope object instead. Say
you have a user named LVogel who is assigned a user-defined role of Billy. The Billy role has permission to access the Mail form, but the Mail form accesses
objects that the Billy role does not have permissions to. By assigning the Mail form a proxy role of King_Billy(who has access to all objects the Mail form
needs to access), the Mail form now has permissions to do what it needs for the Billy role, who only has permission to the Mail form object.
To assign a proxy to an object, access the object and go to its Proxy page and assign a user-defined role to the object. It's that simple.
The End
TODO: Understanding what purpose user defined roles have at the Control Panel level.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/lvogel/proxy?pp=1 (2 of 2) [05/07/2000 11:21:00]
Using Apache with ZServer (NOT Zope.cgi)
How-To: Using Apache with ZServer (NOT
Zope.cgi)
Created by anser. Last modified on 2000/01/04.
New: a complete solution using SiteAccess!
The existing How-To's for combining Zope with the popular Apache server mostly require you to use the CGI interface to Zope,
instead of the much faster, multithreaded ZServer. This incurs a big penalty in startup overhead for each run of the CGI, and it
forces single threading of the Zope database.
On the other hand, running ZServer alone isn't always practical! Versatile Apache is much better for serving legacy HTML/CGI
content, it's often intricately configured in ways that are hard to achieve in ZServer, and even if you want to, you may not be
allowed for business or other reasons.
Fortunately, it's easy to embed ZServer behind Apache, using the ProxyPass and ProxyPassReverse configuration directives
from Apache's mod_proxy. Your ZServer can run on the same machine as Apache, or some other machine - it doesn't matter! A
load balanced "farm" of front-line Apache servers can share a ZServer. There are lots of possibilities, but let's look at how we
work this miracle.
What does ProxyPass do?
Apache's ProxyPass directive implements what is known as an "inverse proxy," where one server sits in front of another and
"hides" the back server's content by accepting requests, massaging the URL, passing it to the back server, waiting for a response,
and handing it back to the client. This technique has many uses: in-house corporate servers with no direct Internet access can serve
limited content to the outside world by masquerading behind a public server with a ProxyPass directive; load-balanced and fault
tolerant "server farms" can serve clients through a massively cached front end box; and so forth.
There are actually two directives:
ProxyPass /visiblepath http://backserver:port/path...
This intercepts incoming client requests beginning with /visiblepath (interpreted as a Location, not a Directory), strips that part
off, prepends http://backserver:port/path... instead, goes to backserver:port to satisy the request, then returns the result to the
client as though it had come from Apache's own document tree.
ProxyPassReverse /visiblepath http://backserver:port/path...
This matching directive watches in the other direction! URL Redirects from backserver:port, and other responses containing a
URL in the header, are automatically back-translated to begin with /visiblepath instead, so the "illusion" is preserved. Otherwise
simple redirects within the backserver would immediately "break" the inverse proxy. But note that this back-translation does
not extend to the document body! This will come back to haunt us below. But for now, we are ready to talk to ZServer via
ProxyPass.
First example: Partial Zope-ification
Let's say that we host the website http://www.fightclub.org, using Apache 1.3 on good old port 80, and we want to add a Zope
area at http://www.fightclub.org/Zope, without affecting any of the legacy content that's already online. We install Zope on the
machine, and set up ZServer to listen to port 8080.
First, we have to make sure that Apache's mod_proxy is configured and activated, so we check the following lines in httpd.conf
or apache.conf:
LoadModule proxy_module libexec/apache/libproxy.so
AddModule mod_proxy.c
Now we add our inverse proxy directives, within the main <Directory> context:
ProxyPass /Zope/ http://fightclub.org:8080/
ProxyPassReverse /Zope/ http://fightclub.org:8080/
Restart Apache and that's almost it! Every request to http://www.fightclub.org/Zope/* will get passed through to the ZServer,
while all other content remains under Apache's control as before.
http://www.zope.org/Members/anser/apache_zserver?pp=1 (1 of 3) [05/07/2000 11:21:04]
Using Apache with ZServer (NOT Zope.cgi)
But there is one problem: Zope's emitted HTML is thickly laced with absolute URL HREF's containing the exact machine and
port that Zope thinks it's running on. There is a forest of HTTP environment variables and DTML variables forcing the
backserver:port info into the body of the document, where ProxyPassReverse can't get at it. The result is that part or all of your
page, linked subpages, embedded graphics, etc, won't load -- a big mess.
And you can't use the clever solution (Apache's SetEnv directive) Dave Parker came up with for the people using CGI and
mod_rewrite, in his How-To on dynamic host names, because environment variables aren't passed across the internal proxy
"pipeline" to ZServer!
Fortunately there is an elegant solution: add Evan Simpson's SiteAccess product into your Zope installation. SiteAccess lets you
add a SiteRoot object into any Folder (or subclass thereof): the SiteRoot defines a Base that replaces the URL stem in all the
absolute URL's generated by Zope at and below that folder. After installing SiteAccess, restart ZServer and manage your content
folder. Add a SiteRoot object and edit its fields as follows:
Title
(ignored)
Base
http://www.fightclub.org/Zope
Path
/
Change
Now you are really in business. Users can access http://www.fightclub.org/Zope/ and they see your Zope content as served by
ZServer. Bliss.
You can even do content management through the Apache proxy! ( http://www.fightclub.org/Zope/manage ) But when you do,
there is one tiny annoyance left: the icon graphics for the object tree don't load. (That's because they are being loaded by plain
old IMG SRC="/misc_/xxx_image" tags - no site_url DTML variable for SiteAccess to fix.) To fix this, go back to Apache and
add two more ProxyPass directives, next to the ones you already entered:
ProxyPass /misc_ http://fightclub.org:8080/misc_
ProxyPass /p_ http://fightclub.org:8080/p_
(You don't need matching ProxyPassReverse directives.) This sends client requests for images in /p_ and /misc_ through to the
ZServer as well... which shouldn't be a problem unless your legacy content already included root directories with those names!
Tip: To see the HTTP environment Zope is passed, put this in a DTML method and view it:
<pre>
REQUEST: <dtml-var REQUEST>
</pre>
Second example: a complete virtual host
All right, now suppose we are using Apache to run a bunch of virtual hosts (very popular nowadays) and we want to set up
fightclub.org again, this time as an all-Zope site, without affecting our other virtual host clients. We'll use named virtual hosts on
an Apache 1.3 server at port 80 of IP address 192.192.123.234.
Just to show the versatility of this approach, let's put the ZServer we want to proxy to on a separate machine, port 9180 of
zserver.xyz.com. That external ZServer will contain content for many clients, with the fightclub.org material rooted in the Zope
Folder /fightclub. We make sure it has the SiteAccess product installed.
OK, first the Apache config file. If you don't understand virtual hosts, see the Apache docs.
LoadModule proxy_module libexec/apache/libproxy.so
AddModule mod_proxy.c
...
NameVirtualHost 192.192.123.234
<VirtualHost 192.192.123.234>
ServerName www.fightclub.org
http://www.zope.org/Members/anser/apache_zserver?pp=1 (2 of 3) [05/07/2000 11:21:04]
Using Apache with ZServer (NOT Zope.cgi)
ServerAdmin tyler@fightclub.org
ProxyPass / http://zserver.xyz.com:9180/fightclub/
ProxyPassReverse / http://zserver.xyz.com:9180/fightclub/
ProxyPass /misc_ http://zserver.xyz.com:9180/misc_
ProxyPass /p_ http://zserver.xyz.com:9180/p_
</VirtualHost>
Now we can restart Apache, and visit the management interface for our ZServer at
http://zserver.xyz.com:9180/fightclub/manage . In the /fightclub Folder, we add a SiteRoot object, and edit its
fields:
Title
(ignored)
Base
http://www.fightclub.org/
Path
/
Change
As soon as that change is made, proxy access ought to work! Note: you will probably want to exit your browser and restart it
after changing SiteRoot, because the client can get confused about authentication!
In conclusion
My thanks to Evan Simpson and to the Zope and Apache teams for their great work!
-anser
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/anser/apache_zserver?pp=1 (3 of 3) [05/07/2000 11:21:04]
Using DTML With Local File System Objects
How-To: Using DTML With Local File System
Objects
Created by jfarr. Last modified on 2000/06/19.
Using DTML with LocalFS objects
This how-to explains how to perform common DTML operations using LocalFS objects.
Iterating Local Directory Contents
One of the most basic operations on a local file system object is iterating through the contents of a directory. LocalFS represents
local directories with a LocalDirectory object, which provides methods for doing just that. These methods are called
fileIds, fileValues, and fileItems. They work similarly to the objectIds, objectValues, and objectItems
methods of a Folder object. The difference is that fileValues and fileItems return LocalFile objects which are simply
pointers to the file objects rather than the actual Zope objects, for performance reasons.
Examples
Suppose you create a LocalFS object called 'test' which points to the directory '/usr/local/zope/test'.
If you wanted to print the names of all the files in that directory, this is the correct DTML.
<dtml-in "test.fileIds()">
<dtml-var sequence-item><br>
</dtml-in>
The LocalFile object also provides various meta-information about the local file, so for example, to add the last modified time
of the files to the output, use DTML like the following.
<dtml-in "test.fileValues()">
<dtml-var id> <dtml-var mtime><br>
</dtml-in>
Refer to the product documentation for more information about LocalFS objects and their attributes.
Accessing Local File Contents
It is also possible to access the contents of files stored in the local file system from DTML.
Examples
Using the same LocalFS object from the previous example, if you wanted to access the contents of a file called 'foo.txt' in that
directory from DTML, this is the correct syntax.
<dtml-var "test['foo.txt'].data">
If you the file you wanted were in a directory called 'sub1', this would be the correct syntax.
<dtml-var "test['sub1']['foo.txt'].data">
The reason for this syntax is that objects in LocalFS directories are accessed by key as if the directory object were a dictionary,
with the key being the filename. They are not accessed as attributes for a number of reasons, one of which is that filenames can
contain periods so a syntax like 'test.sub1.foo.txt' would make no sense. There would be no way to determine whether the object
being requested was a file called 'txt' in a directory called 'sub1.foo' or a file called 'foo.txt' in a directory called 'sub1', etc.
The object actually returned by LocalFS is a Zope File object, so you use the 'data' attribute to access the data contained in the file.
The exception is when the file ends in the extension '.dtml', in which case the object returned by LocalFS is a DTMLMethod
object. To access the raw (unprocessed) text of the file, you would use:
http://www.zope.org/Members/jfarr/HowTo/DTML_with_LocalFS?pp=1 (1 of 2) [05/07/2000 11:21:07]
Using DTML With Local File System Objects
<dtml-var "test['foo.dtml'].read()">
To insert the text of the file after evaluating it as a DTML template in the current namespace, you would use:
<dtml-var "test['foo.dtml'](_.None, _)">
Of course, you can also specify additional values to the DTML method as keyword arguments.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/jfarr/HowTo/DTML_with_LocalFS?pp=1 (2 of 2) [05/07/2000 11:21:07]
Learning Zope - Using Emacs with Zope on RedHat 6.1
How-To: Using Emacs with Zope on RedHat 6.1
Created by nemeth. Last modified on 2000/01/16.
Author: Miklos Nemeth, nemeth@iqsoft.hu
Last modified: 2000.1.16
●
Why using Emacs with Zope?
●
Using ange-ftp in general
●
Using ange-ftp with Zope
●
Using efs with Zope
Why using Emacs with Zope?
Zope has its own development interface, where the programmer has a multiline edit box in an HTML form for editing DTML
source code.
This IDE (integrated development environment) is quite acceptable for browsing the application repository, creating and
customizing objects, managing the environment and even for writing small to medium size DTML scripts (mainly DTML methods
and PythonMethod (a separate (non-standard) Zope product) objects). However, it may be clumsy for editing large HTML/DTML
documents.
Perhaps the most popular programmer editor is Emacs/XEmacs (besides vi). There are (at least two) packages for Emacs that
allow programmers to edit files on-line through FTP. These Emacs packages are ange-ftp and efs; both available in Andy
Norman's Web Zone (hplbwww.hpl.hp.com/people/ange). The package ange-ftp is part of the emacs (20.4) package for RedHat
6.1, but efs is not.
Using ange-ftp in general
The package ange-ftp is included in the Emacs package shipped with RedHat. Using ange-ftp is very simple:
1. Just start opening a file the normal way: C-x C-f
2. Enter a special path to the ftp server: /user1@host1.iqsoft.hu:/dir1/file1
3. The password of user1 is requested by Emacs
4. You may even use the path or file name completion facility (pressing the TAB button).
The ange-ftp package may also be used with ftp servers listening on non-standard (ie. non-21) ports, but you must use an emacs
data entry trick. The only problem is in getting the "space" charachter between the hostname and the port number. You do that by
using C-q which is a quoted insert, because if you just press the space bar, Emacs thinks you are done entering your request and
goes to look up the source.
/playername@mud.net[C-q][SPACEBAR]2222:filename
where [C-q] is a control-q and [SPACEBAR] is where you hit the spacebar. For this info many thanks to Kathleen G. Holland
(holland@lobo.net).
By default ange-ftp does not create backup files.
Using ange-ftp with Zope
Zope has a built in FTP server running by default on port 8021. You may configure the start script to use the default FTP port (21)
if your host does not have the standard ftp server using this port. Be warned, if you specify a port below 1024 you have to start
ZServer as root.
You may open DTML documents and methods using Emacs (ie. ange-ftp): just enter the path to your folder: /superuser@localhost
http://www.zope.org/Members/nemeth/usingEmacsWithZope?pp=1 (1 of 2) [05/07/2000 11:21:14]
Learning Zope - Using Emacs with Zope on RedHat 6.1
8021:/Cms and you will get a list of the content of the directory. (Remember pressing C-q to be able to enter the space character
before the port number.)
You may navigate through your Zope repository by pressing enter on the name of a folder, and may open the required file by
pressing enter on its name.
You may also use the path and file name completion facility. If you open a DTML script, its id (normally) does not have an
extension. If you want to use HTML mode for the file opened switch emacs to html-mode: M-x html-mode.
You may create new files, but these always will be of type DTML Document: /superuser@nemeth20.iqsoft.hu
8021:/Cms/myNewDoc
Before using emacs with your Zope repository you should be aware of that
1. You cannot use version objects via FTP. If you open a file stored in Zope with emacs it is always opened outside of any
version object. If someone changed a document in a version, and you open that document, you will get the original
version, but you cannot save your changes.
2. You cannot login with the name of a user who has no permission to perform an FTP ls (ie. list) command in the /
or any directory in the path to the file. For example in my initial setup I created users only in the "application folders"
(folders in the root folder). These users cannot use Emacs with Zope. If your programmers want to use Emecs (which is
highly probable) you should create the users in the root folder. This is a problem with ange-ftp and efs, not with FTP or
Zope.
Using efs with Zope
The efs package is used by many Emacs/XEmacs users. The efs package has a bit different way of specifying the port number:
/user1@host1.dom.com#8021:/dir1/file1
Nonetheless, the usage of eps is (almost) the same as ange-ftp.
However, efs makes backup files by default by renaming the original to a file name with a ~ character suffixed, and then creates a
new file with the original name. When this file is created Zope takes it as a DTML Document object. A general solution is to
switch off making backup files:
(setq efs-make-backup-file nil)
(setq make-backup-files nil)
Sometimes switching off only efs-make-backup-file is enough but sometimes not. Switching off make-backup-file has the
disadvantage that no backup files are created at all, even not for local files.
For a RedHat user efs is not required, ange-ftp works fine. Downloading and installing efs may not be an easy job, and efs also has
the same problems (mentioned above) with Zope as ange-ftp.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/nemeth/usingEmacsWithZope?pp=1 (2 of 2) [05/07/2000 11:21:14]
Using External Methods
How-To: Using External Methods
Created by Brian. Last modified on 1999/09/15.
Using External Methods
What are External Methods?
One of the core components of Zope is DTML (Document Template Markup Language), a powerful variable insertion and
expression language that provides "safe scripting" of Zope objects and dynamic content generation. DTML is highly integrated
with the Zope security model, and this security integration makes it possible to let less privileged users perform simple scripting
and use the services provided by Zope objects without compromising the security of the site.
A key concept of "safe scripting" is that unlike some other templating systems such as PHP or ASP, DTML does not allow you to
create blocks of arbitrary script code. The reason for this is security. It would be difficult to safely delegate work on a site to less
priveleged users if their code could access the filesystem, perform system calls or consume system resources.
Instead of allowing arbitrary code embedded in DTML, Zope provides External Methods. External Methods allow site managers
to write unrestricted code and provide those services to DTML authors in a controlled, secure way. External Methods are created
by writing a method in a normal Python module, then creating a new External Method object on your Zope site associated with
your Python method.
An External Method is a Zope object in its own right, and has its own security settings. This gives a site manager the ability to
selectively provide extended services to DTML authors. External Methods can be added to Folders or any container object in
Zope. When you add an External Method to a Folder, it can be thought of as adding a new custom method to that Folder.
Note that the actual Python code that an External Method encapsulates must be created in a file on the server machine - it is not
possible to create a working External Method completely through the Web. This is a security measure - in order to write
unrestricted code, you must have access to the machine.
The following examples assume that you have a basic familiarity with DTML and the Python programming language.
Creating an External Method
Let's look at the standard "Hello World" example implemented with an External Method. In this example we will create a
helloWorld method in a Python module, create a new External Method object in Zope associated with it, and finally create a
simple DTML Document that uses the services of our new External Method.
If you have not yet started using External Methods with your Zope installation, you will first need to create a directory named
Extensions in the root directory of your Zope installation. For example, if you installed Zope in /usr/local/zope2, your
Extensions directory should be /usr/local/zope2/Extensions. Note that the directory name is case-sensitive, and make
sure that the new directory is readable by the user that is used to run your Zope server.
Next we will create a new file in the Extensions directory named demo.py. This is the Python module we will use to define our
custom logic. Note that Python modules which define External Methods must be located in the Extensions directory. Edit the
demo.py file and add the following Python code:
def helloWorld(self):
"""A simple external method."""
return 'Hello World!'
With your Web browser, access the management interface of your Zope site and create a new Folder object named demo. This is
where we will implement our examples. In the new Folder, select "External Method" from the add list (at the bottom of the right
hand management frame for the demo Folder). This will bring up the "Add External Method" form, which will let us provide an
id and title for the External Method and provides fields for entering the function name and Python module file that implement the
new External Method.
In the "Id" field of the form, enter "helloWorld". The id will be used as the id of the Zope External Method object. It does not have
to be the same as the actual function name, but for these examples we will give our External Methods ids that match the names of
the methods in the Python module for clarity.
In the "Title" field you may optionally enter a title for the External Method. In the "Function name" field, enter "helloWorld" and
http://www.zope.org/Documentation/How-To/ExternalMethods?pp=1 (1 of 4) [05/07/2000 11:21:19]
Using External Methods
in the "Python module file" field enter "demo" (Note that you should omit the ".py" from the name of the Python module file).
Click the "Add" button and you will be returned to the management screen of the demo Folder which now includes the newly
created helloWorld External Method object in its contents listing.
Finally, to use the new External Method, create a new DTML Document object named test in the demo Folder. To call the
External Method from the DTML Document, add the following tag to the source of your DTML Document:
<dtml-var helloWorld>
The actual External Method object in Zope acts as a kind of pointer to the method definition in your Python module. To make
changes to an External Method:
●
●
Make your changes and save the Python module.
Visit the the management interface of the corresponding External Method object and click the Edit button. This will
"reload" the code of your External Method.
Tip: if you are running Zope in development mode, you don't need to click the "Edit" for External Methods when you make
changes. When in development mode, External Methods detect changes to their corresponding Python modules and will reload as
needed automatically when changes are made. To run Zope in development mode, pass the -D command line option to Zope at
startup.
Key Concepts
Basic usage of simple External Methods is pretty straightforward, as demonstrated by the helloWorld example above. For more
complex usage however, it is important to keep several key concepts in mind that will help you take full advantage of External
Methods.
●
External Methods are methods of their containers
As its name implies, an External Method acts as a method of the object that contains it. You may have noticed that the
code for the helloWorld method in the previous example takes a self argument. When an External Method is called, the
self argument will be the container of the actual External Method object - in the case of our example, self was the
demo folder containing the helloWorld method.
Understanding the nature of self in an External Method is important because self is usually what you will use when
you need to interact with the Zope object system. A common question among new Zope users when writing External
Methods is "what do I import to get access to the object in my Zope site?". The answer is that you don't import anything you use the self parameter to interact with the object system.
For example, knowing that self is the container of the External Method you can find and work with subobjects or
parent objects of self:
# Get refs to two subfolders of "self" that have
# ids of "subfolder1" and "subfolder2".
sub1=self.subfolder1
sub2=self.subfolder2
# Get a ref to the parent of "self".
parent=self.aq_parent
Also note that if self contains other External Methods, you can call them from an External Method just as if they were
normal Python methods:
# Call another external method called sayHello.
msg=self.sayHello()
At the time of this writing, API documentation describing the interfaces provided by the various Zope objects is still in
progress. Until the API documentation is complete, a good way to find out about the services of various Zope object is to
consult the Zope source code in the lib/python directory of your Zope installation.
●
Arguments must be passed explicitly from DTML
In the previous example, we were able to call the helloWorld method using a normal DTML var tag. This is because
http://www.zope.org/Documentation/How-To/ExternalMethods?pp=1 (2 of 4) [05/07/2000 11:21:19]
Using External Methods
the helloWorld method took only one argument (self). As in standard Python, the self argument is implicit when
calling an External Method and does not need to be passed explicitly by the programmer. Zope will automatically bind
self when an External Method is called from DTML.
If you define an External Method that takes more arguments than simply self, you need to pass those arguments when
calling the External Method from DTML. To expand on the previous example, let's update the helloWorld code to
personalize its greeting based on the identity of the Web user. To do this, the helloWorld method will need access to
the REQUEST object, so we will add it to the method argument list:
def helloWorld(self, REQUEST):
"""A first external method."""
user=REQUEST.AUTHENTICATED_USER
return 'Hello %s!' % user
Now that the helloWorld method requires an argument, it will be necessary to change the way it is called from DTML
to make sure that the required argument is passed. To do this, we will use the DTML expression syntax to pass in the
REQUEST object that exists in the DTML namespace when calling helloWorld:
<dtml-var "helloWorld(REQUEST)">
Argument semantics for External Methods are identical to those for normal Python methods. You can define External
Methods with default or keyword arguments just as you can in Python. Like Python methods, External Methods can
return any type of data. An External Method could return a list or tuple for example, which would allow you to iterate
over the result in DTML using the "in" tag.
●
External Methods are Zope objects too
External Methods have a dual personality - they act primarily as methods of their containers, but they are also Zope
objects with many of the common features of other Zope objects. They have their own security settings like any other
Zope object so you can control access to them on a method by method basis (though the actual code may all exist in a
single Python module).
Like DTML Documents and DTML Methods, you can also navigate directly to an External Method object through the
Web. In the case of the helloWorld example, you can point your Web browser to the url /demo/helloWorld and
the result of the External Method will be returned to the browser. While accessing External Methods directly through the
Web is less common than calling them from DTML, the fact that you can access them directly can be extremely useful.
One difference to note when External Methods are called through the Web is that Zope will automatically map form
fields and other values found in the Web request to the arguments required by the External Method based on their names.
A Real World Example
The following example demonstrates an External Method that is called directly via the Web. This example implements a virtual
"default document" that selects the correct actual default document to return to a user based on the language preferences sent by
the user's Web browser.
The External Method is named index_html, which is the name that Zope looks for by default to use as the default document for
a Folder. When the External method is called, it will look in the REQUEST object to determine the client's preferred language.
Finally it will look in it's container (self) to see if there is a custom default document for that language and attempt to render the
document and return it:
def index_html(self, REQUEST):
"""A smart index document redirector"""
# Look for preferred lang in the REQUEST. If it
# is not found, 'en' will be used as the default.
lang=REQUEST.get('HTTP_ACCEPT_LANGUAGE', 'en')
#
#
#
#
#
By convention, we have saved different language
versions of our index document in the Folder with
ids of 'index_xx.html', where 'xx' is the lang id.
This code will attempt to get the right document,
then return the rendered result to the client.
docname='index_%s.html' % lang
http://www.zope.org/Documentation/How-To/ExternalMethods?pp=1 (3 of 4) [05/07/2000 11:21:19]
Using External Methods
if hasattr(self, docname):
# Try to return the document for the
# requested language.
doc=getattr(self, docname)
return doc(self, REQUEST)
else:
# Return english if we don't have the
# requested language.
doc=getattr(self, 'index_en.html')
return doc(self, REQUEST)
More Information
More information and examples of External Methods can be found in the Zope Content Manager's Guide.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Documentation/How-To/ExternalMethods?pp=1 (4 of 4) [05/07/2000 11:21:19]
Using Keyword Indexes in ZCatalogs
How-To: Using Keyword Indexes in ZCatalogs
Created by AlexR. Last modified on 1999/11/24.
A simple example
Version 0.2
This doc explains how to use keyword indexes to create a keyword search form. It is based on information collected on the Zope
lists (including postings from Michel Pelletier and Stuart 'Zen' Bishop). Feel free to send your feedback.
Version History
●
Version 0.2 - 24th November 1999 - Minor changes
●
Version 0.1 - 22nd November 1999 - First draft
Index types in ZCatalogs
Traditionally ZCatalogs support two index types: Text Indexes and Field Indexes. In Zope 2.1b1 and later you can also use
Keyword Indexes.
A Field Index looks at the entire property value as a single value. A TextIndex breaks the property up into words. For example, if
you have a FieldIndex for "category", and you search for "Green", you will only find the items with the category "Green" (but not
"Green Shirts" or "Green Eggs"). If this was a TextIndex, searching for Green would match all three of those.
So, you use Field Indexes for distinct values that you will be looking for (category, last name, zip code, etc.). Use Text Indexes for
things like titles, or content that you want to be full text searchable.
For the record, here is further technical information (stolen from a posting by Michel Pelletier):
●
●
●
ZCatalog indexes objects into an arbitrary set of various kinds of indexes. Each index is responsible for indexing one
particular property of an object. If the an object being indexed does not have a property that an index is looking for, the
object is simply not indexed in that index (but it may have a property that another index is looking for, and therefore will
be indexed in *that* index).
Text Index: property values are applied against a lexicon object that stems, stops, and parses the value into a full text
index. The index may be queried with a simple boolean query language that allows 'and', 'or', phrasing, parenthesized
boolean expressions, and proximity matching. Relevance ranking is supported and returns the sum of the occurances of
all query terms in the "hit". A normalized score is also provided that is normalized from 0 to 100 over the whole result
set.
Field Index: property values are treated atomically. Indexes can be queried for all objects that match that value. Range
searches can also be done on indexed object values that support comparison (like numbers, dates, special purpose
"length" objects, etc). Indexes can also be queried for the set of unique values in the index, for example, you can ask for
the set of unique "meta_types" of all objects indexed. A good example of this is the search by "type" on the Zope site.
OK. What about keyword indexes then?
Keyword Indexes allow you to index a sequence of "keywords" or "key phrases" as a single property of an object. They have the
same behavior as field indexes, except that property values are treated as a sequence of keywords. Thus you get the best of both
worlds: multiple values in one property with 100% matching for every keyword (and case is relevant).
This is useful for buiding categorical hierarchies and many-many relationships, said the gurus. Well, so far, I've only build a
keyword search form. Here's how.
Creating a simple keyword search form
This section describes how to add a keyword field in a Zclass, register the field as a keyword index in the ZCatalog, then add a
drop-down list with individual keywords in the search form. I won't go into details that are covered elsewhere. For more info see
the following docs.
http://www.zope.org/Members/AlexR/KeywordIndexes?pp=1 (1 of 4) [05/07/2000 11:21:23]
Using Keyword Indexes in ZCatalogs
Information sources
Here is a selection of docs. You can find many more on the Zope site.
You'll need to read this to get started:
●
Zcatalog Tutorial
●
ZClass section in the Zope Developer's Guide
●
ZClass Properties Howto
Then read this to add features:
●
Advanced ZCatalog Searching Howto
●
Inheriting from ZCatalog as a Container Howto
●
ZClass containers with a private ZCatalog Tip
●
Creating a CatalogAware ZClass Howto
Adding a keyword field in your ZClass
Let's create a product called MyDoc. It contains a ZClass called MyDocClass. This ZClass subclasses CatalogAware (so that it
self-registers into a ZCatalog), DTML Document and DTML Method. Its metatype is "MyDoc".
Note you may need to work around a couple of issues such as the Resource not found bug (that may strike when you create a
ZClass instance) and the Missing ID bug (when you try to access an instance id from DTML).
Now in your ZClass, create a custom propertysheet called Info. On this propertysheet, create a lines property called
doc_keywords. This property will store the keywords in the ZClass instances. (You need to store your properties on a custom
propertysheet if your want your ZCatalog to be updated when properties are edited. For more info see the CatalogAware Howto.)
To edit your properties, create a form called myPropForm in the Zclass. Here is a code example:
<form action="myPropHandler">
<table>
...
<tr><th>Keywords</th>
<td>
<textarea name="doc_keywords:lines" rows="6" cols="40">
<dtml-var "_.string.join(doc_keywords,'\n')"></textarea>
</td></tr>
...
<tr><td>
<input type=submit value=" Save "></td></tr>
</table>
</form>
Note two important points:
●
●
In the textarea tag, add a :lines to the name attribute so that values are converted to lines before stored in the
doc_keywords property.
You can use _.string.join(doc_keywords,'\n') to convert the current value for the doc_keywords property
to lines before it is displayed in the text area.
To save the property values, create a handler method whose name must be specified in the FORM tag. Here it is called
myPropHandler.
In myPropHandler, add code to store the property values. Example:
<dtml-call "propertysheets.Info.manage_changeProperties(REQUEST)">
<dtml-call reindex_object>
Note you need to specify the propertysheet name (Info here) when using a custom propertysheet. The second lines is useful if you
build a CatalogAware ZClass: it will update the property values in the ZCatalog.
http://www.zope.org/Members/AlexR/KeywordIndexes?pp=1 (2 of 4) [05/07/2000 11:21:23]
Using Keyword Indexes in ZCatalogs
Remember to map myPropForm to a view so that you can easily edit properties in your ZClass instance. In your ZClass definition,
click on the Views tab and map a Tab name to a file name (myPropForm here).
Registering the keyword index in the ZCatalog
Edit your ZCatalog. On the Indexes tab, enter doc_keywords in the Add Index field and choose KeywordIndex in the Index
Type list. Then click the Add button.
On the MetaData Table tab, enter doc_keywords in the "Add column to the Meta Data table" field and click the Add button.
Creating ZClass instances
Now create a couple of MyDoc instances and add keywords in the Keywords field. Add one keyword per line. These keywords
can actually be "key phrases"; they can contain several words. For instance, enter "Squids" in one document and "Squids are
beautiful" (as one line) in another one.
If you created a CatalogAware ZClass, your MyDoc instances and their properties will be registered into the ZCatalog. If not, edit
the ZCatalog and "force-index" them from the Find Items to ZCatalog tab. Your ZClass instances should then be listed on the
Cataloged Objects tab.
Creating a search form
Create a new ZSearch Interface in your database. Select your ZCatalog in the searchable objects list. Let's call the search form
mySearch and the result report myResults.
In mySearch, replace the default code for doc_keywords with the following code:
<SELECT name="doc_keywords:list" multiple size="4">
<OPTION value=""></OPTION>
<dtml-in "Catalog.uniqueValuesFor('doc_keywords')">
<OPTION value="<dtml-var sequence-item>">
<dtml-var sequence-item>
</OPTION>
</dtml-in>
</SELECT>
Here Catalog is the name of the ZCatalog the docs are indexed in. Note the following points:
●
●
●
Specify :list in the SELECT tag so that the selection is returned as a list.
The uniqueValuesFor('doc_keywords') method returns one instance of all existing keywords. These values
are used to prefill the selection list with keywords actually used in the docs.
The multiple attribute in the SELECT tag allows multiple selections in the list. The catalog will then return any
document that matches at the least one keyword/keyphrase (OR search).
[To do: how can I control the sort order in the selection list?]
In myResults, delete unnecessary fields, then display mySearch and try out the search feature. In the selection list, click one or
more keyword/phrases. Note every keyword/phrase is displayed as one list entry. Try searching for "Squids". This matches at least
one document. It doesn't match the document with the "Squids are beautiful" phrase.
Searching from DTML
You can also search the ZCatalog from DTML as for standard keywords. Example:
<dtml-in "Catalog.searchResults(doc_keywords='Squids')">
...
</dtml-in>
or
<dtml-in "Catalog.searchResults(doc_keywords in ['Squids', 'foo', 'bar'])">
...
</dtml-in>
http://www.zope.org/Members/AlexR/KeywordIndexes?pp=1 (3 of 4) [05/07/2000 11:21:23]
Using Keyword Indexes in ZCatalogs
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/AlexR/KeywordIndexes?pp=1 (4 of 4) [05/07/2000 11:21:23]
Learning Zope - Using SQLSession
How-To: Using SQLSession
Created by nemeth. Last modified on 2000/06/09.
Author: Miklos Nemeth, nemeth@iqsoft.hu
Last modified: 2000.1.17
●
What is SQLSession?
●
What is HTTP session management?
●
Downloading and installing SQLSession
●
Preparing the database connection
●
Setting up a Session object
●
DTML programming with SQLSession
●
The List Manipulation Demo
●
Exercises
What is SQLSession?
SQLSession is a Zope product written by Anthony Baxter (anthony@interlink.com.au). The current version is 0.2.3. SQLSession
is designed to store the data of a session consisting of a series of HTTP interaction. The data are stored in any SQL database
having ZSQL interface.
What is HTTP session management?
The main inconvenience of the HTTP architecture is its stateless nature. Once a HTTP request is serviced, the server forgets all
about it. Even if the same remote user connects a few seconds later, from the server's point of view it is a completely new
interaction and your application has to reconstruct the previous interaction's state. This makes even simple applications like
shopping carts and multipage questionnaires a challenge to write.
You can save state information inside the fields of fill-out forms, stuff it into the URL as additional path information, save it in a
cookie, ferret it away in a server side database, or rewrite the URL to include a session ID. The main issue in preserving state
information is where to store it.
Store state in
1. hidden (HTML form) fields
2. cookies
3. URL
4. web server process memory
5. file
6. database
Server side techniques include any method for tracking a user session with a session ID.
Downloading and installing SQLSession
SQLSession is not a standard part of the Zope distribution. You can download it from the www.zope.org/Products. THe
SQLSession0-2-3.tgz is very small (8K).
The installation is very simple:
1. Change your pwd to the Zope installation directory:
cd ~/zope
2. tar xfz ~/SQLSession0-2-3.tgz The SQLSession product is installed in the lib/python/Products/SQLSession directory.
3. You have to restart your Zope to be able to use a newly installed Zope product.
To make sure SQLSession is properly installed and available, have a look at the folder /Control_Panel.
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (1 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
The SQLSession product also has a README pane providing information on how to use SQLSession.
Preparing the database connection
Before using SQLSession you must create the necessary database and database tables to store the session data. SQLSession does
not do this automatically. SQLSession is completely configurable. In our lesson we are going to use Gadfly shipped with Zope.
The Gadfly demo database is perfect for our purposes to store SQLSession's data. Let's create a "Z Gadfly Database Connection"
object in the /Cms folder.
Create the necessary tables (SESSION and SESSION_DATA) in the Gadfly_demo connection object's "Test" pane.
The following three SQL statements have to be executed:
1. create table sessions(session varchar)
2. create unique index session_idx on session(session)
3. create table session_data ( session varchar, name varchar, value varchar)
When you execute the create statements you get the information message "This was not a query.", which is completely ignorable.
You may make sure that the necessary tables are created by opening the "Browse" pane of the database connectin object
Cms/Gadfly_demo.
Setting up a Session object
When the database is prepared you can create an "SQL Session" object in the Cms folder.
The available ZSQL database connections are listed and you may select the correct one. In our case there is only one database
connection: Gadfly_demo. The name of the object will be "Session", which you should not alter. Normally a Session object is
created up in the folder hierarchy. The "Session Cookie Name" is by default _ZSession. You should alter this name to an
application specific name (eg. _CMSSession) to avoid cookie name conflicts with other Zope applications using SQLSession. The
cookie is saved with a "/" cookie-path attribute (see the HTTP documentation on cookie-paths). SQLSession's cookies are not
persistent, that is they are retained only for the duration of a browser session. If the clients browser is restarted all SQLSession
cookies are lost.
For Gadfly and some other SQL engines it is preferred to specify base64oneline value encoding format.
At this point your SQLSession object is ready to be used. For the curious it may be interesting how SQLSession works. Have a
look at the newly created Session object:
It contains five ZSQL methods:
sqlSetValue
<dtml-comment>
This query sets or updates a data item belonging
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (2 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
to a session. Updates are bad, so we delete/insert.
</dtml-comment>
DELETE FROM Session_data
WHERE
<dtml-sqltest session type=string>
AND
<dtml-sqltest name type=string>
<dtml-var sql_delimiter>
INSERT INTO Session_data
( session, name, value )
VALUES
( <dtml-sqlvar session type="string">,
<dtml-sqlvar name type="string">,
<dtml-sqlvar value type="string"> )
sqlSessionLookup
<dtml-comment>
This query looks up a particular session. It
should return one or more rows if the session
is active.
</dtml-comment>
SELECT * FROM Sessions
WHERE
<dtml-sqltest session type=string>
sqlGetValue
<dtml-comment>
This query looks up some or all data belonging
to a session.
</dtml-comment>
SELECT * FROM Session_data
WHERE
<dtml-sqltest session type=string>
<dtml-if name>
AND <dtml-sqltest name type=string>
</dtml-if name>
sqlSessionCreate
<dtml-comment>
This SQL creates a new session.
</dtml-comment>
INSERT INTO Sessions
( session )
values
( <dtml-sqlvar session type="string"> )
sqlDeleteValue
Check if the argument called name has a default value. If not specify None: name=None
<dtml-comment>
This query deletes a data item for a session.
</dtml-comment>
DELETE FROM Session_data
WHERE
<dtml-sqltest session type=string>
<dtml-if name>
AND
<dtml-sqltest name type=string>
</dtml-if>
You can alter these ZSQL methods to your very needs. You should not change the arguments.
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (3 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
The database schema and the ZSQL methods as shipped will work for gadfly, but probably won't work without modifications on
other databases.
DTML programming with SQLSession
In all DTML scripts that want to use the an SQLSession object put
<dtml-call Session>.
Of course if you renamed your SQLSession object use that name. During this call SQLSession looks for the session cookie (by
default named _ZSession). If it does not find one it inserts a new row into the SESSIONS table by invoking the ZSQL method
sqlSessionCreate. If the REQUEST has a _force_new_session variable having a true value (eg "1") a new session is created. The
session cookie is set to the value in the database column SESSIONS.SESSION of the newly created row. At this point a session
cookie does exist in the REQUEST, the corresponding row in the SESSIONS database table is retrieved by invoking
sqlSessionLookup, a new SessionObj object is created and put in the REQUEST with the name SESSION. You may access this
object by "REQUEST['SESSION']" or simply "SESSION". This SessionObj acts like a dictionary and providing functions like:
set(), has_key(), __getitem__(), __setitem()__, keys(), values(), items(), clear(), getName(), etc.
After calling Session you can use the SESSION object:
<dtml-call "SESSION.set('attribute1',formField1)">
<dtml-var "SESSION['attribute1']">
<dtml-in "SESSION.keys()">
<dtml-var sequence-item> =
"<dtml-var "SESSION[_['sequence-item']]">"<br>
</dtml-in>
<dtml-call "SESSION.clear()">
The "SESSION.clear()" expression removes all variables from the session object (ie all rows from the database table
SESSION_DATE related to the session object). The session ID is retained.
If you want to initiate a completely new session, you may do it with _force_new_session:
<dtml-call "REQUEST.set('_force_new_session',1)">
<dtml-call Session>
The sessio object may also be called with an explicit session id string argument, and with some optional boolean keyword
argumentts: noCookie and validSession.
<dtml-call "Session('mySessionId',noCookie = 1)">
If you specify a session id string this value will be the id of your session. Normally you do not provide your own session id, and
SQLSession creates a unique random id string for the session. If noCookie is set to true no session cookie (by default _ZSession)
is used and created. If validSession is set to true an exception is raised when the session id rerquested cannot be found in the
SESSIONS database table.
Why on earth might I want to specify my own session ids? If you are not content with the session ids generated by
SQLSession, or you need a special session management (eg. concurrent access to the same session data by several web users), you
may want to provide your own session ids.
Why on earth might I want to specify noCookie=1?
The end users may disable cookies in their web browsers. If you want to support these users, you should not use the default
behaviour of SQLSession. However, you don not have to throw away SQLSession, it may be used without cookies.
In this case you specify noCookie=1 when calling the session object and let SQLSession create the session id:
<dtml-call "Session(noCookie = 1)">. Then it is up to you to save the session id in the HTML document.
You may save it
1. in a hidden form field:
<form ...>
<input type=hidden name=sessionid
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (4 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
value="<dtml-var "SESSION.getName()" html_quote>">
</form>
The variable "sessionid" will be available in the subsequent REQUEST object, and you should pass its value to the
session object:
<dtml-call "Session(sessionid,noCookie = 1)">
The first time the variable sessionid may not be available so you have to check the presence of sessionid:
<dtml-if "REQUEST.has_key('sessionid') and sessionid != ''">
<dtml-call "Session(sessionid,noCookie = 1)">
<dtml-else>
<dtml-call "Session(noCookie = 1)">
</dtml-if>
2. in the URL-s as additional path information available in the variable PATH_INFO:
<form method=POST
action="processForm/<dtml-var "SESSION.getName()" html_quote>">
...
</form>
Then you get the session id in PATH_INFO:
<dtml-if "PATH_INFO != ''">
<dtml-call "Session(PATH_INFO,noCookie = 1)">
<dtml-else>
<dtml-call "Session(noCookie = 1)">
</dtml-if>
3. in the URL before path to the object: That way all relative URL-s inherit the session id. The full implementation of this
technique may be very complex. You can use the product SiteAccess, and define an "Access Rule" to extract the session
ID, and the method setURL() to automatically put the session id in generated URLs.
Both of these solution requires an extremelly lot of (basically unnecessary) extra work. You are advised to use SQLSession in its
default way with cookies. Cookies are invented exactly for this kind of purpose.
What can I store in the session object?
You may store any Python object that can be serialized (in Python parlance picled) by cPickle.dumps() (and deserialized by
cPickle.loads()). SQLSession uses the cPickle module.
The List Manipulation Demo
Here is a quite complex demo of using SQLSession. These are the components:
What LMD does?
LMD is used to create list with an arbitrary number of string items. Start the demo application with:
http://nemeth20.iqsoft.hu:8080/Cms/lmd. You get the index_html DTML document.
Specify a name (for example MyList) for the list and press "Create new list".
Enter a listen name (eg. Apple) and press "Add".
You may repeat the previous step by adding any number of items to the list. You may delete items from the list by checking the
items and then pressing Delete.
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (5 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
You may remove all items by pressing the button "Clear"
If you are ready with the list press "Commit" and you get back to index_html.
Now index_html shows you the list you have created.
You may create a new list. Creating a new list starts a new session.
The Session object in the folder lmd
A new SQLSession object is created with the session cookie name "_LMDSession". This session object is dedicated to this LMD
application. Why may this be important? A separate cookie name is necessary to avoid cookie name collisions. The namespace for
cookies are shared by all applications at the client side (ie. the browser has a common are to store cookies). It is possible to restrict
cookies with path names but it is quite often simply "/" is used (as in the case with SQLSession).
Specify base64oneline as value encoding format.
Do not forget to check the sqlDeleteValue ZSQL method's "name" argument. This should have a default None value, or
SESSION.clear() will not work.
The nmlib.py external module
We need two Python external methods:
●
●
createListIfArgNonList checks if its what argument is of type list. If it is this list object is returned, if not a new list is
created and the what object is added to it.
This function is required to handle a problem with HTML checkbox form items.
delListItem deletes the i'th item from the list lst. The "_" namespace does not contain any method tho delete an item
from a list. The "del" and assignment operations are not allowed in DTML scripts: The following DTML statements are
INVALID:
<dtml-call "del mylist[1]">
<dtml-call "mylist = mylist[:]">
Here is the source code of nmlib.py:
def createListIfArgNonList(self,what):
if type(what) is type([]):
return what
else:
l = []
l.append(what)
return l
def delListItem(self,lst,i):
del lst[i]
return lst
The index_html
<dtml-var standard_html_header>
<h2><dtml-var title_or_id></h2>
<p>
<dtml-if "REQUEST.has_key('listName')">
<dtml-call Session>
<dtml-if "SESSION.has_key(listName)">
<dtml-let lt="SESSION[listName]">
<dtml-if "_.len(lt) > 0">
<p>List <dtml-var listName> created.<br>
<dtml-var lt>
<dtml-else>
List <dtml-var listName> is empty.
</dtml-if>
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (6 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
</dtml-let>
<dtml-else>
Warning: 'listName' found but <dtml-var listName> does not exist!
</dtml-if>
</dtml-if>
<p>
<form method=POST action="listCreator">
Name: <input type=text name=listName size=10>
<p><input type=submit value="Create new list">
</form>
<dtml-var standard_html_footer>
Comments:
1. When index_html first invoked the REQUEST variable listName is not defined. However when it is invoked by
listCreator when its Commit button is pressed, listName is defined.
2. listNamevariable has the name of the list entered by the user in the listName text input field.
3. The SESSION object has two values, one of them is a list named by the listName variable. The "SESSION[listName]"
expression returns the list of strings stored in the session database.
The listCreator DTML script
<dtml-var standard_html_header>
<h2><dtml-var title_or_id></h2>
<dtml-call Session>
<dtml-if "REQUEST.has_key('listName')">
<dtml-call "SESSION.set('listName',listName)">
</dtml-if>
<dtml-unless "SESSION.has_key('listName')">
<dtml-call "SESSION.set('listName','XYZ')">
</dtml-unless>
<dtml-call "REQUEST.set('listName',SESSION['listName'])">
<dtml-unless "SESSION.has_key(listName)">
<dtml-call "SESSION.set(listName,[])">
</dtml-unless>
<p>
<dtml-comment>****DELETE*********************</dtml-comment>
<dtml-if "REQUEST.has_key('cmdDelete')
and REQUEST.has_key('listItems')">
<dtml-call "REQUEST.set('listItems',createListIfArgNonList(listItems))">
<dtml-let lt="SESSION[listName]">
<dtml-in listItems>
<dtml-let i="_.int(_['sequence-item'])">
<dtml-call "delListItem(lt,i)">
</dtml-let>
</dtml-in>
<dtml-call "SESSION.set(listName,lt)">
</dtml-let>
Delete command requested!
<dtml-comment>****ADD*********************</dtml-comment>
<dtml-elif "REQUEST.has_key('cmdAdd')">
<dtml-unless "newValue == ''">
<dtml-let lt="SESSION[listName]">
<dtml-call "lt.append(newValue)">
<dtml-call "SESSION.set(listName,lt)">
</dtml-let>
</dtml-unless>
Add command requested for <dtml-var newValue>.
<dtml-comment>****COMMIT*********************</dtml-comment>
<dtml-elif "REQUEST.has_key('cmdCommit')">
Commit command requested!
<dtml-call "RESPONSE.redirect('index_html?listName=' + listName)">
<dtml-comment>****CLEAR*********************</dtml-comment>
<dtml-elif "REQUEST.has_key('cmdClear')">
Clear command requested!
<dtml-call "SESSION.clear()">
<dtml-call "SESSION.set('listName',listName)">
<dtml-call "SESSION.set(listName,[])">
</dtml-if>
<p>
<dtml-comment>****FORM*********************</dtml-comment>
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (7 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
<form method=POST action="<dtml-var id>">
<dtml-let lt="SESSION[listName]">
<dtml-if "_.len(lt) > 0">
Items of list <dtml-var listName>:<br>
<ol>
<dtml-in lt>
<li>
<input type=checkbox name=listItems value="<dtml-var sequence-index>">
<dtml-var sequence-item html_quote>
</dtml-in>
</ol>
<dtml-else>
List <dtml-var listName> is empty.
</dtml-if>
</dtml-let>
<p>
<input type=submit name=cmdDelete value=Delete>
<input type=submit name=cmdClear value=Clear>
<input type=submit name=cmdCommit value=Commit>
<p>
<input type=text name=newValue size=30>
<input type=submit name=cmdAdd value=Add>
</form>
<dtml-var standard_html_footer>
Comments:
1. The REQUEST object should have a 'listName' variable.
2. The SESSION object has two values:
❍
❍
'listName' is a string value containing the actual name of the string
The other is a list value having the name specified by the variable listValue. It would have been simpler to use a
non-variant name like 'listValues'.
3. If no 'listName' exists in the session database save it there: "SESSION.set('listName',listName)". The SESSION.set()
statement saves the value into the database.
4. If no listName variable is available 'XYZ' is used as the default name. This behaviour allows the user to directly use the
script listCreator by http://nemeth20.iqsoft.hu:8080/Cms/lmd/listCreator.
5. If there is no list object in the SESSION object named by the variable listName, an empty list is created and saved into
the session database: "SESSION.set(listName,[])".
6. When the cmdDelete button is pressed, the web browser returns the list of checkboxes checked by the user. Zope creates
a list variable named listItems (because this is the name of all checkboxes) in the REQUEST object. However if no
checkbox is checked no listItems variable is created. And if only one checkbox is checked listItem is a simple string
variable; that's why is the need for the external Python method createListIfArgNonList().
7. The values of the listItems are the values of the checked checkboxes: "0","1","2", etc. If they are converted to integer,
they can be used as the indexes of the list items to be deleted. The items are deketed by the external Python method
delListItem(). When all requested items are deleted the new list is saved back into the session database:
"SESSION.set(listName,lt)".
8. New items are simply added to the list named by the variable listName: "lt.append(newValue)". The variable newValue
is the input field of the HTML form.
9. "SESSION.clear()" simply deletes all values belonging to the session (the name of which is stored in the cookie
_LMDSession). In this situation this SESSION.clear() is not necessary; The last statement would be enough:
<dtml-call "SESSION.set(listName,[])">
Managing session data
Currently sqlsession does no cleanup whatsoever, so eventually the table will fill. Something in, e.g. cron, should be called to
clean up old entries.
If you want to delete only old session data you have to alter the database schema used for storing the session data. If you have an
Oracle database for example you may alter your schema: create table sessions(session varchar2(100), cdate date). You should
alter the insert statement in the sqlSessionCreate method:
INSERT INTO Sessions
(session,cdate)
values
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (8 of 9) [05/07/2000 11:21:44]
Learning Zope - Using SQLSession
(<dtml-sqlvar session type="string">, sysdate)
And then you may delete old session objects:
-- Remove session rows older than two days.
delete from sessions where cdate < sysdate - 2
Exercises
1. Create a wizard application consisting of three data entry forms, and a fourth confirmation form. The confirmation form
contains all data (as simple text) entered during the previous forms, a "Commit" and "Back" button. The data entry forms
have two buttons: "Previous" (except the first form) and "Next". Use a dictionary for storing the session data.
2. Create a ZSQL method retrieving all session data from the database. Create a related HTML pade displaying the session
data. Create a ZSQL method that deletes all session data from the database, and create a simple form with a button which
invokes this method.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/nemeth/usingSQLSession?pp=1 (9 of 9) [05/07/2000 11:21:44]
Using Squid to serve Zope pages
How-To: Using Squid to serve Zope pages
Created by itamar. Last modified on 2000/05/07.
Squid, the well know HTTP proxy, can also be used as a reverse proxy in front of Zope. That is, instead of users connecting to
Zope, they'll connect to Squid (although they won't know that!).
Why this is good:
1. Any static content (Images, Files, or your owne DTML pages if they the correct HTTP headers) is going to get served
from Squid. Squid is way faster than Zope!
2. If Zope dies, the non-dynamic parts of your site will still work, so you have a bit more leeway in getting Zope back up :)
Why this is bad:
1. You'll have to use Squid's access logs to count how many visitors you had, and Squid doesn't store the Referer or
User-Agent headers in it's access log.
Anyways, here's how you do it:
1. Add HTTP headers to your static DTML pages so that they get cached. I'm going to write a separate Howto on this.
2. Install Squid:
Download the latest version from http://www.squid-cache.org .
tar xvfz squid-X.tar.gz
cd squid-X
./configure
make
su
make install
Unless you changed the defaults, squid is now installed in /usr/local/squid.
3. We'll have Squid running on port 80, so when people connect to our website (http://www.example.com) they'll go
straight to Squid, and won't even know that Zope is running on port 8080. This means squid must run as root. We
therefore need to set the right permissions on squid's directorys - when squid runs as root it gets changed to run as
nobody.
cd /usr/local/squid
bin/squid -z # this sets up the cache directories
chown -R nobody.nobody cache
mkdir logs # may not be neccesary
chown -R nobody.nobody logs
4. Next, we need to setup squid so it runs as a http-accelerator, save its logs in standard webserver format instead of it's own
format, and of course let the public view our site.
The squid configuration file in /usr/local/squid/etc/squid.conf is commented quite nicely. Most options we'll just ignore
and leave as is. The options we do need to change and/or uncomment are:
http_port 80
# listen on port 80
emulate_httpd_log on
# use NCSA https style logfile
http_access allow all
# this line replaces 'http_access deny all'
httpd_accel_host www.example.com # hostname where Zope is running
httpd_accel_port 8080
# port where Zope is running
5. And last but not least, we need to tell Zope that it's running (from the user's point of view) on port 80, not 8080. For that
we'll use Evan Simpson's SiteAccess product. We add a SiteRoot object in Zope's root folder, and set BASE to
http://www.example.com and the PATH to /.
That's it! Now you can connect to http://www.example.com and Squid will serve up Zope pages for you.
For more info, visit:
http://www.squid-cache.org - Squid homepage
http://www.zope.org/Members/anser/apache_zserver/- the same as (and the basis for) this Howto, only using Apache as the proxy
http://www.zope.org/Members/4am/SiteAccess
http://www.zope.org/Members/itamar/Squid?pp=1 (1 of 2) [05/07/2000 11:21:47]
Using Squid to serve Zope pages
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/itamar/Squid?pp=1 (2 of 2) [05/07/2000 11:21:47]
Using VIM and Zope via FTP
How-To: Using VIM and Zope via FTP
Created by philh. Last modified on 2000/06/06.
Using VIM with Zope
(Sticking FTP into Vim)
There has been discussion on the Zope list lately about which editors work best with Zope.
Like a few others I prefer using Vim but it's one great failing is that it has no native FTP ability.
So... I decided to give it that ability :)
The only real caveat in all this is that Vim must have the Python extensions compiled in.
Instructions
1. Get the files from here
2. Edit the files for your particular circumstance:
Basically you need to change ftpsave.py and ftpopen.py to have your connection details in the four vars at the top of the
files.
Also if you use gvim then you can add a few entries to your menu.vim file, search the downloaded menu.vim for FTP and
you'll see which ones to add and where.
If your using vim in console mode then it's slightly more complicated to use:
You can only run the pyfiles from the ex command prompt like so:
:pyf /path/to/the/pyfiles/supplied/ftpopen.py
:pyf /path/to/the/pyfiles/supplied/ftpsave.py
If your using GVim then after making the changes to your menu.vim file you should have two new options on it (FTP
Open and FTP Save).
Buyer beware
This software is guaranteed not to work as advertised.
Use it at your own risk.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/philh/vim_html?pp=1 [05/07/2000 11:21:49]
Using ZClient to Access Another Server
How-To: Using ZClient to Access Another Server
Created by lstaffor. Last modified on 1999/09/27.
Sometimes you need to get data from another server and serve it as if it came from your server. For example, you want to show
the current weather from www.weather.com on the front page of your Intranet site. Or your main server is on Unix, but you want
to get data from Microsoft databases on an NT server through another Zope on that machine.
You want to use ZClient, which is located in your Zope directory at ../lib/python/ZPublisher/Client.py. Read the comments to get
an idea of how it works. Basically, it acts as a web browser, but returns the fetched page to your program.
Because, ZClient is an external Python program, you have to create an external method to drive it from within DTML. Consult the
How-To: Using External Methods to help you add an external Python method similar to this:
from ZPublisher import Client
def web_client(url = 'http://www.yahoo.com/', username = None, password = None, **kw):
'''access http servers'''
if kw:
return Client.call(url,username,password,kw)[1]
else:
return Client.call(url,username,password)[1]
My version of this Python code resides at ../Extensions/WebClient.py and the external method is called "webClient".
Create one or more DTML methods that call this external method with the correct parameters to access the other server. Here's a
simple example that doesn't require any more parameters than the URL of an SQL method on the database server:
<!--#var standard_html_header-->
<h2><!--#var document_title--></h2>
<dtml-var "webClient('http://nt-server:8080/Marketing/AllCustHave')">
<!--#var standard_html_footer-->
That's all!
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/lstaffor/ZClientMethod?pp=1 [05/07/2000 11:21:51]
Using ZOPE with MS SQL Server
How-To: Using ZOPE with MS SQL Server
Created by roberth. Last modified on 2000/05/04.
Dear Fellow Zope Users,
Many thanks go to the following persons for their patient assistance.
Henny van der Linde,
Julio Dinis and
Julio Silva
Success at last.
Here, finally is what I had to do.
After installing the ZODBC DA, I needed to ensure that the Zope server runs as a
known username (with a known password). Then I needed to create a Windows NT
account to correspond to that username and password. Next I needed to ensure
that the SQL Server admits a login using that combination of username and
password with at least dbowner access to the database I want the ZOPE server to
use. Once I did that, I was able to simply choose a DSN from the list box that
appears on the add ODBC connection screen and it worked.
It all came together when Julio Dinis asked me if the server I was trying to
connect to was a remote server or a local (on the same machine as the ZOPE
installation) SQL server. When I tried it on a local SQL Server DSN it worked,
but when I tried it on my remote SQL Server it worked. When Henny van der Linde
suggested I use SQL Server's logging to see if the connections were being
rejected, I saw that ZOPE was trying to establish a trusted connection with an
undefined username. That was when I finally figured out what I needed to do.
And I was just about to give up on ZOPE. Good thing I did not. Perhaps they
need to update the documentation.
If you need any further explanation of the setup, drop me a line.
Original Question:
>
> Anyone ever figured out how to get ZOPE's ODBC DA to work with MS SQL Server?
>
> I am a Zope newbie using MS SQL Server 7.0, Zope version 2.1.6, Python 1.5.2 Win
> 386 and ZODBC
> Version 3.1.0b2.
>
> Whenever I attempt to open an ODBC connection to the SQL Server I get the
> message "Invalid connection string:" followed by the name of the DSN I am trying
> to use. Can anyone help me along with this or is this the curse for using
> Microsoft software?
>
Yours sincerely,
Robert Honore.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/roberth/ZMSSQLServer?pp=1 [05/07/2000 11:21:53]
Using ZSQL Methods with Acquisition
How-To: Using ZSQL Methods with Acquisition
Created by rob. Last modified on 1999/09/15.
Zope Acquisition -- Real Life Example
By Rob Page (rob@digicool.com)
Over the last several years Digital Creations has delivered a number of custom software development projects. Many of these have
involved using relational databases for data storage. This document describes a specific approach to leveraging Zope's Acquisition
technology in a real-life application.
Budget Application
In this particular budgeting application individual departments had relatively autonomous control over their funds. However,
every department does the same kinds of things with their funds. For example, every department needs to:
●
spend their Department's money
●
report how much they have spent by Expense Aaccount
●
report how much they have spent by Project
●
and several, several more
As importantly however, the Engineering Department Head should not be able to view the expenditures of the Marketing
Department and vice versa.
An Answer
In this case we stored the generic SQL Methods in the Budget folder and allowed Acquisition to customize them for us. For
example, a slightly simplified version of the SQL Method used to spend money follows:
Id
Title
Arguments
sqlInsertRequisition
Insert a Requisition
description, qty, unit_cost
http://www.zope.org/Members/rob/SQLAcquisitionHowTo/SQLAcquisition?pp=1 (1 of 2) [05/07/2000 11:21:57]
Using ZSQL Methods with Acquisition
Query
INSERT INTO requisitions
(
department_id, description, qty, unit_cost
)
VALUES
(
<!--#sqlvar department_id type=string-->,
<!--#sqlvar description type=string-->,
<!--#sqlvar qty type=int-->,
<!--#sqlvar unit_cost type=float-->
)
In this case, we are expecting the following variables to come in with the REQUEST:
●
description
●
qty
●
unit_cost
Significantly, we are not expecting the department_id to come in with the REQUEST. Rather, we want to look for the
department_id Property of the Department's folder and use that in the SQL Method. More importantly, we DO NOT want to use a
department_id that comes in with the REQUEST as this would open the SQL Method to attack!
We assigned a property to each Department's folder. We also intentionally omitted department_id from the list of arguments in the
definition of the SQL Method. This precluded the SQL Method from getting a department_id (even if present) from the
REQUEST.
This leverages the lookup order for DTML variables. This lookup order is documented in the DTML User's Reference. Briefly,
variables excluded from the ZSQL Method's argument list will not be found in the REQUEST. Rather, some alternative way of
finding the variable (e.g., Acquisition) must be used.
Why
In this application approximately twenty (20) queries were relatively generic and required only department_id and/or project_id
tweaking to become widely applicable. Subsequent maintenance efforts are thereby limited to one instance of a query.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/rob/SQLAcquisitionHowTo/SQLAcquisition?pp=1 (2 of 2) [05/07/2000 11:21:57]
Using Zope on a third-party web host
How-To: Using Zope on a third-party web host
Created by gldnspud. Last modified on 2000/02/23.
DISCLAIMER: Before configuring a v-host account like this, please double check your host's policies regarding long-running
processes. The host I use is very flexible with long-running processes such as Zope, but yours may not be. I hereby disclaim any
damages or loss of service related to the steps outlined below; caveat emptor.
After I evaluated Zope on my home machine and started using it to develop a web site, I wanted to use it to revamp and improve
upon another web site I run. The problem was, I only evaluated and started using Zope on machines that I had full, administrator
access to. The web site I wanted to revamp is on a third-party hosting site where I don't have much control.
I searched through mailing list archives but couldn't find a suitable solution that would work on the provider I use (csoft.net). I
figured out a solution that is only slightly awkward and works fine for my uses, so I am posting it here for others to use.
Hopefully this will allow more people to evaluate Zope and more people to use the product on third-party virtual hosts.
Some configuration details about csoft.net's servers before I outline the solution. You'll have to take these into account if you
adapt this to another provider.
* telnet/ssh access is provided
* necessary compilers/interpreters (gcc and python) are installed
* CGI scripts can run anywhere in the www tree as long as they have the .cgi extension
* scripts can be called with or without their extension -- i.e. as long as there are no other files starting with "zope" you can call
zope.cgi with either zope or zope.cgi in the URL
* htaccess files can be used for authentication
* your CGI scripts are run under your UID
The solution (adapt to your provider):
* unpack zope into your home directory (I named my folder ~/zope)
* go into ~/zope and run: python w_pcgi.py
This will compile and prepare Zope for use
* edit your ~/zope/start script to read as follows (this turns off the web, FTP, and monitor servers as well as turning off debugging)
#! /bin/sh
reldir=`dirname $0`
PYTHONHOME=`cd $reldir; pwd`
export PYTHONHOME
exec /usr/bin/python \
$PYTHONHOME/z2.py \
-w '' \
-f '' \
-m ''
* edit the "access" file (or create it if it doesn't exist yet) and put in one line:
superuser:
(You can name the user whatever you like. Remember the colon after the username; do not put anything after the colon though)
* cd ~
* htpasswd -c zopeusers superuser (This will prompt you for the superuser password. Change superuser to whatever name you
want your superuser to be)
* cd ~/www (this is the root document folder on csoft.net accounts)
http://www.zope.org/Members/gldnspud/zope3rdparty?pp=1 (1 of 2) [05/07/2000 11:22:00]
Using Zope on a third-party web host
* cp ~/zope/Zope.cgi ./zope.cgi (Copy the zope CGI script to your root document folder. I actually called mine domain.cgi so I
can go to www.domain.cgi/domain/folder/etc but you can name it whatever you like
* mkdir manage; cd manage (This will make a management folder that will authenticate your password. This is where it gets just a
tad klugey)
* create a .htaccess file in this folder containing this:
AuthType Basic
AuthName Zope-realm
AuthUserFile /path/to/your/home/zopeusers
require valid-user
* cp ../zope.cgi . (This provies your manage folder with access to Zope)
I hope I didn't leave any steps out. Here is how you view and manage your new Zope installation based on the instructions above:
* to view your Zope objects as normal, just go to http://www.domain.com/zope/ (or http://www.domain.com/zope.cgi/ if you
cannot leave off the trailing .cgi)
* to view your Zope management interface, you need to go to http://www.domain.com/manage/zope.cgi/manage and authenticate
yourself. You can actually view your Zope web here by leaving off the last /manage but you will still need to authenticate
yourself.
Some notes:
* if you want to add users to the Zope interface, add them there with no passwords. Then, go to your home directory and type
"htpasswd zopeusers username" to add/change a user in the password list that apache uses.
* if you need to use Zope as your index file, use your index file in your root document folder to redirect. For instance, since
csoft.net supports PHP, I can create a small one-line index.php3 file to accomplish this very quickly:
<? Header('Redirect: http://www.domain.com/zope/'); ?>
Please let me know if there are any improvements you can see in this setup, or any pitfalls I have not yet discovered.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/gldnspud/zope3rdparty?pp=1 (2 of 2) [05/07/2000 11:22:00]
Using a Chameleon Folder
How-To: Using a Chameleon Folder
Created by hellmann. Last modified on 2000/05/08.
This HowTo demonstrates how to create a simple example page using a Chameleon Folder and two different Skins.
Known Bugs
Please note that there is a known bug in Chameleon 0.1.0. The overrides to normal Acquisition breaks Zope's user authentication
code. I believe the problem is in finding the acl_users folder itself, but have not had an opportunity to work out what is going on.
Please be forewarned.
What is a Chameleon?
A Chameleon Folder, or Chameleon, is a type of Folder that overrides the default behavior of Zope's Acquisition feature to allow a
content designer to create multiple views of the same data by applying Skins. Chameleon Skins are analogous to themes for X
window managers such as Enlightenment.
Content designed to use Skins should separate the management of data from the rendering of that data. This is accomplished with
Zope by using Z SQL Methods, External Methods, or DTML Methods to produce data that can be rendered through DTML
Methods or DTML Documents. Using a Chameleon is a natural extension of the design separation already encouraged by Zope,
and a quick way to add flexibility to a web site.
An Example Project
Creating a Chameleon
This example is rather simplistic, to keep this How-To short. Chameleon is distributed with a demonstration instance
(ChameleonDemonstration.zexp) that can be imported and examined for a more complex example. Once you understand the
principle of what Chameleon can do, it should be straightforward to design your own presentation.
After the Chameleon product has been installed in your Zope instance, the first step is to create a Chameleon in which to work.
Chameleon should appear in the Available Objects list of any Folder where you have permission to create new objects. The Add
Chameleon Folder interface takes as inputs:
Parameters for creating a Chameleon
Id
Title
The Zope identifier of the new object.
A title for the Chameleon. This is not
mandatory, but will be acquired by the contents
of the Chameleon if specified. If not specified,
the title value follows the Chameleon
Acquisition Rules discussed below.
Skin preference cookie
The name of the cookie that the Chameleon
monitors to decide which skin to present to
incoming requests. This cookie is not set by the
Chameleon directly, but is read for every
access.
Shared content folder
Due to the implementation of the Chameleon,
content files which are shared among multiple
skins should be placed in a folder within the
Chameleon. The name and id of this folder are
not important, and can be changed after the
Chameleon is created. The folder name is
requested here so the constructor can create a
content folder for you when the Chameleon is
created.
http://www.zope.org/Members/hellmann/Chameleon/Chameleon-How-To?pp=1 (1 of 4) [05/07/2000 11:22:05]
Using a Chameleon Folder
Create user folder
Similar to a regular Folder, a Chameleon
allows you to create a UserFolder to control
access to the contents
Create default Chameleon Skin
folder
Creating a Chameleon with no Skins is not
very interesting. Leaving this checkbox turned
on will create a Skin with the name defaultskin.
This is the automatic default value for the
default skin property of the Chameleon. The
default skin property can be changed using the
Edit interface of the Chameleon once it is
created.
Create a new Chameleon with id my_chameleon, a title, and all other inputs left at their default values. The Chameleon folder
should appear in the management interface with a green lizard on top of a standard Folder icon.
Examining the contents of a Chameleon
Click on the Chameleon you created to edit it. You will see a list of contents similar to the view of Contents for a Folder. The
content includes a UserFolder (acl_users), a Chameleon Skin (defaultskin), and a Folder (view).
Click on the Edit tab to look at the properties for the Chameleon. This interface allows you to change the cookie monitored by the
Chameleon, and to set the Skin to be displayed as the default appearance of the contents of the Chameleon.
Go back to the Contents tab and select the view folder. There is an index_html object. Notice that this is a copy of the default
index_html object created when a Folder object is created.
Go back to the view of the contents of the Chameleon, then select the defaultskin object. A Skin Folder is another kind of Folder
automatically recognized by a Chameleon as having special behavior. Once the Chameleon picks a Skin to present to the incoming
request, any object contained in a Skin is treated by the Chameleon as though it exists within the Chameleon folder directly. This
allows objects in the view Folder to refer to objects in the Skin without having to know which Skin is being applied (or even that
they are inside a Chameleon). The appropriate object is available via Acquisition from the Chameleon.
Chameleon Acquisition Rules
Content is available through a Chameleon in the view Folder based on these rules, in this order:
1. Content present in the view Folder.
2. Content present in the Chameleon Folder.
3. Content acquired through the Chameleon Folder, that exists in the Skin.
4. Content acquired through the Chameleon Folder, that exists higher in the object hierarchy.
Creating different standard headers
An easy way to change the appearance of content is by changing the standard_html_header and standard_html_footer objects to
set special characteristics of the page (such as background color). For this example, we will create two default color sets for a
document by creating two skins. In the default skin, we will not provide a standard_html_header so the system-wide header object
is used. In the secondary skin, we will create a new standard_html_header so that when that Skin is being used the colors on the
page will be different.
Use the management interface to look at the contents of the defaultskin object. Notice that there is no standard_html_header by
default.
Use the management interface to view the contents of the Chameleon. Create a new Chameleon Skin in the Chameleon, and name
it secondaryskin. You do not need to create a public interface or a user folder for the Skin. Navigate into secondaryskin with the
management interface.
Create a new DTML Method named standard_html_header. Edit the method so the contents are:
standard_html_header
http://www.zope.org/Members/hellmann/Chameleon/Chameleon-How-To?pp=1 (2 of 4) [05/07/2000 11:22:05]
Using a Chameleon Folder
<HTML>
<HEAD>
<TITLE><dtml-var title_or_id></TITLE>
</HEAD>
<BODY BGCOLOR="#000000">
<font color="#ffffff">
Now you have two Skins which will provide different appearances for the document in the view folder. It is time to provide an
interface for the user to choose the view.
Cookie Control
The Chameleon Folder picks a Skin to apply to a request by looking for a cookie you specify when it is created. If this cookie is
not present in the request, the Chameleon will use a default setting. For our example, we need to create a DTML Method that sets
the right cookie value.
In the root folder of the Chameleon, create a DTML Method named cookie_list to serve as a start for this interface by listing the
available views. The contents should be:
cookie_list
<dtml-var standard_html_header>
<H2><dtml-var title_or_id> <dtml-var document_title></H2>
Choose the view:
<ul>
<dtml-in SkinIDs>
<li><a href="set_cookie?skin=<dtml-var sequence-item>">
<dtml-var sequence-item></a>
</dtml-in>
</ul>
<dtml-var standard_html_footer>
Next, create a DTML Method named set_cookie as a sibling of cookie_list to actually set the cookie:
set_cookie
<dtml-var standard_html_header>
<dtml-call "RESPONSE.setCookie('chameleon_skin',
skin,
path='/')">
<a href="view">Click to see the contents</a>
<dtml-var standard_html_footer>
You may want to leave out the path argument when calling setCookie, if you have different Chameleon objects using different
cookies to control their appearance.
Trying the example
Now you have all of the parts required for the Chameleon to work:
●
a Chameleon Folder
●
a couple of Skin Folders
●
an interface to set the Chameleon's cookie
http://www.zope.org/Members/hellmann/Chameleon/Chameleon-How-To?pp=1 (3 of 4) [05/07/2000 11:22:05]
Using a Chameleon Folder
Try out the Chameleon by visiting the cookie_list page (my_chameleon/cookie_list). Click on one of the items in the list to set the
cookie appropriately. Then click on the Click to see the contents link to look at the view document through the chosen Skin. Go
back and forth a few times to see how changing the cookie value in your browser changes the appearance of various pages.
For more information, see the Reference Manual.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/hellmann/Chameleon/Chameleon-How-To?pp=1 (4 of 4) [05/07/2000 11:22:05]
Using a tokens property
How-To: Using a tokens property
Created by cba. Last modified on 2000/05/10.
The property type called "tokens" turns out to be a reasonable place to store a short list of "words" or tokens - strings that do not
include whitespace. A Zope object id meets the criteria to be a token, and a tokens property can be quite fast and simple to use.
I had to find a workaround due to a performance problem in Zope: I had a folder full of documents (maybe 50, but expected to
grow to 600+), and each document had a couple of boolean proerties. For each document with both boolean values set to True, I
needed to display a link. However, processing the DTML method that did the filtering took over a minute!
Since I knew I would only have 3 - 4 documents at any one time that met the criteria, I decided to use a tokens property to store to
document id values
Setting the value of the tokens property in the parent folder was easy in the context of a method that processed a form:
<dtml-call "manage_changeProperties(mytoken=[])">
<dtml-in "objectValues('DTML Document')">
<dtml-if formboolean>
<dtml-call "mytoken.append(_['sequence-var-id'])">
</dtml-if>
</dtml-in>
Using the documents referenced in the tokens property was also simple, once you know a certain Zope technique: getitem.
<dtml-in mytokens>
<dtml-with "_.getitem['sequence-var-id']">
<a href="<dtml-var id>"<dtml-var title><a><br>
</dtml-with>
</dtml-in>
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/cba/tokens_usage?pp=1 [05/07/2000 11:22:07]
Using cron with Zope
How-To: Using cron with Zope
Created by lalo. Last modified on 2000/02/08.
One of the big difficulties people have with a "100% pure Zope" sollution is that Zope is, after all, user-driven. There is
(apparently) no way of executing some task periodically, or in response to a mail message.
Well, these premises are not actually true. Yes, things only happen in Zope when someone makes a request (via HTTP, FTP, the
Monitor, or whatever else someone happens to add in the future). However, it isn't really difficult to fulfill this simple
requirement.
Let's assume you have a queue of comic strips; you want to allow the author to upload a new one anytime he wishes, but you want
it to only go live by midnight (local time). In a static webserving world (and an Unix-like system, of course), you would store the
queue somewhere, as physical files, and then use a cron job to move the oldest file in the queue to the static page everyday.
Well, the good news is that you can do exactly that with Zope.
Here are the steps you must follow:
●
first, of course, write the method you want to run periodically; make it a DTML Method, or a Python Method if it's more
appropriated. Test it by running it manually (by entering the URL in your browser).
●
make sure your script returns an empty string on success. This is very important.
●
if you want to make sure this script won't run by accident, remove the "view" permission for Anonymous.
●
write your cron line like: "lynx -auth user:password -source http://somewhere.foo/path/to/script"
With this command, if the method is successful, cron will consider everything OK; if the method fails, Zope will return an error
page, which lynx will dump, which cron will consider an error and mail back to you!
Security considerations
You don't want to have your Manager password in /etc/crontab or somewhere as visible as that - not to mention that it will appear
to anyone who happens to type "ps ax" on the machine while the job is running. So, create a "cron" user as high as you can in the
Zope hierarchy, then don't grant it any useful powers, except running these methods.
Other useful tools
There are other things you can use instead of lynx; for example, with wget: "wget -O - --http-user=username
--http-password=password http://somewhere.foo/path/to/script". In this case, it is possible to save the password in a config file,
and then use the standard filesystem security to keep it safe; consult the wget documentation about how to do that.
If you need anything more complex than running a simple script with no parameters, take a look at XML-RPC.
© 2000, Digital Creations, Inc. All rights reserved.
http://www.zope.org/Members/lalo/cron?pp=1 [05/07/2000 11:22:09]
Using selection lists
How-To: Using selection lists
Created by AlexR. Last modified on 1999/11/07.
Version 0.3
This howto show basic ways to implement a selection list (i.e. a drop-down list or multiline selection area) in a form. Most
information were found on the zope@zope.org list and looking at the Zope UI pages. Feel free to send me your feedback.
Version History
●
Version 0.3 - 7th November 1999 - Added example for Python methods
●
Version 0.2 - 3rd November 1999 - Corrected layout error and added section about external methods
●
Version 0.1 - 31st October 1999 - First draft
Using the SELECT tag in forms
To create a drop-down list, use a SELECT tag within FORM tags. In SELECT tags, each option is itemized by an OPTION tag.
These tags are not Zope-specific; they are standard HTML tags. For more information, see the HTML 4 Specification or an
HTML tutorial.
Let's create a basic selection list with hard-coded options.
<FORM action="myMethod">
...
<SELECT name="myList">
<OPTION value="optionName1">optionValue1</option>
<OPTION value="optionName2">optionValue2</option>
<OPTION value="optionName3">optionValue3</option>
<OPTION value="optionName4">optionValue4</option>
</SELECT>
...
</FORM>
The displayed part is optionValueN. The SELECT structure need to be located in FORM tags. I will omit the FORM tags
below for short.
Within the SELECT tag, you can specify attributes (these are standard HTML attributes):
●
multiple - allows multiple option to be selected
●
size=N - creates a selection area N rows high
●
disabled - control is unavailable in this context
●
tabindex - position in tabbing order
Example:
<SELECT name="myList" multiple size="4">
<OPTION value="optionName1">optionValue1</OPTION>
<OPTION value="optionName2">optionValue2</OPTION>
<OPTION value="optionName3">optionValue3</OPTION>
<OPTION value="optionName4">optionValue4</OPTION>
</SELECT>
Additionally, DTML allows to specify the type of the data returned by the list. To specify a type, append a colon (:) and a type
name to the list name.
Example:
<SELECT name="myList:list" multiple size="4">
<OPTION value="optionName1" selected>optionValue1</OPTION>
http://www.zope.org/Members/AlexR/SelectionLists?pp=1 (1 of 11) [05/07/2000 11:22:18]
Using selection lists
<OPTION value="optionName2">optionValue2</OPTION>
<OPTION value="optionName3" selected>optionValue3</OPTION>
<OPTION value="optionName4">optionValue4</OPTION>
</SELECT>
Here the myList selection will be returned in list format. You can specify other types. For more information about these types in
forms, see the Form Variable Types and Typechecking Howto.
Within the OPTION tag, you can also specify attributes (standard HTML attributes here too):
●
selected - allows to preselect one or several lines
●
disabled - control is unavailable in this context
Example:
<SELECT name="myList" multiple size="4">
<OPTION value="optionName1" selected>optionValue1</OPTION>
<OPTION value="optionName2">optionValue2</OPTION>
<OPTION value="optionName3" selected>optionValue3</OPTION>
<OPTION value="optionName4">optionValue4</OPTION>
</SELECT>
Here option 1 and 4 are preselected and displayed in a 4-line selection box.
Filling lists from loops
Using loops
You can fill a list from a loop. You use the dtml-in tag to specify the object to loop on. When looping on an object, you can
directly access its properties within the dtml-in tags. For more information about this tag, see the Z Document Template
Markup Language Guide. The following code creates a selection list of DTML document titles:
<SELECT name="myList">
<dtml-in "objectValues(['DTML Document'])">
<OPTION value='<dtml-var id>'><dtml-var title></OPTION>
</dtml-in>
</SELECT>
For more information about loops, see the Looping in DTML Howto.
You can also loop on a list of hard-coded values. Example:
<SELECT name="myList">
<dtml-in "('un', 'deux', 'trois', 'quatre',
'cinq', 'six', 'sept', 'huit',
'neuf', 'dix')">
<OPTION value="<dtml-var sequence-item>"><dtml-var
sequence-item></OPTION>
</dtml-in>
</SELECT>
You get a nice list with French figure names. How wonderful for you.
You can also specify a list of figures. Example:
<SELECT name="myList:int">
<dtml-in "(0, 1, 2, 3, 4, 5 ,6, 7, 8, 9)">
<OPTION value="<dtml-var sequence-item>"><dtml-var
sequence-item></OPTION>
</dtml-in>
</SELECT>
http://www.zope.org/Members/AlexR/SelectionLists?pp=1 (2 of 11) [05/07/2000 11:22:18]
Using selection lists
Testing and excluding values
You can use Python expressions to test values. This allows you to preselect or filter out values from your list. Example:
<SELECT name="myList">
<dtml-in "objectValues(['News'])">
<dtml-if "id() == 'foo'">
<OPTION SELECTED VALUE='<dtml-var id>'>
<dtml-var title_or_id></OPTION>
<dtml-else>
<OPTION VALUE='<dtml-var id>'>
<dtml-var title_or_id></OPTION>
</dtml-if>
</dtml-in>
</SELECT>
The following example exclude objects whose type is "News".
<SELECT name="myList">
<dtml-in objectItems>
<dtml-if "meta_type != 'News'">
<OPTION><dtml-var id></OPTION>
</dtml-in>
</dtml-in>
</SELECT>
Note you need to use a special syntax to test the sequence-item variable because of naming conventions.
<dtml-if "_['sequence-item']=='foo'">
Example:
<SELECT NAME="myList:list" SIZE="5" MULTIPLE>
<dtml-in valid_roles>
<dtml-if "_['sequence-item'] != 'Anonymous'">
<dtml-if "_['sequence-item'] != 'Shared'">
<OPTION VALUE="<dtml-var sequence-item html_quote>">
<dtml-var sequence-item></OPTION>
</dtml-if>
</dtml-if>
</dtml-in>
</SELECT>
dtml-if tests can check values against a list:
<SELECT name="myList:list" size="5" multiple>
<dtml-in valid_roles>
<dtml-if "_['sequence-item'] not in ('Anonymous', 'Shared')">
<OPTION value="<dtml-var sequence-item html_quote>">
<dtml-var sequence-item></OPTION>
</dtml-if>
</dtml-in>
</SELECT>
Filling lists from properties
Create a property with type lines. For instance, you can assign this property to the folder that holds your form. Fill in several
lines item for testing. Let's call this property my_subjects. Back in the form, enter the following code:
<SELECT NAME="myList">
<dtml-in my_subjects>
<OPTION VALUE="<dtml-var sequence-index>">
<dtml-var sequence-index>:
http://www.zope.org/Members/AlexR/SelectionLists?pp=1 (3 of 11) [05/07/2000 11:22:18]
Using selection lists
<dtml-var sequence-item></OPTION>
</dtml-in>
</SELECT>
sequence-index is the line number (1st line is 0). sequence-item is the actual line text. The code described above will
create a list with the following content :
0: Your 1st line
1: Your 2nd line
...
Filling lists from TinyTables
TinyTable is a product that allows you to create table objects in Zope. You can download it from here.
After installing the product, create a new TinyTable (choose TinyTable in the object list). Enter an ID (let's use myTable) and
column names (subject_id and subject_name, for instance). In the Data area, enter a few lines in comma-separated
format: on each line, enter an id and a name as in the following example:
"my_id_1", "This is subject 1"
"my_id_2", "This is subject 2"
...
Click on the View tab to check your entry. You should see a nice table with your data.
Back in your form, enter the following code:
<SELECT NAME="myList">
<dtml-in myTable>
<OPTION VALUE="<dtml-var subject_id>">
<dtml-var subject_name></OPTION>
</dtml-in>
</SELECT>
That's all: you get a selection list based on your TinyTable. So you can easily avoid hard-coding list options in your forms.
Filling lists from SQL methods
Believe it or not, this is easy. You'll need a working database, database connection and SQL query. You'll find below a very short
summary of the process. For more information, see the Z SQL Methods User's Guide. There are also several howtos about ZSQL
Methods.
1. You'll need a database. If you want to access an ODBC database on Win32, you'll need to set up an ODBC Data source
first in your system.
2. First create a database connection in your folder. I used the Zope ODBC Database Adapter Product; you can download it
from here.
3. Then create a SQL Method linked to your database connection. Let's call it mySQLQuery. Here is very basic code I used
for testing; it doesn't take any argument because it returns the whole content of the table:
SELECT * FROM myTable
Let's imagine your table (myTable) has two columns, Field1 and Field2. This simple code fills up your list from your table
through the SQL query:
<SELECT name="myList">
<dtml-in mySQLQuery>
<OPTION VALUE='<dtml-var Field1 null="">'>
<dtml-var Field2 null="">
</dtml-in>
</SELECT>
http://www.zope.org/Members/AlexR/SelectionLists?pp=1 (4 of 11) [05/07/2000 11:22:18]
Using selection lists
Filling lists from DTML methods
You can generate list data in a DTML method and pass it to the selection list in a form. The code is a bit more complex because
you need to define a variable to store the data. [Todo: Is there a better way? Please fill in.]
First example: passing one variable
Create a DTML Method called myMethod and enter the following code:
<dtml-call "REQUEST.set('myOptionList',[])">
<dtml-in "objectValues(['News', 'DTML Document'])">
<dtml-call "myOptionList.append(title_or_id)">
</dtml-in>
<dtml-return myOptionList>
myOptionList is a Python list. The 1st dtml-call tag creates an empty list. The second call appends a value (titles or IDs for
DTML documents or News document in the current directory) to the list. The list is then returned by the method.
In your form, enter the following code for the selection list:
<SELECT NAME="myList" multiple>
<dtml-in myMethod>
<OPTION value="<dtml-var sequence-index>">
<dtml-var sequence-index>:
<dtml-var sequence-item></OPTION>
</dtml-in>
</SELECT>
The values displayed in the list will look like 1: My title or id.
Second example: passing two or more variables
Suppose I need to pass several properties per document listed by the method? I guess we can store them as lists within a list.
Here's how.
In myMethod, enter the following code:
<dtml-call "REQUEST.set('myOptionList',[])">
<dtml-in "objectValues(['DTML Document'])">
<dtml-call "REQUEST.set('myIDTitle',[id(), title_or_id()])">
<dtml-call "myOptionList.append(myIDTitle)">
</dtml-in>
<dtml-return myOptionList>
Here we loop on DTML document objects in the current directory, we retrieve their IDs and titles, we store both properties in a
list (myIDTitle), then we store these lists in a larger list (myOptionList) which is returned by the method.
In your form, enter the following code:
<SELECT NAME="myList" multiple>
<dtml-in myMethod>
<dtml-in sequence-item>
<dtml-if "_['sequence-index']==0">
<OPTION "<dtml-var sequence-item>">
<dtml-else>
<dtml-var sequence-item></OPTION>
</dtml-if>
</dtml-in>
</dtml-in>
</SELECT>
The first dtml-in tag loops on the 1st, larger Python list. For each option line in the selection list, it extract a smaller, two-item
http://www.zope.org/Members/AlexR/SelectionLists?pp=1 (5 of 11) [05/07/2000 11:22:18]
Using selection lists
list (stored in the first sequence-item). The second dtml-in tag loops on this smaller list. If list index is 0 (1st item, i.e. the
document ID), the value is used for the OPTION tag. If list index is 1 (2nd item, i.e. the document title), the value is used for the
displayed list text.
[I suppose there is a better way. How can I access a list within a list? Could I pass the data in another structure, a dictionary for
instance?]
Filling lists from External methods
External methods are Python code module that are stored in the Extensions subdirectory of your Zope server. With External
methods, you can create complex scripts and call them from your DTML pages. For more information, see the Using External
Methods Howto.
You can fill a selection list with data returned by an external method. Here's how:
1. In your yourZopeDir/Extensions directory, create a new text file called demo.py.
2. In this file, enter the following code:
def countTo(fromNumber=10, toNumber=100, myStep=10):
"""Returns a list of numbers."""
myList=[]
for i in range(fromNumber, toNumber, myStep):
myList.append(i)
return myList
This simple example returns a list of numbers generated by a for loop. The loop runs from fromNumber to
toNumber by myStep.
3. In Zope, create a news External Method. In Id, enter myExternalMethod. In Function name, enter countTo. This is
the name of the function or sub in the Python file. In Python module file, enter demo. This is the file name (without the
.py extension). Click Edit to save your changes.
4. In your form, enter the following code:
<SELECT NAME="myList">
<dtml-in myExternalMethod>
<OPTION "<dtml-var sequence-index>">
<dtml-var sequence-item></OPTION>
</dtml-in>
</SELECT>
Note we use the same code as with properties or TinyTables. This will create a selection list based on the default
parameters that are coded in the Python file.
5. We can also pass parameters to the Python module from the form. Here's how:
<SELECT NAME="myList">
<dtml-in "myExternalMethod(50, 100, 5)">
<OPTION "<dtml-var sequence-index>">
<dtml-var sequence-item></OPTION>
</dtml-in>
</SELECT>
Here we get a list with values between 50 and 100 incremented by 5. Note we use quotes in the dtml-in tag when
passing parameters.
Filling lists from Python methods
PythonMethods is a product that allows you to create Python script objects from the Zope Web UI and to call them from any other
object. With this product you can use safe Python scripts in your server; you don't need file access on the server to create or edit
the scripts. You can download the product from here.
When you have installed the product, crea