PATCH: Remove Property gunk from Field

Mark Mitchell mark at codesourcery.com
Wed Jun 18 17:16:10 UTC 2003


I started working on updating and improving the "how to write a test
class" section of the manual.  That means that I need to talk about
'Field' in some detail, and when I looked at fields.py I realized that
there was a lot of complexity in there that we no longer need.

Removed with this patch.

--
Mark Mitchell
CodeSourcery, LLC
mark at codesourcery.com

2003-06-18  Mark Mitchell  <mark at codesourcery.com>

	* GNUmakefile.in (PYTHON_VERSION): Remove.
	(PYTHON_PREFIX): Remove.
	(doc-python): Conditionalize on the avaialability of happydoc.
	* configure.in (PYTHON_VERSION): Don't AC_SUBST it.
	(PYTHON_PREFIX): Likewise.
	* qm/fields.py: Remove all traces of PropertyDeclaration.
	* qm/xmlutil.py (load_xml): Update comments.
	* qm/test/qmtest.py: Try to make sure that gc.collect is called.
	* qm/test/doc/reference.xml: Correct typos.
	* qm/test/share/dtml/show.dtml: Use Field.IsHidden.
	* qm/test/web/web.py (ShowItemPage.FormatFieldValue): Likewise.
	(QMTestServer.HandleRunTests): Tidy.

Index: fields.py
===================================================================
RCS file: /home/sc/Repository/qm/qm/fields.py,v
retrieving revision 1.67
diff -c -5 -p -r1.67 fields.py
*** fields.py	16 Jun 2003 23:45:51 -0000	1.67
--- fields.py	18 Jun 2003 17:10:41 -0000
***************
*** 11,20 ****
--- 11,43 ----
  #
  # For license terms see the file COPYING.
  #
  ########################################################################
  
+ """A 'Field' determines how data is displayed and stored.
+ 
+ A 'Field' is a component of a data structure.  Every 'Field' has a type.
+ For example, an 'IntegerField' stores a signed integer while a
+ 'TextField' stores a string.
+ 
+ The value of a 'Field' can be represented as HTML (for display in the
+ GUI), or as XML (when written to persistent storage).  Every 'Field' can
+ create an HTML form that can be used by the user to update the value of
+ the 'Field'.
+ 
+ Every 'Extension' class has a set of arguments composed of 'Field'.  An
+ instance of that 'Extension' class can be constructed by providing a
+ value for each 'Field' object.  The GUI can display the 'Extension'
+ object by rendering each of the 'Field' values as HTML.  The user can
+ change the value of a 'Field' in the GUI, and then write the 'Extension'
+ object to persistent storage.
+ 
+ Additional derived classes of 'Field' can be created for use in
+ domain-specific situations.  For example, the QMTest 'Test' class
+ defines a derived class which allows the user to select from among a set
+ of test names."""
+ 
  ########################################################################
  # imports
  ########################################################################
  
  import attachment
*************** import user
*** 37,54 ****
  import web
  import xml.dom
  import xmlutil
  
  ########################################################################
- # constants
- ########################################################################
- 
- query_field_property_prefix = "_prop_"
- # The prefix for names of query fields for field properties in web
- # requests. 
- 
- ########################################################################
  # exceptions
  ########################################################################
  
  class DomNodeError(Exception):
      """An error extracting a field value from an XML DOM node.
--- 60,69 ----
*************** class DomNodeError(Exception):
*** 61,302 ****
  
  ########################################################################
  # classes
  ########################################################################
  
! class PropertyDeclaration:
!     """A declaration of a property.
! 
!     A 'PropertyDeclaration' is used to declare a property, which
!     consists of a string name and a string value, and provide auxiliary
!     information, including a user-friendly description and a default
!     value.
! 
!     The name of a property is composed of lower-case letters, digits,
!     and underscores.  Properties are string-valued, but there are no
!     typographic restriction on the value."""
! 
!     def __init__(self, name, description, default_value):
!         """Declare a field property.
! 
!         'name' -- The property name.
! 
!         'description' -- A user-friendly description, in structured text
!         format, of the property.
! 
!         'default_value' -- The default value for this property."""
! 
!         self.name = name
!         self.description = description
!         self.default_value = default_value
! 
! 
! 
! class FieldEditPage(web.DtmlPage):
!     """DTML page for editing a field.
! 
!     The DTML template 'field.dtml' is used to generate a page for
!     displaying and editing the configuration of a field.  The field's
!     type and name may not be modified, but its other properties may.
! 
!     See 'Field.GenerateEditWebPage'."""
! 
!     def __init__(self, field, server, submit_request):
!         """Create a new page info object.
  
!         'server' -- The 'WebServer' in use.
! 
!         'field' -- The field being edited.
! 
!         'submit_request' -- A 'WebRequest' object to which the field
!         edit form is submitted."""
!         
!         # Initialize the base class.
!         web.DtmlPage.__init__(self, "field.dtml")
!         # Store properties for later.
!         self.field = field
!         self.__server = server
!         self.property_controls = field.MakePropertyControls(server)
!         self.submit_request = submit_request
! 
! 
!     def MakeExtraPropertyInputs(self):
!         """Construct form inputs for arbitrary field properties.
! 
!         These inputs are used for displaying and editing properties
!         other than the standard properties understood by a field.  Any
!         properties for which there are controls included in the field's
!         'MakePropertyControls' are omitted from this control."""
! 
!         # Construct a map from property names to corresponding values.
!         # Only include properties for which there is no control in
!         # 'property_controls'. 
!         properties = {}
!         for name in self.field.GetPropertyNames():
!             if not self.property_controls.has_key(name):
!                 value = self.field.GetProperty(name)
!                 properties[name] = value
!         # Generate the inputs.
!         return qm.web.make_properties_control(form_name="form",
!                                               field_name="extra_properties",
!                                               properties=properties)
! 
! 
!     def GetFieldType(self, field):
!         """Return the class name of this field."""
!         
!         if isinstance(field, SetField):
!             return "<tt>%s</tt> of <tt>%s</tt>" \
!                    % (field.__class__, field.GetContainedField().__class__)
!         else:
!             return "<tt>%s</tt>" % field.__class__
! 
! 
!     def GetDocString(self, field):
!         """Return the doc string for 'field', formatted as HTML."""
! 
!         doc_string = field.__class__.__doc__
!         if doc_string is None:
!             return " "
!         else:
!             return qm.web.format_structured_text(doc_string)
! 
! 
!     def MakeDefaultValueControl(self, field):
!         """Return a control for editing the default value of a field."""
! 
!         if field.IsProperty("read_only"):
!             style = "full"
!         else:
!             style = "new"
!         default_value = field.GetDefaultValue()
!         return field.FormatValueAsHtml(self.__server,
!                                        default_value, style,
!                                        name="_default_value")
  
  
  
! class Field:
!     """Base class for field types."""
! 
!     property_declarations = [
!         PropertyDeclaration(
!             name="name",
!             description="""The internal name for this field. QM uses
!             this name to identify the field. This name is also used when
!             referring to the field in Python expressions.""",
!             default_value=""
!             ),
! 
!         PropertyDeclaration(
!             name="title",
!             description="""The name displayed for this field in user
!             interfaces.""",
!             default_value=""
!             ),
! 
!         PropertyDeclaration(
!             name="description",
!             description="A description of this field's role or purpose.",
!             default_value=""
!             ),
! 
!         PropertyDeclaration(
!             name="hidden",
!             description="""If true, the field is for internal purposes,
!             and not shown in user interfaces.""",
!             default_value="false"
!             ),
! 
!         PropertyDeclaration(
!             name="read_only",
!             description="If true, the field may not be modified by users.",
!             default_value="false"
!             ),
! 
!         PropertyDeclaration(
!             name="computed",
!             description="""If true, the field is computed automatically.
!             All computed fields are implicitly hidden, and implicitly
!             readonly.""",
!             default_value="false"
!             ),
!         ]
  
  
!     form_field_prefix = "_field_"
!     
  
!     def __init__(self, name, default_value, **properties):
!         """Create a new (generic) field.
  
