Code

Applied Stefan Seefeld's html4/xhtml patch with some changes.
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Sat, 6 Dec 2003 00:00:54 +0000 (00:00 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Sat, 6 Dec 2003 00:00:54 +0000 (00:00 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2019 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
doc/customizing.txt
doc/index.txt
roundup/cgi/templating.py
templates/classic/config.py
templates/minimal/config.py

index fd2a449d930ca88d833dfb7a1c95a2a01a3358dc..685ab7058171f5f12d0cf819c4e86dd818fcf815 100644 (file)
@@ -21,6 +21,8 @@ Feature:
   828963)
 - ignore incoming email with "Precedence: bulk" (sf patch 843489)
 - use HTTP 'Content-Length' header (modified sf patch 844577)
   828963)
 - ignore incoming email with "Precedence: bulk" (sf patch 843489)
 - use HTTP 'Content-Length' header (modified sf patch 844577)
+- HTML generated is now HTML4 (or optionally XHTML) compliant (sf feature
+  814314 and sf patch 834620)
 
 Fixed:
 - mysql documentation fixed to note requirement of 4.0+ and InnoDB
 
 Fixed:
 - mysql documentation fixed to note requirement of 4.0+ and InnoDB
index 0136601910ec8a56f34d249378bd9c36dd0a9db6..137ca79761d0ba10a48398f62121b1522103746b 100644 (file)
@@ -2,7 +2,7 @@
 Customising Roundup
 ===================
 
 Customising Roundup
 ===================
 
-:Version: $Revision: 1.105 $
+:Version: $Revision: 1.106 $
 
 .. This document borrows from the ZopeBook section on ZPT. The original is at:
    http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
 
 .. This document borrows from the ZopeBook section on ZPT. The original is at:
    http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -180,6 +180,11 @@ The configuration variables available are:
  Default class to use in the mailgw if one isn't supplied in email
  subjects. To disable, comment out the variable below or leave it blank.
 
  Default class to use in the mailgw if one isn't supplied in email
  subjects. To disable, comment out the variable below or leave it blank.
 
+**HTML_VERSION** -  ``'html4'`` or ``'xhtml'``
+ HTML version to generate. The templates are html4 by default. If you
+ wish to make them xhtml, then you'll need to change this var to 'xhtml'
+ too so all auto-generated HTML is compliant.
+
 The default config.py is given below - as you
 can see, the MAIL_DOMAIN must be edited before any interaction with the
 tracker is attempted.::
 The default config.py is given below - as you
 can see, the MAIL_DOMAIN must be edited before any interaction with the
 tracker is attempted.::
@@ -255,6 +260,11 @@ tracker is attempted.::
     MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
     #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
 
     MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
     #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
 
+    # HTML version to generate. The templates are html4 by default. If you
+    # wish to make them xhtml, then you'll need to change this var to 'xhtml'
+    # too so all auto-generated HTML is compliant.
+    HTML_VERSION = 'html4'         # either 'html4' or 'xhtml'
+
     # 
     # SECURITY DEFINITIONS
     #
     # 
     # SECURITY DEFINITIONS
     #
@@ -1054,6 +1064,10 @@ returning.
 Default templates
 -----------------
 
 Default templates
 -----------------
 
+The default templates are html4 compliant. If you wish to change them to be
+xhtml compliant, you'll need to change the ``HTML_VERSION`` configuration
+variable in ``config.py`` to ``'xhtml'`` instead of ``'html4'``.
+
 Most customisation of the web view can be done by modifying the
 templates in the tracker ``'html'`` directory. There are several types
 of files in there. The *minimal* template includes:
 Most customisation of the web view can be done by modifying the
 templates in the tracker ``'html'`` directory. There are several types
 of files in there. The *minimal* template includes:
