blob: d1e6a55a338a74e4e0e4b3a62ac4fff6cd480834 [file] [log] [blame]
Guido van Rossumadb3a9d1997-05-21 07:24:50 +00001#! /depot/sundry/plat/bin/python1.4
2
3"""Interactive FAQ project."""
4
5import cgi, string, os
6
7NAMEPAT = "faq??.???.htp"
8
9class FAQServer:
10
11 def __init__(self):
12 pass
13
14 def main(self):
15 self.form = cgi.FieldStorage()
16 req = self.req or 'frontpage'
17 try:
18 method = getattr(self, 'do_%s' % req)
19 except AttributeError:
20 print "Unrecognized request type", req
21 else:
22 method()
23
Guido van Rossum3c3354c1997-05-21 16:52:18 +000024 KEYS = ['req', 'query', 'name', 'text', 'commit', 'title',
25 'author', 'email', 'log']
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000026
27 def __getattr__(self, key):
28 if key not in self.KEYS:
29 raise AttributeError
Guido van Rossum3c3354c1997-05-21 16:52:18 +000030 try:
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000031 item = self.form[key]
32 return item.value
Guido van Rossum3c3354c1997-05-21 16:52:18 +000033 except KeyError:
34 return ''
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000035
36 def do_frontpage(self):
37 print """
38 <TITLE>Python FAQ (alpha 1)</TITLE>
39 <H1>Python FAQ Front Page</H1>
40 <UL>
41 <LI><A HREF="faq.py?req=search">Search the FAQ</A>
42 <LI><A HREF="faq.py?req=browse">Browse the FAQ</A>
Guido van Rossum3c3354c1997-05-21 16:52:18 +000043 <LI><A HREF="faq.py?req=submit">Submit a new FAQ entry</A> (not yet)
44 <LI><A HREF="faq.py?req=roulette">FAQ roulette</A>
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000045 </UL>
46
47 <FORM ACTION="faq.py?req=query">
48 <INPUT TYPE=text NAME=query>
49 <INPUT TYPE=submit VALUE="Search">
50 <INPUT TYPE=hidden NAME=req VALUE=query>
51 </FORM>
52
53 Disclaimer: these pages are intended to be edited by anyone.
54 Please exercise discretion when editing, don't be rude, etc.
55 """
56
57 def do_browse(self):
58 print """
59 <TITLE>Python FAQ</TITLE>
60 <H1>Python FAQ</H1>
61 <HR>
62 """
63 names = os.listdir(os.curdir)
64 names.sort()
65 n = 0
66 for name in names:
Guido van Rossum3c3354c1997-05-21 16:52:18 +000067 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000068 if headers:
Guido van Rossum3c3354c1997-05-21 16:52:18 +000069 self.show(name, headers['title'], text, 1)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000070 n = n+1
71 if not n:
72 print "No FAQ entries?!?!"
73
74 def do_roulette(self):
75 import whrandom
76 print """
77 <TITLE>Python FAQ Roulette</TITLE>
78 <H1>Python FAQ Roulette</H1>
79 Please check the correctness of the entry below.
80 If you find any problems, please edit the entry.
81 <P>
82 <HR>
83 """
84 names = os.listdir(os.curdir)
85 while names:
86 name = whrandom.choice(names)
Guido van Rossum3c3354c1997-05-21 16:52:18 +000087 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000088 if headers:
Guido van Rossum3c3354c1997-05-21 16:52:18 +000089 self.show(name, headers['title'], text, 1)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +000090 print '<P><A HREF="faq.py?req=roulette">Show another one</A>'
91 break
92 else:
93 names.remove(name)
94 else:
95 print "No FAQ entries?!?!"
96
97 def do_search(self):
98 print """
99 <TITLE>Search the Python FAQ</TITLE>
100 <H1>Search the Python FAQ</H1>
101
102 <FORM ACTION="faq.py?req=query">
103 <INPUT TYPE=text NAME=query>
104 <INPUT TYPE=submit VALUE="Search">
105 <INPUT TYPE=hidden NAME=req VALUE=query>
106 </FORM>
107 """
108
109 def do_query(self):
110 import regex
111 print "<TITLE>Python FAQ Query Results</TITLE>"
112 print "<H1>Python FAQ Query Results</H1>"
113 query = self.query
114 if not query:
115 print "No query string"
116 return
117 p = regex.compile(query, regex.casefold)
118 names = os.listdir(os.curdir)
119 names.sort()
120 print "<HR>"
121 n = 0
122 for name in names:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000123 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000124 if headers:
125 title = headers['title']
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000126 if p.search(title) >= 0 or p.search(text) >= 0:
127 self.show(name, title, text, 1)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000128 n = n+1
129 if not n:
130 print "No hits."
131
132 def do_edit(self):
133 name = self.name
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000134 headers, text = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000135 if not headers:
136 print "Invalid file name", name
137 return
138 print """
139 <TITLE>Python FAQ Edit Form</TITLE>
140 <H1>Python FAQ Edit Form</H1>
141 """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000142 title = headers['title']
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000143 print "<FORM METHOD=POST ACTION=faq.py>"
144 self.showedit(name, headers, text)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000145 print """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000146 <INPUT TYPE=submit VALUE="Review Edit">
147 <INPUT TYPE=hidden NAME=req VALUE=review>
148 <INPUT TYPE=hidden NAME=name VALUE=%s>
149 </FORM>
150 <HR>
151 """ % name
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000152 self.show(name, title, text)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000153
154 def do_review(self):
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000155 if self.commit:
156 self.checkin()
157 return
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000158 name = self.name
159 text = self.text
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000160 title = self.title
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000161 headers, oldtext = self.read(name)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000162 if not headers:
163 print "Invalid file name", name
164 return
165 print """
166 <TITLE>Python FAQ Review Form</TITLE>
167 <H1>Python FAQ Review Form</H1>
168 """
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000169 self.show(name, title, text)
170 print "<FORM METHOD=POST ACTION=faq.py>"
171 if self.log and self.author and '@' in self.email:
172 print """
173 <INPUT TYPE=submit NAME=commit VALUE="Commit">
174 Click this button to commit the change.
175 <P>
176 <HR>
177 <P>
178 """
179 else:
180 print """
181 To commit this change, please enter your name,
182 email and a log message in the form below.
183 <P>
184 <HR>
185 <P>
186 """
187 self.showedit(name, headers, text)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000188 print """
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000189 <BR>
190 <INPUT TYPE=submit VALUE="Review Edit">
191 <INPUT TYPE=hidden NAME=req VALUE=review>
192 <INPUT TYPE=hidden NAME=name VALUE=%s>
193 </FORM>
194 <HR>
195 """ % name
196
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000197 def checkin(self):
198 import regsub, time, tempfile
199 name = self.name
200
201 headers, oldtext = self.read(name)
202 if not headers:
203 print "Invalid file name", name
204 return
205 text = self.text
206 title = self.title
207 author = self.author
208 email = self.email
209 log = self.log
210 text = regsub.gsub("\r\n", "\n", text)
211 log = regsub.gsub("\r\n", "\n", log)
212 author = string.join(string.split(author))
213 email = string.join(string.split(email))
214 title = string.join(string.split(title))
215 oldtitle = headers['title']
216 oldtitle = string.join(string.split(oldtitle))
217 text = string.strip(text)
218 oldtext = string.strip(oldtext)
219 if text == oldtext and title == oldtitle:
220 print "No changes."
221 # XXX Should exit more ceremoniously
222 return
223 # Check that the question number didn't change
224 if string.split(title)[:1] != string.split(oldtitle)[:1]:
225 print "Don't change the question number please."
226 # XXX Should exit more ceremoniously
227 return
228 remhost = os.environ["REMOTE_HOST"]
229 remaddr = os.environ["REMOTE_ADDR"]
230 try:
231 os.unlink(name + "~")
232 except os.error:
233 pass
234 try:
235 os.rename(name, name + "~")
236 except os.error:
237 pass
238 try:
239 os.unlink(name)
240 except os.error:
241 pass
242 try:
243 f = open(name, "w")
244 except IOError, msg:
245 print "Can't open", name, "for writing:", cgi.escape(str(msg))
246 # XXX Should exit more ceremoniously
247 return
248 now = time.ctime(time.time())
249 f.write("Title: %s\n" % title)
250 f.write("Last-Changed-Date: %s\n" % now)
251 f.write("Last-Changed-Author: %s\n" % author)
252 f.write("Last-Changed-Email: %s\n" % email)
253 f.write("Last-Changed-Remote-Host: %s\n" % remhost)
254 f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
255 keys = headers.keys()
256 keys.sort()
257 keys.remove('title')
258 for key in keys:
259 if key[:13] != 'last-changed-':
260 f.write("%s: %s\n" % (string.capwords(key, '-'),
261 headers[key]))
262 f.write("\n")
263 f.write(text)
264 f.write("\n")
265 f.close()
266
267 tfn = tempfile.mktemp()
268 f = open(tfn, "w")
269 f.write("Last-Changed-Date: %s\n" % now)
270 f.write("Last-Changed-Author: %s\n" % author)
271 f.write("Last-Changed-Email: %s\n" % email)
272 f.write("Last-Changed-Remote-Host: %s\n" % remhost)
273 f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
274 f.write("\n")
275 f.write(log)
276 f.write("\n")
277 f.close()
278
279 p = os.popen("""
280 /depot/gnu/plat/bin/rcs -l %s </dev/null 2>&1
281 /depot/gnu/plat/bin/ci -u %s <%s 2>&1
282 rm -f %s
283 """ % (name, name, tfn, tfn))
284 output = p.read()
285 sts = p.close()
286 if not sts:
287 print """
288 <H1>Python FAQ Entry Edited</H1>
289 <HR>
290 """
291 self.show(name, title, text, 1)
292 if output:
293 print "<PRE>%s</PRE>" % cgi.escape(output)
294 else:
295 print """
296 <H1>Python FAQ Entry Commit Failed</H1>
297 Exit status 0x%04x
298 """ % sts
299 if output:
300 print "<PRE>%s</PRE>" % cgi.escape(output)
301
302 def showedit(self, name, headers, text):
303 title = headers['title']
304 print """
305 Title: <INPUT TYPE=text SIZE=70 NAME=title VALUE="%s"<BR>
306 <TEXTAREA COLS=80 ROWS=20 NAME=text>""" % title
307 print cgi.escape(string.strip(text))
308 print """</TEXTAREA>
309 <BR>
310 Please provide the following information for logging purposes:
311 <BR>
312 <CODE>Name : </CODE><INPUT TYPE=text SIZE=70 NAME=author VALUE="%s"<BR>
313 <CODE>Email: </CODE><INPUT TYPE=text SIZE=70 NAME=email VALUE="%s"<BR>
314 Log message (reason for the change):<BR>
315 <TEXTAREA COLS=80 ROWS=5 NAME=log>\n%s\n</TEXTAREA>
316 """ % (self.author, self.email, self.log)
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000317
318 def showheaders(self, headers):
319 print "<UL>"
320 keys = map(string.lower, headers.keys())
321 keys.sort()
322 for key in keys:
323 print "<LI><B>%s:</B> %s" % (string.capwords(key, '-'),
324 headers[key] or '')
325 print "</UL>"
326
327 def read(self, name):
328 import fnmatch, rfc822
329 if not fnmatch.fnmatch(name, NAMEPAT):
330 return None, None
331 f = open(name)
332 headers = rfc822.Message(f)
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000333 text = f.read()
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000334 f.close()
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000335 return headers, text
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000336
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000337 def show(self, name, title, text, edit=0):
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000338 # XXX Should put <A> tags around recognizable URLs
339 # XXX Should also turn "see section N" into hyperlinks
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000340 print "<H2>%s</H2>" % title
341 pre = 0
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000342 for line in string.split(text, '\n'):
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000343 if not string.strip(line):
344 if pre:
345 print '</PRE>'
346 pre = 0
347 else:
348 print '<P>'
349 else:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000350 if line == string.lstrip(line): # I.e., no leading whitespace
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000351 if pre:
352 print '</PRE>'
353 pre = 0
354 else:
355 if not pre:
356 print '<PRE>'
357 pre = 1
358 print line
359 if pre:
360 print '</PRE>'
361 pre = 0
362 print '<P>'
363 if edit:
364 print '<A HREF="faq.py?req=edit&name=%s">Edit this entry</A>' %name
365 print '<P>'
366 print "<HR>"
367
368
369print "Content-type: text/html\n"
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000370dt = 0
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000371try:
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000372 import time
373 t1 = time.time()
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000374 x = FAQServer()
375 x.main()
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000376 t2 = time.time()
377 dt = t2-t1
Guido van Rossumadb3a9d1997-05-21 07:24:50 +0000378except:
379 print "<HR>Sorry, an error occurred"
380 cgi.print_exception()
Guido van Rossum3c3354c1997-05-21 16:52:18 +0000381print "<!-- dt = %s -->" % str(round(dt, 3))