!         'name' -- The value of the name property.  Must be a valid
!         label.
  
!         'default_value' -- The default value for this field.
  
!         'properties' -- A mapping of additional property assignments
!         to set."""
  
!         self.__properties = {}
!         # Initialize declared properties to their default values.
!         for declaration in self.property_declarations:
!             self.__properties[declaration.name] = \
!                 declaration.default_value
!         # Set the name.
!         self.__properties["name"] = name
          # Use the name as the title, if no other was specified.
!         if not properties.has_key("title"):
!             self.__properties["title"] = name
!         # Make sure that all properties provided are actually declared.
!         # Otherwise, typos in extension classes where the wrong
!         # properties are set are hard to debug.  This is handled by an
!         # exception, rather than an assert, because asserts are only
!         # visible when running Python in debug mode.  We want to make
!         # sure that these errors are always visible to extension class
!         # programmers.
!         declared_property_names = map(lambda pd: pd.name,
!                                       self.property_declarations)
!         for k in properties.keys():
!             if k not in declared_property_names:
!                 raise qm.common.QMException, \
!                       qm.error("unexpected extension argument",
!                                name = k,
!                                class_name = self.__class__)
!         # Update any additional properties provided explicitly.
!         self.__properties.update(properties)
!         self.SetDefaultValue(default_value)
  
          # All computed fields are also read-only and hidden.
          if (self.IsComputed()):
!             self.__properties.update({"read_only" : "true",
!                                       "hidden" : "true"})
  
  
      def __repr__(self):
          return "<%s %s>" % (self.__class__, self.GetName())
  
  
      def GetName(self):
          """Return the name of the field."""
  
!         return self.GetProperty("name")
  
  
      def GetTitle(self):
          """Return the user-friendly title of the field."""
  
!         return self.GetProperty("title")
  
  
      def GetDescription(self):
          """Return a description of this field.
  
          This description is used when displaying detailed help
          information about the field."""
  
!         return self.GetProperty("description")
  
  
      def GetBriefDescription(self):
          """Return a brief description of this field.
  
--- 76,165 ----
  
  ########################################################################
  # classes
  ########################################################################
  
! class Field(object):
!     """A 'Field' is a named, typed component of a data structure."""
  
!     form_field_prefix = "_field_"
!     
  
+     def __init__(self,
+                  name,
+                  default_value,
+                  title = "",
+                  description = "",
+                  hidden = "false",
+                  read_only = "false",
+                  computed = "false"):
+         """Create a new (generic) field.
  
+         'name' -- The name of the field.
  
!         'default_value' -- The default value for this field.
  
+         'title' -- The name given this field when it is displayed in
+         user interfaces.
  
!         'description' -- A description of this field's role or purpose.
  
!         'hidden' -- If true, this field is for internal puprpose only
!         and is not shown in user interfaces.
  
!         'read_only' -- If true, this field may not be modified by users.
  
!         'computed' -- If true, this field is computed automatically.
!         All computed fields are implicitly hidden and implicitly
!         read-only.
  
!         The boolean parameters (such as 'hidden') use the convention
!         that true is represented by the string '"true"'; any other value
!         is false.  This convention is a historical artifact."""
  
!         self.__name = name
          # Use the name as the title, if no other was specified.
!         if not title:
!             self.__title = name
!         else:
!             self.__title = title
!         self.__description = description
!         self.__hidden = hidden == "true"
!         self.__read_only = read_only == "true"
!         self.__computed = computed == "true"
  
          # All computed fields are also read-only and hidden.
          if (self.IsComputed()):
!             self.__read_only = 1
!             self.__hidden = 1
! 
!         self.default_value = default_value
  
  
      def __repr__(self):
          return "<%s %s>" % (self.__class__, self.GetName())
  
  
      def GetName(self):
          """Return the name of the field."""
  
!         return self.__name
  
  
      def GetTitle(self):
          """Return the user-friendly title of the field."""
  
!         return self.__title
  
  
      def GetDescription(self):
          """Return a description of this field.
  
          This description is used when displaying detailed help
          information about the field."""
  
!         return self.__description
  
  
      def GetBriefDescription(self):
          """Return a brief description of this field.
  
*************** class Field:
*** 307,388 ****
          description = self.GetDescription()
          # Return the first paragraph.
          return structured_text.get_first(description)
  
          
-     def GetTypeDescription(self):
-         """Return a structured text description of valid values."""
- 
-         raise NotImplementedError
- 
- 
-     def SetDefaultValue(self, value):
-         """Make 'value' the default value for this field."""
- 
-         self.default_value = value
- 
- 
      def GetDefaultValue(self):
          """Return the default value for this field."""
  
          return common.copy(self.default_value)
  
  
-     def GetProperty(self, property_name, default_value=None):
-         """Return the value of a property.
- 
-         Return the value of the property named by 'property_name'.
-         If that property is not set, return 'default_value'."""
- 
-         if self.__properties.has_key(property_name):
-             return self.__properties[property_name]
-         else:
-             if default_value is None:
-                 for declaration in self.property_declarations:
-                     if declaration.name == property_name:
-                         return declaration.default_value
-                 raise qm.common.QMException, \
-                       "no default value for %s" % property_name
-             else:
-                 return default_value
- 
- 
-     def IsProperty(self, property_name):
-         """Return a true value if a property has the value "true"."""
- 
-         return self.GetProperty(property_name, "false") == "true"
- 
- 
-     def GetPropertyNames(self):
-         """Return a sequence of names of properties defined for this field."""
- 
-         return self.__properties.keys()
- 
- 
-     def SetProperty(self, property_name, value):
-         """Set the value of a property."""
- 
-         self.__properties[property_name] = value
- 
- 
-     def SetProperties(self, properties):
-         """Set the value of several properties.
- 
-         'properties' -- A map from property names to values."""
- 
-         self.__properties.update(properties)
- 
- 
-     def UnsetProperty(self, property_name):
-         """Remove a property.
- 
-         If there is no property named 'property_name', does nothing."""
- 
-         if self.__properties.has_key(property_name):
-             del self.__properties[property_name]
- 
- 
      def Validate(self, value):
          """Validate a field value.
  
          For an acceptable type and value, return the representation of
          'value' in the underlying field storage.
