diff options
author | Georg Brandl <georg@python.org> | 2010-08-02 22:28:01 (GMT) |
---|---|---|
committer | Georg Brandl <georg@python.org> | 2010-08-02 22:28:01 (GMT) |
commit | e27c906c52a5827294037100662a7b0ccb303a47 (patch) | |
tree | 42f3d416500e5b57ab7100f3da2a28a1455b836d /Tools/faqwiz | |
parent | 0063958c2ed210a0d70da2ab26f660a092888cf0 (diff) | |
download | cpython-e27c906c52a5827294037100662a7b0ccb303a47.zip cpython-e27c906c52a5827294037100662a7b0ccb303a47.tar.gz cpython-e27c906c52a5827294037100662a7b0ccb303a47.tar.bz2 |
Remove faqwiz tool.
Diffstat (limited to 'Tools/faqwiz')
-rw-r--r-- | Tools/faqwiz/README | 114 | ||||
-rw-r--r-- | Tools/faqwiz/faqconf.py | 577 | ||||
-rw-r--r-- | Tools/faqwiz/faqcust.py | 1 | ||||
-rwxr-xr-x | Tools/faqwiz/faqw.py | 33 | ||||
-rw-r--r-- | Tools/faqwiz/faqwiz.py | 840 | ||||
-rwxr-xr-x | Tools/faqwiz/move-faqwiz.sh | 55 |
6 files changed, 0 insertions, 1620 deletions
diff --git a/Tools/faqwiz/README b/Tools/faqwiz/README deleted file mode 100644 index a1e4d44..0000000 --- a/Tools/faqwiz/README +++ /dev/null @@ -1,114 +0,0 @@ -FAQ Wizard ----------- - -Author: Guido van Rossum <guido@python.org> -Version: 1.0 -Date: 6 April 1998 - - -This is a CGI program that maintains a user-editable FAQ. It uses RCS -to keep track of changes to individual FAQ entries. It is fully -configurable; everything you might want to change when using this -program to maintain some other FAQ than the Python FAQ is contained in -the configuration module, faqconf.py. - -Note that the bulk of the code is not an executable script; it's an -importable module. The actual script in cgi-bin is minimal. - -Files: - -faqw.py executable script to be edited and installed in cgi-bin -faqwiz.py main module, lives in same directory as FAQ entry files -faqconf.py main configuration module -faqcust.py additional local customization module (optional) -move-faqwiz.sh Script to move faqwiz entries. - - -What's New? ------------ - -Version 1.0 corrects some minor bugs and uses tab-agnostic -indentation; it is otherwise unchanged from version 0.9.0. - -Version 0.9.0 uses the re module (Perl style regular expressions) for -all its regular expression needs, instead of the regex and regsub -modules (Emacs style). This affects the syntax for regular -expressions entered by the user as search strings (with "regular -expression" checked), hence the version number jump. - - -Setup Information ------------------ - -This assumes you are familiar with Python, with your http server, and -with running CGI scripts under your http server. You need Python 1.5 -or better. - -Select a place where the Python modules that constitute the FAQ wizard -will live (the directory where you unpacked it is an obvious choice). -This will be called the SRCDIR. This directory should not be writable -by other users of your system (since they would be able to execute -arbitrary code by invoking the FAQ wizard's CGI script). - -Create a dedicated working directory, preferably one that's not -directly reachable from your http server. This will be called the -FAQDIR. Create a subdirectory named RCS. Make both the working -directory and the RCS subdirectory wrld-writable. (This is essential, -since the FAQ wizard runs as use nobody, and needs to create -additional files here!) - -Edit faqconf.py to reflect your setup. You only need to edit the top -part, up till the line of all dashes. The comments should guide you -in your edits. (Actually, you can also choose to add your changes to -faqcust.py and leave faqconf.py alone. This is essential if you are -maintaining multiple FAQs; see below.) - -Don't forget to edit the SECTION_TITLES variables to reflect the set -of section titles for your FAQ! - -Next, edit faqw.py to reflect the pathname of your Python interpreter -and the values for SRCDIR and FAQDIR that you just chose. Then -install faqw.py in your cgi-bin directory. Make sure that it is -world-executable. You should now be able to connect to the FAQ wizard -by entering the following URL in your web client (subsituting the -appropriate host and port for "your.web.server", and perhaps -specifying a different directory for "cgi-bin" if local conventions so -dictate): - - http://your.web.server/cgi-bin/faqw.py - -If you are unable to get this working, check your server's error_log -file. The documentation for Python's cgi module in the Python Library -Reference Manual gives plentyu additional information about installing -and debugging CGI scripts, including setup debugging. This -documentation is repeated in the doc string in the cgi module; try -``import cgi; print cgi.__doc__''. - -Assuming this works, you should now be able to add the first entry to -your FAQ using the FAQ wizard interface. This creates a file -faq01.001.htp in your working directory and an RCS revision history -file faq01.001.htp,v in the RCS subdirectory. You can now exercise -the other FAQ wizard features (search, index, whole FAQ, what's new, -roulette, and so on). - - -Maintaining Multiple FAQs -------------------------- - -If you have multiple FAQs, you need a separate FAQDIR per FAQ, and a -different customization file per FAQ. The easiest thing to do would -be to have the faqcust.py for each FAQ live in the FAQDIR for that -FAQ, but that creates some security concerns, since the FAQDIR must be -world writable: *if* someone who breaks into your system (or a -legitimate user) manages to edit the faqcust.py file they can get -arbitrary code to execute through the FAQ wizard. Therefore, you will -need a more complex setup. - -The best way is probably to have a directory that is only writable by -you for each FAQ, where you place the copy of faqcust.py for that FAQ, -and have a world-writable subdirectory DATA for the data. You then -set FAQDIR to point to the DATA directory and change the faqw.py -bootstrap script to add FAQDIR/.. to sys.path (in front of SRCDIR, so -the dummy faqcust.py from SRCDIR is ignored). - ---Guido van Rossum (home page: http://www.python.org/~guido/) diff --git a/Tools/faqwiz/faqconf.py b/Tools/faqwiz/faqconf.py deleted file mode 100644 index d1acd80..0000000 --- a/Tools/faqwiz/faqconf.py +++ /dev/null @@ -1,577 +0,0 @@ -"""FAQ Wizard customization module. - -Edit this file to customize the FAQ Wizard. For normal purposes, you -should only have to change the FAQ section titles and the small group -of parameters below it. - -""" - -# Titles of FAQ sections - -SECTION_TITLES = { - # SectionNumber : SectionTitle; need at least one entry - 1: "General information and availability", -} - -# Parameters you definitely want to change - -SHORTNAME = "Generic" # FAQ name with "FAQ" omitted -PASSWORD = "" # Password for editing -OWNERNAME = "FAQ owner" # Name for feedback -OWNEREMAIL = "nobody@anywhere.org" # Email for feedback -HOMEURL = "http://www.python.org" # Related home page -HOMENAME = "Python home" # Name of related home page -RCSBINDIR = "/usr/local/bin/" # Directory containing RCS commands - # (must end in a slash) - -# Parameters you can normally leave alone - -MAXHITS = 10 # Max #hits to be shown directly -COOKIE_LIFETIME = 28*24*3600 # Cookie expiration in seconds - # (28*24*3600 = 28 days = 4 weeks) -PROCESS_PREFORMAT = 1 # toggle whether preformatted text - # will replace urls and emails with - # HTML links - -# Markers appended to title to indicate recently change -# (may contain HTML, e.g. <IMG>); and corresponding - -MARK_VERY_RECENT = " **" # Changed very recently -MARK_RECENT = " *" # Changed recently -DT_VERY_RECENT = 24*3600 # 24 hours -DT_RECENT = 7*24*3600 # 7 days - -EXPLAIN_MARKS = """ -<P>(Entries marked with ** were changed within the last 24 hours; -entries marked with * were changed within the last 7 days.) -<P> -""" - -# Version -- don't change unless you edit faqwiz.py - -WIZVERSION = "1.0.4" # FAQ Wizard version - -import os, sys -if os.name in ['nt',]: - # On NT we'll probably be running python from a batch file, - # so sys.argv[0] is not helpful - FAQCGI = 'faq.bat' # Relative URL of the FAQ cgi script - # LOGNAME is not typically set on NT - os.environ[ 'LOGNAME' ] = "FAQWizard" -else: - # This parameter is normally overwritten with a dynamic value - FAQCGI = 'faqw.py' # Relative URL of the FAQ cgi script - FAQCGI = os.path.basename(sys.argv[0]) or FAQCGI -del os, sys - -# Perl (re module) style regular expression to recognize FAQ entry -# files: group(1) should be the section number, group(2) should be the -# question number. Both should be fixed width so simple-minded -# sorting yields the right order. - -OKFILENAME = r"^faq(\d\d)\.(\d\d\d)\.htp$" - -# Format to construct a FAQ entry file name - -NEWFILENAME = "faq%02d.%03d.htp" - -# Load local customizations on top of the previous parameters - -try: - from faqcust import * -except ImportError: - pass - -# Calculated parameter names - -COOKIE_NAME = SHORTNAME + "-FAQ-Wizard" # Name used for Netscape cookie -FAQNAME = SHORTNAME + " FAQ" # Name of the FAQ - -# ---------------------------------------------------------------------- - -# Anything below this point normally needn't be changed; you would -# change this if you were to create e.g. a French translation or if -# you just aren't happy with the text generated by the FAQ Wizard. - -# Most strings here are subject to substitution (string%dictionary) - -# RCS commands - -import os -if os.name in ['nt', ]: - SH_RLOG = RCSBINDIR + "rlog %(file)s < NUL" - SH_RLOG_H = RCSBINDIR + "rlog -h %(file)s < NUL" - SH_RDIFF = RCSBINDIR + "rcsdiff -r%(prev)s -r%(rev)s %(file)s < NUL" - SH_REVISION = RCSBINDIR + "co -p%(rev)s %(file)s < NUL" - ### Have to use co -l, or the file is not marked rw on NT - SH_LOCK = RCSBINDIR + "co -l %(file)s < NUL" - SH_CHECKIN = RCSBINDIR + "ci -u %(file)s < %(tfn)s" -else: - SH_RLOG = RCSBINDIR + "rlog %(file)s </dev/null 2>&1" - SH_RLOG_H = RCSBINDIR + "rlog -h %(file)s </dev/null 2>&1" - SH_RDIFF = RCSBINDIR + "rcsdiff -r%(prev)s -r%(rev)s %(file)s </dev/null 2>&1" - SH_REVISION = RCSBINDIR + "co -p%(rev)s %(file)s </dev/null 2>&1" - SH_LOCK = RCSBINDIR + "rcs -l %(file)s </dev/null 2>&1" - SH_CHECKIN = RCSBINDIR + "ci -u %(file)s <%(tfn)s 2>&1" -del os - -# Titles for various output pages (not subject to substitution) - -T_HOME = FAQNAME + " Wizard " + WIZVERSION -T_ERROR = "Sorry, an error occurred" -T_ROULETTE = FAQNAME + " Roulette" -T_ALL = "The Whole " + FAQNAME -T_INDEX = FAQNAME + " Index" -T_SEARCH = FAQNAME + " Search Results" -T_RECENT = "What's New in the " + FAQNAME -T_SHOW = FAQNAME + " Entry" -T_LOG = "RCS log for %s entry" % FAQNAME -T_REVISION = "RCS revision for %s entry" % FAQNAME -T_DIFF = "RCS diff for %s entry" % FAQNAME -T_ADD = "Add an entry to the " + FAQNAME -T_DELETE = "Deleting an entry from the " + FAQNAME -T_EDIT = FAQNAME + " Edit Wizard" -T_REVIEW = T_EDIT + " - Review Changes" -T_COMMITTED = T_EDIT + " - Changes Committed" -T_COMMITFAILED = T_EDIT + " - Commit Failed" -T_CANTCOMMIT = T_EDIT + " - Commit Rejected" -T_HELP = T_EDIT + " - Help" - -# Generic prologue and epilogue - -PROLOGUE = ''' -<HTML> -<HEAD> -<TITLE>%(title)s</TITLE> -</HEAD> - -<BODY - BGCOLOR="#FFFFFF" - TEXT="#000000" - LINK="#AA0000" - VLINK="#906A6A"> -<H1>%(title)s</H1> -''' - -EPILOGUE = ''' -<HR> -<A HREF="%(HOMEURL)s">%(HOMENAME)s</A> / -<A HREF="%(FAQCGI)s?req=home">%(FAQNAME)s Wizard %(WIZVERSION)s</A> / -Feedback to <A HREF="mailto:%(OWNEREMAIL)s">%(OWNERNAME)s</A> - -</BODY> -</HTML> -''' - -# Home page - -HOME = """ -<H2>Search the %(FAQNAME)s:</H2> - -<BLOCKQUOTE> - -<FORM ACTION="%(FAQCGI)s"> - <INPUT TYPE=text NAME=query> - <INPUT TYPE=submit VALUE="Search"><BR> - <INPUT TYPE=radio NAME=querytype VALUE=simple CHECKED> - Simple string - / - <INPUT TYPE=radio NAME=querytype VALUE=regex> - Regular expression - /<BR> - <INPUT TYPE=radio NAME=querytype VALUE=anykeywords> - Keywords (any) - / - <INPUT TYPE=radio NAME=querytype VALUE=allkeywords> - Keywords (all) - <BR> - <INPUT TYPE=radio NAME=casefold VALUE=yes CHECKED> - Fold case - / - <INPUT TYPE=radio NAME=casefold VALUE=no> - Case sensitive - <BR> - <INPUT TYPE=hidden NAME=req VALUE=search> -</FORM> - -</BLOCKQUOTE> - -<HR> - -<H2>Other forms of %(FAQNAME)s access:</H2> - -<UL> -<LI><A HREF="%(FAQCGI)s?req=index">FAQ index</A> -<LI><A HREF="%(FAQCGI)s?req=all">The whole FAQ</A> -<LI><A HREF="%(FAQCGI)s?req=recent">What's new in the FAQ?</A> -<LI><A HREF="%(FAQCGI)s?req=roulette">FAQ roulette</A> -<LI><A HREF="%(FAQCGI)s?req=add">Add a FAQ entry</A> -<LI><A HREF="%(FAQCGI)s?req=delete">Delete a FAQ entry</A> -</UL> -""" - -# Index formatting - -INDEX_SECTION = """ -<P> -<HR> -<H2>%(sec)s. %(title)s</H2> -<UL> -""" - -INDEX_ADDSECTION = """ -<P> -<LI><A HREF="%(FAQCGI)s?req=new&section=%(sec)s">Add new entry</A> -(at this point) -""" - -INDEX_ENDSECTION = """ -</UL> -""" - -INDEX_ENTRY = """\ -<LI><A HREF="%(FAQCGI)s?req=show&file=%(file)s">%(title)s</A> -""" - -LOCAL_ENTRY = """\ -<LI><A HREF="#%(sec)s.%(num)s">%(title)s</A> -""" - -# Entry formatting - -ENTRY_HEADER1 = """ -<HR> -<H2><A NAME="%(sec)s.%(num)s">%(title)s</A>\ -""" - -ENTRY_HEADER2 = """\ -</H2> -""" - -ENTRY_FOOTER = """ -<A HREF="%(FAQCGI)s?req=edit&file=%(file)s">Edit this entry</A> / -<A HREF="%(FAQCGI)s?req=log&file=%(file)s">Log info</A> -""" - -ENTRY_LOGINFO = """ -/ Last changed on %(last_changed_date)s by -<A HREF="mailto:%(last_changed_email)s">%(last_changed_author)s</A> -""" - -# Search - -NO_HITS = """ -No hits. -""" - -ONE_HIT = """ -Your search matched the following entry: -""" - -FEW_HITS = """ -Your search matched the following %(count)s entries: -""" - -MANY_HITS = """ -Your search matched more than %(MAXHITS)s entries. -The %(count)s matching entries are presented here ordered by section: -""" - -# RCS log and diff - -LOG = """ -Click on a revision line to see the diff between that revision and the -previous one. -""" - -REVISIONLINK = """\ -<A HREF="%(FAQCGI)s?req=revision&file=%(file)s&rev=%(rev)s" ->%(line)s</A>\ -""" -DIFFLINK = """\ - (<A HREF="%(FAQCGI)s?req=diff&file=%(file)s&\ -prev=%(prev)s&rev=%(rev)s" ->diff -r%(prev)s -r%(rev)s</A>)\ -""" - -# Recently changed entries - -NO_RECENT = """ -<HR> -No %(FAQNAME)s entries were changed in the last %(period)s. -""" - -VIEW_MENU = """ -<HR> -View entries changed in the last... -<UL> -<LI><A HREF="%(FAQCGI)s?req=recent&days=1">24 hours</A> -<LI><A HREF="%(FAQCGI)s?req=recent&days=2">2 days</A> -<LI><A HREF="%(FAQCGI)s?req=recent&days=3">3 days</A> -<LI><A HREF="%(FAQCGI)s?req=recent&days=7">week</A> -<LI><A HREF="%(FAQCGI)s?req=recent&days=28">4 weeks</A> -<LI><A HREF="%(FAQCGI)s?req=recent&days=365250">millennium</A> -</UL> -""" - -ONE_RECENT = VIEW_MENU + """ -The following %(FAQNAME)s entry was changed in the last %(period)s: -""" - -SOME_RECENT = VIEW_MENU + """ -The following %(count)s %(FAQNAME)s entries were changed -in the last %(period)s, most recently changed shown first: -""" - -TAIL_RECENT = VIEW_MENU - -# Last changed banner on "all" (strftime format) -LAST_CHANGED = "Last changed on %c %Z" - -# "Compat" command prologue (this has no <BODY> tag) -COMPAT = """ -<H1>The whole %(FAQNAME)s</H1> -See also the <A HREF="%(FAQCGI)s?req=home">%(FAQNAME)s Wizard</A>. -<P> -""" - -# Editing - -EDITHEAD = """ -<A HREF="%(FAQCGI)s?req=help">Click for Help</A> -""" - -REVIEWHEAD = EDITHEAD - - -EDITFORM1 = """ -<FORM ACTION="%(FAQCGI)s" METHOD=POST> -<INPUT TYPE=hidden NAME=req VALUE=review> -<INPUT TYPE=hidden NAME=file VALUE=%(file)s> -<INPUT TYPE=hidden NAME=editversion VALUE=%(editversion)s> -<HR> -""" - -EDITFORM2 = """ -Title: <INPUT TYPE=text SIZE=70 NAME=title VALUE="%(title)s"><BR> -<TEXTAREA COLS=72 ROWS=20 NAME=body>%(body)s -</TEXTAREA><BR> -Log message (reason for the change):<BR> -<TEXTAREA COLS=72 ROWS=5 NAME=log>%(log)s -</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="%(author)s"> - <TR> - <TD>Email: - <TD><INPUT TYPE=text SIZE=40 NAME=email VALUE="%(email)s"> - <TR> - <TD>Password: - <TD><INPUT TYPE=password SIZE=20 NAME=password VALUE="%(password)s"> -</TABLE> - -<INPUT TYPE=submit NAME=review VALUE="Preview Edit"> -Click this button to preview your changes. -""" - -EDITFORM3 = """ -</FORM> -""" - -COMMIT = """ -<INPUT TYPE=submit NAME=commit VALUE="Commit"> -Click this button to commit your changes. -<HR> -""" - -NOCOMMIT_HEAD = """ -To commit your changes, please correct the following errors in the -form below and click the Preview Edit button. -<UL> -""" -NOCOMMIT_TAIL = """ -</UL> -<HR> -""" - -CANTCOMMIT_HEAD = """ -Some required information is missing: -<UL> -""" -NEED_PASSWD = "<LI>You must provide the correct password.\n" -NEED_AUTHOR = "<LI>You must enter your name.\n" -NEED_EMAIL = "<LI>You must enter your email address.\n" -NEED_LOG = "<LI>You must enter a log message.\n" -CANTCOMMIT_TAIL = """ -</UL> -Please use your browser's Back command to correct the form and commit -again. -""" - -NEWCONFLICT = """ -<P> -You are creating a new entry, but the entry number specified is not -correct. -<P> -The two most common causes of this problem are: -<UL> -<LI>After creating the entry yourself, you went back in your browser, - edited the entry some more, and clicked Commit again. -<LI>Someone else started creating a new entry in the same section and - committed before you did. -</UL> -(It is also possible that the last entry in the section was physically -deleted, but this should not happen except through manual intervention -by the FAQ maintainer.) -<P> -<A HREF="%(FAQCGI)s?req=new&section=%(sec)s">Click here to try -again.</A> -<P> -""" - -VERSIONCONFLICT = """ -<P> -You edited version %(editversion)s but the current version is %(version)s. -<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="%(FAQCGI)s?req=show&file=%(file)s">Click here to reload -the entry and try again.</A> -<P> -""" - -CANTWRITE = """ -Can't write file %(file)s (%(why)s). -""" - -FILEHEADER = """\ -Title: %(title)s -Last-Changed-Date: %(date)s -Last-Changed-Author: %(author)s -Last-Changed-Email: %(email)s -Last-Changed-Remote-Host: %(REMOTE_HOST)s -Last-Changed-Remote-Address: %(REMOTE_ADDR)s -""" - -LOGHEADER = """\ -Last-Changed-Date: %(date)s -Last-Changed-Author: %(author)s -Last-Changed-Email: %(email)s -Last-Changed-Remote-Host: %(REMOTE_HOST)s -Last-Changed-Remote-Address: %(REMOTE_ADDR)s - -%(log)s -""" - -COMMITTED = """ -Your changes have been committed. -""" - -COMMITFAILED = """ -Exit status %(sts)s. -""" - -# Add/Delete - -ADD_HEAD = """ -At the moment, new entries can only be added at the end of a section. -This is because the entry numbers are also their -unique identifiers -- it's a bad idea to renumber entries. -<P> -Click on the section to which you want to add a new entry: -<UL> -""" - -ADD_SECTION = """\ -<LI><A HREF="%(FAQCGI)s?req=new&section=%(section)s">%(section)s. %(title)s</A> -""" - -ADD_TAIL = """ -</UL> -""" - -ROULETTE = """ -<P>Hit your browser's Reload button to play again.<P> -""" - -DELETE = """ -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). -""" - -# Help file for the FAQ Edit Wizard - -HELP = """ -Using the %(FAQNAME)s Edit Wizard speaks mostly for itself. Here are -some answers to questions you are likely to ask: - -<P><HR> - -<H2>I can review an entry but I can't commit it.</H2> - -The commit button only appears if the following conditions are met: - -<UL> - -<LI>The Name field is not empty. - -<LI>The Email field contains at least an @ character. - -<LI>The Log message box is not empty. - -<LI>The Password field contains the proper password. - -</UL> - -<P><HR> - -<H2>What is the password?</H2> - -At the moment, only PSA members will be told the password. This is a -good time to join the PSA! See <A -HREF="http://www.python.org/psa/">the PSA home page</A>. - -<P><HR> - -<H2>Can I use HTML in the FAQ entry?</H2> - -Yes, if you include it in <HTML&rt; and </HTML> tags. -<P> -Also, if you include a URL or an email address in the text it will -automatigally become an anchor of the right type. Also, *word* -is made italic (but only for single alphabetic words). - -<P><HR> - -<H2>How do I delineate paragraphs?</H2> - -Use blank lines to separate paragraphs. - -<P><HR> - -<H2>How do I enter example text?</H2> - -Any line that begins with a space or tab is assumed to be part of -literal text. Blocks of literal text delineated by blank lines are -placed inside <PRE>...</PRE>. -""" - -# Load local customizations again, in case they set some other variables - -try: - from faqcust import * -except ImportError: - pass diff --git a/Tools/faqwiz/faqcust.py b/Tools/faqwiz/faqcust.py deleted file mode 100644 index 8f16781..0000000 --- a/Tools/faqwiz/faqcust.py +++ /dev/null @@ -1 +0,0 @@ -# Add your customizations here -- modified copies of what's in faqconf.py. diff --git a/Tools/faqwiz/faqw.py b/Tools/faqwiz/faqw.py deleted file mode 100755 index 01536c0..0000000 --- a/Tools/faqwiz/faqw.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 - -"""FAQ wizard bootstrap.""" - -# This is a longer version of the bootstrap script given at the end of -# faqwin.py; it prints timing statistics at the end of the regular CGI -# script's output (so you can monitor how it is doing). - -# This script should be placed in your cgi-bin directory and made -# executable. - -# You need to edit the first line and the lines that define FAQDIR and -# SRCDIR, below: change /usr/local/bin/python to where your Python -# interpreter lives, change the value for FAQDIR to where your FAQ -# lives, and change the value for SRCDIR to where your faqwiz.py -# module lives. The faqconf.py and faqcust.py files live there, too. - -import os -t1 = os.times() # If this doesn't work, just get rid of the timing code! -try: - FAQDIR = "/usr/people/guido/python/FAQ" - SRCDIR = "/usr/people/guido/python/src/Tools/faqwiz" - import os, sys - os.chdir(FAQDIR) - sys.path.insert(0, SRCDIR) - import faqwiz -except SystemExit as n: - sys.exit(n) -except: - t, v, tb = sys.exc_info() - print() - import cgi - cgi.print_exception(t, v, tb) diff --git a/Tools/faqwiz/faqwiz.py b/Tools/faqwiz/faqwiz.py deleted file mode 100644 index b9ab65d..0000000 --- a/Tools/faqwiz/faqwiz.py +++ /dev/null @@ -1,840 +0,0 @@ -"""Generic FAQ Wizard. - -This is a CGI program that maintains a user-editable FAQ. It uses RCS -to keep track of changes to individual FAQ entries. It is fully -configurable; everything you might want to change when using this -program to maintain some other FAQ than the Python FAQ is contained in -the configuration module, faqconf.py. - -Note that this is not an executable script; it's an importable module. -The actual script to place in cgi-bin is faqw.py. - -""" - -import sys, time, os, stat, re, cgi, faqconf -from faqconf import * # This imports all uppercase names -now = time.time() - -class FileError: - def __init__(self, file): - self.file = file - -class InvalidFile(FileError): - pass - -class NoSuchSection(FileError): - def __init__(self, section): - FileError.__init__(self, NEWFILENAME %(section, 1)) - self.section = section - -class NoSuchFile(FileError): - def __init__(self, file, why=None): - FileError.__init__(self, file) - self.why = why - -def escape(s): - s = s.replace('&', '&') - s = s.replace('<', '<') - s = s.replace('>', '>') - return s - -def escapeq(s): - s = escape(s) - s = s.replace('"', '"') - return s - -def _interpolate(format, args, kw): - try: - quote = kw['_quote'] - except KeyError: - quote = 1 - d = (kw,) + args + (faqconf.__dict__,) - m = MagicDict(d, quote) - return format % m - -def interpolate(format, *args, **kw): - return _interpolate(format, args, kw) - -def emit(format, *args, **kw): - try: - f = kw['_file'] - except KeyError: - f = sys.stdout - f.write(_interpolate(format, args, kw)) - -translate_prog = None - -def translate(text, pre=0): - global translate_prog - if not translate_prog: - translate_prog = prog = re.compile( - r'\b(http|ftp|https)://\S+(\b|/)|\b[-.\w]+@[-.\w]+') - else: - prog = translate_prog - i = 0 - list = [] - while 1: - m = prog.search(text, i) - if not m: - break - j = m.start() - list.append(escape(text[i:j])) - i = j - url = m.group(0) - while url[-1] in '();:,.?\'"<>': - url = url[:-1] - i = i + len(url) - url = escape(url) - if not pre or (pre and PROCESS_PREFORMAT): - if ':' in url: - repl = '<A HREF="%s">%s</A>' % (url, url) - else: - repl = '<A HREF="mailto:%s">%s</A>' % (url, url) - else: - repl = url - list.append(repl) - j = len(text) - list.append(escape(text[i:j])) - return ''.join(list) - -def emphasize(line): - return re.sub(r'\*([a-zA-Z]+)\*', r'<I>\1</I>', line) - -revparse_prog = None - -def revparse(rev): - global revparse_prog - if not revparse_prog: - revparse_prog = re.compile(r'^(\d{1,3})\.(\d{1,4})$') - m = revparse_prog.match(rev) - if not m: - return None - [major, minor] = map(int, m.group(1, 2)) - return major, minor - -logon = 0 -def log(text): - if logon: - logfile = open("logfile", "a") - logfile.write(text + "\n") - logfile.close() - -def load_cookies(): - if 'HTTP_COOKIE' not in os.environ: - return {} - raw = os.environ['HTTP_COOKIE'] - words = [s.strip() for s in raw.split(';')] - cookies = {} - for word in words: - i = word.find('=') - if i >= 0: - key, value = word[:i], word[i+1:] - cookies[key] = value - return cookies - -def load_my_cookie(): - cookies = load_cookies() - try: - value = cookies[COOKIE_NAME] - except KeyError: - return {} - import urllib.parse - value = urllib.parse.unquote(value) - words = value.split('/') - while len(words) < 3: - words.append('') - author = '/'.join(words[:-2]) - email = words[-2] - password = words[-1] - return {'author': author, - 'email': email, - 'password': password} - -def send_my_cookie(ui): - name = COOKIE_NAME - value = "%s/%s/%s" % (ui.author, ui.email, ui.password) - import urllib.parse - value = urllib.parse.quote(value) - then = now + COOKIE_LIFETIME - gmt = time.gmtime(then) - path = os.environ.get('SCRIPT_NAME', '/cgi-bin/') - print("Set-Cookie: %s=%s; path=%s;" % (name, value, path), end=' ') - print(time.strftime("expires=%a, %d-%b-%y %X GMT", gmt)) - -class MagicDict: - - def __init__(self, d, quote): - self.__d = d - self.__quote = quote - - def __getitem__(self, key): - for d in self.__d: - try: - value = d[key] - if value: - value = str(value) - if self.__quote: - value = escapeq(value) - return value - except KeyError: - pass - return '' - -class UserInput: - - def __init__(self): - self.__form = cgi.FieldStorage() - #log("\n\nbody: " + self.body) - - def __getattr__(self, name): - if name[0] == '_': - raise AttributeError - try: - value = self.__form[name].value - except (TypeError, KeyError): - value = '' - else: - value = value.strip() - setattr(self, name, value) - return value - - def __getitem__(self, key): - return getattr(self, key) - -class FaqEntry: - - def __init__(self, fp, file, sec_num): - self.file = file - self.sec, self.num = sec_num - if fp: - import email - self.__headers = email.message_from_file(fp) - self.body = fp.read().strip() - else: - self.__headers = {'title': "%d.%d. " % sec_num} - self.body = '' - - def __getattr__(self, name): - if name[0] == '_': - raise AttributeError - key = '-'.join(name.split('_')) - try: - value = self.__headers[key] - except KeyError: - value = '' - setattr(self, name, value) - return value - - def __getitem__(self, key): - return getattr(self, key) - - def load_version(self): - command = interpolate(SH_RLOG_H, self) - p = os.popen(command) - version = '' - while 1: - line = p.readline() - if not line: - break - if line[:5] == 'head:': - version = line[5:].strip() - p.close() - self.version = version - - def getmtime(self): - if not self.last_changed_date: - return 0 - try: - return os.stat(self.file)[stat.ST_MTIME] - except os.error: - return 0 - - def emit_marks(self): - mtime = self.getmtime() - if mtime >= now - DT_VERY_RECENT: - emit(MARK_VERY_RECENT, self) - elif mtime >= now - DT_RECENT: - emit(MARK_RECENT, self) - - def show(self, edit=1): - emit(ENTRY_HEADER1, self) - self.emit_marks() - emit(ENTRY_HEADER2, self) - pre = 0 - raw = 0 - for line in self.body.split('\n'): - # Allow the user to insert raw html into a FAQ answer - # (Skip Montanaro, with changes by Guido) - tag = line.rstrip().lower() - if tag == '<html>': - raw = 1 - continue - if tag == '</html>': - raw = 0 - continue - if raw: - print(line) - continue - if not line.strip(): - if pre: - print('</PRE>') - pre = 0 - else: - print('<P>') - else: - if not line[0].isspace(): - if pre: - print('</PRE>') - pre = 0 - else: - if not pre: - print('<PRE>') - pre = 1 - if '/' in line or '@' in line: - line = translate(line, pre) - elif '<' in line or '&' in line: - line = escape(line) - if not pre and '*' in line: - line = emphasize(line) - print(line) - if pre: - print('</PRE>') - pre = 0 - if edit: - print('<P>') - emit(ENTRY_FOOTER, self) - if self.last_changed_date: - emit(ENTRY_LOGINFO, self) - print('<P>') - -class FaqDir: - - entryclass = FaqEntry - - __okprog = re.compile(OKFILENAME) - - def __init__(self, dir=os.curdir): - self.__dir = dir - self.__files = None - - def __fill(self): - if self.__files is not None: - return - self.__files = files = [] - okprog = self.__okprog - for file in os.listdir(self.__dir): - if self.__okprog.match(file): - files.append(file) - files.sort() - - def good(self, file): - return self.__okprog.match(file) - - def parse(self, file): - m = self.good(file) - if not m: - return None - sec, num = m.group(1, 2) - return int(sec), int(num) - - def list(self): - # XXX Caller shouldn't modify result - self.__fill() - return self.__files - - def open(self, file): - sec_num = self.parse(file) - if not sec_num: - raise InvalidFile(file) - try: - fp = open(file) - except IOError as msg: - raise NoSuchFile(file, msg) - try: - return self.entryclass(fp, file, sec_num) - finally: - fp.close() - - def show(self, file, edit=1): - self.open(file).show(edit=edit) - - def new(self, section): - if section not in SECTION_TITLES: - raise NoSuchSection(section) - maxnum = 0 - for file in self.list(): - sec, num = self.parse(file) - if sec == section: - maxnum = max(maxnum, num) - sec_num = (section, maxnum+1) - file = NEWFILENAME % sec_num - return self.entryclass(None, file, sec_num) - -class FaqWizard: - - def __init__(self): - self.ui = UserInput() - self.dir = FaqDir() - - def go(self): - print('Content-type: text/html') - req = self.ui.req or 'home' - mname = 'do_%s' % req - try: - meth = getattr(self, mname) - except AttributeError: - self.error("Bad request type %r." % (req,)) - else: - try: - meth() - except InvalidFile as exc: - self.error("Invalid entry file name %s" % exc.file) - except NoSuchFile as exc: - self.error("No entry with file name %s" % exc.file) - except NoSuchSection as exc: - self.error("No section number %s" % exc.section) - self.epilogue() - - def error(self, message, **kw): - self.prologue(T_ERROR) - emit(message, kw) - - def prologue(self, title, entry=None, **kw): - emit(PROLOGUE, entry, kwdict=kw, title=escape(title)) - - def epilogue(self): - emit(EPILOGUE) - - def do_home(self): - self.prologue(T_HOME) - emit(HOME) - - def do_debug(self): - self.prologue("FAQ Wizard Debugging") - form = cgi.FieldStorage() - cgi.print_form(form) - cgi.print_environ(os.environ) - cgi.print_directory() - cgi.print_arguments() - - def do_search(self): - query = self.ui.query - if not query: - self.error("Empty query string!") - return - if self.ui.querytype == 'simple': - query = re.escape(query) - queries = [query] - elif self.ui.querytype in ('anykeywords', 'allkeywords'): - words = [_f for _f in re.split('\W+', query) if _f] - if not words: - self.error("No keywords specified!") - return - words = [r'\b%s\b' % w for w in words] - if self.ui.querytype[:3] == 'any': - queries = ['|'.join(words)] - else: - # Each of the individual queries must match - queries = words - else: - # Default to regular expression - queries = [query] - self.prologue(T_SEARCH) - progs = [] - for query in queries: - if self.ui.casefold == 'no': - p = re.compile(query) - else: - p = re.compile(query, re.IGNORECASE) - progs.append(p) - hits = [] - for file in self.dir.list(): - try: - entry = self.dir.open(file) - except FileError: - constants - for p in progs: - if not p.search(entry.title) and not p.search(entry.body): - break - else: - hits.append(file) - if not hits: - emit(NO_HITS, self.ui, count=0) - elif len(hits) <= MAXHITS: - if len(hits) == 1: - emit(ONE_HIT, count=1) - else: - emit(FEW_HITS, count=len(hits)) - self.format_all(hits, headers=0) - else: - emit(MANY_HITS, count=len(hits)) - self.format_index(hits) - - def do_all(self): - self.prologue(T_ALL) - files = self.dir.list() - self.last_changed(files) - self.format_index(files, localrefs=1) - self.format_all(files) - - def do_compat(self): - files = self.dir.list() - emit(COMPAT) - self.last_changed(files) - self.format_index(files, localrefs=1) - self.format_all(files, edit=0) - sys.exit(0) # XXX Hack to suppress epilogue - - def last_changed(self, files): - latest = 0 - for file in files: - entry = self.dir.open(file) - if entry: - mtime = mtime = entry.getmtime() - if mtime > latest: - latest = mtime - print(time.strftime(LAST_CHANGED, time.localtime(latest))) - emit(EXPLAIN_MARKS) - - def format_all(self, files, edit=1, headers=1): - sec = 0 - for file in files: - try: - entry = self.dir.open(file) - except NoSuchFile: - continue - if headers and entry.sec != sec: - sec = entry.sec - try: - title = SECTION_TITLES[sec] - except KeyError: - title = "Untitled" - emit("\n<HR>\n<H1>%(sec)s. %(title)s</H1>\n", - sec=sec, title=title) - entry.show(edit=edit) - - def do_index(self): - self.prologue(T_INDEX) - files = self.dir.list() - self.last_changed(files) - self.format_index(files, add=1) - - def format_index(self, files, add=0, localrefs=0): - sec = 0 - for file in files: - try: - entry = self.dir.open(file) - except NoSuchFile: - continue - if entry.sec != sec: - if sec: - if add: - emit(INDEX_ADDSECTION, sec=sec) - emit(INDEX_ENDSECTION, sec=sec) - sec = entry.sec - try: - title = SECTION_TITLES[sec] - except KeyError: - title = "Untitled" - emit(INDEX_SECTION, sec=sec, title=title) - if localrefs: - emit(LOCAL_ENTRY, entry) - else: - emit(INDEX_ENTRY, entry) - entry.emit_marks() - if sec: - if add: - emit(INDEX_ADDSECTION, sec=sec) - emit(INDEX_ENDSECTION, sec=sec) - - def do_recent(self): - if not self.ui.days: - days = 1 - else: - days = float(self.ui.days) - try: - cutoff = now - days * 24 * 3600 - except OverflowError: - cutoff = 0 - list = [] - for file in self.dir.list(): - entry = self.dir.open(file) - if not entry: - continue - mtime = entry.getmtime() - if mtime >= cutoff: - list.append((mtime, file)) - list.sort() - list.reverse() - self.prologue(T_RECENT) - if days <= 1: - period = "%.2g hours" % (days*24) - else: - period = "%.6g days" % days - if not list: - emit(NO_RECENT, period=period) - elif len(list) == 1: - emit(ONE_RECENT, period=period) - else: - emit(SOME_RECENT, period=period, count=len(list)) - self.format_all([mtime_file[1] for mtime_file in list], headers=0) - emit(TAIL_RECENT) - - def do_roulette(self): - import random - files = self.dir.list() - if not files: - self.error("No entries.") - return - file = random.choice(files) - self.prologue(T_ROULETTE) - emit(ROULETTE) - self.dir.show(file) - - def do_help(self): - self.prologue(T_HELP) - emit(HELP) - - def do_show(self): - entry = self.dir.open(self.ui.file) - self.prologue(T_SHOW) - entry.show() - - def do_add(self): - self.prologue(T_ADD) - emit(ADD_HEAD) - sections = sorted(SECTION_TITLES.items()) - for section, title in sections: - emit(ADD_SECTION, section=section, title=title) - emit(ADD_TAIL) - - def do_delete(self): - self.prologue(T_DELETE) - emit(DELETE) - - def do_log(self): - entry = self.dir.open(self.ui.file) - self.prologue(T_LOG, entry) - emit(LOG, entry) - self.rlog(interpolate(SH_RLOG, entry), entry) - - def rlog(self, command, entry=None): - output = os.popen(command).read() - sys.stdout.write('<PRE>') - athead = 0 - lines = output.split('\n') - while lines and not lines[-1]: - del lines[-1] - if lines: - line = lines[-1] - if line[:1] == '=' and len(line) >= 40 and \ - line == line[0]*len(line): - del lines[-1] - headrev = None - for line in lines: - if entry and athead and line[:9] == 'revision ': - rev = line[9:].split() - mami = revparse(rev) - if not mami: - print(line) - else: - emit(REVISIONLINK, entry, rev=rev, line=line) - if mami[1] > 1: - prev = "%d.%d" % (mami[0], mami[1]-1) - emit(DIFFLINK, entry, prev=prev, rev=rev) - if headrev: - emit(DIFFLINK, entry, prev=rev, rev=headrev) - else: - headrev = rev - print() - athead = 0 - else: - athead = 0 - if line[:1] == '-' and len(line) >= 20 and \ - line == len(line) * line[0]: - athead = 1 - sys.stdout.write('<HR>') - else: - print(line) - print('</PRE>') - - def do_revision(self): - entry = self.dir.open(self.ui.file) - rev = self.ui.rev - mami = revparse(rev) - if not mami: - self.error("Invalid revision number: %r." % (rev,)) - self.prologue(T_REVISION, entry) - self.shell(interpolate(SH_REVISION, entry, rev=rev)) - - def do_diff(self): - entry = self.dir.open(self.ui.file) - prev = self.ui.prev - rev = self.ui.rev - mami = revparse(rev) - if not mami: - self.error("Invalid revision number: %r." % (rev,)) - if prev: - if not revparse(prev): - self.error("Invalid previous revision number: %r." % (prev,)) - else: - prev = '%d.%d' % (mami[0], mami[1]) - self.prologue(T_DIFF, entry) - self.shell(interpolate(SH_RDIFF, entry, rev=rev, prev=prev)) - - def shell(self, command): - output = os.popen(command).read() - sys.stdout.write('<PRE>') - print(escape(output)) - print('</PRE>') - - def do_new(self): - entry = self.dir.new(section=int(self.ui.section)) - entry.version = '*new*' - self.prologue(T_EDIT) - emit(EDITHEAD) - emit(EDITFORM1, entry, editversion=entry.version) - emit(EDITFORM2, entry, load_my_cookie()) - emit(EDITFORM3) - entry.show(edit=0) - - def do_edit(self): - entry = self.dir.open(self.ui.file) - entry.load_version() - self.prologue(T_EDIT) - emit(EDITHEAD) - emit(EDITFORM1, entry, editversion=entry.version) - emit(EDITFORM2, entry, load_my_cookie()) - emit(EDITFORM3) - entry.show(edit=0) - - def do_review(self): - send_my_cookie(self.ui) - if self.ui.editversion == '*new*': - sec, num = self.dir.parse(self.ui.file) - entry = self.dir.new(section=sec) - entry.version = "*new*" - if entry.file != self.ui.file: - self.error("Commit version conflict!") - emit(NEWCONFLICT, self.ui, sec=sec, num=num) - return - else: - entry = self.dir.open(self.ui.file) - entry.load_version() - # Check that the FAQ entry number didn't change - if self.ui.title.split()[:1] != entry.title.split()[:1]: - self.error("Don't change the entry number please!") - return - # Check that the edited version is the current version - if entry.version != self.ui.editversion: - self.error("Commit version conflict!") - emit(VERSIONCONFLICT, entry, self.ui) - return - commit_ok = ((not PASSWORD - or self.ui.password == PASSWORD) - and self.ui.author - and '@' in self.ui.email - and self.ui.log) - if self.ui.commit: - if not commit_ok: - self.cantcommit() - else: - self.commit(entry) - return - self.prologue(T_REVIEW) - emit(REVIEWHEAD) - entry.body = self.ui.body - entry.title = self.ui.title - entry.show(edit=0) - emit(EDITFORM1, self.ui, entry) - if commit_ok: - emit(COMMIT) - else: - emit(NOCOMMIT_HEAD) - self.errordetail() - emit(NOCOMMIT_TAIL) - emit(EDITFORM2, self.ui, entry, load_my_cookie()) - emit(EDITFORM3) - - def cantcommit(self): - self.prologue(T_CANTCOMMIT) - print(CANTCOMMIT_HEAD) - self.errordetail() - print(CANTCOMMIT_TAIL) - - def errordetail(self): - if PASSWORD and self.ui.password != PASSWORD: - emit(NEED_PASSWD) - if not self.ui.log: - emit(NEED_LOG) - if not self.ui.author: - emit(NEED_AUTHOR) - if not self.ui.email: - emit(NEED_EMAIL) - - def commit(self, entry): - file = entry.file - # Normalize line endings in body - if '\r' in self.ui.body: - self.ui.body = re.sub('\r\n?', '\n', self.ui.body) - # Normalize whitespace in title - self.ui.title = ' '.join(self.ui.title.split()) - # Check that there were any changes - if self.ui.body == entry.body and self.ui.title == entry.title: - self.error("You didn't make any changes!") - return - - # need to lock here because otherwise the file exists and is not writable (on NT) - command = interpolate(SH_LOCK, file=file) - p = os.popen(command) - output = p.read() - - try: - os.unlink(file) - except os.error: - pass - try: - f = open(file, 'w') - except IOError as why: - self.error(CANTWRITE, file=file, why=why) - return - date = time.ctime(now) - emit(FILEHEADER, self.ui, os.environ, date=date, _file=f, _quote=0) - f.write('\n') - f.write(self.ui.body) - f.write('\n') - f.close() - - import tempfile - tf = tempfile.NamedTemporaryFile() - emit(LOGHEADER, self.ui, os.environ, date=date, _file=tf) - tf.flush() - tf.seek(0) - - command = interpolate(SH_CHECKIN, file=file, tfn=tf.name) - log("\n\n" + command) - p = os.popen(command) - output = p.read() - sts = p.close() - log("output: " + output) - log("done: " + str(sts)) - log("TempFile:\n" + tf.read() + "end") - - if not sts: - self.prologue(T_COMMITTED) - emit(COMMITTED) - else: - self.error(T_COMMITFAILED) - emit(COMMITFAILED, sts=sts) - print('<PRE>%s</PRE>' % escape(output)) - - try: - os.unlink(tf.name) - except os.error: - pass - - entry = self.dir.open(file) - entry.show() - -wiz = FaqWizard() -wiz.go() diff --git a/Tools/faqwiz/move-faqwiz.sh b/Tools/faqwiz/move-faqwiz.sh deleted file mode 100755 index b3bcc92..0000000 --- a/Tools/faqwiz/move-faqwiz.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -# -# Christian Reis <kiko@async.com.br> -# -# Moves -# -# Example: -# -# blackjesus:~> ./move-faqwiz.sh 2\.1 3\.2 -# Moving FAQ question 02.001 to 03.002 - -if [ x$2 = x ]; then - echo "Need 2 args: original_version final_version." - exit 2 -fi - -if [ ! -d data -o ! -d data/RCS ]; then - echo "Run this inside the faqwiz data/ directory's parent dir." - exit 2 -fi - -cut_n_pad() { - t=`echo $1 | cut -d. -f $2` - export $3=`echo $t | awk "{ tmp = \\$0; l = length(tmp); for (i = 0; i < $2-l+1; i++) { tmp = "0".tmp } print tmp }"` -} - -cut_n_pad $1 1 prefix1 -cut_n_pad $1 2 suffix1 -cut_n_pad $2 1 prefix2 -cut_n_pad $2 2 suffix2 -if which tempfile >/dev/null; then - tmpfile=$(tempfile -d .) -elif [ -n "$RANDOM" ]; then - tmpfile=tmp$RANDOM.tmp -else - tmpfile=tmp$$.tmp -fi -file1=faq$prefix1.$suffix1.htp -file2=faq$prefix2.$suffix2.htp - -echo "Moving FAQ question $prefix1.$suffix1 to $prefix2.$suffix2" - -sed -e "s/$1\./$2\./g" data/$file1 > ${tmpfile}1 -sed -e "s/$1\./$2\./g" data/RCS/$file1,v > ${tmpfile}2 - -if [ -f data/$file2 ]; then - echo "Target FAQ exists. Won't clobber." - exit 2 -fi - -mv ${tmpfile}1 data/$file2 -mv ${tmpfile}2 data/RCS/$file2,v -mv data/$file1 data/$file1.orig -mv data/RCS/$file1,v data/RCS/$file1,v.orig - |