import make_schema_doc import schema_remarks import cgi import os import re import string import StringIO import sys import time import urllib # 1. GENERIC CGI SUPPORT FOR RAVENBROOK # # Adapted from issue.cgi. # # Objects belonging to the webpage class output a web page when their # print_page() method is called. Methods starting print_ actually print # to stdout whereas methods starting prepare_ only prepare some portion # of the output. Subclasses of webpage should override the # prepare_body() method so that it constructs an appropriate body for # the web page (by making a series of calls to the b() method to # accumulate lines of body text). # # The reason for constructing the whole body before printing anything is # so that errors can be handled simply gracefully. A method that # encounters an error should call # # raise error, (status, status_message, error_message) # # The status and status_message arguments to the error are used to make # the HTTP status header: for example (404, 'Not found') or (400, # 'Missing form parameter'). See [RFC 2616] for HTTP status codes. The # status_message is also used to make the page's title. The # error_message is used for the body of the page. # # The script supports two behaviours for other errors: # # 1. Leave them uncaught, causing the script to crash and an HTTP error # log entry to be written by Apache. # # 2. Generate an error page as if by raise error, (500, 'Python error', # exc_type + ': ' + exc_value). # # It's a security risk to give out detailed information about errors, so # we use (1) on the public server and (2) on the internal server. # # The directory_links list is used to build the list of links in the # header and footer of the outputted web page in the usual Ravenbrook # format. It is a list of pairs of (directory, description). # # This could be separated out into a module of its own. error = 'error' class webpage: body = '' # Page body body = None # Body of page: list of strings. directory_links = None # Directories to link to in header, footer h1 = None # Top-level header (if None, use title) status = 200 # HTTP status of output status_message = 'OK' # Message to go with the status title = 'Web page' # Page title debug_messages = [] # no debug messages yet! debug_level = 0 # don't accumulate any debug messages def __init__(self): self.body = [] self.title = "Default webpage title" self.debug_messages = [] self.debug_level = 0 self.directory_links = [ ( '', 'Ravenbrook' ), ( 'project', 'Projects' ), ( 'p4dti', 'Perforce Defect Tracking Integration' ), ( 'tool', 'Tools' ), ] # Append a line of HTML to the body of the webpage. def b(self, s): self.body.append(s) # Check that the form parameters are correct. This is a placeholder # that should be overridden in subclasses of webpage. def check_form_parameters(self): pass # Check and obtain the debugging level. This is a placeholder that # should be overridden in subclasses of webpage. def check_debug_level(self): pass # Print an 'Expires' header [RFC 2616, 14.21] specifying that the # page expires at midnight tonight. The reason for expiring the # output is that the same query to this script (e.g., action=list) # may generate different output each time it's called. Pages # lasting for a day means that people won't be misled by a cached # result from a long time ago. The format for the date is specified # in [RFC 822, 5.1] and modified by [RFC 1123, 5.2.14]; a date looks # like "Thu, 01 Dec 1994 16:00:00 GMT". def print_expires(self): print 'Expires:', print time.strftime("%a, %d %b %Y 00:00:00 GMT", time.gmtime(time.time() + 60*60*24)) # Prepare the body of the webpage by making calls to the b() method. # This is a placeholder that should be overridden in subclasses of # webpage. def prepare_body(self): pass # Print the directory links that go at the top and bottom of the # page. def print_directory_links(self): print '

' url = '' separator = '' for dir, name in self.directory_links: url = url + dir + '/' print '%s%s' % (separator, url, name) separator = '/ ' print '

' # Print the start of the webpage: the HTTP headers, the XML # declaration, the XHTML document type, the HTML element, # the directory links and the title. def print_header(self): print 'Status:', self.status, self.status_message print 'Content-Type: text/html' print print '' print ('') print ('') print '' print '', cgi.escape(self.title), '' print '' print ('') print '
' self.print_directory_links() print '
' if self.h1: print '

', self.h1, '

' else: print '

', cgi.escape(self.title), '

' print '
' # Print the coyright message and the license conditions. def print_copyright(self): print ('

This document is copyright © 2001-2004 ' 'Perforce Software, Inc. All rights reserved.

\n') print ('

Redistribution and use of this document in any form, ' 'with or without modification, is permitted provided that ' 'redistributions of this document retain the above copyright ' 'notice, this condition and the following disclaimer.

\n') print('

This document is provided by the copyright ' 'holders and contributors "as is" and any express or implied ' 'warranties, including, but not limited to, the implied warranties ' 'of merchantability and fitness for a particular purpose are ' 'disclaimed. In no event shall the copyright holders and ' 'contributors be liable for any direct, indirect, incidental, ' 'special, exemplary, or consequential damages (including, but ' 'not limited to, procurement of substitute goods or services; ' 'loss of use, data, or profits; or business interruption) ' 'however caused and on any theory of liability, whether in ' 'contract, strict liability, or tort (including negligence or ' 'otherwise) arising in any way out of the use of this document, ' 'even if advised of the possibility of such damage. ' '

\n') def log(self, level, message): if level <= self.debug_level: self.debug_messages.append(message) # Print any accumulated debugging log. def print_debug(self): if self.debug_level > 0: if self.debug_messages: print '