--- 170,185 ----
*************** class Field:
*** 397,416 ****
          Implementations of this method must be idempotent."""
  
          raise NotImplementedError
  
  
-     def CompareValues(self, value1, value2):
-         """Return a comparison of two values of this field.
- 
-         returns -- A comparison value, with the same interpretation as
-         values of 'cmp'."""
- 
-         # In absence of a better comparison, use the Python built-in.
-         return cmp(value1, value2)
- 
- 
      def GetHtmlFormFieldName(self):
          """Return the form field name corresponding this field.
  
          returns -- The name that is used for the control representing
          this field in an HTML form."""
--- 194,203 ----
*************** class Field:
*** 572,724 ****
          <hr noshade size="2">
          <p>Refer to this field as <tt>%s</tt> in Python expressions.</p>
          ''' % (self.GetTitle(), description, help, self.GetName(), )
  
  
-     def GenerateEditWebPage(self, server, request, submit_request):
-         """Generate a web page for editing a field.
- 
-         'server' -- The server processing this request.
-         
-         'request' -- The request in response to which this page is being
-         generated.
- 
-         'submit_request' -- The 'WebRequest' to which the field edit
-         form should be submitted.
- 
-         The 'UpdateFromRequest' method should generally be used to
-         process the submission request."""
- 
-         return FieldEditPage(self, server, submit_request)(request)
- 
- 
      def IsComputed(self):
          """Returns true if this field is computed automatically.
  
          returns -- True if this field is computed automatically.  A
          computed field is never displayed to users and is not stored
          should not be stored; the class containing the field is
          responsible for recomputing it as necessary."""
  
!         return self.IsProperty("computed")
!         
!         
!     def _MakeTextPropertyControl(self, property_name):
!         """Generate HTML inputs for a text-valued property.
! 
!         'property_name' -- The name of the property.
! 
!         returns -- HTML text for form inputs suitable for use in
!         'MakePropertyControls'."""
  
-         return '<input type="text" name="%s%s" size="40" value="%s"/>' \
-                % (query_field_property_prefix, property_name,
-                   self.GetProperty(property_name))
  
  
!     def _MakeBooleanPropertyControl(self, property_name):
!         """Generate HTML inputs for a boolean-valued property.
  
!         'property_name' -- The name of the property.
  
-         returns -- HTML text for form inputs suitable for use in
-         'MakePropertyControls'."""
  
!         property_value = self.GetProperty(property_name)
!         assert property_value in ["true", "false"]
!         if property_value == "true":
!             true_checked = "checked"
!             false_checked = ""
!         else:
!             true_checked = ""
!             false_checked = "checked"
!         return '''
!         <input type="radio" name="%s%s" value="true" %s /> true
!            
!         <input type="radio" name="%s%s" value="false" %s /> false
!         ''' % (query_field_property_prefix, property_name, true_checked,
!                query_field_property_prefix, property_name, false_checked)
  
  
!     def MakePropertyControls(self, server):
!         """Return controls for editing the properties of this field.
! 
!         'server' -- The 'WebServer' in use.
!         
!         returns -- A map from property names to strings containing HTML
!         source for Web form controls for editing the corresponding
!         property.
! 
!         Not all properties understood by the field need be included in
!         the map.
! 
!         The names of the form inputs for property values should
!         generally be of the form '"%s%s" % (query_field_property_prefix,
!         field_name)'.
! 
!         A subclass which override this method should include map entries
!         added by its base class version in its own return value."""
! 
!         return {
!             "title":
!                 self._MakeTextPropertyControl("title"),
! 
!             # The name is made read-only here.  It should not be changed
!             # after the field has been created, to preserve referential
!             # integrity. 
!             "name":
!                 self.GetProperty("name"),
! 
!             "description":
!                 '''<textarea name="%sdescription"
!                              cols="40"
!                              rows="8">%s</textarea>'''
!                 % (query_field_property_prefix, self.GetDescription()),
! 
!             "hidden":
!                 self._MakeBooleanPropertyControl("hidden"),
! 
!             "read_only":
!                 self._MakeBooleanPropertyControl("read_only"),
! 
!             }
! 
! 
!     def MakeDomNode(self, document):
!         """Construct a DOM element node describing this field.
! 
!         'document' -- A DOM document object in which to create the node.
! 
!         returns -- A DOM node for a "field" element."""
! 
!         # Construct the main element node.
!         element = document.createElement("field")
!         # Store the field name as an attribute.
!         element.setAttribute("name", self.GetName())
!         # Store the Python class name of this field.
!         class_element = xmlutil.create_dom_text_element(
!             document, "class", self.__class__.__name__)
!         element.appendChild(class_element)
!         # Store the default value.
!         default_value = self.GetDefaultValue()
!         default_value_element = \
!             self.MakeDomNodeForValue(default_value, document)
!         default_element = document.createElement("default-value")
!         default_element.appendChild(default_value_element)
!         element.appendChild(default_element)
! 
!         # Create an element for each property.
!         for name, value in self.__properties.items():
!             if name == "name":
!                 continue
!             property_element = xmlutil.create_dom_text_element(
!                 document, "property", str(value))
!             property_element.setAttribute("name", name)
!             element.appendChild(property_element)
! 
!         return element
! 
  
  
  ########################################################################
  
  class IntegerField(Field):
--- 359,395 ----
          <hr noshade size="2">
          <p>Refer to this field as <tt>%s</tt> in Python expressions.</p>
          ''' % (self.GetTitle(), description, help, self.GetName(), )
  
  
      def IsComputed(self):
          """Returns true if this field is computed automatically.
  
          returns -- True if this field is computed automatically.  A
          computed field is never displayed to users and is not stored
          should not be stored; the class containing the field is
          responsible for recomputing it as necessary."""
  
!         return self.__computed
  
  
+     def IsHidden(self):
+         """Returns true if this 'Field' should be hidden from users.
  
!         returns -- True if this 'Field' should be hidden from users.
!         The value of a hidden field is not displayed in the GUI."""
  
!         return self.__hidden
  
  
!     def IsReadOnly(self):
!         """Returns true if this 'Field' cannot be modified by users.
  
