blob: a202e30e095be2651e0f0d4fee3ca524be8cbc5e [file] [log] [blame]
Guido van Rossumd7bfa801997-05-21 21:31:39 +00001"""Interactive FAQ project.
2
Guido van Rossumed531fd1997-05-22 15:21:57 +00003Note that this is not an executable script; it's an importable module.
4The actual CGI script can be kept minimal; it's appended at the end of
5this file as a string constant.
6
Guido van Rossumd7bfa801997-05-21 21:31:39 +00007XXX TO DO
8
Guido van Rossum74427e51997-05-21 23:43:39 +00009- generic error handler
10- should have files containing section headers
Guido van Rossumf701bf11997-05-21 22:25:56 +000011- customize rcs command pathnames
12- recognize urls and email addresses and turn them into <A> tags
Guido van Rossumd7bfa801997-05-21 21:31:39 +000013- use cookies to keep Name/email the same
14- explanation of editing somewhere
15- various embellishments, GIFs, crosslinks, hints, etc.
16- create new sections
17- rearrange entries
18- delete entries
Guido van Rossumd7bfa801997-05-21 21:31:39 +000019- send email on changes
20- optional staging of entries until reviewed?
Guido van Rossumd7bfa801997-05-21 21:31:39 +000021- freeze entries
22- username/password for editors
23- Change references to other Q's and whole sections
24- Browse should display menu of 7 sections & let you pick
25 (or frontpage should have the option to browse a section or all)
26- support adding annotations, too
Guido van Rossumed531fd1997-05-22 15:21:57 +000027- make it more generic (so you can create your own FAQ)
Guido van Rossumd7bfa801997-05-21 21:31:39 +000028
29"""
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000030
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000031NAMEPAT = "faq??.???.htp"
Guido van Rossumd7bfa801997-05-21 21:31:39 +000032NAMEREG = "^faq\([0-9][0-9]\)\.\([0-9][0-9][0-9]\)\.htp$"
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000033
34class FAQServer:
35
36 def __init__(self):
37 pass
38
39 def main(self):
40 self.form = cgi.FieldStorage()
41 req = self.req or 'frontpage'
42 try:
43 method = getattr(self, 'do_%s' % req)
44 except AttributeError:
45 print "Unrecognized request type", req
46 else:
47 method()
Guido van Rossum74427e51997-05-21 23:43:39 +000048 self.epilogue()
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000049
Guido van Rossum3c3354c1997-05-21 16:52:18 +000050 KEYS = ['req', 'query', 'name', 'text', 'commit', 'title',
Guido van Rossum74427e51997-05-21 23:43:39 +000051 'author', 'email', 'log', 'section', 'number', 'add',
52 'version']
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000053
54 def __getattr__(self, key):
55 if key not in self.KEYS:
56 raise AttributeError
Guido van Rossum3c3354c1997-05-21 16:52:18 +000057 try:
Guido van Rossumed531fd1997-05-22 15:21:57 +000058 form = self.form
59 try:
60 item = form[key]
61 except TypeError, msg:
62 raise KeyError, msg, sys.exc_traceback
Guido van Rossumf701bf11997-05-21 22:25:56 +000063 value = self.form[key].value
64 setattr(self, key, value)
65 return value
Guido van Rossum3c3354c1997-05-21 16:52:18 +000066 except KeyError:
67 return ''
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000068
69 def do_frontpage(self):
Guido van Rossum74427e51997-05-21 23:43:39 +000070 self.prologue("Python FAQ (alpha) Front Page")
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000071 print """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000072 <UL>
Guido van Rossumd7bfa801997-05-21 21:31:39 +000073 <LI><A HREF="faq.py?req=index">FAQ index</A>
74 <LI><A HREF="faq.py?req=all">The whole FAQ</A>
Guido van Rossum3c3354c1997-05-21 16:52:18 +000075 <LI><A HREF="faq.py?req=roulette">FAQ roulette</A>
Guido van Rossumd7bfa801997-05-21 21:31:39 +000076 <LI><A HREF="faq.py?req=recent">Recently changed FAQ entries</A>
77 <LI><A HREF="faq.py?req=add">Add a new FAQ entry</A>
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000078 </UL>
79
Guido van Rossumd7bfa801997-05-21 21:31:39 +000080 <H2>Search the FAQ</H2>
81
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000082 <FORM ACTION="faq.py?req=query">
83 <INPUT TYPE=text NAME=query>
84 <INPUT TYPE=submit VALUE="Search">
85 <INPUT TYPE=hidden NAME=req VALUE=query>
86 </FORM>
87
88 Disclaimer: these pages are intended to be edited by anyone.
89 Please exercise discretion when editing, don't be rude, etc.
90 """
91
Guido van Rossumd7bfa801997-05-21 21:31:39 +000092 def do_index(self):
Guido van Rossum74427e51997-05-21 23:43:39 +000093 self.prologue("Python FAQ Index")
Guido van Rossumd7bfa801997-05-21 21:31:39 +000094 names = os.listdir(os.curdir)
95 names.sort()
96 section = None
97 for name in names:
98 headers, text = self.read(name)
99 if headers:
100 title = headers['title']
101 i = string.find(title, '.')
102 nsec = title[:i]
103 if nsec != section:
104 if section:
105 print """
106 <P>
107 <LI><A HREF="faq.py?req=add&amp;section=%s"
108 >Add new entry</A> (at this point)
109 </UL>
110 """ % section
111 section = nsec
112 print "<H2>Section %s</H2>" % section
113 print "<UL>"
114 print '<LI><A HREF="faq.py?req=show&name=%s">%s</A>' % (
115 name, cgi.escape(title))
116 if section:
117 print """
118 <P>
119 <LI><A HREF="faq.py?req=add&amp;section=%s">Add new entry</A>
120 (at this point)
121 </UL>
122 """ % section
123 else:
124 print "No FAQ entries?!?!"
125
126 def do_show(self):
127 name = self.name
128 headers, text = self.read(name)
129 if not headers:
130 print "Invalid file name", name
131 return
Guido van Rossumed531fd1997-05-22 15:21:57 +0000132 self.show(name, headers['title'], text)
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000133
134 def do_all(self):
Guido van Rossum74427e51997-05-21 23:43:39 +0000135 self.prologue("The Whole Python FAQ")
136 print "<HR>"
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000137 names = os.listdir(os.curdir)
138 names.sort()
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000139 section = None
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000140 for name in names:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000141 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000142 if headers:
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000143 title = headers['title']
144 i = string.find(title, '.')
145 nsec = title[:i]
146 if nsec != section:
147 section = nsec
148 print "<H1>Section %s</H1>" % section
149 print "<HR>"
Guido van Rossumed531fd1997-05-22 15:21:57 +0000150 self.show(name, title, text)
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000151 if not section:
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000152 print "No FAQ entries?!?!"
153
154 def do_roulette(self):
155 import whrandom
Guido van Rossum74427e51997-05-21 23:43:39 +0000156 self.prologue("Python FAQ Roulette")
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000157 print """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000158 Please check the correctness of the entry below.
159 If you find any problems, please edit the entry.
160 <P>
161 <HR>
162 """
163 names = os.listdir(os.curdir)
164 while names:
165 name = whrandom.choice(names)
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000166 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000167 if headers:
Guido van Rossumed531fd1997-05-22 15:21:57 +0000168 self.show(name, headers['title'], text)
169 print "<P>Use `Reload' to show another one."
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000170 break
171 else:
172 names.remove(name)
173 else:
174 print "No FAQ entries?!?!"
175
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000176 def do_recent(self):
177 import fnmatch, stat
178 names = os.listdir(os.curdir)
179 now = time.time()
180 list = []
181 for name in names:
182 if not fnmatch.fnmatch(name, NAMEPAT):
183 continue
184 try:
185 st = os.stat(name)
186 except os.error:
187 continue
188 tuple = (st[stat.ST_MTIME], name)
189 list.append(tuple)
190 list.sort()
191 list.reverse()
Guido van Rossum74427e51997-05-21 23:43:39 +0000192 self.prologue("Python FAQ, Most Recently Modified First")
193 print "<HR>"
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000194 n = 0
195 for (mtime, name) in list:
196 headers, text = self.read(name)
197 if headers:
Guido van Rossumed531fd1997-05-22 15:21:57 +0000198 self.show(name, headers['title'], text)
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000199 n = n+1
200 if not n:
201 print "No FAQ entries?!?!"
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000202
203 def do_query(self):
204 import regex
Guido van Rossum74427e51997-05-21 23:43:39 +0000205 self.prologue("Python FAQ Query Results")
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000206 query = self.query
207 if not query:
208 print "No query string"
209 return
210 p = regex.compile(query, regex.casefold)
211 names = os.listdir(os.curdir)
212 names.sort()
213 print "<HR>"
214 n = 0
215 for name in names:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000216 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000217 if headers:
218 title = headers['title']
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000219 if p.search(title) >= 0 or p.search(text) >= 0:
Guido van Rossumed531fd1997-05-22 15:21:57 +0000220 self.show(name, title, text)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000221 n = n+1
222 if not n:
223 print "No hits."
224
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000225 def do_add(self):
226 section = self.section
227 if not section:
Guido van Rossum74427e51997-05-21 23:43:39 +0000228 self.prologue("How to add a new FAQ entry")
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000229 print """
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000230 Go to the <A HREF="faq.py?req=index">FAQ index</A>
231 and click on the "Add new entry" link at the end
232 of the section to which you want to add the entry.
233 """
234 return
235 try:
236 nsec = string.atoi(section)
237 except ValueError:
238 print "Bad section number", nsec
239 names = os.listdir(os.curdir)
240 max = 0
241 import regex
242 prog = regex.compile(NAMEREG)
243 for name in names:
244 if prog.match(name) >= 0:
245 s1, s2 = prog.group(1, 2)
246 n1, n2 = string.atoi(s1), string.atoi(s2)
247 if n1 == nsec:
248 if n2 > max:
249 max = n2
250 if not max:
251 print "Can't add new sections yet."
252 return
253 num = max+1
254 name = "faq%02d.%03d.htp" % (nsec, num)
255 self.name = name
256 self.add = "yes"
257 self.number = str(num)
258 self.do_edit()
259
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000260 def do_edit(self):
261 name = self.name
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000262 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000263 if not headers:
264 print "Invalid file name", name
265 return
Guido van Rossum74427e51997-05-21 23:43:39 +0000266 self.prologue("Python FAQ Edit Form")
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000267 title = headers['title']
Guido van Rossum74427e51997-05-21 23:43:39 +0000268 version = self.getversion(name)
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000269 print "<FORM METHOD=POST ACTION=faq.py>"
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000270 self.showedit(name, title, text)
271 if self.add:
272 print """
273 <INPUT TYPE=hidden NAME=add VALUE=%s>
274 <INPUT TYPE=hidden NAME=section VALUE=%s>
275 <INPUT TYPE=hidden NAME=number VALUE=%s>
276 """ % (self.add, self.section, self.number)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000277 print """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000278 <INPUT TYPE=submit VALUE="Review Edit">
279 <INPUT TYPE=hidden NAME=req VALUE=review>
280 <INPUT TYPE=hidden NAME=name VALUE=%s>
Guido van Rossum74427e51997-05-21 23:43:39 +0000281 <INPUT TYPE=hidden NAME=version VALUE=%s>
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000282 </FORM>
283 <HR>
Guido van Rossum74427e51997-05-21 23:43:39 +0000284 """ % (name, version)
Guido van Rossumed531fd1997-05-22 15:21:57 +0000285 self.show(name, title, text, edit=0)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000286
287 def do_review(self):
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000288 if self.commit:
289 self.checkin()
290 return
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000291 name = self.name
292 text = self.text
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000293 title = self.title
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000294 headers, oldtext = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000295 if not headers:
296 print "Invalid file name", name
297 return
Guido van Rossum74427e51997-05-21 23:43:39 +0000298 self.prologue("Python FAQ Review Form")
299 print "<HR>"
Guido van Rossumed531fd1997-05-22 15:21:57 +0000300 self.show(name, title, text, edit=0)
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000301 print "<FORM METHOD=POST ACTION=faq.py>"
302 if self.log and self.author and '@' in self.email:
303 print """
304 <INPUT TYPE=submit NAME=commit VALUE="Commit">
305 Click this button to commit the change.
306 <P>
307 <HR>
308 <P>
309 """
310 else:
311 print """
312 To commit this change, please enter your name,
313 email and a log message in the form below.
314 <P>
315 <HR>
316 <P>
317 """
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000318 self.showedit(name, title, text)
319 if self.add:
320 print """
321 <INPUT TYPE=hidden NAME=add VALUE=%s>
322 <INPUT TYPE=hidden NAME=section VALUE=%s>
323 <INPUT TYPE=hidden NAME=number VALUE=%s>
324 """ % (self.add, self.section, self.number)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000325 print """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000326 <BR>
327 <INPUT TYPE=submit VALUE="Review Edit">
328 <INPUT TYPE=hidden NAME=req VALUE=review>
329 <INPUT TYPE=hidden NAME=name VALUE=%s>
Guido van Rossum74427e51997-05-21 23:43:39 +0000330 <INPUT TYPE=hidden NAME=version VALUE=%s>
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000331 </FORM>
332 <HR>
Guido van Rossum74427e51997-05-21 23:43:39 +0000333 """ % (name, self.version)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000334
Guido van Rossumf701bf11997-05-21 22:25:56 +0000335 def do_info(self):
336 name = self.name
337 headers, text = self.read(name)
338 if not headers:
339 print "Invalid file name", name
340 return
341 print '<PRE>'
342 sys.stdout.flush()
343 os.system("/depot/gnu/plat/bin/rlog -r %s </dev/null 2>&1" % self.name)
344 print '</PRE>'
345 print '<A HREF="faq.py?req=rlog&name=%s">View full rcs log</A>' % name
346
347 def do_rlog(self):
348 name = self.name
349 headers, text = self.read(name)
350 if not headers:
351 print "Invalid file name", name
352 return
353 print '<PRE>'
354 sys.stdout.flush()
355 os.system("/depot/gnu/plat/bin/rlog %s </dev/null 2>&1" % self.name)
356 print '</PRE>'
357
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000358 def checkin(self):
359 import regsub, time, tempfile
360 name = self.name
361
362 headers, oldtext = self.read(name)
363 if not headers:
364 print "Invalid file name", name
365 return
Guido van Rossum74427e51997-05-21 23:43:39 +0000366 version = self.version
367 curversion = self.getversion(name)
368 if version != curversion:
369 print "Version conflict."
370 print "You edited version %s but current version is %s." % (
371 version, curversion)
372 print '<A HREF="faq.py?req=show&name=%s">Reload.</A>' % name
373 return
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000374 text = self.text
375 title = self.title
376 author = self.author
377 email = self.email
378 log = self.log
379 text = regsub.gsub("\r\n", "\n", text)
380 log = regsub.gsub("\r\n", "\n", log)
381 author = string.join(string.split(author))
382 email = string.join(string.split(email))
383 title = string.join(string.split(title))
384 oldtitle = headers['title']
385 oldtitle = string.join(string.split(oldtitle))
386 text = string.strip(text)
387 oldtext = string.strip(oldtext)
388 if text == oldtext and title == oldtitle:
389 print "No changes."
390 # XXX Should exit more ceremoniously
391 return
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000392 # Check that the FAQ entry number didn't change
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000393 if string.split(title)[:1] != string.split(oldtitle)[:1]:
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000394 print "Don't change the FAQ entry number please."
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000395 # XXX Should exit more ceremoniously
396 return
397 remhost = os.environ["REMOTE_HOST"]
398 remaddr = os.environ["REMOTE_ADDR"]
399 try:
400 os.unlink(name + "~")
401 except os.error:
402 pass
403 try:
404 os.rename(name, name + "~")
405 except os.error:
406 pass
407 try:
408 os.unlink(name)
409 except os.error:
410 pass
411 try:
412 f = open(name, "w")
413 except IOError, msg:
414 print "Can't open", name, "for writing:", cgi.escape(str(msg))
415 # XXX Should exit more ceremoniously
416 return
417 now = time.ctime(time.time())
418 f.write("Title: %s\n" % title)
419 f.write("Last-Changed-Date: %s\n" % now)
420 f.write("Last-Changed-Author: %s\n" % author)
421 f.write("Last-Changed-Email: %s\n" % email)
422 f.write("Last-Changed-Remote-Host: %s\n" % remhost)
423 f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
424 keys = headers.keys()
425 keys.sort()
426 keys.remove('title')
427 for key in keys:
428 if key[:13] != 'last-changed-':
429 f.write("%s: %s\n" % (string.capwords(key, '-'),
430 headers[key]))
431 f.write("\n")
432 f.write(text)
433 f.write("\n")
434 f.close()
435
436 tfn = tempfile.mktemp()
437 f = open(tfn, "w")
438 f.write("Last-Changed-Date: %s\n" % now)
439 f.write("Last-Changed-Author: %s\n" % author)
440 f.write("Last-Changed-Email: %s\n" % email)
441 f.write("Last-Changed-Remote-Host: %s\n" % remhost)
442 f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
443 f.write("\n")
444 f.write(log)
445 f.write("\n")
446 f.close()
447
448 p = os.popen("""
449 /depot/gnu/plat/bin/rcs -l %s </dev/null 2>&1
450 /depot/gnu/plat/bin/ci -u %s <%s 2>&1
451 rm -f %s
452 """ % (name, name, tfn, tfn))
453 output = p.read()
454 sts = p.close()
455 if not sts:
Guido van Rossum74427e51997-05-21 23:43:39 +0000456 self.prologue("Python FAQ Entry Edited")
457 print "<HR>"
Guido van Rossumed531fd1997-05-22 15:21:57 +0000458 self.show(name, title, text)
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000459 if output:
460 print "<PRE>%s</PRE>" % cgi.escape(output)
461 else:
462 print """
463 <H1>Python FAQ Entry Commit Failed</H1>
464 Exit status 0x%04x
465 """ % sts
466 if output:
467 print "<PRE>%s</PRE>" % cgi.escape(output)
468
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000469 def showedit(self, name, title, text):
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000470 print """
Guido van Rossumed531fd1997-05-22 15:21:57 +0000471 Title: <INPUT TYPE=text SIZE=70 NAME=title VALUE="%s"><BR>
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000472 <TEXTAREA COLS=80 ROWS=20 NAME=text>""" % title
473 print cgi.escape(string.strip(text))
474 print """</TEXTAREA>
475 <BR>
476 Please provide the following information for logging purposes:
477 <BR>
Guido van Rossumed531fd1997-05-22 15:21:57 +0000478 <CODE>Name : </CODE><INPUT TYPE=text SIZE=40 NAME=author VALUE="%s">
479 <BR>
480 <CODE>Email: </CODE><INPUT TYPE=text SIZE=40 NAME=email VALUE="%s">
481 <BR>
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000482 Log message (reason for the change):<BR>
483 <TEXTAREA COLS=80 ROWS=5 NAME=log>\n%s\n</TEXTAREA>
484 """ % (self.author, self.email, self.log)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000485
486 def showheaders(self, headers):
487 print "<UL>"
488 keys = map(string.lower, headers.keys())
489 keys.sort()
490 for key in keys:
491 print "<LI><B>%s:</B> %s" % (string.capwords(key, '-'),
492 headers[key] or '')
493 print "</UL>"
494
Guido van Rossumed531fd1997-05-22 15:21:57 +0000495 headers = None
496
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000497 def read(self, name):
Guido van Rossumed531fd1997-05-22 15:21:57 +0000498 self.headers = None
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000499 import fnmatch, rfc822
500 if not fnmatch.fnmatch(name, NAMEPAT):
501 return None, None
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000502 if self.add:
503 try:
504 fname = "faq%02d.%03d.htp" % (string.atoi(self.section),
505 string.atoi(self.number))
506 except ValueError:
507 return None, None
508 if fname != name:
509 return None, None
510 headers = {'title': "%s.%s. " % (self.section, self.number)}
511 text = ""
Guido van Rossumed531fd1997-05-22 15:21:57 +0000512 else:
513 f = open(name)
514 headers = rfc822.Message(f)
515 text = f.read()
516 f.close()
517 self.headers = headers
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000518 return headers, text
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000519
Guido van Rossumed531fd1997-05-22 15:21:57 +0000520 def show(self, name, title, text, edit=1):
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000521 # XXX Should put <A> tags around recognizable URLs
522 # XXX Should also turn "see section N" into hyperlinks
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000523 print "<H2>%s</H2>" % cgi.escape(title)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000524 pre = 0
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000525 for line in string.split(text, '\n'):
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000526 if not string.strip(line):
527 if pre:
528 print '</PRE>'
529 pre = 0
530 else:
531 print '<P>'
532 else:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000533 if line == string.lstrip(line): # I.e., no leading whitespace
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000534 if pre:
535 print '</PRE>'
536 pre = 0
537 else:
538 if not pre:
539 print '<PRE>'
540 pre = 1
Guido van Rossumd7bfa801997-05-21 21:31:39 +0000541 print cgi.escape(line)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000542 if pre:
543 print '</PRE>'
544 pre = 0
545 print '<P>'
546 if edit:
Guido van Rossumf701bf11997-05-21 22:25:56 +0000547 print """
548 <A HREF="faq.py?req=edit&name=%s">Edit this entry</A> /
549 <A HREF="faq.py?req=info&name=%s" TARGET=_blank>Log info</A>
550 """ % (name, name)
Guido van Rossumed531fd1997-05-22 15:21:57 +0000551 if self.headers:
552 try:
553 date = self.headers['last-changed-date']
554 author = self.headers['last-changed-author']
555 email = self.headers['last-changed-email']
556 except KeyError:
557 pass
558 else:
559 s = '(last changed on %s by <A HREF="%s">%s</A>)'
560 print s % (date, email, author)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000561 print '<P>'
562 print "<HR>"
563
Guido van Rossum74427e51997-05-21 23:43:39 +0000564 def getversion(self, name):
565 p = os.popen("/depot/gnu/plat/bin/rlog -h %s </dev/null 2>&1" % name)
566 head = ""
567 while 1:
568 line = p.readline()
569 if not line:
570 break
571 if line[:5] == 'head:':
572 head = string.strip(line[5:])
573 p.close()
574 return head
575
576 def prologue(self, title):
577 title = cgi.escape(title)
578 print '''\
579 <HTML>
580 <HEAD>
581 <TITLE>%s</TITLE>
582 </HEAD>
583 <BODY BACKGROUND="http://www.python.org/pics/RedShort.gif"
584 BGCOLOR="#FFFFFF"
585 TEXT="#000000"
586 LINK="#AA0000"
587 VLINK="#906A6A">
588 <H1>%s</H1>
589 ''' % (title, title)
590
591 def epilogue(self):
592 print '''
593 <P>
594 <HR>
Guido van Rossumed531fd1997-05-22 15:21:57 +0000595 <A HREF="http://www.python.org">Python home</A> /
596 <A HREF="faq.py">FAQ home</A> /
Guido van Rossum74427e51997-05-21 23:43:39 +0000597 <A HREF="mailto:guido@python.org">GvR</A>
598 </BODY>
599 </HTML>
600 '''
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000601
602print "Content-type: text/html\n"
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000603dt = 0
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000604try:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000605 import time
606 t1 = time.time()
Guido van Rossum74427e51997-05-21 23:43:39 +0000607 import cgi, string, os, sys
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000608 x = FAQServer()
609 x.main()
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000610 t2 = time.time()
611 dt = t2-t1
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000612except:
613 print "<HR>Sorry, an error occurred"
614 cgi.print_exception()
Guido van Rossum74427e51997-05-21 23:43:39 +0000615print "<P>(running time = %s seconds)" % str(round(dt, 3))
Guido van Rossumed531fd1997-05-22 15:21:57 +0000616
617# The following bootstrap script must be placed in cgi-bin/faq.py:
618BOOTSTRAP = """
619#! /usr/local/bin/python
620FAQDIR = "/usr/people/guido/python/FAQ"
621import os, sys
622os.chdir(FAQDIR)
623sys.path.insert(0, os.curdir)
624import faqmain
625"""