mirror of https://github.com/python/cpython.git
Removed faqmain.py -- it was an earlier implementation and no
longer relevant.
This commit is contained in:
parent
282290f821
commit
8a2d216047
|
@ -1,858 +0,0 @@
|
|||
"""Interactive FAQ project.
|
||||
|
||||
Note that this is not an executable script; it's an importable module.
|
||||
The actual CGI script can be kept minimal; it's appended at the end of
|
||||
this file as a string constant.
|
||||
|
||||
XXX TO DO
|
||||
|
||||
XXX User Features TO DO
|
||||
|
||||
- next/prev/index links in do_show???
|
||||
- explanation of editing somewhere
|
||||
- embellishments, GIFs, hints, etc.
|
||||
- support adding annotations, too
|
||||
- restrict recent changes to last week (or make it an option)
|
||||
- extended search capabilities
|
||||
|
||||
XXX Management Features TO DO
|
||||
|
||||
- username/password for authors
|
||||
- create new sections
|
||||
- rearrange entries
|
||||
- delete entries
|
||||
- freeze entries
|
||||
- send email on changes?
|
||||
- send email on ERRORS!
|
||||
- optional staging of entries until reviewed?
|
||||
(could be done using rcs branches!)
|
||||
- prevent race conditions on nearly simultaneous commits
|
||||
|
||||
XXX Performance
|
||||
|
||||
- could cache generated HTML
|
||||
- could speed up searches with a separate index file
|
||||
|
||||
XXX Code organization TO DO
|
||||
|
||||
- read section titles from a file (could be a Python file: import faqcustom)
|
||||
- customize rcs command pathnames (and everything else)
|
||||
- make it more generic (so you can create your own FAQ)
|
||||
- more OO structure, e.g. add a class representing one FAQ entry
|
||||
|
||||
"""
|
||||
|
||||
# NB for timing purposes, the imports are at the end of this file
|
||||
|
||||
PASSWORD = "Spam"
|
||||
|
||||
NAMEPAT = "faq??.???.htp"
|
||||
NAMEREG = "^faq\([0-9][0-9]\)\.\([0-9][0-9][0-9]\)\.htp$"
|
||||
|
||||
SECTIONS = {
|
||||
"1": "General information and availability",
|
||||
"2": "Python in the real world",
|
||||
"3": "Building Python and Other Known Bugs",
|
||||
"4": "Programming in Python",
|
||||
"5": "Extending Python",
|
||||
"6": "Python's design",
|
||||
"7": "Using Python on non-UNIX platforms",
|
||||
}
|
||||
|
||||
class FAQServer:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def main(self):
|
||||
self.form = cgi.FieldStorage()
|
||||
req = self.req or 'frontpage'
|
||||
try:
|
||||
method = getattr(self, 'do_%s' % req)
|
||||
except AttributeError:
|
||||
print "Unrecognized request type", req
|
||||
else:
|
||||
method()
|
||||
self.epilogue()
|
||||
|
||||
KEYS = ['req', 'query', 'name', 'text', 'commit', 'title',
|
||||
'author', 'email', 'log', 'section', 'number', 'add',
|
||||
'version', 'edit', 'password']
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key not in self.KEYS:
|
||||
raise AttributeError
|
||||
try:
|
||||
form = self.form
|
||||
try:
|
||||
item = form[key]
|
||||
except TypeError, msg:
|
||||
raise KeyError, msg, sys.exc_traceback
|
||||
except KeyError:
|
||||
return ''
|
||||
value = self.form[key].value
|
||||
value = string.strip(value)
|
||||
setattr(self, key, value)
|
||||
return value
|
||||
|
||||
def do_frontpage(self):
|
||||
self.prologue("Python FAQ Wizard (beta test)")
|
||||
print """
|
||||
<UL>
|
||||
<LI><A HREF="faq.py?req=index">FAQ index</A>
|
||||
<LI><A HREF="faq.py?req=all">The whole FAQ</A>
|
||||
<LI><A HREF="faq.py?req=roulette">FAQ roulette</A>
|
||||
<LI><A HREF="faq.py?req=recent">Recently changed FAQ entries</A>
|
||||
<LI><A HREF="faq.py?req=add">Add a new FAQ entry</A>
|
||||
<LI><A HREF="faq.py?req=delete">Delete a FAQ entry</A>
|
||||
</UL>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2>Search the FAQ</H2>
|
||||
|
||||
<FORM ACTION="faq.py">
|
||||
<INPUT TYPE=text NAME=query>
|
||||
<INPUT TYPE=submit VALUE="Search"><BR>
|
||||
(Case insensitive regular expressions.)
|
||||
<INPUT TYPE=hidden NAME=req VALUE=query>
|
||||
</FORM>
|
||||
<HR>
|
||||
<P>
|
||||
Disclaimer: these pages are intended to be edited by anyone.
|
||||
Please exercise discretion when editing, don't be rude, etc.
|
||||
"""
|
||||
|
||||
def do_index(self):
|
||||
self.prologue("Python FAQ Index")
|
||||
names = os.listdir(os.curdir)
|
||||
names.sort()
|
||||
section = None
|
||||
for name in names:
|
||||
headers, text = self.read(name)
|
||||
if headers:
|
||||
title = headers['title']
|
||||
i = string.find(title, '.')
|
||||
nsec = title[:i]
|
||||
if nsec != section:
|
||||
if section:
|
||||
print """
|
||||
<P>
|
||||
<LI><A HREF="faq.py?req=add&section=%s"
|
||||
>Add new entry</A> (at this point)
|
||||
</UL>
|
||||
""" % section
|
||||
section = nsec
|
||||
if SECTIONS.has_key(section):
|
||||
stitle = SECTIONS[section]
|
||||
else:
|
||||
stitle = ""
|
||||
print "<H2>Section %s. %s</H2>" % (section, stitle)
|
||||
print "<UL>"
|
||||
print '<LI><A HREF="faq.py?req=show&name=%s">%s</A>' % (
|
||||
name, cgi.escape(title))
|
||||
if section:
|
||||
print """
|
||||
<P>
|
||||
<LI><A HREF="faq.py?req=add&section=%s">Add new entry</A>
|
||||
(at this point)
|
||||
</UL>
|
||||
""" % section
|
||||
else:
|
||||
print "No FAQ entries?!?!"
|
||||
|
||||
def do_show(self):
|
||||
self.prologue("Python FAQ Entry")
|
||||
print "<HR>"
|
||||
name = self.name
|
||||
headers, text = self.read(name)
|
||||
if not headers:
|
||||
self.error("Invalid file name", name)
|
||||
return
|
||||
self.show(name, headers['title'], text)
|
||||
|
||||
def do_all(self):
|
||||
import fnmatch, stat
|
||||
self.prologue("The Whole Python FAQ")
|
||||
names = os.listdir(os.curdir)
|
||||
lastmtime = 0
|
||||
for name in names:
|
||||
if not fnmatch.fnmatch(name, NAMEPAT):
|
||||
continue
|
||||
try:
|
||||
st = os.stat(name)
|
||||
except os.error:
|
||||
continue
|
||||
lastmtime = max(lastmtime, st[stat.ST_MTIME])
|
||||
if lastmtime:
|
||||
print time.strftime("Last changed on %c %Z",
|
||||
time.localtime(lastmtime))
|
||||
names.sort()
|
||||
section = None
|
||||
print "<HR>"
|
||||
for name in names:
|
||||
headers, text = self.read(name)
|
||||
if headers:
|
||||
title = headers['title']
|
||||
i = string.find(title, '.')
|
||||
nsec = title[:i]
|
||||
if nsec != section:
|
||||
section = nsec
|
||||
if SECTIONS.has_key(section):
|
||||
stitle = SECTIONS[section]
|
||||
else:
|
||||
stitle = ""
|
||||
print "<H1>Section %s. %s</H1>" % (section, stitle)
|
||||
print "<HR>"
|
||||
self.show(name, title, text, edit=(self.edit != 'no'))
|
||||
if not section:
|
||||
print "No FAQ entries?!?!"
|
||||
|
||||
def do_roulette(self):
|
||||
import whrandom
|
||||
self.prologue("Python FAQ Roulette")
|
||||
print """
|
||||
Please check the correctness of the entry below.
|
||||
If you find any problems, please edit the entry.
|
||||
<P>
|
||||
<HR>
|
||||
"""
|
||||
names = os.listdir(os.curdir)
|
||||
while names:
|
||||
name = whrandom.choice(names)
|
||||
headers, text = self.read(name)
|
||||
if headers:
|
||||
self.show(name, headers['title'], text)
|
||||
print "<P>Use `Reload' to show another one."
|
||||
break
|
||||
else:
|
||||
names.remove(name)
|
||||
else:
|
||||
print "No FAQ entries?!?!"
|
||||
|
||||
def do_recent(self):
|
||||
import fnmatch, stat
|
||||
names = os.listdir(os.curdir)
|
||||
now = time.time()
|
||||
list = []
|
||||
for name in names:
|
||||
if not fnmatch.fnmatch(name, NAMEPAT):
|
||||
continue
|
||||
try:
|
||||
st = os.stat(name)
|
||||
except os.error:
|
||||
continue
|
||||
tuple = (st[stat.ST_MTIME], name)
|
||||
list.append(tuple)
|
||||
list.sort()
|
||||
list.reverse()
|
||||
self.prologue("Python FAQ, Most Recently Modified First")
|
||||
print "<HR>"
|
||||
n = 0
|
||||
for (mtime, name) in list:
|
||||
headers, text = self.read(name)
|
||||
if headers and headers.has_key('last-changed-date'):
|
||||
self.show(name, headers['title'], text)
|
||||
n = n+1
|
||||
if not n:
|
||||
print "No FAQ entries?!?!"
|
||||
|
||||
def do_query(self):
|
||||
query = self.query
|
||||
if not query:
|
||||
self.error("No query string")
|
||||
return
|
||||
import regex
|
||||
self.prologue("Python FAQ Query Results")
|
||||
p = regex.compile(query, regex.casefold)
|
||||
names = os.listdir(os.curdir)
|
||||
names.sort()
|
||||
print "<HR>"
|
||||
n = 0
|
||||
for name in names:
|
||||
headers, text = self.read(name)
|
||||
if headers:
|
||||
title = headers['title']
|
||||
if p.search(title) >= 0 or p.search(text) >= 0:
|
||||
self.show(name, title, text)
|
||||
n = n+1
|
||||
if not n:
|
||||
print "No hits."
|
||||
|
||||
def do_add(self):
|
||||
section = self.section
|
||||
if not section:
|
||||
self.prologue("How to add a new FAQ entry")
|
||||
print """
|
||||
Go to the <A HREF="faq.py?req=index">FAQ index</A>
|
||||
and click on the "Add new entry" link at the end
|
||||
of the section to which you want to add the entry.
|
||||
"""
|
||||
return
|
||||
try:
|
||||
nsec = string.atoi(section)
|
||||
except ValueError:
|
||||
print "Bad section number", nsec
|
||||
names = os.listdir(os.curdir)
|
||||
max = 0
|
||||
import regex
|
||||
prog = regex.compile(NAMEREG)
|
||||
for name in names:
|
||||
if prog.match(name) >= 0:
|
||||
s1, s2 = prog.group(1, 2)
|
||||
n1, n2 = string.atoi(s1), string.atoi(s2)
|
||||
if n1 == nsec:
|
||||
if n2 > max:
|
||||
max = n2
|
||||
if not max:
|
||||
self.error("Can't add new sections yet.")
|
||||
return
|
||||
num = max+1
|
||||
name = "faq%02d.%03d.htp" % (nsec, num)
|
||||
self.name = name
|
||||
self.add = "yes"
|
||||
self.number = str(num)
|
||||
self.do_edit()
|
||||
|
||||
def do_delete(self):
|
||||
self.prologue("How to delete a FAQ entry")
|
||||
print """
|
||||
At the moment, there's no direct way to delete entries.
|
||||
This is because the entry numbers are also their
|
||||
unique identifiers -- it's a bad idea to renumber entries.
|
||||
<P>
|
||||
If you really think an entry needs to be deleted,
|
||||
change the title to "(deleted)" and make the body
|
||||
empty (keep the entry number in the title though).
|
||||
"""
|
||||
|
||||
def do_edit(self):
|
||||
name = self.name
|
||||
headers, text = self.read(name)
|
||||
if not headers:
|
||||
self.error("Invalid file name", name)
|
||||
return
|
||||
self.prologue("Python FAQ Edit Wizard - Edit Form")
|
||||
print '<A HREF="/python/faqhelp.html">Click for Help</A>'
|
||||
title = headers['title']
|
||||
version = self.getversion(name)
|
||||
print "<FORM METHOD=POST ACTION=faq.py>"
|
||||
self.showedit(name, title, text)
|
||||
if self.add:
|
||||
print """
|
||||
<INPUT TYPE=hidden NAME=add VALUE=%s>
|
||||
<INPUT TYPE=hidden NAME=section VALUE=%s>
|
||||
<INPUT TYPE=hidden NAME=number VALUE=%s>
|
||||
""" % (self.add, self.section, self.number)
|
||||
print """
|
||||
<INPUT TYPE=submit VALUE="Review Edit">
|
||||
<INPUT TYPE=hidden NAME=req VALUE=review>
|
||||
<INPUT TYPE=hidden NAME=name VALUE=%s>
|
||||
<INPUT TYPE=hidden NAME=version VALUE=%s>
|
||||
</FORM>
|
||||
<HR>
|
||||
""" % (name, version)
|
||||
self.show(name, title, text, edit=0)
|
||||
|
||||
def do_review(self):
|
||||
if self.commit:
|
||||
self.checkin()
|
||||
return
|
||||
name = self.name
|
||||
text = self.text
|
||||
title = self.title
|
||||
headers, oldtext = self.read(name)
|
||||
if not headers:
|
||||
self.error("Invalid file name", name)
|
||||
return
|
||||
if self.author or '@' in self.email or self.password:
|
||||
self.set_cookie(self.author, self.email, self.password)
|
||||
self.prologue("Python FAQ Edit Wizard - Review Form")
|
||||
print '<A HREF="/python/faqhelp.html">Click for Help</A>'
|
||||
print "<HR>"
|
||||
self.show(name, title, text, edit=0)
|
||||
print "<FORM METHOD=POST ACTION=faq.py>"
|
||||
if self.password == PASSWORD \
|
||||
and self.log and self.author and '@' in self.email:
|
||||
print """
|
||||
<INPUT TYPE=submit NAME=commit VALUE="Commit">
|
||||
Click this button to commit the change.
|
||||
<P>
|
||||
<HR>
|
||||
<P>
|
||||
"""
|
||||
else:
|
||||
print """
|
||||
To commit this change, please enter a log message,
|
||||
your name, your email address,
|
||||
and the correct password in the form below.
|
||||
<P>
|
||||
<HR>
|
||||
<P>
|
||||
"""
|
||||
self.showedit(name, title, text)
|
||||
if self.add:
|
||||
print """
|
||||
<INPUT TYPE=hidden NAME=add VALUE=%s>
|
||||
<INPUT TYPE=hidden NAME=section VALUE=%s>
|
||||
<INPUT TYPE=hidden NAME=number VALUE=%s>
|
||||
""" % (self.add, self.section, self.number)
|
||||
print """
|
||||
<BR>
|
||||
<INPUT TYPE=submit VALUE="Review Edit">
|
||||
<INPUT TYPE=hidden NAME=req VALUE=review>
|
||||
<INPUT TYPE=hidden NAME=name VALUE=%s>
|
||||
<INPUT TYPE=hidden NAME=version VALUE=%s>
|
||||
</FORM>
|
||||
<HR>
|
||||
""" % (name, self.version)
|
||||
|
||||
def do_info(self):
|
||||
name = self.name
|
||||
headers, text = self.read(name)
|
||||
if not headers:
|
||||
self.error("Invalid file name", name)
|
||||
return
|
||||
self.prologue("Info for %s" % name)
|
||||
print '<PRE>'
|
||||
p = os.popen("/depot/gnu/plat/bin/rlog -r %s </dev/null 2>&1" %
|
||||
self.name)
|
||||
output = p.read()
|
||||
p.close()
|
||||
print cgi.escape(output)
|
||||
print '</PRE>'
|
||||
print '<A HREF="faq.py?req=rlog&name=%s">View full rcs log</A>' % name
|
||||
|
||||
def do_rlog(self):
|
||||
name = self.name
|
||||
headers, text = self.read(name)
|
||||
if not headers:
|
||||
self.error("Invalid file name", name)
|
||||
return
|
||||
self.prologue("RCS log for %s" % name)
|
||||
print '<PRE>'
|
||||
p = os.popen("/depot/gnu/plat/bin/rlog %s </dev/null 2>&1" % self.name)
|
||||
output = p.read()
|
||||
p.close()
|
||||
print cgi.escape(output)
|
||||
print '</PRE>'
|
||||
|
||||
def checkin(self):
|
||||
import regsub, time, tempfile
|
||||
name = self.name
|
||||
password = self.password
|
||||
if password != PASSWORD:
|
||||
self.error("Invalid password.")
|
||||
return
|
||||
if not (self.log and self.author and '@' in self.email):
|
||||
self.error("No log message, no author, or invalid email.")
|
||||
return
|
||||
headers, oldtext = self.read(name)
|
||||
if not headers:
|
||||
self.error("Invalid file name", name)
|
||||
return
|
||||
version = self.version
|
||||
curversion = self.getversion(name)
|
||||
if version != curversion:
|
||||
self.error(
|
||||
"Version conflict.",
|
||||
"You edited version %s but current version is %s." % (
|
||||
version, curversion),
|
||||
"""
|
||||
<P>
|
||||
The two most common causes of this problem are:
|
||||
<UL>
|
||||
<LI>After committing a change, you went back in your browser,
|
||||
edited the entry some more, and clicked Commit again.
|
||||
<LI>Someone else started editing the same entry and committed
|
||||
before you did.
|
||||
</UL>
|
||||
<P>
|
||||
""",
|
||||
'<A HREF="faq.py?req=show&name=%s"' % name,
|
||||
'>Click here to reload the entry and try again.</A>')
|
||||
return
|
||||
text = self.text
|
||||
title = self.title
|
||||
author = self.author
|
||||
email = self.email
|
||||
log = self.log
|
||||
text = regsub.gsub("\r\n", "\n", text)
|
||||
log = regsub.gsub("\r\n", "\n", log)
|
||||
author = string.join(string.split(author))
|
||||
email = string.join(string.split(email))
|
||||
title = string.join(string.split(title))
|
||||
oldtitle = headers['title']
|
||||
oldtitle = string.join(string.split(oldtitle))
|
||||
text = string.strip(text)
|
||||
oldtext = string.strip(oldtext)
|
||||
if text == oldtext and title == oldtitle:
|
||||
self.error("No changes.")
|
||||
return
|
||||
# Check that the FAQ entry number didn't change
|
||||
if string.split(title)[:1] != string.split(oldtitle)[:1]:
|
||||
self.error("Don't change the FAQ entry number please.")
|
||||
return
|
||||
remhost = os.environ["REMOTE_HOST"]
|
||||
remaddr = os.environ["REMOTE_ADDR"]
|
||||
try:
|
||||
os.unlink(name + "~")
|
||||
except os.error:
|
||||
pass
|
||||
try:
|
||||
os.rename(name, name + "~")
|
||||
except os.error:
|
||||
pass
|
||||
try:
|
||||
os.unlink(name)
|
||||
except os.error:
|
||||
pass
|
||||
try:
|
||||
f = open(name, "w")
|
||||
except IOError, msg:
|
||||
self.error("Can't open", name, "for writing:", cgi.escape(str(msg)))
|
||||
return
|
||||
now = time.ctime(time.time())
|
||||
f.write("Title: %s\n" % title)
|
||||
f.write("Last-Changed-Date: %s\n" % now)
|
||||
f.write("Last-Changed-Author: %s\n" % author)
|
||||
f.write("Last-Changed-Email: %s\n" % email)
|
||||
f.write("Last-Changed-Remote-Host: %s\n" % remhost)
|
||||
f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
|
||||
keys = headers.keys()
|
||||
keys.sort()
|
||||
keys.remove('title')
|
||||
for key in keys:
|
||||
if key[:13] != 'last-changed-':
|
||||
f.write("%s: %s\n" % (string.capwords(key, '-'),
|
||||
headers[key]))
|
||||
f.write("\n")
|
||||
f.write(text)
|
||||
f.write("\n")
|
||||
f.close()
|
||||
|
||||
tfn = tempfile.mktemp()
|
||||
f = open(tfn, "w")
|
||||
f.write("Last-Changed-Date: %s\n" % now)
|
||||
f.write("Last-Changed-Author: %s\n" % author)
|
||||
f.write("Last-Changed-Email: %s\n" % email)
|
||||
f.write("Last-Changed-Remote-Host: %s\n" % remhost)
|
||||
f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
|
||||
f.write("\n")
|
||||
f.write(log)
|
||||
f.write("\n")
|
||||
f.close()
|
||||
|
||||
# Do this for show() below
|
||||
self.headers = {
|
||||
'title': title,
|
||||
'last-changed-date': now,
|
||||
'last-changed-author': author,
|
||||
'last-changed-email': email,
|
||||
'last-changed-remote-host': remhost,
|
||||
'last-changed-remote-address': remaddr,
|
||||
}
|
||||
|
||||
p = os.popen("""
|
||||
/depot/gnu/plat/bin/rcs -l %s </dev/null 2>&1
|
||||
/depot/gnu/plat/bin/ci -u %s <%s 2>&1
|
||||
rm -f %s
|
||||
""" % (name, name, tfn, tfn))
|
||||
output = p.read()
|
||||
sts = p.close()
|
||||
if not sts:
|
||||
self.set_cookie(author, email, password)
|
||||
self.prologue("Python FAQ Entry Edited")
|
||||
print "<HR>"
|
||||
self.show(name, title, text)
|
||||
if output:
|
||||
print "<PRE>%s</PRE>" % cgi.escape(output)
|
||||
else:
|
||||
self.error("Python FAQ Entry Commit Failed",
|
||||
"Exit status 0x%04x" % sts)
|
||||
if output:
|
||||
print "<PRE>%s</PRE>" % cgi.escape(output)
|
||||
|
||||
def set_cookie(self, author, email, password):
|
||||
name = "Python-FAQ-Wizard"
|
||||
value = "%s/%s/%s" % (author, email, password)
|
||||
import urllib
|
||||
value = urllib.quote(value)
|
||||
print "Set-Cookie: %s=%s; path=/cgi-bin/;" % (name, value),
|
||||
import time
|
||||
now = time.time()
|
||||
then = now + 28 * 24 * 3600
|
||||
gmt = time.gmtime(then)
|
||||
print time.strftime("expires=%a, %d-%b-%x %X GMT", gmt)
|
||||
|
||||
def get_cookie(self):
|
||||
if not os.environ.has_key('HTTP_COOKIE'):
|
||||
return "", "", ""
|
||||
raw = os.environ['HTTP_COOKIE']
|
||||
words = map(string.strip, string.split(raw, ';'))
|
||||
cookies = {}
|
||||
for word in words:
|
||||
i = string.find(word, '=')
|
||||
if i >= 0:
|
||||
key, value = word[:i], word[i+1:]
|
||||
cookies[key] = value
|
||||
if not cookies.has_key('Python-FAQ-Wizard'):
|
||||
return "", "", ""
|
||||
value = cookies['Python-FAQ-Wizard']
|
||||
import urllib
|
||||
value = urllib.unquote(value)
|
||||
words = string.split(value, '/')
|
||||
while len(words) < 3:
|
||||
words.append('')
|
||||
author = string.join(words[:-2], '/')
|
||||
email = words[-2]
|
||||
password = words[-1]
|
||||
return author, email, password
|
||||
|
||||
def showedit(self, name, title, text):
|
||||
author = self.author
|
||||
email = self.email
|
||||
password = self.password
|
||||
if not author or not email or not password:
|
||||
a, e, p = self.get_cookie()
|
||||
author = author or a
|
||||
email = email or e
|
||||
password = password or p
|
||||
print """
|
||||
Title: <INPUT TYPE=text SIZE=70 NAME=title VALUE="%s"><BR>
|
||||
<TEXTAREA COLS=80 ROWS=20 NAME=text>%s\n</TEXTAREA>""" % (
|
||||
self.escape(title), cgi.escape(string.strip(text)))
|
||||
print """<BR>
|
||||
Log message (reason for the change):<BR>
|
||||
<TEXTAREA COLS=80 ROWS=5 NAME=log>%s\n</TEXTAREA><BR>
|
||||
Please provide the following information for logging purposes:
|
||||
<TABLE FRAME=none COLS=2>
|
||||
<TR>
|
||||
<TD>Name:
|
||||
<TD><INPUT TYPE=text SIZE=40 NAME=author VALUE="%s">
|
||||
<TR>
|
||||
<TD>Email:
|
||||
<TD><INPUT TYPE=text SIZE=40 NAME=email VALUE="%s">
|
||||
<TR>
|
||||
<TD>Password:
|
||||
<TD><INPUT TYPE=password SIZE=40 NAME=password VALUE="%s">
|
||||
</TABLE>
|
||||
""" % (self.escape(self.log), self.escape(author),
|
||||
self.escape(email), self.escape(password))
|
||||
|
||||
def escape(self, s):
|
||||
import regsub
|
||||
if '&' in s:
|
||||
s = regsub.gsub("&", "&", s) # Must be done first!
|
||||
if '<' in s:
|
||||
s = regsub.gsub("<", "<", s)
|
||||
if '>' in s:
|
||||
s = regsub.gsub(">", ">", s)
|
||||
if '"' in s:
|
||||
s = regsub.gsub('"', """, s)
|
||||
return s
|
||||
|
||||
def showheaders(self, headers):
|
||||
print "<UL>"
|
||||
keys = map(string.lower, headers.keys())
|
||||
keys.sort()
|
||||
for key in keys:
|
||||
print "<LI><B>%s:</B> %s" % (string.capwords(key, '-'),
|
||||
headers[key] or '')
|
||||
print "</UL>"
|
||||
|
||||
headers = None
|
||||
|
||||
def read(self, name):
|
||||
self.headers = None
|
||||
import fnmatch, rfc822
|
||||
if not fnmatch.fnmatch(name, NAMEPAT):
|
||||
return None, None
|
||||
if self.add:
|
||||
try:
|
||||
fname = "faq%02d.%03d.htp" % (string.atoi(self.section),
|
||||
string.atoi(self.number))
|
||||
except ValueError:
|
||||
return None, None
|
||||
if fname != name:
|
||||
return None, None
|
||||
headers = {'title': "%s.%s. " % (self.section, self.number)}
|
||||
text = ""
|
||||
else:
|
||||
f = open(name)
|
||||
headers = rfc822.Message(f)
|
||||
text = f.read()
|
||||
f.close()
|
||||
self.headers = headers
|
||||
return headers, text
|
||||
|
||||
def show(self, name, title, text, edit=1):
|
||||
print "<H2>%s</H2>" % cgi.escape(title)
|
||||
pre = 0
|
||||
for line in string.split(text, '\n'):
|
||||
if not string.strip(line):
|
||||
if pre:
|
||||
print '</PRE>'
|
||||
pre = 0
|
||||
else:
|
||||
print '<P>'
|
||||
else:
|
||||
if line[0] not in string.whitespace:
|
||||
if pre:
|
||||
print '</PRE>'
|
||||
pre = 0
|
||||
else:
|
||||
if not pre:
|
||||
print '<PRE>'
|
||||
pre = 1
|
||||
if '/' in line or '@' in line:
|
||||
line = self.translate(line)
|
||||
elif '<' in line or '&' in line:
|
||||
line = cgi.escape(line)
|
||||
if not pre and '*' in line:
|
||||
line = self.emphasize(line)
|
||||
print line
|
||||
if pre:
|
||||
print '</PRE>'
|
||||
pre = 0
|
||||
print '<P>'
|
||||
if edit:
|
||||
print """
|
||||
<A HREF="faq.py?req=edit&name=%s">Edit this entry</A> /
|
||||
<A HREF="faq.py?req=info&name=%s" TARGET=rlog>Log info</A>
|
||||
""" % (name, name)
|
||||
if self.headers:
|
||||
try:
|
||||
date = self.headers['last-changed-date']
|
||||
author = self.headers['last-changed-author']
|
||||
email = self.headers['last-changed-email']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
s = '/ Last changed on %s by <A HREF="mailto:%s">%s</A>'
|
||||
print s % (date, email, author)
|
||||
print '<P>'
|
||||
print "<HR>"
|
||||
|
||||
def getversion(self, name):
|
||||
p = os.popen("/depot/gnu/plat/bin/rlog -h %s </dev/null 2>&1" % name)
|
||||
head = "*new*"
|
||||
while 1:
|
||||
line = p.readline()
|
||||
if not line:
|
||||
break
|
||||
if line[:5] == 'head:':
|
||||
head = string.strip(line[5:])
|
||||
p.close()
|
||||
return head
|
||||
|
||||
def prologue(self, title):
|
||||
title = cgi.escape(title)
|
||||
print '''
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>%s</TITLE>
|
||||
</HEAD>
|
||||
<BODY BACKGROUND="http://www.python.org/pics/RedShort.gif"
|
||||
BGCOLOR="#FFFFFF"
|
||||
TEXT="#000000"
|
||||
LINK="#AA0000"
|
||||
VLINK="#906A6A">
|
||||
<H1>%s</H1>
|
||||
''' % (title, title)
|
||||
|
||||
def error(self, *messages):
|
||||
self.prologue("Python FAQ error")
|
||||
print "Sorry, an error occurred:<BR>"
|
||||
for message in messages:
|
||||
print message,
|
||||
print
|
||||
|
||||
def epilogue(self):
|
||||
if self.edit == 'no':
|
||||
global wanttime
|
||||
wanttime = 0
|
||||
else:
|
||||
print '''
|
||||
<P>
|
||||
<HR>
|
||||
<A HREF="http://www.python.org">Python home</A> /
|
||||
<A HREF="faq.py?req=frontpage">FAQ Wizard home</A> /
|
||||
Feedback to <A HREF="mailto:guido@python.org">GvR</A>
|
||||
'''
|
||||
print '''
|
||||
</BODY>
|
||||
</HTML>
|
||||
'''
|
||||
|
||||
translate_prog = None
|
||||
|
||||
def translate(self, text):
|
||||
if not self.translate_prog:
|
||||
import regex
|
||||
url = '\(http\|ftp\)://[^ \t\r\n]*'
|
||||
email = '\<[-a-zA-Z0-9._]+@[-a-zA-Z0-9._]+'
|
||||
self.translate_prog = prog = regex.compile(url + "\|" + email)
|
||||
else:
|
||||
prog = self.translate_prog
|
||||
i = 0
|
||||
list = []
|
||||
while 1:
|
||||
j = prog.search(text, i)
|
||||
if j < 0:
|
||||
break
|
||||
list.append(cgi.escape(text[i:j]))
|
||||
i = j
|
||||
url = prog.group(0)
|
||||
while url[-1] in ");:,.?'\"":
|
||||
url = url[:-1]
|
||||
url = self.escape(url)
|
||||
if ':' in url:
|
||||
repl = '<A HREF="%s">%s</A>' % (url, url)
|
||||
else:
|
||||
repl = '<A HREF="mailto:%s"><%s></A>' % (url, url)
|
||||
list.append(repl)
|
||||
i = i + len(url)
|
||||
j = len(text)
|
||||
list.append(cgi.escape(text[i:j]))
|
||||
return string.join(list, '')
|
||||
|
||||
emphasize_prog = None
|
||||
|
||||
def emphasize(self, line):
|
||||
import regsub
|
||||
if not self.emphasize_prog:
|
||||
import regex
|
||||
pat = "\*\([a-zA-Z]+\)\*"
|
||||
self.emphasize_prog = prog = regex.compile(pat)
|
||||
else:
|
||||
prog = self.emphasize_prog
|
||||
return regsub.gsub(prog, "<I>\\1</I>", line)
|
||||
|
||||
print "Content-type: text/html"
|
||||
dt = 0
|
||||
wanttime = 0
|
||||
try:
|
||||
import time
|
||||
t1 = time.time()
|
||||
import cgi, string, os, sys
|
||||
x = FAQServer()
|
||||
x.main()
|
||||
t2 = time.time()
|
||||
dt = t2-t1
|
||||
wanttime = 1
|
||||
except:
|
||||
print "\n<HR>Sorry, an error occurred"
|
||||
cgi.print_exception()
|
||||
if wanttime:
|
||||
print "<BR>(running time = %s seconds)" % str(round(dt, 3))
|
||||
|
||||
# The following bootstrap script must be placed in cgi-bin/faq.py:
|
||||
BOOTSTRAP = """
|
||||
#! /usr/local/bin/python
|
||||
FAQDIR = "/usr/people/guido/python/FAQ"
|
||||
import os, sys
|
||||
os.chdir(FAQDIR)
|
||||
sys.path.insert(0, os.curdir)
|
||||
import faqmain
|
||||
"""
|
Loading…
Reference in New Issue