+         returns -- True if this 'Field' cannot be modified by users.
+         The GUI does not allow users to modify a read-only field."""
  
!         return self.__read_only
  
  
  ########################################################################
  
  class IntegerField(Field):
*************** class IntegerField(Field):
*** 734,747 ****
  
          # Perform base class initialization.
          apply(Field.__init__, (self, name, default_value), properties)
  
  
-     def GetTypeDescription(self):
-         return "an integer"
- 
- 
      def Validate(self, value):
          return int(value)
  
  
      def FormatValueAsText(self, value, columns=72):
--- 405,414 ----
*************** class IntegerField(Field):
*** 813,901 ****
  ########################################################################
  
  class TextField(Field):
      """A field that contains text."""
  
!     property_declarations = Field.property_declarations + [
!         PropertyDeclaration(
!             name="multiline",
!             description="""If false, a value for this field is a single
!             line of text.  If true, multi-line text is allowed.""",
!             default_value="false"
!             ),
! 
!         PropertyDeclaration(
!             name="structured",
!             description="""If true, the field contains structured
!             text.""",
!             default_value="false"
!             ),
! 
!         PropertyDeclaration(
!             name="verbatim",
!             description="""If true, the contents of the field are
!             treated as preformatted text.""",
!             default_value="false"
!             ),
! 
!         PropertyDeclaration(
!             name="not_empty_text",
!             description="""The value of this field is considered invalid
!             if it empty or composed only of whitespace.""",
!             default_value="false"
!             ),
! 
!         ]
  
  
!     def __init__(self, name, default_value="", **properties):
!         """Create a text field."""
  
!         # Perform base class initialization.
!         apply(Field.__init__, (self, name, default_value), properties)
  
  
!     def CompareValues(self, value1, value2):
!         # First, compare strings case-insensitively.
!         comparison = cmp(string.lower(value1), string.lower(value2))
!         if comparison == 0:
!             # If the strings are the same ignoring case, re-compare them
!             # taking case into account.
!             return cmp(value1, value2)
!         else:
!             return comparison
  
  
-     def GetTypeDescription(self):
-         return "a string"
-     
  
      def Validate(self, value):
          # Be forgiving, and try to convert 'value' to a string if it
          # isn't one.
          value = str(value)
          # Clean up unless it's a verbatim string.
!         if not self.IsProperty("verbatim"):
              # Remove leading whitespace.
              value = string.lstrip(value)
          # If this field has the not_empty_text property set, make sure the
          # value complies.
!         if self.IsProperty("not_empty_text") and value == "":
              raise ValueError, \
                    qm.error("empty text field value",
                             field_title=self.GetTitle()) 
          # If this is not a multi-line text field, remove line breaks
          # (and surrounding whitespace).
!         if not self.IsProperty("multiline"):
              value = re.sub(" *\n+ *", " ", value)
          return value
  
  
      def FormatValueAsText(self, value, columns=72):
!         if self.IsProperty("structured"):
              return structured_text.to_text(value, width=columns)
!         elif self.IsProperty("verbatim"):
              return value
          else:
              return common.wrap_lines(value, columns)
      
  
--- 480,547 ----
  ########################################################################
  
  class TextField(Field):
      """A field that contains text."""
  
!     def __init__(self,
!                  name,
!                  default_value = "",
!                  multiline = "false",
!                  structured = "false",
!                  verbatim = "false",
!                  not_empty_text = "false",
!                  **properties):
!         """Construct a new 'TextField'.
  
+         'multiline' -- If false, a value for this field is a single line
+         of text.  If true, multi-line text is allowed.
  
!         'structured' -- If true, the field contains structured text.
  
!         'verbatim' -- If true, the contents of the field are treated as
!         preformatted text.
!             
!         'not_empty_text' -- The value of this field is considered
!         invalid if it empty or composed only of whitespace.
  