index fac1c91c84424afecac01d4ede346178116d5506..953ecf65af8a5986d65de9eaa92d71d9d3eadca1 100644 (file)
@@ -49,6 +49,7 @@ implement this system on their time.
 
 Thanks also to the many people on the mailing list, in the sourceforge
 project and those who just report bugs:
 
 Thanks also to the many people on the mailing list, in the sourceforge
 project and those who just report bugs:
+Thomas Arendsen Hein,
 Anthony Baxter,
 Cameron Blackwood,
 Jeff Blaine,
 Anthony Baxter,
 Cameron Blackwood,
 Jeff Blaine,
@@ -66,7 +67,6 @@ Johannes Gijsbers,
 Gus Gollings,
 Dan Grassi,
 Engelbert Gruber,
 Gus Gollings,
 Dan Grassi,
 Engelbert Gruber,
-Thomas Arendsen Hein,
 Juergen Hermann,
 Tobias Hunger,
 James Kew,
 Juergen Hermann,
 Tobias Hunger,
 James Kew,
index 537a1cbf1e3aa251ac7db48ad8102df63beda2df..6fa573e9d26483c066c576942a7a6f0788913b8b 100644 (file)
@@ -24,6 +24,14 @@ from roundup.cgi.PageTemplates.Expressions import getEngine
 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
 from roundup.cgi import ZTUtils
 
 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
 from roundup.cgi import ZTUtils
 
+def input_html4(**attrs):
+    """Generate an 'input' (html4) element with given attributes"""
+    return '<input %s>'%' '.join(['%s="%s"'%item for item in attrs.items()])
+
+def input_xhtml(**attrs):
+    """Generate an 'input' (xhtml) element with given attributes"""
+    return '<input %s/>'%' '.join(['%s="%s"'%item for item in attrs.items()])
+
 class NoTemplate(Exception):
     pass
 
 class NoTemplate(Exception):
     pass
 
@@ -295,6 +303,14 @@ class HTMLClass(HTMLPermissions):
         self._klass = self._db.getclass(self.classname)
         self._props = self._klass.getprops()
 
         self._klass = self._db.getclass(self.classname)
         self._props = self._klass.getprops()
 
+        html_version = 'html4'
+        if hasattr(self._client.instance.config, 'HTML_VERSION'):
+            html_version = self._client.instance.config.HTML_VERSION
+        if html_version == 'xhtml':
+            self.input = input_xhtml
+        else:
+            self.input = input_html4
+
     def __repr__(self):
         return '<HTMLClass(0x%x) %s>'%(id(self), self.classname)
 
     def __repr__(self):
         return '<HTMLClass(0x%x) %s>'%(id(self), self.classname)
 
@@ -478,8 +494,8 @@ class HTMLClass(HTMLPermissions):
     def submit(self, label="Submit New Entry"):
         ''' Generate a submit button (and action hidden element)
         '''
     def submit(self, label="Submit New Entry"):
         ''' Generate a submit button (and action hidden element)
         '''
-        return '  <input type="hidden" name="@action" value="new">\n'\
-        '  <input type="submit" name="submit" value="%s">'%label
+        return self.input(type="hidden",name="@action",value="new") + '\n' + \
+               self.input(type="submit",name="submit",value=label)
 
     def history(self):
         return 'New node - no history'
 
     def history(self):
         return 'New node - no history'
@@ -556,8 +572,8 @@ class HTMLItem(HTMLPermissions):
     def submit(self, label="Submit Changes"):
         ''' Generate a submit button (and action hidden element)
         '''
     def submit(self, label="Submit Changes"):
         ''' Generate a submit button (and action hidden element)
         '''
-        return '  <input type="hidden" name="@action" value="edit">\n'\
-        '  <input type="submit" name="submit" value="%s">'%label
+        return self.input(type="hidden",name="@action",value="edit") + '\n' + \
+               self.input(type="submit",name="submit",value=label)
 
     def journal(self, direction='descending'):
         ''' Return a list of HTMLJournalEntry instances.
 
     def journal(self, direction='descending'):
         ''' Return a list of HTMLJournalEntry instances.