Debugging Log:

' print '' for m in self.debug_messages: print self.format_text(m) print '
' print '
' else: print '

No Debugging Messages

' print '
' # Print the bottom of the webpage: the time the page was generated # (the is important because the contents may depend on the time the # page was created, and if the page is archived or printed readers # will need to know when the contents apply), the script that # generated the page, directory links, and closing tags. def print_footer(self): print '
' self.print_debug() self.print_copyright() print '
' self.print_directory_links() print '
' print '' print '' # Print the page by calling the check_form_parameters and # prepare_body methods, then printing the header, body and footer. # If an error occurs in check_form_parameters or prepare_body, an # error page is produced instead. def print_page(self): try: self.check_debug_level() self.check_form_parameters() self.prepare_body() except: (error_type, error_value, _) = sys.exc_info() if error_type == error: (self.status, self.status_message, error_message) = error_value else: self.status = 500 error_message = '%s: %s' % (error_type, error_value) self.status_message = 'Python error' self.title = self.status_message self.h1 = self.title self.body = ['

%s

' % error_message] self.print_header() for b in self.body: print b self.print_footer() # 2. SCHEMA WEBPAGE CLASS # # This is a base class for all the schema webpage classes in section 3. class schema_webpage(webpage): def __init__(self, form, action): # Call superclass method. webpage.__init__(self) self.action = action self.form = form self.directory_links.append(( 'cgi/bugzilla-schema', 'Bugzilla Schema' )) # Return the form parameter named by parameter, as a string. Return # None if there is no such parameter or if the parameter is the # empty string. def param(self, parameter): if self.form.has_key(parameter): if self.form[parameter].file: self.log(8, "Parameter %s: file type" % parameter) return None v = self.form[parameter].value if v: self.log(8, "Parameter %s: %s." % (parameter, str(v))) return str(v) else: self.log(8, "Parameter %s has no value." % parameter) self.log(6, "No parameter %s." % parameter) return None def check_bugzilla_version(self, param): version = self.param(param) if not version: raise error, (400, 'Bad form parameters', 'No %s parameter.' % param) if not (version in schema_remarks.version_order): raise error, (404, 'No such Bugzilla version', 'No such Bugzilla version: %s.' % version) return version def check_bugzilla_from(self): self.from_version = self.check_bugzilla_version('from') def check_bugzilla_to(self): v = self.check_bugzilla_version('to') if (schema_remarks.version_order.index(v) >= schema_remarks.version_order.index(self.from_version)): self.to_version = v else: self.to_version = self.from_version self.from_version = v def check_bugzilla_single(self): self.version = self.check_bugzilla_version('version') # Get and check the debugging level. def check_debug_level(self): level = self.param('debug') if not level: level = '0' try: debug_level = int(level) except ValueError: raise error, (404, 'Bad debugging level', 'Bad debugging level: %s.' % level) if debug_level < 0: level = 0 self.debug_level = debug_level self.log(10, "Logging at debug level %d." % debug_level) # 3. SCHEMA WEBPAGES # # Each subclass of schema_webpage implements a particular kind of report. class range_webpage(schema_webpage): def check_form_parameters(self): self.check_bugzilla_from() self.check_bugzilla_to() def prepare_body(self): if self.from_version == self.to_version: self.title = ('Bugzilla Schema for Version %s' % self.from_version) else: self.title = ('Bugzilla Schema for Versions %s to %s' % (self.from_version, self.to_version)) self.h1 = self.title self.b(make_schema_doc.make_body(self.from_version, self.to_version)) class single_webpage(schema_webpage): def check_form_parameters(self): self.check_bugzilla_single() def prepare_body(self): self.title = ('Bugzilla Schema for Version %s' % self.version) self.h1 = self.title self.b(make_schema_doc.make_body(self.version, self.version)) class index_webpage(schema_webpage): def prepare_body(self): # Page title. self.title = 'Bugzilla Schema Documentation' self.b('
') self.b('') #### self.b('') #### self.b('
') self.b('
') self.b('') self.b('
Schema for a single version') self.b('') self.b('') self.b('
') self.b('
') self.b('
') self.b('
') self.b('
Schema for a range of versions') self.b('') self.b('') self.b('') self.b('') self.b('
') self.b('
') self.b('
') def options(self, options, selected = None): if selected == None: selected = options[-1] for o in options: l = '%s' % (o, o) self.b(l) # 6. OUTPUT THE PAGE action_class_map = { 'single': single_webpage, 'range': range_webpage, 'index': index_webpage, } def show_page(): form = cgi.FieldStorage() if form.has_key('action'): action = form['action'].value else: action = 'index' if action_class_map.has_key(action): action_class = action_class_map[action] else: action_class = index_webpage action_class(form, action).print_page() # A. REFERENCES # # # B. DOCUMENT HISTORY # # 2004-11-12 NB Adapted from issue.cgi. # # # C. COPYRIGHT AND LICENSE # # This file is copyright (c) 2004 Perforce Software, Inc. All rights # reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH # DAMAGE. # # # $Id: //info.ravenbrook.com/project/p4dti/tool/cgi/bugzilla-schema/index.py#3 $