+         'properties' -- A dictionary of other keyword arguments which
+         are provided to the base class constructor."""
  
!         # Initialize the base class.
!         super(TextField, self).__init__(name, default_value, **properties)
  
+         self.__multiline = multiline == "true"
+         self.__structured = structured == "true"
+         self.__verbatim = verbatim == "true"
+         self.__not_empty_text = not_empty_text == "true"
  
  
      def Validate(self, value):
          # Be forgiving, and try to convert 'value' to a string if it
          # isn't one.
          value = str(value)
          # Clean up unless it's a verbatim string.
!         if not self.__verbatim:
              # Remove leading whitespace.
              value = string.lstrip(value)
          # If this field has the not_empty_text property set, make sure the
          # value complies.
!         if self.__not_empty_text and value == "":
              raise ValueError, \
                    qm.error("empty text field value",
                             field_title=self.GetTitle()) 
          # If this is not a multi-line text field, remove line breaks
          # (and surrounding whitespace).
!         if not self.__multiline:
              value = re.sub(" *\n+ *", " ", value)
          return value
  
  
      def FormatValueAsText(self, value, columns=72):
!         if self.__structured:
              return structured_text.to_text(value, width=columns)
!         elif self.__verbatim:
              return value
          else:
              return common.wrap_lines(value, columns)
      
  
*************** class TextField(Field):
*** 908,928 ****
          # Use the default field form field name if requested.
          if name is None:
              name = self.GetHtmlFormFieldName()
  
          if style == "new" or style == "edit":
!             if self.IsProperty("multiline"):
                  result = '<textarea cols="64" rows="8" name="%s">' \
                           '%s</textarea>' \
                           % (name, web.escape(value))
              else:
                  result = \
                      '<input type="text" size="40" name="%s" value="%s"/>' \
                      % (name, web.escape(value))
              # If this is a structured text field, add a note to that
              # effect, so users aren't surprised.
!             if self.IsProperty("structured"):
                  result = result \
                  + '<br><font size="-1">This is a ' \
                  + qm.web.make_help_link_html(
                      qm.structured_text.html_help_text,
                      "structured text") \
--- 554,574 ----
          # Use the default field form field name if requested.
          if name is None:
              name = self.GetHtmlFormFieldName()
  
          if style == "new" or style == "edit":
!             if self.__multiline:
                  result = '<textarea cols="64" rows="8" name="%s">' \
                           '%s</textarea>' \
                           % (name, web.escape(value))
              else:
                  result = \
                      '<input type="text" size="40" name="%s" value="%s"/>' \
                      % (name, web.escape(value))
              # If this is a structured text field, add a note to that
              # effect, so users aren't surprised.
!             if self.__structured:
                  result = result \
                  + '<br><font size="-1">This is a ' \
                  + qm.web.make_help_link_html(
                      qm.structured_text.html_help_text,
                      "structured text") \
*************** class TextField(Field):
*** 932,942 ****
          elif style == "hidden":
              return '<input type="hidden" name="%s" value="%s"/>' \
                     % (name, web.escape(value))            
  
          elif style == "brief":
!             if self.IsProperty("structured"):
                  # Use only the first line of text.
                  value = string.split(value, "\n", 1)
                  value = web.format_structured_text(value[0])
              else:
                  # Replace all whitespace with ordinary space.
--- 578,588 ----
          elif style == "hidden":
              return '<input type="hidden" name="%s" value="%s"/>' \
                     % (name, web.escape(value))            
  
          elif style == "brief":
!             if self.__structured:
                  # Use only the first line of text.
                  value = string.split(value, "\n", 1)
                  value = web.format_structured_text(value[0])
              else:
                  # Replace all whitespace with ordinary space.
*************** class TextField(Field):
*** 944,965 ****
  
              # Truncate to 80 characters, if it's longer.
              if len(value) > 80:
                  value = value[:80] + "..."
  
!             if self.IsProperty("verbatim"):
                  # Put verbatim text in a <tt> element.
                  return '<tt>%s</tt>' % web.escape(value)
!             elif self.IsProperty("structured"):
                  # It's already formatted as HTML; don't escape it.
                  return value
              else:
                  # Other text set normally.
                  return web.escape(value)
  
          elif style == "full":
!             if self.IsProperty("verbatim"):
                  # Wrap lines before escaping special characters for
                  # HTML.  Use a special tag to indicate line breaks.  If
                  # we were to escape first, line lengths would be
                  # computed using escape codes rather than visual
                  # characters. 
--- 590,611 ----
  
              # Truncate to 80 characters, if it's longer.
              if len(value) > 80:
                  value = value[:80] + "..."
  
!             if self.__verbatim:
                  # Put verbatim text in a <tt> element.
                  return '<tt>%s</tt>' % web.escape(value)
!             elif self.__structured:
                  # It's already formatted as HTML; don't escape it.
                  return value
              else:
                  # Other text set normally.
                  return web.escape(value)
  
          elif style == "full":
!             if self.__verbatim:
                  # Wrap lines before escaping special characters for
                  # HTML.  Use a special tag to indicate line breaks.  If
                  # we were to escape first, line lengths would be
                  # computed using escape codes rather than visual
                  # characters. 
*************** class TextField(Field):
*** 972,982 ****
                  # the break.
                  value = string.replace(value,
                                         break_delimiter, r"<blink>\</blink>")
                  # Place verbatim text in a <pre> element.
                  return '<pre>%s</pre>' % value
!             elif self.IsProperty("structured"):
                  return web.format_structured_text(value)
              else:
                  if value == "":
                      # Browsers don't deal nicely with empty table cells,
                      # so put an extra space here.
--- 618,628 ----
                  # the break.
                  value = string.replace(value,
                                         break_delimiter, r"<blink>\</blink>")
                  # Place verbatim text in a <pre> element.
                  return '<pre>%s</pre>' % value
!             elif self.__structured:
                  return web.format_structured_text(value)
              else:
                  if value == "":
                      # Browsers don't deal nicely with empty table cells,
                      # so put an extra space here.
*************** class TextField(Field):
*** 1017,1070 ****
  
  
      def GetHelp(self):
          help = """
              A text field.  """
!         if self.IsProperty("structured"):
              help = help + '''
              The text is interpreted as structured text, and formatted
              appropriately for the output device.  See "Structured Text
              Formatting
              Rules":http://www.python.org/sigs/doc-sig/stext.html for
              more information.  '''
!         elif self.IsProperty("verbatim"):
              help = help + """
              The text is stored verbatim; whitespace and indentation are
              preserved.  """
!         if self.IsProperty("not_empty_text"):
              help = help + """
              This field may not be empty.  """
          help = help + """
              The default value of this field is "%s".
              """ % self.GetDefaultValue()
          return help
  
  
-     def MakePropertyControls(self, server):
- 
-         # Start the with the base controls.
-         controls = Field.MakePropertyControls(self, server)
-         # Add controls for our own properties.
-         controls.update({
-             "multiline":
-                 self._MakeBooleanPropertyControl("multiline"),
- 
-             "structured":
-                 self._MakeBooleanPropertyControl("structured"),
- 
-             "verbatim":
-                 self._MakeBooleanPropertyControl("verbatim"),
- 
-             "not_empty_text":
-                 self._MakeBooleanPropertyControl("not_empty_text"),
- 
-             })
- 
-         return controls
- 
- 
- 
  ########################################################################
  
  class TupleField(Field):
      """A 'TupleField' contains zero or more other 'Field's.
  
--- 663,692 ----
  
  
      def GetHelp(self):
          help = """
              A text field.  """
!         if self.__structured:
              help = help + '''
              The text is interpreted as structured text, and formatted
              appropriately for the output device.  See "Structured Text
              Formatting
              Rules":http://www.python.org/sigs/doc-sig/stext.html for
              more information.  '''
!         elif self.__verbatim:
              help = help + """
              The text is stored verbatim; whitespace and indentation are
              preserved.  """
!         if self.__not_empty_text:
              help = help + """
              This field may not be empty.  """
          help = help + """
              The default value of this field is "%s".
              """ % self.GetDefaultValue()
          return help
  
  
  ########################################################################
  
  class TupleField(Field):
      """A 'TupleField' contains zero or more other 'Field's.
  
*************** class SetField(Field):
*** 1161,1231 ****
      All contents must be of the same field type.  A set field may not
      contain sets.
  
      The default field value is set to an empty set."""
  
!     set_property_declarations = [
!         PropertyDeclaration(
!             name="not_empty_set",
!             description="""If true, this field may not be empty,
!             i.e. the value of this field must contain at least one
!             element.""",
!             default_value="false"
!             ),
!         
!         ]
! 
!     def __init__(self, contained):
          """Create a set field.
  
          The name of the contained field is taken as the name of this
          field.
  
          'contained' -- An 'Field' instance describing the
!         elements of the set. 
  
          raises -- 'ValueError' if 'contained' is a set field.
  
          raises -- 'TypeError' if 'contained' is not a 'Field'."""
  
          # A set field may not contain a set field.
          if isinstance(contained, SetField):
              raise ValueError, \
                    "A set field may not contain a set field."
          if not isinstance(contained, Field):
              raise TypeError, "A set must contain another field."
-         # Use the properties from the contained field, rather than
-         # making a different set.
-         self._Field__properties = contained._Field__properties
          # Remeber the contained field type.
          self.__contained = contained
!         # Masquerade property declarations as for contained field.
!         self.property_declarations = contained.property_declarations \
!                                      + self.set_property_declarations
!         # Set the default field value to the empty set.
!         self.SetDefaultValue([])
! 
! 
!     def CompareValues(self, value1, value2):
!         # Sort set values by length.
!         comparison = cmp(len(value1), len(value2))
!         # If they're the same length, compare the contents themselves.
!         if comparison == 0:
!             return cmp(value1, value2)
!         else:
!             return comparison
  
  
-     def GetTypeDescription(self):
-         return "a sequence; each element is %s" \
-                % self.GetContainedField().GetTypeDescription()
-     
- 
      def Validate(self, value):
          # If this field has the not_empty_set property set, make sure
          # the value complies.