@@ -844,6 +860,15 @@ class HTMLProperty:
             self._formname = '%s%s@%s'%(classname, nodeid, name)
         else:
             self._formname = name
             self._formname = '%s%s@%s'%(classname, nodeid, name)
         else:
             self._formname = name
+
+        html_version = 'html4'
+        if hasattr(self._client.instance.config, 'HTML_VERSION'):
+            html_version = self._client.instance.config.HTML_VERSION
+        if html_version == 'xhtml':
+            self.input = input_xhtml
+        else:
+            self.input = input_html4
+        
     def __repr__(self):
         return '<HTMLProperty(0x%x) %s %r %r>'%(id(self), self._formname,
             self._prop, self._value)
     def __repr__(self):
         return '<HTMLProperty(0x%x) %s %r %r>'%(id(self), self._formname,
             self._prop, self._value)
@@ -918,7 +943,7 @@ class StringHTMLProperty(HTMLProperty):
         else:
             value = cgi.escape(str(self._value))
             value = '&quot;'.join(value.split('"'))
         else:
             value = cgi.escape(str(self._value))
             value = '&quot;'.join(value.split('"'))
-        return '<input name="%s" value="%s" size="%s">'%(self._formname, value, size)
+        return self.input(name=self._formname,value=value,size=size)
 
     def multiline(self, escape=0, rows=5, cols=40):
         ''' Render a multiline form edit field for the property
 
     def multiline(self, escape=0, rows=5, cols=40):
         ''' Render a multiline form edit field for the property
@@ -958,15 +983,15 @@ class PasswordHTMLProperty(HTMLProperty):
     def field(self, size = 30):
         ''' Render a form edit field for the property.
         '''
     def field(self, size = 30):
         ''' Render a form edit field for the property.
         '''
-        return '<input type="password" name="%s" size="%s">'%(self._formname, size)
+        return self.input(type="password", name=self._formname, size=size)
 
     def confirm(self, size = 30):
         ''' Render a second form edit field for the property, used for 
             confirmation that the user typed the password correctly. Generates
             a field with name "@confirm@name".
         '''
 
     def confirm(self, size = 30):
         ''' Render a second form edit field for the property, used for 
             confirmation that the user typed the password correctly. Generates
             a field with name "@confirm@name".
         '''
-        return '<input type="password" name="@confirm@%s" size="%s">'%(
-            self._formname, size)
+        return self.input(type="password", name="@confirm@%s"%self._formname,
+            size=size)
 
 class NumberHTMLProperty(HTMLProperty):
     def plain(self):
 
 class NumberHTMLProperty(HTMLProperty):
     def plain(self):
@@ -982,7 +1007,7 @@ class NumberHTMLProperty(HTMLProperty):
         else:
             value = cgi.escape(str(self._value))
             value = '&quot;'.join(value.split('"'))
         else:
             value = cgi.escape(str(self._value))
             value = '&quot;'.join(value.split('"'))
-        return '<input name="%s" value="%s" size="%s">'%(self._formname, value, size)
+        return self.input(name=self._formname,value=value,size=size)
 
     def __int__(self):
         ''' Return an int of me
 
     def __int__(self):
         ''' Return an int of me
@@ -1007,14 +1032,16 @@ class BooleanHTMLProperty(HTMLProperty):
         ''' Render a form edit field for the property
         '''
         checked = self._value and "checked" or ""
         ''' Render a form edit field for the property
         '''
         checked = self._value and "checked" or ""
-        s = '<input type="radio" name="%s" value="yes" %s>Yes'%(self._formname,
-            checked)
-        if checked:
-            checked = ""
+        if self._value:
+            s = self.input(type="radio",name=self._formname,value="yes",checked="checked")
+            s += 'Yes'
+            s +=self.input(type="radio",name=self._formname,value="no")
+            s += 'No'
         else:
         else:
-            checked = "checked"
-        s += '<input type="radio" name="%s" value="no" %s>No'%(self._formname,
-            checked)
+            s = self.input(type="radio",name=self._formname,value="yes")
+            s += 'Yes'
+            s +=self.input(type="radio",name=self._formname,value="no",checked="checked")
+            s += 'No'
         return s
 
 class DateHTMLProperty(HTMLProperty):
         return s
 
 class DateHTMLProperty(HTMLProperty):
@@ -1042,7 +1069,7 @@ class DateHTMLProperty(HTMLProperty):
         else:
             value = cgi.escape(str(self._value.local(self._db.getUserTimezone())))
             value = '&quot;'.join(value.split('"'))
         else:
             value = cgi.escape(str(self._value.local(self._db.getUserTimezone())))
             value = '&quot;'.join(value.split('"'))
-        return '<input name="%s" value="%s" size="%s">'%(self._formname, value, size)
+        return self.input(name=self._formname,value=value,size=size)
 
     def reldate(self, pretty=1):
         ''' Render the interval between the date and now.
 
     def reldate(self, pretty=1):
         ''' Render the interval between the date and now.
@@ -1099,7 +1126,7 @@ class IntervalHTMLProperty(HTMLProperty):
         else:
             value = cgi.escape(str(self._value))
             value = '&quot;'.join(value.split('"'))
         else:
             value = cgi.escape(str(self._value))
             value = '&quot;'.join(value.split('"'))
-        return '<input name="%s" value="%s" size="%s">'%(self._formname, value, size)
+        return self.input(name=self._formname,value=value,size=size)
 
 class LinkHTMLProperty(HTMLProperty):
     ''' Link HTMLProperty
 
 class LinkHTMLProperty(HTMLProperty):
     ''' Link HTMLProperty
@@ -1155,7 +1182,7 @@ class LinkHTMLProperty(HTMLProperty):
         l = ['<select name="%s">'%self._formname]
         k = linkcl.labelprop(1)
         if self._value is None:
         l = ['<select name="%s">'%self._formname]
         k = linkcl.labelprop(1)
         if self._value is None:
-            s = 'selected '
+            s = 'selected="selected" '
         else:
             s = ''
         l.append(_('<option %svalue="-1">- no selection -</option>')%s)
         else:
             s = ''
         l.append(_('<option %svalue="-1">- no selection -</option>')%s)
@@ -1171,7 +1198,7 @@ class LinkHTMLProperty(HTMLProperty):
             # figure if this option is selected
             s = ''
             if optionid == self._value:
             # figure if this option is selected
             s = ''
             if optionid == self._value:
-                s = 'selected '
+                s = 'selected="selected" '
 
             # figure the label
             if showid:
 
             # figure the label
             if showid:
@@ -1200,7 +1227,7 @@ class LinkHTMLProperty(HTMLProperty):
         k = linkcl.labelprop(1)
         s = ''
         if value is None:
         k = linkcl.labelprop(1)
         s = ''
         if value is None:
-            s = 'selected '
+            s = 'selected="selected" '
         l.append(_('<option %svalue="-1">- no selection -</option>')%s)
         if linkcl.getprops().has_key('order'):  
             sort_on = ('+', 'order')
         l.append(_('<option %svalue="-1">- no selection -</option>')%s)
         if linkcl.getprops().has_key('order'):  
             sort_on = ('+', 'order')
@@ -1219,7 +1246,7 @@ class LinkHTMLProperty(HTMLProperty):
             # figure if this option is selected
             s = ''
             if value in [optionid, option]:
             # figure if this option is selected
             s = ''
             if value in [optionid, option]:
-                s = 'selected '
+                s = 'selected="selected" '
 
             # figure the label
             if showid:
 
             # figure the label
             if showid:
@@ -1315,7 +1342,7 @@ class MultilinkHTMLProperty(HTMLProperty):
             k = linkcl.labelprop(1)
             value = [linkcl.get(v, k) for v in value]
         value = cgi.escape(','.join(value))
             k = linkcl.labelprop(1)
             value = [linkcl.get(v, k) for v in value]
         value = cgi.escape(','.join(value))