!         if self.IsProperty("not_empty_set") and len(value) == 0:
              raise ValueError, \
                    qm.error("empty set field value",
                             field_title=self.GetTitle()) 
          # Assume 'value' is a sequence.  Copy it, simultaneously
          # validating each element in the contained field.
--- 783,825 ----
      All contents must be of the same field type.  A set field may not
      contain sets.
  
      The default field value is set to an empty set."""
  
!     def __init__(self, contained, not_empty_set = "false"):
          """Create a set field.
  
          The name of the contained field is taken as the name of this
          field.
  
          'contained' -- An 'Field' instance describing the
!         elements of the set.
! 
!         'not_empty_set' -- If true, this field may not be empty,
!         i.e. the value of this field must contain at least one element.
  
          raises -- 'ValueError' if 'contained' is a set field.
  
          raises -- 'TypeError' if 'contained' is not a 'Field'."""
  
+         super(SetField, self).__init__(contained.GetName(), [])
+                                        
          # A set field may not contain a set field.
          if isinstance(contained, SetField):
              raise ValueError, \
                    "A set field may not contain a set field."
          if not isinstance(contained, Field):
              raise TypeError, "A set must contain another field."
          # Remeber the contained field type.
          self.__contained = contained
!         self.__not_empty_set = not_empty_set == "true"
  
  
      def Validate(self, value):
          # If this field has the not_empty_set property set, make sure
          # the value complies.
!         if self.__not_empty_set and len(value) == 0:
              raise ValueError, \
                    qm.error("empty set field value",
                             field_title=self.GetTitle()) 
          # Assume 'value' is a sequence.  Copy it, simultaneously
          # validating each element in the contained field.
*************** class SetField(Field):
*** 1432,1466 ****
              the form.   Then, click the <i>Remove</i> button.</p>
              """
          return help
  
  
-     def MakePropertyControls(self, server):
- 
-         # Use property controls for the contained field.
-         controls = self.GetContainedField().MakePropertyControls(server)
-         # Add controls for properties in 'set_property_declarations'.
-         controls["not_empty_set"] = \
-             self._MakeBooleanPropertyControl("not_empty_set")
-         return controls
- 
- 
-     def MakeDomNode(self, document):
-         # Construct the basic 'Field' DOM node.
-         node = Field.MakeDomNode(self, document)
-         # Properties will be specified by the contained field, so remove
-         # them here.
-         for property_node in node.getElementsByTagName("property"):
-             node.removeChild(property_node)
-         # Construct an element for the contained field.
-         contained_node = self.GetContainedField().MakeDomNode(document)
-         node.appendChild(contained_node)
- 
-         return node
-     
- 
- 
  ########################################################################
  
  class UploadAttachmentPage(web.DtmlPage):
      """DTML context for generating upload-attachment.dtml."""
  
--- 1026,1035 ----
*************** class AttachmentField(Field):
*** 1539,1552 ****
  
          # Perform base class initialization. 
          apply(Field.__init__, (self, name, None), properties)
  
  
-     def GetTypeDescription(self):
-         return "an attachment"
- 
- 
      def Validate(self, value):
          # The value should be an instance of 'Attachment', or 'None'.
          if value != None and not isinstance(value, attachment.Attachment):
              raise ValueError, \
                    "the value of an attachment field must be an 'Attachment'"
--- 1108,1117 ----
*************** class EnumerationField(TextField):
*** 1809,1827 ****
      Names are converted to strings, and values are stored as integers.
  
      ordered -- If non-zero, the enumerals are presented to the user
      ordered by value."""
  
-     property_declarations = TextField.property_declarations + [
-         PropertyDeclaration(
-             name="enumerals",
-             description="""The enumerals allowed for this field.
-             Enumerals are presented in the order listed.""",
-             default_value="[]"),
- 
-         ]
- 
      def __init__(self,
                   name,
                   default_value=None,
                   enumerals=[],
                   **properties):
--- 1374,1383 ----
*************** class EnumerationField(TextField):
*** 1839,1863 ****
          # Make sure the default value is legitimate.
          if not default_value in enumerals and len(enumerals) > 0:
              default_value = enumerals[0]
          # Perform base class initialization.
          apply(TextField.__init__, (self, name, default_value), properties)
!         # Set the enumerals.
!         self.SetEnumerals(enumerals)
! 
! 
!     def CompareValues(self, value1, value2):
!         # Sort enumerals by position in the enumeration.
!         enumerals = self.GetEnumerals()
!         if value1 not in enumerals or value2 not in enumerals:
!             return 1
!         return cmp(enumerals.index(value1), enumerals.index(value2))
! 
! 
!     def GetTypeDescription(self):
!         enumerals = self.GetEnumerals()
!         return 'an enumeration of "%s"' % string.join(enumerals, '," "')
  
  
      def Validate(self, value):
          value = str(value)
          enumerals = self.GetEnumerals()
--- 1395,1406 ----
          # Make sure the default value is legitimate.
          if not default_value in enumerals and len(enumerals) > 0:
              default_value = enumerals[0]
          # Perform base class initialization.
          apply(TextField.__init__, (self, name, default_value), properties)
!         # Remember the enumerals.
!         self.__enumerals = string.join(enumerals, ",")
  
  
      def Validate(self, value):
          value = str(value)
          enumerals = self.GetEnumerals()
*************** class EnumerationField(TextField):
*** 1870,1899 ****
                             value=value,
                             field_title=self.GetTitle(),
                             values=string.join(values, ", "))
  
  
-     def SetProperty(self, enumeral_name, value):
-         # Call the base implementation.
-         Field.SetProperty(self, enumeral_name, value)
-             
- 
-     def SetEnumerals(self, enumerals):
-         """Set the list of valid enumerals.
- 
-         'enumerals' -- A list of strings representing enumeral names."""
- 
-         self.SetProperty("enumerals", string.join(enumerals, ","))
- 
- 
      def GetEnumerals(self):
          """Return a sequence of enumerals.
  
          returns -- A sequence consisting of string enumerals objects, in
          the appropriate order."""
  
!         enumerals = self.GetProperty("enumerals")
          if enumerals == "":
              return []
          else:
              return string.split(enumerals, ",")
  
--- 1413,1429 ----
                             value=value,
                             field_title=self.GetTitle(),
                             values=string.join(values, ", "))
  
  
      def GetEnumerals(self):
          """Return a sequence of enumerals.
  
          returns -- A sequence consisting of string enumerals objects, in
          the appropriate order."""
  
!         enumerals = self.__enumerals
          if enumerals == "":
              return []
          else:
              return string.split(enumerals, ",")
  
*************** class EnumerationField(TextField):
*** 1959,2000 ****
          The default value of this field is "%s".
          ''' % str(self.GetDefaultValue())
          return help
  
  
-     def MakePropertyControls(self, server):
- 
-         # Start with controls for base-class properties.
-         controls = TextField.MakePropertyControls(self, server)
-         # These text field controls aren't relevant to enumerations.
-         controls["structured"] = None
-         controls["verbatim"] = None
-         controls["not_empty_text"] = None
- 
-         # Now to add controls for editing the set of available
-         # enumerals.  Construct query field names.
-         field_name = query_field_property_prefix + "enumerals"
-         select_name = "_set_" + field_name
-         # Generate the page for entering a new enumeral name.
-         add_page = web.DtmlPage("add-enumeral.dtml",
-                                 field_name=field_name,
-                                 select_name=select_name)()
-         url = server.CachePage(add_page).AsUrl()
-         # Start with the current set of enumerals.  'make_set_control'
-         # expects pairs of elements.
-         initial_elements = map(lambda e: (e, e), self.GetEnumerals())
-         # Construct the controls.
-         controls["enumerals"] = web.make_set_control(
-             form_name="form",
-             field_name=field_name,
-             add_page=url,
-             initial_elements=initial_elements,
-             ordered=1)
- 
-         return controls
- 
- 
  
  class BooleanField(EnumerationField):
      """A field containing a boolean value.
  
      The enumeration contains two values: true and false."""
--- 1489,1498 ----
*************** class BooleanField(EnumerationField):
*** 2006,2018 ****
                                    ["true", "false"], **properties)
  
          
  
  class ChoiceField(TextField):
!     """An 'ChoiceField' allows choosing one of several values.
  
!     An 'ChoiceField' is similar to an 'EnumerationField' -- but the
      choices for an 'ChoiceField' are computed dynamically, rather than
      chosen statically."""
  
      def FormatValueAsHtml(self, server, value, style, name = None):
  
--- 1504,1516 ----
                                    ["true", "false"], **properties)
  
          
  
  class ChoiceField(TextField):
!     """A 'ChoiceField' allows choosing one of several values.
  
!     A 'ChoiceField' is similar to an 'EnumerationField' -- but the
      choices for an 'ChoiceField' are computed dynamically, rather than
      chosen statically."""
  
      def FormatValueAsHtml(self, server, value, style, name = None):
  
*************** class TimeField(IntegerField):
*** 2061,2078 ****
          The field is given a default value for this field is 'None', which
          corresponds to the current time when the field value is first
          created."""
  
          # Perform base class initalization.
!         apply(IntegerField.__init__, (self, name), properties)
!         # Set the default value.
!         self.default_value = None
! 
! 
!     def GetTypeDescription(self):
!         return "a date/time (right now, it is %s)" \
!                % self.FormatValueAsText(self.GetCurrentTime())
  
  
      def FormatValueAsText(self, value, columns=72):
          if value is None:
              return "now"
--- 1559,1569 ----
          The field is given a default value for this field is 'None', which
          corresponds to the current time when the field value is first
          created."""
  
          # Perform base class initalization.
!         super(TimeField, self).__init__(name, None, **properties)
  
  
      def FormatValueAsText(self, value, columns=72):
          if value is None:
              return "now"
Index: xmlutil.py
===================================================================
RCS file: /home/sc/Repository/qm/qm/xmlutil.py,v
retrieving revision 1.20
diff -c -5 -p -r1.20 xmlutil.py
*** xmlutil.py	6 Nov 2002 19:38:49 -0000	1.20
--- xmlutil.py	18 Jun 2003 17:10:41 -0000
*************** def load_xml_file(path):
*** 42,52 ****
  
  
  def load_xml(file):
      """Return a DOM document loaded from the XML file object 'file'.
  
!     'validate' -- If true, a validating XML parser is used."""
  
      try:
          document = xml.dom.minidom.parse(file)
      finally:
          file.close()
--- 42,57 ----
  
  
  def load_xml(file):
      """Return a DOM document loaded from the XML file object 'file'.
  