-        return '<input name="%s" size="%s" value="%s">'%(self._formname, size, value)
+        return self.input(name=self._formname,size=size,value=value)
 
     def menu(self, size=None, height=None, showid=0, additional=[],
             **conditions):
 
     def menu(self, size=None, height=None, showid=0, additional=[],
             **conditions):
@@ -1342,7 +1369,7 @@ class MultilinkHTMLProperty(HTMLProperty):
             # figure if this option is selected
             s = ''
             if optionid in value or option in value:
             # figure if this option is selected
             s = ''
             if optionid in value or option in value:
-                s = 'selected '
+                s = 'selected="selected" '
 
             # figure the label
             if showid:
 
             # figure the label
             if showid:
@@ -1451,6 +1478,14 @@ class HTMLRequest:
         # the special char to use for special vars
         self.special_char = '@'
 
         # the special char to use for special vars
         self.special_char = '@'
 
+        html_version = 'html4'
+        if hasattr(self.client.instance.config, 'HTML_VERSION'):
+            html_version = self.client.instance.config.HTML_VERSION
+        if html_version == 'xhtml':
+            self.input = input_xhtml
+        else:
+            self.input = input_html4
+
         self._post_init()
 
     def _post_init(self):
         self._post_init()
 
     def _post_init(self):
@@ -1603,7 +1638,7 @@ env: %(env)s
         ''' return the current index args as form elements '''
         l = []
         sc = self.special_char
         ''' return the current index args as form elements '''
         l = []
         sc = self.special_char
-        s = '<input type="hidden" name="%s" value="%s">'
+        s = self.input(type="hidden",name="%s",value="%s")
         if columns and self.columns:
             l.append(s%(sc+'columns', ','.join(self.columns)))
         if sort and self.sort[1] is not None:
         if columns and self.columns:
             l.append(s%(sc+'columns', ','.join(self.columns)))
         if sort and self.sort[1] is not None:
index 8f9057d340cbd076e813bcf2f0aa8d2b0cf09f4e..e6f61334bcdbf579d020f4646c9734946b935791 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: config.py,v 1.3 2003-04-24 07:19:59 richard Exp $
+# $Id: config.py,v 1.4 2003-12-06 00:00:54 richard Exp $
 
 import os
 
 
 import os
 
@@ -106,6 +106,11 @@ EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
 MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
 #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
 
 MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
 #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
 
+# HTML version to generate. The templates are html4 by default. If you
+# wish to make them xhtml, then you'll need to change this var to 'xhtml'
+# too so all auto-generated HTML is compliant.
+HTML_VERSION = 'html4'         # either 'html4' or 'xhtml'
+
 # 
 # SECURITY DEFINITIONS
 #
 # 
 # SECURITY DEFINITIONS
 #
index f2d3f677dad655dcb3448054553a98d7c7302a79..a3ceeb601a1d1359f6610b82ebc116b8c16b297c 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: config.py,v 1.2 2003-04-24 07:20:00 richard Exp $
+# $Id: config.py,v 1.3 2003-12-06 00:00:54 richard Exp $
 
 import os
 
 
 import os
 
@@ -110,4 +110,9 @@ EMAIL_LEAVE_BODY_UNCHANGED = 'no'   # either 'yes' or 'no'
 MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
 #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
 
 MAIL_DEFAULT_CLASS = 'issue'   # use "issue" class by default
 #MAIL_DEFAULT_CLASS = ''        # disable (or just comment the var out)
 
+# HTML version to generate. The templates are html4 by default. If you
+# wish to make them xhtml, then you'll need to change this var to 'xhtml'
+# too so all auto-generated HTML is compliant.
+HTML_VERSION = 'html4'         # either 'html4' or 'xhtml'
+
 # vim: set filetype=python ts=4 sw=4 et si
 # vim: set filetype=python ts=4 sw=4 et si