!     'file' -- A file object, opened for reading.
!     
!     returns -- The DOM document contained in 'file'.
!     
!     This function closes 'file', whether or not reading the document was
!     successful."""
  
      try:
          document = xml.dom.minidom.parse(file)
      finally:
          file.close()
Index: test/qmtest.py
===================================================================
RCS file: /home/sc/Repository/qm/qm/test/qmtest.py,v
retrieving revision 1.18
diff -c -5 -p -r1.18 qmtest.py
*** test/qmtest.py	16 Jun 2003 23:56:49 -0000	1.18
--- test/qmtest.py	18 Jun 2003 17:10:41 -0000
*************** def main():
*** 94,145 ****
      
  ########################################################################
  # script
  ########################################################################
  
! # Set the program name.
! qm.common.program_name = "QMTest"
  
! # Load messages.
! qm.diagnostic.load_messages("test")
  
! # Load RC options.
! qm.rc.Load("test")
!                                                        
! try:
!     exit_code = main()
! except qm.cmdline.CommandError, msg:
!     print_error_message(msg)
!     sys.stderr.write(
!         "Run 'qmtest --help' to get instructions about how to use QMTest.\n")
!     exit_code = 2
! except qm.common.QMException, msg:
!     print_error_message(msg)
!     exit_code = 1
! except NotImplementedError:
!     exc_info = sys.exc_info()
!     method_name = traceback.extract_tb(exc_info[2])[-1][2]
!     print_error_message(qm.message("not implemented",
!                                    method_name = method_name))
!     sys.stderr.write(qm.common.format_traceback(exc_info))
!     exit_code = 1
! except KeyboardInterrupt:
!     # User killed it; that's OK.
!     sys.stderr.write("\nqmtest: Interrupted.\n")
!     exit_code = 0
! except qm.platform.SignalException, se:
!     # SIGTERM indicates a request to shut down.
!     if se.GetSignalNumber() == signal.SIGTERM:
!         exit_code = 1
!     # Other signals should be handled earlier.
!     else:
!         raise
  
! # Collect garbage so that any "__del__" methods with externally
! # visible side-effects are executed.
! del qm.test.cmdline._the_qmtest
! gc.collect()
  
  # End the program.
  sys.exit(exit_code)
  
  ########################################################################
--- 94,146 ----
      
  ########################################################################
  # script
  ########################################################################
  
! try:
!     # Set the program name.
!     qm.common.program_name = "QMTest"
  
!     # Load messages.
!     qm.diagnostic.load_messages("test")
  
!     # Load RC options.
!     qm.rc.Load("test")
  
!     try:
!         exit_code = main()
!     except qm.cmdline.CommandError, msg:
!         print_error_message(msg)
!         sys.stderr.write(
!             "Run 'qmtest --help' to get instructions about how to use QMTest.\n")
!         exit_code = 2
!     except qm.common.QMException, msg:
!         print_error_message(msg)
!         exit_code = 1
!     except NotImplementedError:
!         exc_info = sys.exc_info()
!         method_name = traceback.extract_tb(exc_info[2])[-1][2]
!         print_error_message(qm.message("not implemented",
!                                        method_name = method_name))
!         sys.stderr.write(qm.common.format_traceback(exc_info))
!         exit_code = 1
!     except KeyboardInterrupt:
!         # User killed it; that's OK.
!         sys.stderr.write("\nqmtest: Interrupted.\n")
!         exit_code = 0
!     except qm.platform.SignalException, se:
!         # SIGTERM indicates a request to shut down.
!         if se.GetSignalNumber() == signal.SIGTERM:
!             exit_code = 1
!         # Other signals should be handled earlier.
!         else:
!             raise
! finally:
!     # Collect garbage so that any "__del__" methods with externally
!     # visible side-effects are executed.
!     del qm.test.cmdline._the_qmtest
!     gc.collect()
  
  # End the program.
  sys.exit(exit_code)
  
  ########################################################################
Index: test/doc/reference.xml
===================================================================
RCS file: /home/sc/Repository/qm/qm/test/doc/reference.xml,v
retrieving revision 1.23
diff -c -5 -p -r1.23 reference.xml
*** test/doc/reference.xml	11 Jun 2003 17:37:04 -0000	1.23
--- test/doc/reference.xml	18 Jun 2003 17:10:43 -0000
***************
*** 233,247 ****
  
     <section id="sec-annotations">
      <title>Annotations</title>
    
      <para>An annotation is a key/value pair.  Both the keys and values
!     must are strings.  When a test (or resource) runs it may add
!     annotations to the result.  These annotations are displayed by
!     &qmtest; and preserved in the results file.  If you write your own
!     test class, you can use annotations to store information that will
!     make your test class more informative.</para>
  
     </section> <!-- sec-annotations -->
  
    </section> <!-- sec-test-results -->
  
--- 233,248 ----
  
     <section id="sec-annotations">
      <title>Annotations</title>
    
      <para>An annotation is a key/value pair.  Both the keys and values
!     are strings.  The value is HTML.  When a test (or resource) runs
!     it may add annotations to the result.  These annotations are
!     displayed by &qmtest; and preserved in the results file.  If you
!     write your own test class, you can use annotations to store
!     information that will make your test class more
!     informative.</para>
  
     </section> <!-- sec-annotations -->
  
    </section> <!-- sec-test-results -->
  
***************
*** 1548,1559 ****
        <glossterm>
         <property>pid-file</property>
        </glossterm>
        <glossdef>
         <para>The default path to use when creating a PID file with the
!        <option>&dashdash;pid-file</option>.  If this entry is not
!        present, an appropriate platform-specific default value is 
         used.</para>
        </glossdef>
       </glossentry>
      </glosslist>
  
--- 1549,1560 ----
        <glossterm>
         <property>pid-file</property>
        </glossterm>
        <glossdef>
         <para>The default path to use when creating a PID file with the
!        <option>&dashdash;pid-file</option> option.  If this entry is
!        not present, an appropriate platform-specific default value is
         used.</para>
        </glossdef>
       </glossentry>
      </glosslist>
  
Index: test/share/dtml/show.dtml
===================================================================
RCS file: /home/sc/Repository/qm/qm/test/share/dtml/show.dtml,v
retrieving revision 1.3
diff -c -5 -p -r1.3 show.dtml
*** test/share/dtml/show.dtml	11 Oct 2002 20:23:26 -0000	1.3
--- test/share/dtml/show.dtml	18 Jun 2003 17:10:43 -0000
***************
*** 115,125 ****
      </tr>
  
      <dtml-in name="fields">
      <dtml-let field=sequence-item
                field_name="field.GetName()">
!      <dtml-if expr="not field.IsProperty('hidden')">
        <dtml-comment><!--
  
          Show a row with the field label and a rendering of its value.
  
        --></dtml-comment>
--- 115,125 ----
      </tr>
  
      <dtml-in name="fields">
      <dtml-let field=sequence-item
                field_name="field.GetName()">
!      <dtml-if expr="not field.IsHidden()">
        <dtml-comment><!--
  
          Show a row with the field label and a rendering of its value.
  
        --></dtml-comment>
***************
*** 156,166 ****
        </tr>
       <dtml-else>
        <dtml-if expr="edit">
         <dtml-var expr="FormatFieldValue(field)">
        </dtml-if> <!-- edit -->
!      </dtml-if> <!-- not field.IsProperty('hidden') -->
      </dtml-let>
      </dtml-in>
  
      <tr><td colspan="2"> </td></tr>
  
--- 156,166 ----
        </tr>
       <dtml-else>
        <dtml-if expr="edit">
         <dtml-var expr="FormatFieldValue(field)">
        </dtml-if> <!-- edit -->
!      </dtml-if> <!-- not field.IsHidden() -->
      </dtml-let>
      </dtml-in>
  
      <tr><td colspan="2"> </td></tr>
  
Index: test/web/web.py
===================================================================
RCS file: /home/sc/Repository/qm/qm/test/web/web.py,v
retrieving revision 1.65
diff -c -5 -p -r1.65 web.py
*** test/web/web.py	16 Jun 2003 23:45:51 -0000	1.65
--- test/web/web.py	18 Jun 2003 17:10:43 -0000
*************** class ShowItemPage(QMTestPage):
*** 994,1006 ****
              # Use the default value if none is provided.
              value = field.GetDefaultValue()
          # Format it appropriately.
          server = self.server
          if self.edit:
!             if field.IsProperty("hidden"):
                  return field.FormatValueAsHtml(server, value, "hidden")
!             elif field.IsProperty("read_only"):
                  # For read-only fields, we still need a form input, but
                  # the user shouldn't be able to change anything.  Use a
                  # hidden input, and display the contents as if this
                  # wasn't an editing form.
                  return field.FormatValueAsHtml(server, value, "hidden") \
--- 994,1006 ----
              # Use the default value if none is provided.
              value = field.GetDefaultValue()
          # Format it appropriately.
          server = self.server
          if self.edit:
!             if field.IsHidden():
                  return field.FormatValueAsHtml(server, value, "hidden")
!             elif field.IsReadOnly():
                  # For read-only fields, we still need a form input, but
                  # the user shouldn't be able to change anything.  Use a
                  # hidden input, and display the contents as if this
                  # wasn't an editing form.
                  return field.FormatValueAsHtml(server, value, "hidden") \
*************** class QMTestServer(qm.web.WebServer):
*** 1818,1828 ****
          # Extract and expand the IDs of tests to run.
          if request.has_key("ids"):
              ids = string.split(request["ids"], ",")
          else:
              ids = [""]
!         test_ids, suite_ids = self.GetDatabase().ExpandIds(ids)
  
          # Let the results stream know that we are going to start
          # providing it with results.
          self.__results_stream.Start(test_ids)
          
--- 1818,1828 ----
          # Extract and expand the IDs of tests to run.
          if request.has_key("ids"):
              ids = string.split(request["ids"], ",")
          else:
              ids = [""]
!         test_ids = self.GetDatabase().ExpandIds(ids)[0]
  
          # Let the results stream know that we are going to start
          # providing it with results.
          self.__results_stream.Start(test_ids)
          



More information about the qmtest mailing list