r714 - / zope-atextensions zope-atextensions/trunk zope-atextensions/trunk/Extensions zope-atextensions/trunk/datatype zope-atextensions/trunk/debian zope-atextensions/trunk/examples zope-atextensions/trunk/field zope-atextensions/trunk/i18n zope-atextensions/trunk/skins zope-atextensions/trunk/skins/at_extensions zope-atextensions/trunk/validator zope-atextensions/trunk/widget

Alastair McKinstry mckinstry at alioth.debian.org
Wed Mar 21 14:32:50 UTC 2007


Author: mckinstry
Date: 2007-03-21 14:32:46 +0000 (Wed, 21 Mar 2007)
New Revision: 714

Added:
   zope-atextensions/
   zope-atextensions/branches/
   zope-atextensions/tags/
   zope-atextensions/trunk/
   zope-atextensions/trunk/CHANGES.txt
   zope-atextensions/trunk/Extensions/
   zope-atextensions/trunk/Extensions/Install.py
   zope-atextensions/trunk/Extensions/__init__.py
   zope-atextensions/trunk/Extensions/utils.py
   zope-atextensions/trunk/README
   zope-atextensions/trunk/__init__.py
   zope-atextensions/trunk/ateapi.py
   zope-atextensions/trunk/config.py
   zope-atextensions/trunk/datatype/
   zope-atextensions/trunk/datatype/__init__.py
   zope-atextensions/trunk/datatype/formattablename.py
   zope-atextensions/trunk/datatype/formattablenames.py
   zope-atextensions/trunk/debian/
   zope-atextensions/trunk/debian/changelog
   zope-atextensions/trunk/debian/compat
   zope-atextensions/trunk/debian/control
   zope-atextensions/trunk/debian/copyright
   zope-atextensions/trunk/debian/dzproduct
   zope-atextensions/trunk/debian/postinst
   zope-atextensions/trunk/debian/rules
   zope-atextensions/trunk/examples/
   zope-atextensions/trunk/examples/__init__.py
   zope-atextensions/trunk/examples/demo.py
   zope-atextensions/trunk/examples/formattablename.py
   zope-atextensions/trunk/field/
   zope-atextensions/trunk/field/__init__.py
   zope-atextensions/trunk/field/comment.py
   zope-atextensions/trunk/field/datetime.py
   zope-atextensions/trunk/field/email.py
   zope-atextensions/trunk/field/formattablename.py
   zope-atextensions/trunk/field/formattablenames.py
   zope-atextensions/trunk/field/record.py
   zope-atextensions/trunk/field/record_examples.py
   zope-atextensions/trunk/field/records.py
   zope-atextensions/trunk/field/records_examples.py
   zope-atextensions/trunk/field/remotetext.py
   zope-atextensions/trunk/field/url.py
   zope-atextensions/trunk/i18n/
   zope-atextensions/trunk/i18n/i18nCall.sh
   zope-atextensions/trunk/i18n/plone-da.po
   zope-atextensions/trunk/i18n/plone-de.po
   zope-atextensions/trunk/i18n/plone-en.po
   zope-atextensions/trunk/i18n/plone-fr.po
   zope-atextensions/trunk/i18n/plone-generated.pot
   zope-atextensions/trunk/i18n/plone-it.po
   zope-atextensions/trunk/i18n/plone-manual.pot
   zope-atextensions/trunk/i18n/plone-nl.po
   zope-atextensions/trunk/i18n/plone-pt.po
   zope-atextensions/trunk/skins/
   zope-atextensions/trunk/skins/at_extensions/
   zope-atextensions/trunk/skins/at_extensions/address_widget.pt
   zope-atextensions/trunk/skins/at_extensions/combo_widget.pt
   zope-atextensions/trunk/skins/at_extensions/comment_widget.pt
   zope-atextensions/trunk/skins/at_extensions/customize_remotefield.cpy
   zope-atextensions/trunk/skins/at_extensions/datetime_widget.pt
   zope-atextensions/trunk/skins/at_extensions/deadlines_widget.pt
   zope-atextensions/trunk/skins/at_extensions/email_widget.pt
   zope-atextensions/trunk/skins/at_extensions/formatdemo.py
   zope-atextensions/trunk/skins/at_extensions/formattablename_view.pt
   zope-atextensions/trunk/skins/at_extensions/getDisplayView.py
   zope-atextensions/trunk/skins/at_extensions/more_edit.cpy
   zope-atextensions/trunk/skins/at_extensions/more_edit.cpy.metadata
   zope-atextensions/trunk/skins/at_extensions/record_widget.pt
   zope-atextensions/trunk/skins/at_extensions/records_widget.pt
   zope-atextensions/trunk/skins/at_extensions/remotetext_widget.pt
   zope-atextensions/trunk/skins/at_extensions/uncustomize_remotefield.cpy
   zope-atextensions/trunk/skins/at_extensions/url_widget.pt
   zope-atextensions/trunk/validator/
   zope-atextensions/trunk/validator/__init__.py
   zope-atextensions/trunk/validator/isPartialUrl.py
   zope-atextensions/trunk/version.txt
   zope-atextensions/trunk/widget/
   zope-atextensions/trunk/widget/__init__.py
   zope-atextensions/trunk/widget/combo.py
   zope-atextensions/trunk/widget/comment.py
   zope-atextensions/trunk/widget/datetime.py
   zope-atextensions/trunk/widget/deadlines.py
   zope-atextensions/trunk/widget/email.py
   zope-atextensions/trunk/widget/formattablename.py
   zope-atextensions/trunk/widget/formattablenames.py
   zope-atextensions/trunk/widget/record.py
   zope-atextensions/trunk/widget/records.py
   zope-atextensions/trunk/widget/remotetext.py
   zope-atextensions/trunk/widget/url.py
Log:
Initial import; 0.8.0


Added: zope-atextensions/trunk/CHANGES.txt
===================================================================
--- zope-atextensions/trunk/CHANGES.txt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/CHANGES.txt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,94 @@
+from 0.7.2 to 0.8
+
+ - improved i18n support [Mike Gabriel]
+
+ - added 'comment' field (lets you include formattable narrative 
+   in edit forms)
+
+ - first draft of a 'remotetext' field (not yet working)
+   (the idea is to have a field pull its text from a remote source 
+   but also to be able to customize the text if needed)
+
+from 0.7.1 to 0.7.2
+
+ - fixed bug when deleting more than one entry from a list of records
+   using the records widget (thanks to Mike Gabriel for spotting the
+   bug and to Frank Bennett for the fix)
+
+from 0.7 to 0.7.1
+
+ - fixed references to deprecated calendar_slot [David Siedband]
+
+ - extended Record.getVocabularyFor to accept the use of DisplayLists
+   as subfield_vocabulary entries and to raise errors if a valid
+   DisplayLists was not returned from an expression. [DWM]
+
+from 0.6 to 0.7
+
+ - add the 'formattable name(s)' data types, fields, and widgets
+
+from 0.5 to 0.6
+
+ - rearranged almost everything to be inline with AT-1.4's
+   architecture
+
+ - made it backwards compatible with AT-1.2 again
+   (hopefully)
+
+ - added postprocessing to the URL widget to supply http as
+   default protocol if no-one is supplied.
+
+from 0.4 to 0.5
+
+ - added ComboWidget and combo_widget.pt to create combo boxes
+   where users can select a value or specify one freely
+   it they select 'other'.
+
+ - added a 'delete' flag to each record in RecordsWidget's
+   edit macro.
+
+ - added an optional 'maxlenth' attribute for subfields
+
+ - added subfield validation; from now on AT 1.3 is required
+   (I think)
+
+ - updated the demo type (WorkingGroup) to use subfield validation
+   for the 'homepage' subfield in e-contact
+
+from 0.3 to 0.4:
+
+ - added a 'delete all entries' checkbox to the records widget
+
+ - added 'minimalSize' and 'maximalSize' to the RecordsField's 
+   properties to enable better control of the number of lines 
+   offered in the edit form
+
+ - do not override 'content_edit' anymore; configure the form 
+   controller on install instead and add a custom 'more_edit'
+   to be called when 'more' is pressed (only slight extension 
+   to the original 'content_edit' but I couldn't get around 
+   otherwise with the references - thanks to Roché Compaan
+   for a valuable hint).
+
+ - Record(s)Field should now work with schemata or portal_factory
+   without the risc of data loss
+
+ - adapted the demo content type ('WorkingGroup') to reflect the
+   above changes and made it demonstrate how to enable the 
+   date picker box for a DateTime subfield
+
+ - added support for 'int', 'long' and 'float' as subfield types
+   in RecordField and RecordsField.
+
+from 0.2 to 0.3:
+
+ - made compliant with the new security policy for archetypes 1.3:
+   Registered the UrlField/Widget and replaced 'getRaw' in the
+   Record(s)Widget by an appropriate indirect call to the edit 
+   accessor.
+
+from 0.1 to 0.2:
+
+ - added a RecordsField and -Widget (records are a list of record objects)
+
+ - improved the RecordWidget's view macro (empty subfields are not rendered)

Added: zope-atextensions/trunk/Extensions/Install.py
===================================================================
--- zope-atextensions/trunk/Extensions/Install.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/Extensions/Install.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,96 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2004 ITB, Humboldt-University Berlin           #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+"""plone installer script"""
+
+from StringIO import StringIO
+
+from Products.CMFCore.utils import getToolByName
+from Products.CMFFormController.FormAction import FormActionKey
+from Products.CMFFormController.FormValidator import FormValidatorKey
+
+from Products.Archetypes.public import listTypes
+from Products.Archetypes.Extensions.utils \
+     import installTypes, install_subskin
+
+from Products.ATExtensions.config import *
+
+
+def setupProperties(self, out):
+    ptool = getToolByName(self, 'portal_properties')
+    psheet = getattr(ptool, 'extensions_properties', None)
+
+    if not psheet:
+        ptool.addPropertySheet('extensions_properties',
+                               'Properties of the ATExtension product')
+        ps = getattr(ptool, 'extensions_properties')
+
+        ps._properties = ps._properties + (
+            {'id':'phone_number_types', 'type':'lines', 'mode':'w'},
+            {'id':'country_names', 'type':'lines', 'mode':'w'},
+            )
+        ps._updateProperty('phone_number_types', PHONE_NUMBER_TYPES)
+        ps._updateProperty('country_names', COUNTRY_NAMES)
+        out.write("Added %s's properties." % PROJECTNAME)
+    else:
+        out.write("Found %s's properties." % PROJECTNAME)
+
+def configureFormController(self, out):
+    pfc = getToolByName(self, 'portal_form_controller')
+    pfc.addFormValidators('base_edit',
+                          '',   # context_type
+                          'more',
+                          '')   # validators
+    pfc.addFormAction('base_edit',
+                      'success',
+                      '',    # context_type
+                      'more',
+                      'traverse_to',
+                      'string:more_edit')
+    out.write("Added validator and action for the 'more' button to the form controller.")
+
+
+        
+def install(self):
+    out = StringIO()
+    ## don't install the demao type as it's broken in Zope 2.10/CMF 2.0
+    ## anyways
+    ## installTypes(self, out, listTypes(PROJECTNAME), PROJECTNAME)
+    install_subskin(self, out, GLOBALS)
+    setupProperties(self, out)
+    configureFormController(self, out)
+    out.write("Successfully installed %s." % PROJECTNAME)
+    return out.getvalue()
+
+# for the uninstall we need to take care of the form controller
+# explicitly
+
+def reconfigureFormController(self, out):
+    pfc = getToolByName(self, 'portal_form_controller')
+    
+    #BAAH no Python API for deleting actions or validators in FormController
+    #lets get our hands dirty
+    container = pfc.actions
+    try:
+        container.delete(FormActionKey('base_edit', 'success', '',
+                                       'more', pfc))
+    except KeyError: pass
+    
+    container = pfc.validators
+    try:
+        container.delete(FormValidatorKey('base_edit', '',
+                                          'more', pfc))
+    except KeyError: pass
+
+    out.write("Removed validator and action for the 'more' button from the form controller.")
+
+def uninstall(self):
+    out = StringIO()
+    reconfigureFormController(self, out)
+    # all the rest we leave to the quick installer
+    out.write("Uninstalled %s." % PROJECTNAME)
+    return out.getvalue()

Added: zope-atextensions/trunk/Extensions/__init__.py
===================================================================
--- zope-atextensions/trunk/Extensions/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/Extensions/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,2 @@
+# lifting it up
+from utils import getDisplayList

Added: zope-atextensions/trunk/Extensions/utils.py
===================================================================
--- zope-atextensions/trunk/Extensions/utils.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/Extensions/utils.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,31 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2004 ITB, Humboldt-University Berlin           #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+""" utilities for the ATExtensions product """
+
+from Products.CMFCore.utils import getToolByName
+from Products.Archetypes.public import DisplayList
+
+def getValues(self, prop_name):
+    ptool = getToolByName(self, 'portal_properties', None)
+    if ptool and hasattr(ptool, 'extensions_properties'):
+        return ptool.extensions_properties.getProperty(prop_name, None)
+    else:
+        return None
+    
+def makeDisplayList(values=None):
+    if values and type(values) not in [type([]), type(())]:
+        values = (values,)
+    if not values: values = []
+    results = [['', 'Select'],]
+    for x in values:
+        results.append([x,x])
+    values_tuple = tuple(results)
+    return DisplayList(values_tuple)
+
+def getDisplayList(self, prop_name=None):
+    return makeDisplayList(getValues(self, prop_name))

Added: zope-atextensions/trunk/README
===================================================================
--- zope-atextensions/trunk/README	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/README	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,33 @@
+
+Welcome to ATExtensions
+
+
+This is a development release.
+
+USE AT YOUR OWN RISC. NO REAL TESTING DONE SO FAR.
+
+This package just provides some extensions to archetypes.
+
+So far, there are only a few custom fields and widgets,
+the Record(s)Field/Widget being the most generic ones.
+
+To demonstrate usage, there is a demo content type
+'WorkingGroup'. To enable it after install, go to 
+portal types and make it implicitly addable or include 
+it in some folderish type's 'allowed_content_types'.
+
+Updating
+
+If yo have been using ATExtensions before from your
+products you may encounter some broken imports. This
+is due to the reorganization of the source code and
+easily fixed by linking the new 'ateapi.py' module
+under the old 'at_extensions.py' name.
+
+Enjoy,
+
+	Raphael (r.ritz at biologie.hu-berlin.de)
+
+
+
+

Added: zope-atextensions/trunk/__init__.py
===================================================================
--- zope-atextensions/trunk/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,40 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2004 ITB, Humboldt-University Berlin           #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+"""package installer"""
+
+from Products.CMFCore import utils
+from Products.CMFCore.DirectoryView import registerDirectory
+from Products.Archetypes.public import process_types, listTypes
+from config import *
+
+# the custom validator
+from Products.validation import validation
+from validator.isPartialUrl import PartialUrlValidator
+validation.register(PartialUrlValidator('isPartialUrl'))
+
+# the demo content types
+from examples import demo, formattablename
+
+# backwards compatibility
+import sys
+import ateapi
+sys.modules['Products.ATExtensions.at_extensions'] = ateapi
+
+registerDirectory(SKINS_DIR, GLOBALS)
+
+def initialize(context):
+    content_types, constructors, ftis = process_types(
+        listTypes(PROJECTNAME),
+        PROJECTNAME)
+    utils.ContentInit(
+        PROJECTNAME + ' Content',
+        content_types      = content_types,
+        permission         = ADD_CONTENT_PERMISSION,
+        extra_constructors = constructors,
+        fti                = ftis,
+        ).initialize(context)

Added: zope-atextensions/trunk/ateapi.py
===================================================================
--- zope-atextensions/trunk/ateapi.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/ateapi.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,8 @@
+# the datatypes
+from Products.ATExtensions.datatype import *
+# the fields
+from Products.ATExtensions.field import *
+# the widgets
+from Products.ATExtensions.widget import *
+# the utilities
+from Products.ATExtensions.Extensions.utils import getDisplayList

Added: zope-atextensions/trunk/config.py
===================================================================
--- zope-atextensions/trunk/config.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/config.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,266 @@
+# just to be compatible with the general AT approach
+
+from Products.CMFCore.permissions import AddPortalContent
+
+ADD_CONTENT_PERMISSION = AddPortalContent
+PROJECTNAME = "ATExtensions"
+SKINS_DIR = 'skins'
+GLOBALS = globals()
+
+try:
+    from Products.validation import ValidationChain
+    HAS_VALIDATION_CHAIN = 1
+except ImportError:
+    HAS_VALIDATION_CHAIN = 0
+
+PHONE_NUMBER_TYPES = [
+    "Office",
+    "Secretariat",
+    "Laboratory",
+    "Mobile",
+    "Fax",
+    "Private",
+    ]
+
+COUNTRY_NAMES = [
+    "Afghanistan",
+    "Albania",
+    "Algeria",
+    "American Samoa",
+    "Andorra",
+    "Angola",
+    "Anguilla",
+    "Antarctica",
+    "Antigua Barbuda",
+    "Argentina",
+    "Armenia",
+    "Aruba",
+    "Australia",
+    "Austria",
+    "Azerbaijan",
+    "Bahamas",
+    "Bahrain",
+    "Bangladesh",
+    "Barbados",
+    "Belarus",
+    "Belgium",
+    "Belize",
+    "Benin",
+    "Bermuda",
+    "Bhutan",
+    "Bolivia",
+    "Bosnia Herzegovina",
+    "Botswana",
+    "Bouvet Island",
+    "Brazil",
+    "British Indian Ocean Territory",
+    "Brunei Darussalam",
+    "Bulgaria",
+    "Burkina Faso",
+    "Burundi",
+    "Cambodia",
+    "Cameroon",
+    "Canada",
+    "Cape Verde",
+    "Cayman Islands",
+    "Central African Republic",
+    "Chad",
+    "Chile",
+    "China",
+    "Christmas Island",
+    "Cocos (Keeling) Islands",
+    "Colombia",
+    "Comoros",
+    "Congo",
+    "Congo, Democratic Republic",
+    "Cook Islands",
+    "Costa Rica",
+    "Cote D'Ivoire",
+    "Croatia",
+    "Cuba",
+    "Cyprus",
+    "Czech Republic",
+    "Denmark",
+    "Djibouti",
+    "Dominica",
+    "Dominican Republic",
+    "Ecuador",
+    "Egypt",
+    "El Salvador",
+    "Equatorial Guinea",
+    "Eritrea",
+    "Estonia",
+    "Ethiopia",
+    "Falkland Islands (Malvinas)",
+    "Faroe Islands",
+    "Fiji",
+    "Finland",
+    "France",
+    "French Guiana",
+    "French Polynesia",
+    "French Southern Territories",
+    "Gabon",
+    "Gambia",
+    "Georgia",
+    "Germany",
+    "Ghana",
+    "Gibraltar",
+    "Greece",
+    "Greenland",
+    "Grenada",
+    "Guadeloupe",
+    "Guam",
+    "Guatemala",
+    "Guinea",
+    "Guinea-Bissau",
+    "Guyana",
+    "Haiti",
+    "Heard Island Mcdonald Islands",
+    "Holy See (Vatican City State)",
+    "Honduras",
+    "Hong Kong",
+    "Hungary",
+    "Iceland",
+    "India",
+    "Indonesia",
+    "Iran, Islamic Republic",
+    "Iraq",
+    "Ireland",
+    "Israel",
+    "Italy",
+    "Jamaica",
+    "Japan",
+    "Jordan",
+    "Kazakhstan",
+    "Kenya",
+    "Kiribati",
+    "Korea, Democratic People's Republic",
+    "Korea, Republic",
+    "Kuwait",
+    "Kyrgyzstan",
+    "Lao People's Democratic Republic",
+    "Latvia",
+    "Lebanon",
+    "Lesotho",
+    "Liberia",
+    "Libyan Arab Jamahiriya",
+    "Liechtenstein",
+    "Lithuania",
+    "Luxembourg",
+    "Macao",
+    "Macedonia, Former Yugoslav Republic",
+    "Madagascar",
+    "Malawi",
+    "Malaysia",
+    "Maldives",
+    "Mali",
+    "Malta",
+    "Marshall Islands",
+    "Martinique",
+    "Mauritania",
+    "Mauritius",
+    "Mayotte",
+    "Mexico",
+    "Micronesia, Federated States",
+    "Moldova, Republic",
+    "Monaco",
+    "Mongolia",
+    "Montserrat",
+    "Morocco",
+    "Mozambique",
+    "Myanmar",
+    "Namibia",
+    "Nauru",
+    "Nepal",
+    "Netherlands",
+    "Netherlands Antilles",
+    "New Caledonia",
+    "New Zealand",
+    "Nicaragua",
+    "Niger",
+    "Nigeria",
+    "Niue",
+    "Norfolk Island",
+    "Northern Mariana Islands",
+    "Norway",
+    "Oman",
+    "Pakistan",
+    "Palau",
+    "Palestinian Territory, Occupied",
+    "Panama",
+    "Papua New Guinea",
+    "Paraguay",
+    "Peru",
+    "Philippines",
+    "Pitcairn",
+    "Poland",
+    "Portugal",
+    "Puerto Rico",
+    "Qatar",
+    "Reunion",
+    "Romania",
+    "Russian Federation",
+    "Rwanda",
+    "Saint Helena",
+    "Saint Kitts Nevis",
+    "Saint Lucia",
+    "Saint Pierre Miquelon",
+    "Saint Vincent Grenadines",
+    "Samoa",
+    "San Marino",
+    "Sao Tome Principe",
+    "Saudi Arabia",
+    "Senegal",
+    "Serbia Montenegro",
+    "Seychelles",
+    "Sierra Leone",
+    "Singapore",
+    "Slovakia",
+    "Slovenia",
+    "Solomon Islands",
+    "Somalia",
+    "South Africa",
+    "South Georgia South Sandwich Islands",
+    "Spain",
+    "Sri Lanka",
+    "Sudan",
+    "Suriname",
+    "Svalbard Jan Mayen",
+    "Swaziland",
+    "Sweden",
+    "Switzerland",
+    "Syrian Arab Republic",
+    "Taiwan, Province China",
+    "Tajikistan",
+    "Tanzania, United Republic",
+    "Thailand",
+    "Timor-Leste",
+    "Togo",
+    "Tokelau",
+    "Tonga",
+    "Trinidad Tobago",
+    "Tunisia",
+    "Turkey",
+    "Turkmenistan",
+    "Turks Caicos Islands",
+    "Tuvalu",
+    "Uganda",
+    "Ukraine",
+    "United Arab Emirates",
+    "United Kingdom",
+    "United States",
+    "United States Minor Outlying Islands",
+    "Uruguay",
+    "Uzbekistan",
+    "Vanuatu",
+    "Venezuela",
+    "Vietnam",
+    "Virgin Islands, British",
+    "Virgin Islands, U.S.",
+    "Wallis Futuna",
+    "Western Sahara",
+    "Yemen",
+    "Zambia",
+    "Zimbabwe",
+    ]
+

Added: zope-atextensions/trunk/datatype/__init__.py
===================================================================
--- zope-atextensions/trunk/datatype/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/datatype/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,2 @@
+from formattablename import FormattableName
+from formattablenames import FormattableNames

Added: zope-atextensions/trunk/datatype/formattablename.py
===================================================================
--- zope-atextensions/trunk/datatype/formattablename.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/datatype/formattablename.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,192 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2005 ITB, Humboldt-University Berlin           #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+from string import punctuation
+filtered_punctuation = [c for c in punctuation \
+                        if c not in "()[]{}<>"]
+
+default_format="%T %F %M %P %L %S"
+
+default_formatter = {'T':'title',
+                     'F':'firstname',
+                     'M':'middlename',
+                     'P':'prefix',
+                     'L':'lastname',
+                     'S':'suffix',
+                     'H':'homepage',
+                     'E':'email',
+                     }
+
+default_abbreviations = {}
+
+
+def abbreviate(value, abbr=None, join_abbr=''):
+    """
+    return an abbreviation for 'value'
+    
+    1. from the abbreviations mapping
+    2. or the first letter (or letters if value is multivalued)
+    3. return '' if value is empty
+    """
+    if not value:
+        return ''
+    abbreviations = abbr or default_abbreviations
+    special =  abbreviations.get(value, None)
+    if special is not None:
+        return special
+    values = [v[0] for v in value.split()]
+    return join_abbr.join(values)
+    
+
+class FormattableName:
+
+    __allow_access_to_unprotected_subobjects__ = 1
+
+    def __init__(self, mapping={}):
+        """sets defaults and updates from kw"""
+        self._keys = ()
+        self.update(mapping)
+
+    def __call__(self, *args, **kwargs):
+        value = self.stringformat(*args, **kwargs)
+        if kwargs.get('withURL', False):
+            homepage = self.getHomePage(*args, **kwargs)
+            if homepage:
+                value = '<a href="%s">%s</a>' % (homepage, value)
+        return value
+
+    def getHomePage(self, context=None, methodname='', *args, **kwargs):
+        if self.get('homepage'):
+            return self.get('homepage')
+        try:
+            return getattr(context, methodname)(self)
+        except AttributeError:
+            return None
+
+    def __str__(self):
+        return self.stringformat()
+
+    def __repr__(self):
+        return str(dict(self.items()))
+
+    def __add__(self, other):
+        from formattablenames import FormattableNames
+        if isinstance(other, FormattableName):
+            return FormattableNames([self, other])
+        if isinstance(other, FormattableNames):
+            return FormattableNames([self]) + other
+
+    def stringformat(self,
+                     format=default_format,
+                     formatter=default_formatter,
+                     abbr=None,
+                     join_abbr='',
+                     abbrev=None,        # backwards compat
+                     lastnamefirst=None, # backwards compat
+                     **kw
+                     ):
+        """Takes a format string to control the rendering
+        of the name (inspired by DateTime).
+
+        Default format is '%T %F %M %P %L %S' where the 
+        keys mapped to by the default formatter are:
+
+           %T -> title: for a leading title (e.g. academic)
+           %F -> firstname: for the first first (or given) name
+           %M -> middlename: for the middle name(s)
+           %P -> prefix: for a last name prefix (e.g. a title of nobility)
+           %L -> lastname: for the last name
+           %S -> suffix: for a suffix (e.g. a trailing 'PhD' or 'SA')
+
+        Use lowercase keys to get the abbreviated value
+        (default: first letter; use the 'abbr' mapping
+        to define exceptions; for multi-worded values the first letter
+        of each word, joined by the value of 'join_abbr', is returned)
+
+        Pass in a mapping to 'formatter' for custom format key mapping.
+
+        Usage of 'abbrev' and 'lastnamefirst' is deprecated and for
+        backwards compatibility only.
+        
+        See the test suite for example usages.
+        """
+        if lastnamefirst: 
+            format = "L, %F, %P"
+        if abbrev: format = format.replace("%F", "%f")
+
+        for key in formatter.keys():
+            match = "%"+key
+            value = self[formatter[key]]
+            format = format.replace(match, value)
+            if match.lower() in format:
+                format = format.replace(match.lower(),
+                                        abbreviate(value, abbr, join_abbr))
+
+        return self._normalize(format)
+
+    def _normalize(self, format):
+        """Remove leading, stray, and trailing punctuation
+        and collapse whitespece"""
+        format = self._remove_punctuation(format.strip())
+        for match in ['  ', '()', '[]', '{}', '<>']:
+            while match in format:
+                format = format.replace(match,' ')
+        return format.strip()
+
+    def _remove_punctuation(self, format):
+        for c in filtered_punctuation:
+            match = ' ' + c + ' '
+            format = format.replace(match, ' ')
+            while format.startswith(c):
+                format = format[1:]
+            while format.endswith(c):
+                format = format[:-1]
+        return format
+    
+
+    # behave like a dict
+
+    def __getitem__(self, key, default=''):
+        return getattr(self, key, default)
+
+    def __setitem__(self, key, value):
+        setattr(self, key, value)
+        if key not in self._keys:
+            self._keys += (key,)
+
+    def get(self, key, default=''):
+        return getattr(self, key, default) or default
+
+    def update(self, kw=None):
+        if not kw:
+            return None
+        for k,v in kw.items():
+            self[k] = v
+
+    def keys(self):
+        return self._keys
+
+    def items(self):
+        return [(k, self[k]) for k in self._keys]
+
+    # our one hard-coded exception
+
+    def __setattr__(self, name, value):
+        if value and name == 'firstnames':
+            self['firstname'] = value.split()[0]
+            self['middlename'] = ' '.join(value.split()[1:]).strip()
+        else:
+            self.__dict__[name] = value
+
+    def __getattr__(self, name):
+        if name == 'firstnames':
+            value = ' '.join([self['firstname'], self['middlename']])
+            return value.strip()
+        else:
+            raise AttributeError
+
+

Added: zope-atextensions/trunk/datatype/formattablenames.py
===================================================================
--- zope-atextensions/trunk/datatype/formattablenames.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/datatype/formattablenames.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,59 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2005 ITB, Humboldt-University Berlin           #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+from formattablename import FormattableName
+
+class FormattableNames:
+    def __init__(self,names=[]):
+        self.names = ()
+        for name in names:
+            self.append(name)
+
+    def __call__(self, *args, **kwargs):
+        return self.strfmt(*args, **kwargs)
+
+    def __getitem__(self,key):
+        return self.names[key]
+
+    def __len__(self):
+        return len(self.names)
+
+    def append(self,name):
+        self.names += (FormattableName(name),)
+
+    def __add__(self, other):
+        if isinstance(other, FormattableNames):
+            return FormattableNames(self.names + other.names)
+        sum = FormattableNames(self.names)
+        sum.append(other)
+        return sum
+
+    def __str__(self):
+        return self()
+
+    def __repr__(self):
+        return str(self.names)
+
+    def strfmt(self, *args, **kw):
+        length = len(self)
+        empty_marker = kw.get('empty_marker', "No names specified")
+        sep = kw.get('sep', ', ')
+        lastsep = kw.get('lastsep', ', and ')
+        
+        if length == 0:
+            return empty_marker
+        if length == 1:
+            return self.names[0](**kw)
+        if length == 2:
+            if ',' in lastsep:
+                lastsep = lastsep.replace(',','')
+            return lastsep.join([a(**kw) for a in self.names])
+
+        return sep.join([a(**kw) for a in self.names[:-1]]) \
+               + lastsep + self.names[-1](**kw)
+
+

Added: zope-atextensions/trunk/debian/changelog
===================================================================
--- zope-atextensions/trunk/debian/changelog	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/changelog	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,6 @@
+zope-atextensions (0.8.0-1) unstable; urgency=low
+
+  * Initial release. Closes: #414627.
+
+ -- Alastair McKinstry <mckinstry at debian.org>  Sun,  11 Mar 2007 19:20:05 +0000
+

Added: zope-atextensions/trunk/debian/compat
===================================================================
--- zope-atextensions/trunk/debian/compat	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/compat	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1 @@
+5

Added: zope-atextensions/trunk/debian/control
===================================================================
--- zope-atextensions/trunk/debian/control	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/control	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,31 @@
+Source: zope-atextensions
+Section: web
+Priority: optional
+Maintainer: Alastair McKinstry <mckinstry at debian.org>
+Build-Depends: debhelper (>= 5.0.0)
+Build-Depends-Indep: zope-debhelper (>= 0.3.2.7)
+Standards-Version: 3.7.2
+
+Package: zope-atextensions
+Architecture: all
+Depends: ${zope:Depends}
+Description: Extensions to Archetypes in Zope
+ This package provides some extensions to archetypes.
+ .
+ So far, there are only a few custom fields, widgets and
+ validators, the Record(s)Field/Widget being the mosti
+ generic components. Theses fields exploit the record
+ and records packager directive of Zope's ZPublisher
+ to effectively manage a dictionary (record) or
+ list of dictionaries (records). 
+ The dictionary's keys and the data type of their values
+ can be configured in the AT schema declaration.
+ .
+ To demonstrate their usage, there is a demo content type
+ WorkingGroup. To enable it after install, go to portal types
+ and make it implicitly addable or include it in some folderish
+ type's allowed_content_types.
+ .
+ Future plans are to make this product obsolete by moving the
+ Record(s)Field/Widget to Archetypes proper and the specific fields
+ to More Fields and Widgets.

Added: zope-atextensions/trunk/debian/copyright
===================================================================
--- zope-atextensions/trunk/debian/copyright	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/copyright	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,15 @@
+This package was debianized by Alastair McKinstry <mckinstry at debian.org> on
+Sun, 11 Mar 2007 19:15:01 +0000.
+
+It was downloaded from:
+http://plone.org/products/atextensions
+
+ATExtensions is distributed under the GNU General Public License,
+version 2 or later. A copy of this license may be found
+at /usr/share/common-licenses/GPL-2.
+
+Credits
+
+  Copyright 2003-2006 ITB, Humboldt-University Berlin, Germany
+  written by Raphael Ritz, r.ritz at biologie.hu-berlin.de
+

Added: zope-atextensions/trunk/debian/dzproduct
===================================================================
--- zope-atextensions/trunk/debian/dzproduct	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/dzproduct	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,3 @@
+Name: ATExtensions
+Package: zope-atextensions
+ZopeVersions: 2.9 2.8 2.7

Added: zope-atextensions/trunk/debian/postinst
===================================================================
--- zope-atextensions/trunk/debian/postinst	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/postinst	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,7 @@
+#!/bin/sh -e
+
+. /usr/share/debconf/confmodule
+
+#DEBHELPER#
+
+db_stop

Added: zope-atextensions/trunk/debian/rules
===================================================================
--- zope-atextensions/trunk/debian/rules	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/debian/rules	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,44 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+build: build-stamp
+build-stamp:
+	touch build-stamp
+
+clean:
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp configure-stamp
+	dh_clean
+
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k
+	dh_installdirs
+	dh_installzope  .
+
+binary-indep: build install
+	dh_testdir
+	dh_testroot
+	dh_installdocs -n
+	dh_installexamples
+	dh_installchangelogs 
+	dh_link
+	dh_compress
+	chmod +x debian/zope*/usr/share/zope/Products/ATExtensions/i18n/i18nCall.sh
+	dh_fixperms
+	dh_installdeb
+	dh_shlibdeps
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb
+
+binary-arch:
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary install


Property changes on: zope-atextensions/trunk/debian/rules
___________________________________________________________________
Name: svn:executable
   + 

Added: zope-atextensions/trunk/examples/__init__.py
===================================================================
--- zope-atextensions/trunk/examples/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/examples/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1 @@
+# I'm a package ;-)

Added: zope-atextensions/trunk/examples/demo.py
===================================================================
--- zope-atextensions/trunk/examples/demo.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/examples/demo.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,167 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2004, 2005 ITB, Humboldt-University Berlin     #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+""" demonstrates the use of ATExtensions """
+
+from Products.Archetypes.public import *
+from Products.ATExtensions.ateapi import *
+
+
+schema = BaseSchema + Schema((
+    SimpleNameField('principal_investigator',
+                    widget=RecordWidget(label='Principal Investigator',
+                                        description='The person leading the group',
+                                        ),
+                    ),
+    StringField('home_institute',
+                required=0,
+                widget=StringWidget(label='Institution',
+                                    description='Name of the institution hosting the group'),
+                ),
+    PhoneNumbersField('phone',
+                      schemata="contact",
+                      ),
+    NetAddressField('e_address',
+                    schemata="contact",
+                    widget= RecordWidget(label='E-Contact',
+                                         description='Electronic contact addresses'),
+                    ),
+    AddressField('street_address',
+                 schemata="contact",
+                 required=1,
+                 widget=RecordWidget(label='Address',
+                                     description='Street (or postal) contact address'),
+                 ),
+    # separate schemata page for members and partner groups;
+    # the useage of minimalSize and maximalSize is for demonstration only
+    MultipleNamesField('group_members',
+                       schemata='members',
+                       minimalSize = 1,  # effectively 2 because 'More' is enabled
+                       maximalSize = 4,
+                       widget=RecordsWidget(label="Group Members",
+                                            description="Members of this group; press 'more' to enable additional entries (restricted to 4 at most)."),
+                       ),
+    # and a relation field for partner groups
+    ReferenceField('partners',
+                   schemata='members',
+                   relationship="partner_groups",
+                   multiValued=1,
+                   allowed_types=("WorkingGroup",),
+                   widget=ReferenceWidget(description="Groups with which this group has a collaborations",
+                                          addable=1,
+                                          destination="/",
+                                          ),
+                   ),
+    # and here a record field configured in the schema declaration
+    RecordsField('seminars',
+                 schemata='seminars',
+                 subfields = ('speaker', 'room', 'date'),
+                 subfield_types = {'date':'datetime'},
+                 subfield_sizes = {'speaker' : 15, 'room': 6},
+                 innerJoin = ', ',
+                 outerJoin = '<br />',
+                 widget = RecordsWidget(
+    helper_js=('jscalendar/calendar_stripped.js',
+               'jscalendar/calendar-en.js'),
+    helper_css=('jscalendar/calendar-system.css',),
+            )
+                 
+                ),
+    TextField('short_description',
+              widget=TextAreaWidget(label='Description',
+                                    description="A short (plain text) description of the research group's focus.",
+                                    ),
+              accessor='getShortDescription',
+              edit_accessor='getShortDescription',
+              mutator='setShortDescription',
+              ),
+    StringField('test_combo',
+                schemata='testing',
+                vocabulary='DemoVocab',
+                widget=ComboWidget(description="You need to select 'other' if you want to specify the value yourself below."),
+                ),
+    UrlField('url',
+             schemata='testing',
+             ),
+    RemoteTextField('remote',
+                    schemata='remote',
+                    default_output_type='text/html',
+                    allowable_content_types = ('text/plain',
+                                               'text/html',
+                                               'text/structured',
+                                               ),
+                    ),
+    ))
+
+class WorkingGroup(BaseContent):
+    """
+    Demo from ATExtensions
+    stores information about a research group
+    """
+    content_icon = "group.gif"
+    schema = schema
+    _at_rename_after_creation = True
+    
+    # enable field sharing between 'short_description' and 'description'
+
+    def getShortDescription(self, **kw):
+        """ get the 'description' and put it in the 'short_description'
+        """
+        return self.Description()
+
+    def setShortDescription(self, val, **kw):
+        """ synchronize 'short_description' and 'description'
+        """
+        self.setDescription(val)
+
+    # for the catalog metadata
+
+    def Name(self):
+        """
+        the name of the group
+        """
+        return self.Title() + ', ' + self.getHome_institute()
+
+    def LastName(self):
+        """PI's last name; needed by the catalog for sorting"""
+        return self.getPrincipal_investigator().get('last_name','')
+    
+
+    def PI(self):
+        """
+        the name of the principal investigator
+        """
+        pi_name = self.getPrincipal_investigator().get('first_name','') \
+                  + ' ' + \
+                  self.getPrincipal_investigator().get('last_name','')
+        return pi_name.strip()
+    
+    def City(self):
+        """ subfield of street address """
+        return self.getStreet_address().get('city','')
+
+    def Country(self):
+        """ subfield of street address """
+        return self.getStreet_address().get('country','')
+
+    # the demo vocab
+
+    def DemoVocab(self):
+        """just to have some display list"""
+        return getDisplayList(self, 'phone_number_types')
+    
+    
+def modify_fti(fti):
+    fti['allow_discussion'] = 1
+    fti['global_allow'] = 0
+    # hide unnecessary tabs (usability enhancement)
+    for a in fti['actions']:
+        if a['id'] in ('references',):
+            a['visible'] = 0
+    return fti
+
+registerType(WorkingGroup)

Added: zope-atextensions/trunk/examples/formattablename.py
===================================================================
--- zope-atextensions/trunk/examples/formattablename.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/examples/formattablename.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,43 @@
+##########################################################################
+#                                                                        #
+#           copyright (c) 2005 ITB, Humboldt-University Berlin           #
+#           written by: Raphael Ritz, r.ritz at biologie.hu-berlin.de       #
+#                                                                        #
+##########################################################################
+
+""" demonstrates the use of the formattable name(s) field"""
+
+from Products.Archetypes.public import *
+from Products.ATExtensions.ateapi import *
+
+
+schema = BaseSchema + Schema((
+    FormattableNameField('name1'),
+    FormattableNameField('name2',
+                         subfields = ('title',
+                                      'firstnames',
+                                      'lastname',
+                                      'homepage'),
+                         widget = FormattableNameWidget(
+    format="%f%l: %F %m. %L (%T)",
+    join_abbr=".",
+    withURL = True,
+    ),
+                         ),
+    FormattableNamesField('friends',
+                          widget = FormattableNamesWidget(format="%L %f%m"),
+                          )
+    ))
+
+class FormattableNameDemo(BaseContent):
+    """
+    Demo from ATExtensions
+    Illustrates useage of the formattable name(s) field
+    """
+    content_icon = "user.gif"
+    schema = schema
+    global_allow = 0
+
+    _at_rename_after_creation = True
+
+registerType(FormattableNameDemo)

Added: zope-atextensions/trunk/field/__init__.py
===================================================================
--- zope-atextensions/trunk/field/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,14 @@
+# import the individual fields
+
+from datetime import DateTimeField
+from url import UrlField
+from email import EmailField
+from record import RecordField
+from records import RecordsField
+from record_examples import SimpleNameField, LocationField,\
+     AddressField, ContactField, NetAddressField
+from records_examples import MultipleNamesField, PhoneNumbersField
+from formattablename import FormattableNameField
+from formattablenames import FormattableNamesField
+from comment import CommentField
+from remotetext import RemoteTextField

Added: zope-atextensions/trunk/field/comment.py
===================================================================
--- zope-atextensions/trunk/field/comment.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/comment.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,46 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.CMFCore.utils import getToolByName
+from Products.Archetypes.Field import StringField
+from Products.Archetypes.Registry import registerField
+from Products.Archetypes.Registry import registerPropertyType
+
+from Products.ATExtensions.widget import CommentWidget
+
+class CommentField(StringField):
+    """specific field for URLs""" 
+    _properties = StringField._properties.copy()
+    _properties.update({
+        'type' : 'comment',
+        'comment' : ' ',
+        'comment_type' : 'text/structured',
+        'comment_msgid' : '',
+        'widget' : CommentWidget,
+        })
+    security = ClassSecurityInfo()
+
+    def get(self, instance, **kwargs):
+        domain = self.widget.i18n_domain
+        if self.comment_msgid:
+            comment = instance.translate(domain=domain, msgid=self.comment_msgid, default=self.comment)
+        else:
+            comment = instance.translate(domain=domain, msgid=self.comment, default=self.comment)    
+        transforms = getToolByName(instance, 'portal_transforms', None)
+        if transforms is None:
+            return comment
+        return transforms.convertTo('text/html',
+                                    comment,
+                                    context=instance,
+                                    mimetype=self.comment_type).getData()
+
+    def set(self, instance, value, **kwargs):
+        pass
+
+InitializeClass(CommentField)
+
+registerField(CommentField,
+              title="Comment",
+              description="Used for inserting comments into the views",
+              )
+registerPropertyType('comment', 'string', CommentField)
+registerPropertyType('comment_type', 'string', CommentField)

Added: zope-atextensions/trunk/field/datetime.py
===================================================================
--- zope-atextensions/trunk/field/datetime.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/datetime.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,43 @@
+# -*- coding: UTF-8 -*-
+
+##################################################
+#                                                #
+#    Copyright (C), 2004, Thomas Förster         #
+#    <t.foerster at biologie.hu-berlin.de>          #
+#                                                #
+#    Humboldt University of Berlin               #
+#                                                #
+##################################################
+
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import DateTimeField as BaseField
+from Products.Archetypes.Registry import registerField, \
+     registerPropertyType
+
+from Products.ATExtensions.widget.datetime import DateTimeWidget
+
+class DateTimeField(BaseField):
+    """
+    An improved DateTime Field. It allows to specify
+    whether only dates or only times are interesting.
+    """
+
+    _properties = BaseField._properties.copy()
+    _properties.update({
+        'type' : 'datetime_ng',
+        'widget' : DateTimeWidget,
+        'with_time' : 1, # set to False if you want date only objects
+        'with_date' : 1, # set to False if you want time only objects
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(DateTimeField)
+
+registerField(DateTimeField,
+              title="DateTime Field",
+              description="An improved DateTimeField, which also allows time or date only specifications.",
+              )
+
+registerPropertyType('with_time', 'boolean', DateTimeField)
+registerPropertyType('with_date', 'boolean', DateTimeField)

Added: zope-atextensions/trunk/field/email.py
===================================================================
--- zope-atextensions/trunk/field/email.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/email.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,23 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Field import StringField
+from Products.Archetypes.Registry import registerField
+
+from Products.ATExtensions.widget import EmailWidget
+
+class EmailField(StringField):
+    """specific field for emails""" 
+    _properties = StringField._properties.copy()
+    _properties.update({
+        'type' : 'email',
+        'validators' : ('isEmail'),
+        'widget' : EmailWidget,
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(EmailField)
+
+registerField(EmailField,
+              title="Email",
+              description="Used for storing a validated email.",
+              )

Added: zope-atextensions/trunk/field/formattablename.py
===================================================================
--- zope-atextensions/trunk/field/formattablename.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/formattablename.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,32 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Registry import registerField
+from Products.Archetypes.Field import ObjectField
+from Products.ATExtensions.datatype.formattablename \
+     import FormattableName
+from Products.ATExtensions.widget import FormattableNameWidget
+from record import RecordField
+
+class FormattableNameField(RecordField):
+    """ field for managing a 'FormattableName' (custom type)"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'formattable_name',
+        'default' : FormattableName(),
+        'subfields' : ('title','firstname','middlename','lastname',),
+        'widget' : FormattableNameWidget,
+        })
+    security = ClassSecurityInfo()
+    
+    security.declarePrivate('set')
+    def set(self, instance, value, **kwargs):
+        value = self._decode_strings(value, instance, **kwargs)
+        value = FormattableName(value)
+        ObjectField.set(self, instance, value, **kwargs)
+
+InitializeClass(FormattableNameField)
+
+registerField(FormattableNameField,
+              title="Formattable Name",
+              description="Used for storing a formattable name",
+              )

Added: zope-atextensions/trunk/field/formattablenames.py
===================================================================
--- zope-atextensions/trunk/field/formattablenames.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/formattablenames.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,41 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Registry import registerField
+from Products.Archetypes.Field import ObjectField
+from Products.ATExtensions.datatype.formattablenames \
+     import FormattableNames
+from Products.ATExtensions.widget import FormattableNamesWidget
+from records import RecordsField
+
+class FormattableNamesField(RecordsField):
+    """ field for managing 'FormattableNames' (custom type)"""
+    _properties = RecordsField._properties.copy()
+    _properties.update({
+        'type' : 'formattable_names',
+        'default' : FormattableNames(),
+        'subfields' : ('title','firstname','middlename','lastname',),
+        'subfield_sizes' : {'title':10,
+                            'firstname': 15,
+                            'middlename':15,
+                            'lastname':30},
+        'widget' : FormattableNamesWidget,
+        })
+    security = ClassSecurityInfo()
+    
+    security.declarePrivate('get')
+    def get(self, instance, **kwargs):
+        value = RecordsField.get(self, instance, **kwargs)
+        return FormattableNames(value)
+
+    security.declarePrivate('set')
+    def set(self, instance, value, **kwargs):
+        value = self._decode_strings(value, instance, **kwargs)
+        value = FormattableNames(value)
+        ObjectField.set(self, instance, value, **kwargs)
+
+InitializeClass(FormattableNamesField)
+
+registerField(FormattableNamesField,
+              title="Formattable Names",
+              description="Used for storing formattable names",
+              )

Added: zope-atextensions/trunk/field/record.py
===================================================================
--- zope-atextensions/trunk/field/record.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/record.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,364 @@
+from types import ListType, TupleType, ClassType, FileType, DictType, IntType
+from types import StringType, UnicodeType, StringTypes
+
+from Globals import InitializeClass
+from DateTime import DateTime
+from AccessControl import ClassSecurityInfo
+from Products.PythonScripts.standard import html_quote
+from Products.CMFCore.Expression import Expression
+from Products.CMFCore.Expression import createExprContext
+from Products.Archetypes.debug import log
+from Products.Archetypes.atapi import DisplayList
+from Products.Archetypes.Field import ObjectField
+from Products.Archetypes.Field import encode, decode
+from Products.Archetypes.Registry import registerField, registerPropertyType
+
+# we have to define our own validation handling
+try:
+    from Products.validation import ValidationChain
+    from Products.validation import UnknowValidatorError
+    from Products.validation import FalseValidatorError
+    from Products.validation.interfaces.IValidator \
+         import IValidator, IValidationChain
+    HAS_VALIDATION_CHAIN = 1
+except ImportError:
+    HAS_VALIDATION_CHAIN = 0
+
+from Products.ATExtensions.Extensions.utils import getDisplayList
+from Products.ATExtensions.widget import RecordWidget
+
+class RecordField(ObjectField):
+    """A field that stores a 'record' (dictionary-like) construct"""
+    _properties = ObjectField._properties.copy()
+    _properties.update({
+        'type' : 'record',
+        'default' : {},
+        'subfields' : (),
+        'subfield_types' : {},
+        'subfield_vocabularies' : {},
+        'subfield_labels' : {},
+        'subfield_sizes' : {},
+        'subfield_maxlength' : {},
+        'required_subfields': (),
+        'subfield_validators': {},
+        'subfield_conditions': {},
+        'innerJoin' : ', ',
+        'outerJoin' : ', ',
+        'widget' : RecordWidget,
+        })
+
+    security = ClassSecurityInfo()
+    
+    def getSubfields(self):
+        """the tuple of sub-fields"""
+        return self.subfields
+
+    def getSubfieldType(self, subfield):
+        """
+        optional type declaration
+        default: string
+        """
+        return self.subfield_types.get(subfield, 'string')
+    
+    def getSubfieldLabel(self, subfield):
+        """
+        optional custom label for the subfield
+        default: the id of the subfield
+        """
+        return self.subfield_labels.get(subfield, subfield.capitalize())
+
+    def getSubfieldSize(self, subfield, default=40):
+        """
+        optional custom size for the subfield
+        default: 40
+        only effective for string type subfields
+        """
+        return self.subfield_sizes.get(subfield, default)
+
+    def getSubfieldMaxlength(self, subfield):
+        """
+        otional custom maxlength size for the subfield
+        only effective for string type subfields
+        """
+        return self.subfield_maxlength.get(subfield, 40)
+
+    def isRequired(self,subfield):
+        """
+        looks whether subfield is included in the list of required subfields
+        """
+        return subfield in self.required_subfields
+
+    def isSelection(self,subfield):
+        """select box needed?"""
+        
+        return self.subfield_vocabularies.has_key(subfield)
+
+    def testSubfieldCondition(self, subfield, folder, portal, object):
+        """Test the subfield condition."""
+        try:
+            condition = self.subfield_conditions.get(subfield, None)
+            if condition is not None:
+                __traceback_info__ = (folder, portal, object, condition)
+                ec = createExprContext(folder, portal, object)
+                return Expression(condition)(ec)
+            else:
+                return True
+        except AttributeError:
+            return True
+
+    def getVocabularyFor(self, subfield, instance=None):
+        """the vocabulary (DisplayList) for the subfield"""
+        ## XXX rr: couldn't we just rely on the field's
+        ## Vocabulary method here?
+        value = None
+        vocab = self.subfield_vocabularies.get(subfield, None)
+        if not vocab:
+            raise AttributeError, 'no vocabulary found for %s' %subfield
+
+        if isinstance(vocab, DisplayList):
+            return vocab
+    
+        if type(vocab) in StringTypes:
+            value = None
+            method = getattr(self, vocab, None)
+            if method and callable(method):
+                value = method(instance)
+            else:
+                if instance is not None:
+                    method = getattr(instance, vocab, None)
+                    if method and callable(method):
+                        value = method()
+            if not isinstance(value, DisplayList):
+                raise TypeError, '%s is not a DisplayList %s' %(value, subfield)
+            return value
+        
+        raise TypeError, '%s niether a StringType or a DisplayList for %s' %(vocab, subfield)
+
+    def getViewFor(self, instance, subfield, joinWith=', '):
+        """
+        formatted value of the subfield for display
+        """
+        raw = self.getRaw(instance).get(subfield,'')
+        if type(raw) in (type(()), type([])):
+            raw = joinWith.join(raw)
+        # Prevent XSS attacks by quoting all user input
+        raw = html_quote(str(raw))
+        # this is now very specific
+        if subfield == 'email':
+            return self.hideEmail(raw)
+        if subfield == 'phone':
+            return self.labelPhone(raw)
+        if subfield == 'fax':
+            return self.labelFax(raw)
+        if subfield == 'homepage':
+            return '<a href="%s">%s</a>' % (raw, raw)
+        return raw
+
+    def getSubfieldViews(self,instance,joinWith=', '):
+        """
+        list of subfield views for non-empty subfields
+        """
+        result = []
+        for subfield in self.getSubfields():
+            view = self.getViewFor(instance,subfield,joinWith)
+            if view:
+                result.append(view)
+        return result
+
+    # this is really special purpose and in no ways generic
+    def hideEmail(self,email=''):
+        return 'email: ' + email.replace('@', ' (at) ').replace('.', ' (dot) ')
+
+    def labelPhone(self,phone=''):
+        return 'phone: ' + phone
+
+    def labelFax(self,fax=''):
+        return 'fax: ' + fax
+
+    # enable also a string representation of a dictionary
+    # to be passed in (external edit may need this)
+    # store string values as unicode
+
+    def set(self, instance, value, **kwargs):
+        if type(value) in StringTypes:
+            try:
+                value = eval(value)
+                # more checks to add?
+            except: # what to chatch here?
+                pass
+        value = self._to_dict(value)
+        value = self._decode_strings(value, instance, **kwargs)
+        ObjectField.set(self, instance, value, **kwargs)
+
+    def _to_dict(self, value):
+        if type(value) != type({}) and hasattr(value, 'keys'):
+            new_value = {}
+            new_value.update(value)
+            return new_value
+        return value
+
+    def _decode_strings(self, value, instance, **kwargs):
+        new_value = value
+        for k, v in value.items():
+            if type(v) is type(''):
+                nv =  decode(v, instance, **kwargs)
+                try:
+                    new_value[k] = nv
+                except AttributeError: # Records don't provide __setitem__
+                    setattr(new_value, k , nv)
+
+            # convert datetimes
+            if self.subfield_types.get(k, None) == 'datetime':
+                try:
+                    val = DateTime(v)
+                except: 
+                    val = None
+                    
+                new_value[k] = val
+
+        return new_value
+
+    # Return strings using the site's encoding
+
+    def get(self, instance, **kwargs):
+        value = ObjectField.get(self, instance, **kwargs)
+        return self._encode_strings(value, instance, **kwargs)
+
+    def _encode_strings(self, value, instance, **kwargs):
+        new_value = value
+        for k, v in value.items():
+            if type(v) is type(u''):
+                nv = encode(v, instance, **kwargs)
+                try:
+                    new_value[k] = nv
+                except AttributeError: # Records don't provide __setitem__
+                    setattr(new_value, k , nv)
+        return new_value
+
+    if HAS_VALIDATION_CHAIN:
+        def _validationLayer(self):
+            """
+            Resolve that each validator is in the service. If validator is
+            not, log a warning.
+
+            We could replace strings with class refs and keep things impl
+            the ivalidator in the list.
+
+            Note: XXX this is not compat with aq_ things like scripts with __call__
+            """
+            for subfield in self.getSubfields():
+                self.subfield_validators[subfield] = self._subfieldValidationLayer(subfield)
+        
+        def _subfieldValidationLayer(self, subfield):
+            """
+            for the individual subfields
+            """
+            chainname = 'Validator_%s_%s' % (self.getName(), subfield)
+            current_validators = self.subfield_validators.get(subfield, ())
+
+            if type(current_validators) is DictType:
+                raise NotImplementedError, 'Please use the new syntax with validation chains'
+            elif IValidationChain.isImplementedBy(current_validators):
+                validators = current_validators
+            elif IValidator.isImplementedBy(current_validators):
+                validators = ValidationChain(chainname, validators=current_validators)
+            elif type(current_validators) in (TupleType, ListType, StringType):
+                if len(current_validators):
+                    # got a non empty list or string - create a chain
+                    try:
+                        validators = ValidationChain(chainname, validators=current_validators)
+                    except (UnknowValidatorError, FalseValidatorError), msg:
+                        log("WARNING: Disabling validation for %s/%s: %s" % (self.getName(), subfield, msg))
+                        validators = ()
+                else:
+                    validators = ()
+            else:
+                log('WARNING: Unknow validation %s. Disabling!' % current_validators)
+                validators = ()
+
+            if not subfield in self.required_subfields:
+                if validators == ():
+                    validators = ValidationChain(chainname)
+                if len(validators):
+                    # insert isEmpty validator at position 0 if first validator
+                    # is not isEmpty
+                    if not validators[0][0].name == 'isEmpty':
+                        validators.insertSufficient('isEmpty')
+                else:
+                    validators.insertSufficient('isEmpty')
+
+            return validators
+
+        security.declarePublic('validate')
+        def validate(self, value, instance, errors={}, **kwargs):
+            """
+            Validate passed-in value using all subfield validators.
+            Return None if all validations pass; otherwise, return failed
+            result returned by validator
+            """
+            name = self.getName()
+            if errors and errors.has_key(name):
+                return True
+
+            if self.required_subfields:
+                for subfield in self.required_subfields:
+                    sub_value = value.get(subfield, None)
+                    res = self.validate_required(instance, sub_value, errors)
+                    if res is not None:
+                        return res
+
+            # not touched yet
+            if self.enforceVocabulary:
+                res = self.validate_vocabulary(instance, value, errors)
+                if res is not None:
+                    return res
+
+            # can this part stay like it is?
+            res = instance.validate_field(name, value, errors)
+            if res is not None:
+                return res
+
+            # this should work
+            if self.subfield_validators:
+                res = self.validate_validators(value, instance, errors, **kwargs)
+                if res is not True:
+                    return res
+
+            # all ok
+            return None
+
+        def validate_validators(self, value, instance, errors, **kwargs):
+            result = True
+            total = ''
+            for subfield in self.getSubfields():
+                subfield_validators = self.subfield_validators.get(subfield, None)
+                if subfield_validators:
+                    result = subfield_validators(value.get(subfield),
+                                                 instance=instance,
+                                                 errors=errors,
+                                                 field=self,
+                                                 **kwargs
+                                                 )
+                if result is not True:
+                    total += result
+            return total or result
+
+
+InitializeClass(RecordField)
+
+registerField(RecordField,
+              title="Record",
+              description="Used for storing a 'record' (dictionary-like) construct",
+              )
+
+registerPropertyType('subfields', 'lines', RecordField)
+registerPropertyType('required_subfields', 'lines', RecordField)
+registerPropertyType('subfield_validators', 'mapping', RecordField)
+registerPropertyType('subfield_types', 'mapping', RecordField)
+registerPropertyType('subfield_vocabularies', 'mapping', RecordField)
+registerPropertyType('subfield_labels', 'mapping', RecordField)
+registerPropertyType('subfield_sizes', 'mapping', RecordField)
+registerPropertyType('subfield_maxlength', 'mapping', RecordField)
+registerPropertyType('innerJoin', 'string', RecordField)
+registerPropertyType('outerJoin', 'string', RecordField)
+

Added: zope-atextensions/trunk/field/record_examples.py
===================================================================
--- zope-atextensions/trunk/field/record_examples.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/record_examples.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,115 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Registry import registerField
+
+from Products.ATExtensions.Extensions.utils import getDisplayList
+
+from record import RecordField
+
+class AddressField(RecordField):
+    """ dedicated address field"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'address',
+        'subfields' : ('street1','street2','zip','city', 'country'),
+        'required_subfields': ('city', 'country'),
+        'subfield_labels':{'zip':'ZIP code'},
+        'subfield_vocabularies' :{'country':'CountryNames'},
+        'outerJoin':'<br />',
+        })
+    security = ClassSecurityInfo()
+    
+    security.declarePublic("CountryNames")
+    def CountryNames(self, instance=None):
+        if not instance:
+            instance = self
+        return getDisplayList(instance, 'country_names')
+
+InitializeClass(AddressField)
+
+class LocationField(RecordField):
+    """ dedicated location (city, country) field"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'location',
+        'subfields' : ('city', 'country'),
+        'subfield_types' : {'country' : 'selection'},
+        'subfield_vocabularies' :{'country':'CountryNames'},
+        })
+    security = ClassSecurityInfo()
+    
+    security.declarePublic("CountryNames")
+    def CountryNames(self, instance=None):
+        if not instance:
+            instance = self
+        return getDisplayList(instance, 'country_names')
+
+InitializeClass(LocationField)
+
+class ContactField(RecordField):
+    """name, phone, fax, email, address field"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'contact',
+        'subfields' : ('name','phone','fax','email', 'address'),
+        'innerJoin' : '<br>',
+        'subfield_types' : {'address': 'lines'},
+        'subfield_maxlength' : {'email' : 256},
+        'outerJoin':'<br />',
+        })    
+    security = ClassSecurityInfo()
+
+InitializeClass(ContactField)
+
+class SimpleNameField(RecordField):
+    """just first and last name"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'simple_name',
+        'subfields' : ('first_name','last_name'),
+        'subfield_labels':{'first_name':'First or given name(s)',
+                           'last_name':'Last or family name(s)',
+                           },
+        'outerJoin':' ',
+        })    
+    security = ClassSecurityInfo()
+
+InitializeClass(SimpleNameField)    
+
+class NetAddressField(RecordField):
+    """ dedicated net address field"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'net_address',
+        'subfields' : ('email','homepage'),
+        'subfield_validators':{'homepage':'isURL'},
+        'outerJoin':'<br />',
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(NetAddressField)
+
+registerField(SimpleNameField,
+              title="SimpleName",
+              description="Used for storing a structured (first, last) name",
+              )
+
+registerField(LocationField,
+              title="Location",
+              description="Used for storing a location (city, country)",
+              )
+
+registerField(AddressField,
+              title="Address",
+              description="Used for storing an address (street1, street2, zip, city, country)",
+              )
+
+registerField(ContactField,
+              title="Contact",
+              description="Used for storing contact information (name, phone, fax, email, address)",
+              )
+
+registerField(NetAddressField,
+              title="NetAddress",
+              description="Used for storing email and homepage",
+              )

Added: zope-atextensions/trunk/field/records.py
===================================================================
--- zope-atextensions/trunk/field/records.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/records.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,148 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.PythonScripts.standard import html_quote
+from Products.Archetypes.Registry import registerField, registerPropertyType
+from Products.ATExtensions.config import HAS_VALIDATION_CHAIN
+from Products.ATExtensions.field import RecordField
+from Products.ATExtensions.widget import RecordsWidget
+
+
+class RecordsField(RecordField):
+    """A field that stores a 'record' (dictionary-like) construct"""
+    _properties = RecordField._properties.copy()
+    _properties.update({
+        'type' : 'records',
+        'default' : [],
+        'fixedSize': 0,
+        'minimalSize': 0,
+        'maximalSize': 9999,
+        'innerJoin' : ' ',
+        'outerJoin' : ', ',
+        'widget' : RecordsWidget,
+        })
+
+    security = ClassSecurityInfo()
+
+    def getSize(self, instance):
+        """number of records to store"""
+        return len(self.getRaw(instance))
+
+    def isSizeFixed(self):
+        """do we need an additional line of entry?"""
+        return self.fixedSize
+
+    def showMore(self, values):
+        """
+        return True if the 'More' button should be shown
+        False otherwise
+        """
+        data_length = len(values)
+        if data_length < self.maximalSize and not self.fixedSize:
+            return True
+        else:
+            return False
+
+    def getEditSize(self, instance):
+        """
+        number of record entries to offer in the form
+        at least 'minimalSize' or length of the current
+        list of records (+1 if 'more' is enabled)
+        """
+        data_length = len(self.getRaw(instance))
+        minimum = max(self.minimalSize, data_length)
+        if minimum >= self.maximalSize:
+            return minimum  # not to lose data if uploads had added more
+        if not self.fixedSize:
+            return minimum + 1
+        return minimum
+
+    def getSubfieldValue(self, values, idx, subfield, default=None):
+        """
+        return values[idx].get(key) if existing
+        'default' otherwise
+        """
+        try:
+            return values[idx].get(subfield, default)
+        except IndexError:
+            return default
+
+    def getViewFor(self, instance, idx, subfield, joinWith=', '):
+        """
+        formatted value of the subfield for display
+        """
+        raw = self.getRaw(instance)[idx].get(subfield,'')
+        if type(raw) in (type(()), type([])):
+            raw = joinWith.join(raw)
+        # Prevent XSS attacks by quoting all user input
+        raw = html_quote(str(raw))
+        # this is now very specific
+        if subfield == 'email':
+            return self.hideEmail(raw)
+        if subfield == 'homepage':
+            return '<a href="%s">%s</a>' % (raw, raw)
+        return raw.strip()
+
+    # store string type subfield values as unicode
+    
+    def _encode_strings(self, value, instance, **kwargs):
+        new_value = []
+        for entry in value:
+            new_value.append(
+                RecordField._encode_strings(self, entry, instance, **kwargs)
+                )
+        return new_value
+
+    def _decode_strings(self, value, instance, **kwargs):
+        new_value = []
+        for entry in value:
+            new_value.append(
+                RecordField._decode_strings(self, entry, instance, **kwargs)
+                )
+        return new_value
+
+    # convert the records to persistent dictionaries
+    def _to_dict(self, value):
+        return [RecordField._to_dict(self, entry) for entry in value]
+
+    if HAS_VALIDATION_CHAIN:
+        security.declarePublic('validate')
+        def validate(self, value, instance, errors={}, **kwargs):
+            """
+            Validate passed-in value using all subfield validators.
+            Return None if all validations pass; otherwise, return failed
+            result returned by validator
+            """
+            name = self.getName()
+            if errors and errors.has_key(name):
+                return True
+
+            result = None
+            for record in value:
+                result = RecordField.validate(self,
+                                              record,
+                                              instance,
+                                              errors={},
+                                              **kwargs
+                                              )
+                if result: return result
+            return result
+
+InitializeClass(RecordsField)
+
+registerField(RecordsField,
+              title="Records",
+              description="Used for storing a list of records",
+              )
+
+registerPropertyType('subfields', 'lines', RecordsField)
+registerPropertyType('required_subfields', 'lines', RecordField)
+registerPropertyType('subfield_validators', 'mapping', RecordField)
+registerPropertyType('subfield_types', 'mapping', RecordsField)
+registerPropertyType('subfield_vocabularies', 'mapping', RecordsField)
+registerPropertyType('subfield_labels', 'mapping', RecordsField)
+registerPropertyType('subfield_sizes', 'mapping', RecordsField)
+registerPropertyType('subfield_maxlength', 'mapping', RecordsField)
+registerPropertyType('innerJoin', 'string', RecordsField)
+registerPropertyType('fixedSize', 'boolean', RecordsField)
+registerPropertyType('minimalSize', 'int', RecordsField)
+registerPropertyType('maximalSize', 'int', RecordsField)

Added: zope-atextensions/trunk/field/records_examples.py
===================================================================
--- zope-atextensions/trunk/field/records_examples.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/records_examples.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,54 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Registry import registerField
+
+from Products.ATExtensions.Extensions.utils import getDisplayList
+
+from records import RecordsField
+
+
+class MultipleNamesField(RecordsField):
+    """a list of stuctured names (first, last)"""
+    _properties = RecordsField._properties.copy()
+    _properties.update({
+        'type' : 'multiple_names',
+        'subfields' : ('first_name','last_name'),
+        'subfield_labels':{'first_name':'First or given name(s)',
+                           'last_name':'Last or family name(s)',
+                           },
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(MultipleNamesField)
+
+class PhoneNumbersField(RecordsField):
+    """a list of phone numbers (number, type)"""
+    _properties = RecordsField._properties.copy()
+    _properties.update({
+        'type' : 'phone_numbers',
+        'subfields' : ('type','number'),
+        'subfield_vocabularies':{'type':'PhoneNumberTypes',},
+        'innerJoin':': ',
+        'outerJoin':'<br />',
+        })
+    security = ClassSecurityInfo()
+
+    security.declarePublic("PhoneNumberTypes")
+    def PhoneNumberTypes(self, instance=None):
+        """phone number types"""
+        if not instance:
+            instance = self
+        return getDisplayList(instance, 'phone_number_types')
+
+InitializeClass(PhoneNumbersField)
+
+registerField(MultipleNamesField,
+              title="MultipleNames",
+              description="Used for storing a list of structured names (first_name, last_name)",
+              )
+
+registerField(PhoneNumbersField,
+              title="PhoneNumbers",
+              description="Used for storing a list of phone numbers (type, number)",
+              )
+

Added: zope-atextensions/trunk/field/remotetext.py
===================================================================
--- zope-atextensions/trunk/field/remotetext.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/remotetext.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,180 @@
+import os, urllib
+from DateTime.DateTime import DateTime
+from Products.Archetypes.public import TextField, DisplayList
+from Products.Archetypes.Registry import registerField
+from Products.Archetypes.Registry import registerPropertyType
+from Products.Archetypes.annotations import AT_ANN_STORAGE as KEY
+from Products.Archetypes.annotations import getAnnotation
+
+from Products.ATExtensions.widget.remotetext import RemoteTextWidget
+
+def _pipe(command, input=''):
+    # XXX TODO  what kind of pipe do we want?
+    fi, fo, fe = os.popen3(command, 't')
+    fi.write(input)
+    fi.close()
+    output = fo.read()
+    fo.close()
+    error = fe.read()
+    fe.close()
+    return output, error
+    
+
+class RemoteTextField(TextField):
+    """Field to represent text coming from a remote source like
+    another web site (url) or a subversion repository"""
+
+    _properties = TextField._properties.copy()
+    _properties.update({
+        'type' : 'remote_text',
+        'source_types' : ('svn', 'web'),
+        'timeout' : 1, # should be one day
+        'customized' : False,
+        'widget' : RemoteTextWidget,
+        })
+
+    def getSourceTypeVocabulary(self):
+        """All supported repository types """
+        d = DisplayList()
+        for t in self.source_types:
+            d.add(t,t)
+        return d
+
+    # source handling
+    def getSourcePath(self, instance):
+        """the source path from the instance annotations or empty string"""
+        ann = getAnnotation(instance)
+        subkey = "%s-source-path" % self.getName()
+        return ann.getSubkey(KEY, subkey, default='')
+
+    def setSourcePath(self, instance, value):
+        """Stores source path in the instance annotations"""
+        ann = getAnnotation(instance)
+        subkey = "%s-source-path" % self.getName()
+        return ann.setSubkey(KEY, value, subkey)
+
+    def getSourceType(self, instance):
+        """the source path from the instance annotations or empty string"""
+        ann = getAnnotation(instance)
+        subkey = "%s-source-type" % self.getName()
+        return ann.getSubkey(KEY, subkey, default='')
+
+    def setSourceType(self, instance, value):
+        """Stores source path in the instance annotations"""
+        ann = getAnnotation(instance)
+        subkey = "%s-source-type" % self.getName()
+        return ann.setSubkey(KEY, value, subkey)
+
+    def getTimeout(self, instance):
+        """the timeout from the instance annotations or empty string"""
+        ann = getAnnotation(instance)
+        subkey = "%s-timeout" % self.getName()
+        return ann.getSubkey(KEY, subkey, default=self.timeout)
+
+    def setTimeout(self, instance, value):
+        """Stores the timeout in the instance annotations"""
+        ann = getAnnotation(instance)
+        subkey = "%s-timeout" % self.getName()
+        return ann.setSubkey(KEY, value, subkey)
+
+    def getLastUpdate(self, instance):
+        """time of the last update or None"""
+        ann = getAnnotation(instance)
+        subkey = "%s-last-update" % self.getName()
+        return ann.getSubkey(KEY, subkey, default=None)
+
+    def setLastUpdate(self, instance, value):
+        """Stores the time of the last update in the instance annotations"""
+        ann = getAnnotation(instance)
+        subkey = "%s-last-update" % self.getName()
+        return ann.setSubkey(KEY, value, subkey)
+
+    def is_customized(self, instance):
+        """Flag whether this field has been customized"""
+        ann = getAnnotation(instance)
+        subkey = "%s-customized" % self.getName()
+        return ann.getSubkey(KEY, subkey, default=self.customized)
+
+    def customize(self, instance):
+        """Set the customize flag to True"""
+        ann = getAnnotation(instance)
+        subkey = "%s-customized" % self.getName()
+        ann.setSubkey(KEY, True, subkey)
+        return None
+        
+    def uncustomize(self, instance):
+        """Set the customize flag to False"""
+        ann = getAnnotation(instance)
+        subkey = "%s-customized" % self.getName()
+        ann.setSubkey(KEY, False, subkey)
+        return None
+
+    def get(self, instance, **kw):
+        if self._isexpired(instance):
+            self.set(instance)
+        return TextField.get(self, instance, **kw)
+
+    def _isexpired(self, instance):
+        if self.is_customized(instance):
+            return False
+        last_update = self.getLastUpdate(instance)
+        if last_update is None:
+            return False  # not yet loaded -> not expired
+        timeout = self.getTimeout(instance)
+        now = DateTime()
+        if (now - timeout) > last_update:
+            return True
+        return False
+
+    def set(self, instance, value=None, **kw):
+        source_text = None
+        if value is None or not self.is_customized(instance):
+            source_text = self.getSourceText(instance, **kw)
+        if source_text:
+            value = source_text
+            now = DateTime()
+            self.setLastUpdate(instance, now)
+        TextField.set(self, instance, value, **kw)
+
+    def getSourceText(self, instance, **kw):
+        """main method to fetch the value from the remote site"""
+        stype = self.getSourceType(instance)
+        value = ''
+        if stype == 'web':
+            value = self.getTextFromUrl(instance)
+        if stype == 'svn':
+            value = self.getTextFromSvn(instance)
+        return value or ''
+
+    def getTextFromUrl(self, instance):
+        source_path = self.getSourcePath(instance)
+        try:
+            # better use Tiran's 'urlupload'?
+            value = urllib.urlopen(source_path).read()
+        except:  # XXX FIXME what to catch here?
+            value = ''
+
+        return value
+
+    def getTextFromSvn(self, instance):
+        source_path = self.getSourcePath(instance)
+        error = None
+        command = "svn export %s" % source_path
+        try:
+            value, error = _pipe(command)
+        except:  # XXX FIXME what to catch here? - or is pipe
+            # robust enough never to blow up??
+            value = ''
+
+        if error:
+            return ''
+        return value
+
+registerField(RemoteTextField,
+              title="Remote Text Field",
+              description="Populate a field from a remote source")
+
+registerPropertyType('source_types', 'lines', RemoteTextField)
+registerPropertyType('timeout', 'DateTime', RemoteTextField)
+registerPropertyType('customized', 'boolean', RemoteTextField)
+

Added: zope-atextensions/trunk/field/url.py
===================================================================
--- zope-atextensions/trunk/field/url.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/field/url.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,23 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Field import ObjectField
+from Products.Archetypes.Registry import registerField
+
+from Products.ATExtensions.widget import UrlWidget
+
+class UrlField(ObjectField):
+    """specific field for URLs""" 
+    _properties = ObjectField._properties.copy()
+    _properties.update({
+        'type' : 'url',
+        'validators' : ('isPartialUrl'),
+        'widget' : UrlWidget,
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(UrlField)
+
+registerField(UrlField,
+              title="Url",
+              description="Used for storing a validated url",
+              )

Added: zope-atextensions/trunk/i18n/i18nCall.sh
===================================================================
--- zope-atextensions/trunk/i18n/i18nCall.sh	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/i18nCall.sh	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,5 @@
+#!/bin/bash
+cd ..
+#i18ndude rebuild-pot --pot i18n/atextensions-generated.pot --create atextensions --merge i18n/atextensions-manual.pot ./skins
+cp i18n/plone-manual.pot i18n/plone-generated.pot
+cd i18n
\ No newline at end of file


Property changes on: zope-atextensions/trunk/i18n/i18nCall.sh
___________________________________________________________________
Name: svn:executable
   + 

Added: zope-atextensions/trunk/i18n/plone-da.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-da.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-da.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,19 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-02-07 12:45+0000\n"
+"PO-Revision-Date: 2005-02-07 12:45-0300\n"
+"Last-Translator: Jonas Nielsen <jonasn at mail.tele.dk>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language-code: da\n"
+"Language-name: Danish\n"
+"Domain: plone\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Preferred-encodings: latin1 utf-8\n"
+"X-Is-Fallback-For: da-dk\n"
+

Added: zope-atextensions/trunk/i18n/plone-de.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-de.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-de.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,34 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-04-13 21:25+0000\n"
+"PO-Revision-Date: 2007-01-04 11:07-0000\n"
+"Last-Translator: Mike Gabriel <m.gabriel at sunweavers.net>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Domain: plone\n"
+"Language-Code: de\n"
+"Language-Name: German\n"
+"Preferred-Encodings: latin1 utf-8\n"
+"X-Is-Fallback-For: de-at de-li de-lu de-ch de-de\n"
+
+#. Default: "delete"
+#: records widget
+msgid "delete_this_entry"
+msgstr "löschen"
+
+#. Default: "delete all entries"
+#: records widget
+msgid "delete_all_entries"
+msgstr "alle löschen"
+
+#. Default: " More "
+#: records widget
+msgid "more_entries"
+msgstr " Mehr "
+

Added: zope-atextensions/trunk/i18n/plone-en.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-en.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-en.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,34 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-04-13 21:25+0000\n"
+"PO-Revision-Date: 2007-01-04 11:07-0000\n"
+"Last-Translator: Mike Gabriel <m.gabriel at sunweavers.net>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Domain: plone\n"
+"Language-Code: en\n"
+"Language-Name: English\n"
+"Preferred-Encodings: latin1\n"
+"X-Is-Fallback-For: en-au en-bz en-ca en-ie en-jm en-nz en-ph en-za en-tt en-gb en-us en-zw\n"
+
+#. Default: "delete"
+#: records widget
+msgid "delete_this_entry"
+msgstr "delete"
+
+#. Default: "delete all entries"
+#: records widget
+msgid "delete_all_entries"
+msgstr "delete all entries"
+
+#. Default: " More "
+#: records widget
+msgid "more_entries"
+msgstr " More "
+

Added: zope-atextensions/trunk/i18n/plone-fr.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-fr.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-fr.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,18 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-04-13 21:25+0000\n"
+"PO-Revision-Date: 2004-04-16 21:01+0100\n"
+"Last-Translator: David Convent <david.convent at naturalsciences.be>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Domain: plone\n"
+"Language-Code: fr\n"
+"Language-Name: French\n"
+"Preferred-Encodings: latin1 utf-8\n"
+"X-Is-Fallback-For: fr-be fr-ca fr-lu fr-mc fr-ch\n"

Added: zope-atextensions/trunk/i18n/plone-generated.pot
===================================================================
--- zope-atextensions/trunk/i18n/plone-generated.pot	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-generated.pot	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,29 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"Report-Msgid-Bugs-To: m.gabriel at sunweavers.net\n"
+"POT-Creation-Date: 2006-04-01 16:06+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Domain: plone\n"
+
+#: records widget
+#. Default: "delete"
+msgid "delete_this_entry"
+msgstr ""
+
+#: records widget
+#. Default: "delete all entries"
+msgid "delete_all_entries"
+msgstr ""
+
+#: records widget
+#. Default: " More "
+msgid "more_entries"
+msgstr ""

Added: zope-atextensions/trunk/i18n/plone-it.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-it.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-it.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,19 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-05-14 12:45+0000\n"
+"PO-Revision-Date: 2004-05-18 12:45-0300\n"
+"Last-Translator: Enzo Cesanelli <enzo at noiza.com>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language-code: it\n"
+"Language-name: Italian\n"
+"Domain: plone\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Preferred-encodings: latin1 utf-8\n"
+"X-Is-Fallback-For: it-it it-hr it-sm it-si it-ch\n"
+

Added: zope-atextensions/trunk/i18n/plone-manual.pot
===================================================================
--- zope-atextensions/trunk/i18n/plone-manual.pot	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-manual.pot	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,29 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"Report-Msgid-Bugs-To: m.gabriel at sunweavers.net\n"
+"POT-Creation-Date: 2006-04-01 16:06+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Domain: plone\n"
+
+#: records widget
+#. Default: "delete"
+msgid "delete_this_entry"
+msgstr ""
+
+#: records widget
+#. Default: "delete all entries"
+msgid "delete_all_entries"
+msgstr ""
+
+#: records widget
+#. Default: " More "
+msgid "more_entries"
+msgstr ""

Added: zope-atextensions/trunk/i18n/plone-nl.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-nl.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-nl.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,18 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-04-13 21:25+0000\n"
+"PO-Revision-Date: 2004-04-16 21:01+0100\n"
+"Last-Translator: David Convent <david.convent at naturalsciences.be>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Domain: plone\n"
+"Language-Code: nl\n"
+"Language-Name: Nederlands\n"
+"Preferred-Encodings: latin1 utf-8\n"
+"X-Is-Fallback-For: nl-be\n"

Added: zope-atextensions/trunk/i18n/plone-pt.po
===================================================================
--- zope-atextensions/trunk/i18n/plone-pt.po	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/i18n/plone-pt.po	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,18 @@
+# Gettext Message File for ATExtensions.
+# Raphael Ritz <r.ritz at biologie.hu-berlin.de>, 2002-2006
+msgid ""
+msgstr ""
+"Project-Id-Version: ATExtensions\n"
+"POT-Creation-Date: 2004-05-14 12:45+0000\n"
+"PO-Revision-Date: 2005-04-26 07:34+0100\n"
+"Last-Translator: J M Cerqueira Esteves <jmce at artenumerica.com>\n"
+"Language-Team: ATExtensions i18n <r.ritz at biologie.hu-berlin.de>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language-code: pt\n"
+"Language-name: Português\n"
+"Domain: plone\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Preferred-encodings: latin1 utf-8\n"
+"X-Is-Fallback-For: pt-pt pt-br\n"

Added: zope-atextensions/trunk/skins/at_extensions/address_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/address_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/address_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,82 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      i18n:domain="plone">
+
+  <body>
+
+    <!-- Address Widgets -->
+    <metal:view_macro define-macro="view"
+		      tal:replace="structure python:accessor().replace('\n','<br />')" />
+
+
+    <metal:define define-macro="edit">
+          <fieldset><legend>Address</legend>
+
+          <tal:block tal:define="addr python:request.form.get('address', here.getAddress());
+	                    street1 python:addr.get('street1','');
+	                    street2 python:addr.get('street2','');
+	                    zip python:addr.get('zip','');
+	                    city python:addr.get('city','');
+	                    state python:addr.get('state','');
+	                    country python:addr.get('country','');">
+	    <div class="label">Street</div>
+	    <div class="field"> 
+	      <input name="address.street1:record:ignore_empty"
+		tal:attributes="value street1;
+                                tabindex tabindex/next" />
+	    </div>
+	    <div class="label">Street (cont)</div>
+	    <div class="field">	
+	      <input name="address.street2:record:ignore_empty"
+		tal:attributes="value street2;
+                                tabindex tabindex/next" />
+	    </div>
+	    <div class="label">ZIP Code</div>
+            <div class="field">
+	      <input name="address.zip:record:ignore_empty"
+		tal:attributes="value zip;
+                                tabindex tabindex/next" />
+	    </div>
+	    <div class="label">City</div>
+            <div class="field">
+	      <input name="address.city:record:ignore_empty"
+		tal:attributes="value city;
+                                tabindex tabindex/next" />
+	    </div>
+	    <div class="label">State</div>
+            <div class="field">
+	      <input name="address.state:record:ignore_empty"
+		tal:attributes="value state;
+                                tabindex tabindex/next" /> <br />
+	    </div>
+	    <div class="label">Country</div>
+            <div class="field">
+            <select name="address.country:record:ignore_empty" size="1"
+                       tal:attributes="tabindex tabindex/next" 
+		       tal:define="values python:here.portal_properties.nip_properties.getProperty('country_names')">
+                <option tal:repeat="value values"
+                        tal:content="value"
+			tal:attributes="value value;
+                                        selected python:test(value==country, 'selected', '');"> #
+                </option>
+	    </select>
+            </div>
+          </tal:block>
+
+
+          </fieldset>  
+        
+
+    </metal:define>
+
+    <div metal:define-macro="search">
+      <div metal:use-macro="here/widgets/string/macros/edit">
+      </div>
+    </div>
+
+  </body>
+
+</html>
+

Added: zope-atextensions/trunk/skins/at_extensions/combo_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/combo_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/combo_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,97 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+
+<head>
+    <title></title>
+</head>
+
+<body>
+
+    <!-- Selection Widgets -->
+
+    <metal:view_macro define-macro="view"
+                      tal:define="vocab python:field.Vocabulary(here);
+                                  value python:accessor();
+                                  display python:here.displayValue(vocab, value)"
+                      tal:replace="structure display" />
+
+    <metal:define define-macro="edit">
+
+        <metal:use use-macro="field_macro | here/widgets/field/macros/edit">
+
+            <metal:fill fill-slot="widget_body"
+                        tal:define="vocab python:field.Vocabulary(here);
+                                    vlen python:len(vocab);
+                                    format python:widget.format">
+
+                <tal:shortVocab condition="python:(vlen &lt; 4 and format == 'flex') or (format == 'radio')">
+
+                    <!-- Radio when the vocab is short < 4 -->
+
+                    <tal:radios repeat="item vocab">
+
+                        <input class="noborder"
+                               tabindex=""
+                               type="radio"
+                               tal:define="tabindex tabindex/next"
+                               tal:attributes="name fieldName;
+                                               id string:${fieldName}_${tabindex};
+                                               checked python:here.checkSelected(item, value);
+                                               value item;
+                                               tabindex tabindex;"
+                               />
+
+                        <label tal:content="python:here.translate(vocab.getMsgId(item), default=vocab.getValue(item))"
+                               tal:attributes="for string:${fieldName}_${tabindex/pos}" />
+
+                        <br />
+
+                    </tal:radios>
+
+                </tal:shortVocab>
+
+                <tal:longVocab condition="python:(vlen >= 4 and format == 'flex') or (format in ('select', 'pulldown'))">
+
+                    <!-- Pulldown when longer -->
+
+                    <select tal:attributes="name fieldName;
+                                            id fieldName;
+                                            tabindex tabindex/next;">
+
+                        <option tal:repeat="item vocab"
+                                tal:attributes="value item;
+                                                selected python:test(here.checkSelected(item, value), 'selected', None);"
+                                tal:content="python:here.translate(vocab.getMsgId(item), default=vocab.getValue(item))"
+                                i18n:translate=""
+                                />
+
+                    </select>
+
+                </tal:longVocab>
+		<span>or specify:
+		    <input type="text"
+		           name=""
+			   value=""
+			   size="30"
+			   tabindex="#"
+			   tal:attributes="name string:${fieldName}_other;
+			   value value;
+			   tabindex tabindex/next;"
+			   />
+		</span>
+            </metal:fill>
+
+        </metal:use>
+
+    </metal:define>
+
+    <div metal:define-macro="search">
+        <div metal:use-macro="here/widgets/selection/macros/edit" />
+    </div>
+
+</body>
+
+</html>

Added: zope-atextensions/trunk/skins/at_extensions/comment_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/comment_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/comment_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,26 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+
+<head><title></title></head>
+<body>
+		    
+    <!-- Comment Widgets -->
+
+    <div metal:define-macro="view">
+	<div metal:use-macro="here/widgets/string/macros/view">
+        </div>
+    </div>
+    <div metal:define-macro="edit">
+	<div metal:use-macro="here/widgets/string/macros/view">
+	</div>
+    </div>
+    <div metal:define-macro="search">
+	<div metal:use-macro="here/widgets/string/macros/view">
+	</div>
+    </div>
+
+</body>
+</html>

Added: zope-atextensions/trunk/skins/at_extensions/customize_remotefield.cpy
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/customize_remotefield.cpy	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/customize_remotefield.cpy	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,18 @@
+## Controller Python Script "customize_remotefield"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind state=state
+##bind subpath=traverse_subpath
+##parameters=
+##title=
+##
+field_id = context.REQUEST.get('customize_field', None)
+
+if field_id is not None:
+    field = context.getField(field_id)
+    field.customize(context)
+
+# Always make sure to return the ControllerState object
+return state

Added: zope-atextensions/trunk/skins/at_extensions/datetime_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/datetime_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/datetime_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,282 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+
+  <head><title></title></head>
+
+  <body>
+
+    <!-- Calendar Widgets -->
+
+    <metal:view_macro define-macro="view"
+       tal:define="d accessor;
+                   d python:test(d and d=='None','',d);
+                   format widget/format;
+                   formatted python:format and d and d.strftime(format);
+                   result python:(format and formatted) or (d and here.toPortalTime(d, long_format=1));"
+       tal:replace="structure python:result" />
+
+    <metal:define define-macro="edit"
+       tal:define="format widget/format">
+      <metal:use use-macro="field_macro | here/widgets/field/macros/edit">
+        <metal:fill fill-slot="widget_body">
+          <tal:define define="id fieldName;
+                              inputname fieldName;
+                              formname string:edit_form;
+                              inputvalue python:test(value!='None', value, '');">
+            <metal:use use-macro="here/datetime_widget/macros/calendarDatePickerBoxNG">
+              a calendar, hopefully
+            </metal:use>
+          </tal:define>
+        </metal:fill>
+      </metal:use>
+    </metal:define>
+
+    <metal:define define-macro="search">
+      <metal:field use-macro="here/widgets/field/macros/edit">
+        <metal:slot fill-slot="widget_body">
+          <tal:from define="id fieldName;
+                            inputname fieldName;
+                            formname string:search_form;
+                            inputvalue python:min(request.form.get(fieldName, None) or request.other.get(fieldName, None) or ('1975-05-26',)).strip();
+                            tabindex tabindex/next">
+            <metal:box use-macro="here/datetime_widget/macros/calendarDatePickerBoxNG">
+              a calendar, hopefully
+            </metal:box>
+          </tal:from>
+          -
+          <tal:to define="id fieldName;
+                          inputname fieldName;
+                          formname string:search_form;
+                          inputvalue python:max(request.form.get(fieldName, None) or request.other.get(fieldName, None) or ('2975-05-26',)).strip();
+                          tabindex tabindex/next">
+            <metal:box use-macro="here/datetime_widget/macros/calendarDatePickerBoxNG">
+              a calendar, hopefully
+            </metal:box>
+          </tal:to>
+          <input type="hidden"
+                 name="field_usage"
+                 value="range:min:max"
+                 tal:attributes="name string:${fieldName}_usage"
+                 />
+        </metal:slot>
+      </metal:field>
+    </metal:define>
+
+    <div metal:define-macro="calendarDatePickerBoxNG" tal:define="
+	 tabindex tabindex/next|tabindex|nothing;
+	 input_id string:${formname}_${inputname}_${tabindex};
+	 values   python:here.date_components_support(inputvalue);
+	 days values/days; months values/months; years values/years;
+	 minutes values/minutes; hours values/hours;
+	 yearStart python:years[0]['value'];
+	 yearEnd python:years[-1]['value'];">
+
+      <input type="hidden" tal:attributes="name string:$inputname;
+	     id input_id;
+	     value inputvalue;" />
+
+      <span>
+	<select name="ic_year"
+		id="ic_year"
+		size="1"
+		tal:attributes="tabindex tabindex;
+		name string:${inputname}_year;
+		id string:${input_id}_year;
+		onfocus onfocus|nothing;
+		onblur onblur|nothing;
+		onchange
+		string:update_date_field('${input_id}','${input_id}_year','${input_id}_month','${input_id}_day','${input_id}_hour','${input_id}_minute');"
+		tal:condition="field/with_date">
+
+	  <option value="year"
+		  tal:repeat="year years"
+		  tal:attributes="value year/value; selected year/selected"
+		  i18n:translate=""
+	  tal:content="year/id">year</option>
+
+	</select>
+
+	<input name="ic_year" id="ic_year" type="hidden" tal:attributes="name
+	     string:${inputname}_year; id string:${input_id}_year;
+	     value yearStart;" tal:condition="not:field/with_date"/>
+      </span>
+
+      <span i18n:translate="date_separator" tal:condition="field/with_date">/</span>
+
+      <span>
+	<select name="ic_month"
+		id="ic_month"
+		size="1"
+		tal:attributes="tabindex tabindex;
+		name string:${inputname}_month;
+		id string:${input_id}_month;
+		onfocus onfocus|nothing;
+		onblur onblur|nothing;
+		onchange
+		string:update_date_field('${input_id}','${input_id}_year','${input_id}_month','${input_id}_day','${input_id}_hour','${input_id}_minute');"
+		tal:condition="field/with_date">
+
+	  <option value="subj"
+		  tal:repeat="month months"
+		  tal:attributes="value month/value; selected month/selected"
+		  i18n:translate=""
+	  tal:content="month/id">month name</option>
+
+	</select>
+
+	<input name="ic_month" id="ic_month" type="hidden" tal:attributes="name
+	     string:${inputname}_month; id string:${input_id}_month;
+	     value python: months[1]['value'];"
+	     tal:condition="not:field/with_date"/>
+
+      </span>
+
+      <span i18n:translate="date_separator" tal:condition="field/with_date">/</span>
+
+      <span>
+	<select name="ic_day"
+		id="ic_day"
+		size="1"
+		tal:attributes="tabindex tabindex;
+		name string:${inputname}_day;
+		id string:${input_id}_day;
+		onfocus onfocus|nothing;
+		onblur onblur|nothing;
+		onchange
+		string:update_date_field('${input_id}','${input_id}_year','${input_id}_month','${input_id}_day','${input_id}_hour','${input_id}_minute');"
+		tal:condition="field/with_date">
+
+	  <option value="subj"
+		  tal:repeat="day days"
+		  tal:attributes="value day/value; selected day/selected"
+		  i18n:translate=""
+	  tal:content="day/id">day</option>
+
+	</select>
+
+	<input name="ic_day" id="ic_day" type="hidden" tal:attributes="name
+	     string:${inputname}_day; id string:${input_id}_day;
+	     value python: days[1]['value'];" tal:condition="not:field/with_date"/>
+      </span>
+
+      <a tal:attributes="onclick string:return showJsCalendar('${input_id}_month','$input_id','${input_id}_year','${input_id}_month','${input_id}_day','${input_id}_hour','${input_id}_minute',$yearStart,$yearEnd);" 
+         tal:condition="field/with_date"><img tal:replace="structure here/popup_calendar.gif" /></a>
+
+      <span>
+	<select name="ic_hour"
+		id="ic_hour"
+		size="1"
+		tal:attributes="tabindex tabindex;
+		name string:${inputname}_hour;
+		id string:${input_id}_hour;
+		onfocus onfocus|nothing;
+		onblur onblur|nothing;
+		onchange
+		string:update_date_field('${input_id}','${input_id}_year','${input_id}_month','${input_id}_day','${input_id}_hour','${input_id}_minute');"
+		tal:condition="field/with_time">
+
+	  <option value="subj"
+		  tal:repeat="hour hours"
+		  tal:attributes="value hour/value; selected hour/selected"
+		  i18n:translate=""
+	  tal:content="hour/id">hour</option>
+
+	</select>
+
+	<input name="ic_hour" id="ic_hour" type="hidden" tal:attributes="name
+	     string:${inputname}_hour; id string:${input_id}_hour;
+	     value string:0;" tal:condition="not:field/with_time"/>
+      </span>
+
+      <span i18n:translate="time_separator" tal:condition="field/with_time">:</span>
+
+      <span>
+	<select name="ic_minute"
+		id="ic_minute"
+		size="1"
+		tal:attributes="tabindex tabindex;
+		name string:${inputname}_minute;
+		id string:${input_id}_minute;
+		onfocus onfocus|nothing;
+		onblur onblur|nothing;
+		onchange
+		string:update_date_field('${input_id}','${input_id}_year','${input_id}_month','${input_id}_day','${input_id}_hour','${input_id}_minute');"
+		tal:condition="field/with_time">
+
+	  <option value="subj"
+		  tal:repeat="minute minutes"
+		  tal:attributes="value minute/value; selected minute/selected"
+		  i18n:translate=""
+	  tal:content="minute/id">hour</option>
+
+	</select>
+
+	<input name="ic_minute" id="ic_minute" type="hidden" tal:attributes="name
+	     string:${inputname}_minute; id string:${input_id}_minute;
+	     value string:0;" tal:condition="not:field/with_time"/>
+      </span>
+
+    </div>
+
+    <div metal:define-macro="calendarDatePickerBoxPopupNG"
+	 class="container"
+	 tal:define="DateTime python:modules['DateTime'].DateTime;
+	 current python:DateTime();
+	 month python:request.get('month', DateTime().month());
+	 year python:request.get('year', DateTime().year());
+	 prevMonthTime python:here.getPreviousMonth(month, year);
+	 nextMonthTime python:here.getNextMonth(month, year);
+	 weeks python:here.portal_calendar.getWeeksList(month=month, year=year);
+	 input_id string:${formname}_${inputname}_${tabindex};">
+
+      <table cellpadding="2" cellspacing="0" border="0" class="calendar" id="thecalendar">
+	<tr tal:define="calendarurl python:'%s?input_id=%s' % (path('template/absolute_url'),input_id) ">
+	  <th>
+	    <a href="" tal:attributes="href python:'%s&amp;month:int=%d&amp;year:int=%d' % (calendarurl,prevMonthTime.month(),prevMonthTime.year())">&laquo;</a>
+	  </th>
+	  <th colspan="5" tal:define="date string:$month/1/$year">
+	    <span i18n:translate="" tal:omit-tag="">
+	      <span i18n:name="monthname">
+		<span i18n:translate="" tal:content="python:DateTime(date).strftime('%B').capitalize()" tal:omit-tag="" />
+	      </span>
+	      <span i18n:name="year" tal:content="python:DateTime(date).year()" tal:omit-tag=""/>
+	    </span>
+	  </th>
+
+	  <th>
+	    <a href="" tal:attributes="href python:'%s&amp;month:int=%d&amp;year:int=%d' % (calendarurl,nextMonthTime.month(),nextMonthTime.year())">&raquo;</a>
+	  </th>
+	</tr>
+
+	<tr tal:define="weekdays here/portal_calendar/getDays">
+	  <tal:block repeat="weekday weekdays">
+	    <td class="weekdays" i18n:translate="" tal:content="weekday">Su</td>
+	  </tal:block>
+	</tr>
+	<tr tal:repeat="week weeks">
+	  <tal:block define="days week">
+	    <tal:block repeat="day days" tal:omit-tag="">
+	      <td tal:condition="not: python: day">
+		&nbsp;
+	      </td>
+	      <td class="noevent" tal:condition="python: day"
+		  tal:define="datestring python:'%d/%0.2d/%0.2d'%(year,month,day)"
+		  tal:attributes="class python:test(current.year()==year and current.month()==month and current.day()==int(day), 'todaynoevent', 'noevent')">
+		<a href="" tal:attributes="onclick string:returndate('${datestring}');; return false" tal:content="day">
+		  day number
+		</a>
+	      </td>
+	    </tal:block>
+	  </tal:block>
+	</tr>
+
+      </table>
+
+    </div>
+
+  </body>
+
+</html>

Added: zope-atextensions/trunk/skins/at_extensions/deadlines_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/deadlines_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/deadlines_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      i18n:domain="plone">
+
+  <body>
+
+    <!-- Deadlines Widgets -->
+    <metal:view_macro define-macro="view"
+		      tal:replace="structure accessor" />
+
+
+    <metal:define define-macro="edit">
+          <fieldset><legend>Deadlines</legend>
+          <table>
+          <tr>
+	  <th>Type</th><th>Date</th><th>Comment</th>
+          </tr>
+	  <tr>
+	  <td>the deadline's type</td>
+	  <td>YYYY/MM/DD</td>
+	  <td>optional; e.g., specific time</td>
+	  </tr>
+	  <div tal:condition="python:here.getDeadlineList()"
+	       tal:repeat="dl python:request.get('deadlines',here.getDeadlineList())">
+            <div tal:define="deadline_type python:dl.get('deadline_type','');
+                             date python:dl.get('date','');
+                             comment python:dl.get('comment','');">
+            <tr>
+            <td><select name="deadlines.deadline_type:records:ignore_empty" size="1"
+                       tal:attributes="tabindex tabindex/next" 
+		       tal:define="values python:here.portal_properties.nip_properties.getProperty('deadline_types')">
+                <option tal:repeat="value values"
+                        tal:content="value"
+			tal:attributes="value value;
+                                        selected python:test(value==deadline_type, 'selected', '');"> #
+                </option>
+	    </select>
+            </td>
+            <td><input name="deadlines.date:records:ignore_empty" size="15"
+                       tal:attributes="value date;
+                                       tabindex tabindex/next" /></td>
+            <td><input name="deadlines.comment:records:ignore_empty" size="35"
+                       tal:attributes="value comment;
+                                       tabindex tabindex/next" /></td>
+            </tr>              
+            </div>
+	  </div>
+          <div>
+            <tr tal:define="dl python:request.form.get('nextdeadline', {});
+	                    deadline_type python:dl.get('deadline_type','');
+                            date python:dl.get('date','');
+                            comment python:dl.get('comment','');">
+            <td><select name="nextdeadline.deadline_type:record:ignore_empty" size="1"
+                       tal:attributes="tabindex tabindex/next" 
+		       tal:define="values python:here.portal_properties.nip_properties.getProperty('deadline_types')">
+                <option tal:repeat="value values"
+                        tal:content="value"
+			tal:attributes="value value;
+                                        selected python:test(value==deadline_type, 'selected', '');"> #
+                </option>
+	    </select>
+            </td>
+<!--	    <td tal:define="id date;
+	       inputname  date;
+	       formname   string:edit_form;
+	       inputvalue python: test(date, date, '');
+	       tabindex tabindex/next;"
+	       tal:omit-tag="">
+	    <div metal:use-macro="here/calendar_macros/macros/calendarDatePickerBox|here/calendar_slot/macros/calendarDatePickerBox">
+	      a calendar, hopefully
+	    </div>
+	    </td>
+rr: could not get this to work -->
+            <td><input name="nextdeadline.date:record:ignore_empty" size="15"
+                 tal:attributes="value date;
+                                 tabindex tabindex/next" /></td>
+            <td><input name="nextdeadline.comment:record:ignore_empty" size="35"
+                 tal:attributes="value comment;
+                                 tabindex tabindex/next" /></td>
+            </tr>              
+          </div>
+          </table>
+
+          <div class="row">
+            <div class="label"></div>
+            <div class="field">
+                <input class="context" type="submit" name="more_deadlines" value=" More Deadlines "  
+		 tal:attributes="tabindex tabindex/next" /> 
+            </div>            
+          </div> 
+          </fieldset>  
+        
+
+    </metal:define>
+
+    <div metal:define-macro="search">
+      <div metal:use-macro="here/widgets/string/macros/edit">
+      </div>
+    </div>
+
+  </body>
+
+</html>
+

Added: zope-atextensions/trunk/skins/at_extensions/email_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/email_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/email_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+  <head><title></title></head>
+  <body>
+
+    <!-- Email Widgets -->
+    <metal:view_macro define-macro="view"
+                      tal:define="email accessor;
+                                  at_mask python:widget.at_mask">
+    <div
+     tal:condition="isAnon"
+     tal:content="python:email.replace('@',at_mask)">
+       email
+    </div>
+    <a href="#"
+     tal:condition="not:isAnon"
+     tal:attributes="href string:mailto:$email"
+     tal:content="email">
+       email
+    </a>
+    </metal:view_macro>
+  </body>
+
+</html>

Added: zope-atextensions/trunk/skins/at_extensions/formatdemo.py
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/formatdemo.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/formatdemo.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,26 @@
+## Script (Python) "formatdemo"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=
+##
+# This is s special purpose script to illustrate
+# the formatting capabilities of the formattable name(s)
+#
+# it can only be applied to 'Formattable Name Demo' objects
+#
+n1 = context.getName1()
+n2 = context.getName2()
+g = context.getFriends()
+print n1
+print n1("%f %L")
+print n2
+print n2("%f%l (%F %L)")
+print n1+n2
+print (n1+n2)(lastsep=' & ')
+print g
+print (n1+n2+g)(format="%L %f%m", lastsep=' & ')
+return printed

Added: zope-atextensions/trunk/skins/at_extensions/formattablename_view.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/formattablename_view.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/formattablename_view.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,21 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+  <head><title></title></head>
+  <body>
+
+   <!-- FormattedName view macro -->
+   <metal:view_macro define-macro="view">
+     <div tal:define="name accessor;
+                      kwargs widget/getKwArgs;
+                      value python:name(**kwargs)"
+          tal:content="structure value">
+          -- the formatted name goes here --
+     </div>
+   </metal:view_macro>
+  </body>
+
+</html>
+

Added: zope-atextensions/trunk/skins/at_extensions/getDisplayView.py
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/getDisplayView.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/getDisplayView.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,15 @@
+## Script (Python) "getDisplayView"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=value, join=', '
+##title=
+##
+if same_type(value, '') or same_type(value, u''):
+    return value
+try:
+    return join.join(value)
+except TypeError:
+    return value

Added: zope-atextensions/trunk/skins/at_extensions/more_edit.cpy
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/more_edit.cpy	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/more_edit.cpy	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,149 @@
+## Script (Python) "content_edit"
+##title=Edit content
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind state=state
+##bind subpath=traverse_subpath
+##parameters=id=''
+##
+REQUEST = context.REQUEST
+SESSION = REQUEST.SESSION
+
+old_id = context.getId()
+
+try:
+    new_context = context.portal_factory.doCreate(context, id)
+except AttributeError:
+    # Fallback for AT + plain CMF where we don't have a portal_factory
+    new_context = context
+new_context.processForm()
+
+# Get the current language and put it in request/LANGUAGE
+form = REQUEST.form
+if form.has_key('current_lang'):
+    form['language'] = form.get('current_lang')
+    
+
+portal_status_message = context.translate(
+    msgid='message_content_changes_saved',
+    domain='archetypes',
+    default='Content changes saved.')
+
+portal_status_message = REQUEST.get('portal_status_message', portal_status_message)
+
+# handle navigation for multi-page edit forms
+next = not REQUEST.get('form_next', None) is None
+previous = not REQUEST.get('form_previous', None) is None
+fieldset = REQUEST.get('fieldset', None)
+schemata = new_context.Schemata()
+
+# check for the more button
+more = not REQUEST.get('form.button.more', None) is None
+
+if next or previous:
+    s_names = [s for s in schemata.keys() if s != 'metadata']
+
+    if previous:
+        s_names.reverse()
+
+    next_schemata = None
+    try:
+        index = s_names.index(fieldset)
+    except ValueError:
+        raise 'Non-existing fieldset: %s' % fieldset
+    else:
+        index += 1
+        if index < len(s_names):
+            next_schemata = s_names[index]
+            return state.set(status='next_schemata',
+                             context=new_context,
+                             fieldset=next_schemata,
+                             portal_status_message=portal_status_message)
+
+    if next_schemata != None:
+        return state.set(status='next_schemata', \
+                 context=new_context, \
+                 fieldset=next_schemata, \
+                 portal_status_message=portal_status_message)
+    else:
+        raise 'Unable to find next field set after %s' % fieldset
+
+env = state.kwargs
+reference_source_url = env.get('reference_source_url')
+if not more and reference_source_url is not None:
+    reference_source_url = env['reference_source_url'].pop()
+    reference_source_field = env['reference_source_field'].pop()
+    reference_source_fieldset = env['reference_source_fieldset'].pop()
+    portal = context.portal_url.getPortalObject()
+    reference_obj = portal.restrictedTraverse(reference_source_url)
+    portal_status_message = context.translate(
+        msgid='message_reference_added',
+        domain='archetypes',
+        default='Reference Added.')
+
+    edited_reference_message = context.translate(
+        msgid='message_reference_edited',
+        domain='archetypes',
+        default='Reference Edited.')
+
+    # update session saved data
+    uid = new_context.UID()
+    SESSION = context.REQUEST.SESSION
+    saved_dic = SESSION.get(reference_obj.getId(), None)
+    if saved_dic:
+        saved_value = saved_dic.get(reference_source_field, None)
+        if same_type(saved_value, []):
+            # reference_source_field is a multiValued field, right!?
+            if uid in saved_value:
+                portal_status_message = edited_reference_message
+            else:
+                saved_value.append(uid)
+        else:
+            if uid == saved_value:
+                portal_status_message = edited_reference_message
+            else:
+                saved_value = uid
+        saved_dic[reference_source_field] = saved_value
+        SESSION.set(reference_obj.getId(), saved_dic)
+    
+    context.remove_creation_mark(old_id)
+
+    kwargs = {
+        'status':'success_add_reference',
+        'context':reference_obj,
+        'portal_status_message':portal_status_message,
+        'fieldset':reference_source_fieldset,
+        'field':reference_source_field,
+        'reference_focus':reference_source_field,
+        }
+    return state.set(**kwargs)
+
+if state.errors:
+    errors = state.errors
+    s_items = [(s, schemata[s].keys()) for s in schemata.keys()]
+    fields = []
+    for s, f_names in s_items:
+        for f_name in f_names:
+            fields.append((s, f_name))
+    for s_name, f_name in fields:
+        if errors.has_key(f_name):
+            REQUEST.set('fieldset', s_name)
+            return state.set(
+                status='failure',
+                context=new_context,
+                portal_status_message=portal_status_message)
+
+try:
+    context.remove_creation_mark(old_id)
+except AttributeError:  # for backwards compatibility
+    pass
+
+if not state.errors:
+    from Products.CMFPlone import transaction_note
+    transaction_note('Edited %s %s at %s' % (new_context.meta_type, new_context.title_or_id(), new_context.absolute_url()))
+
+return state.set(status='success',
+                 context=new_context,
+                 portal_status_message=portal_status_message)

Added: zope-atextensions/trunk/skins/at_extensions/more_edit.cpy.metadata
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/more_edit.cpy.metadata	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/more_edit.cpy.metadata	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,13 @@
+[default]
+title = More Edit
+
+[validators]
+validators..more = 
+validators = validate_base
+
+[actions]
+action.success..more = traverse_to_action:string:edit
+action.success = traverse_to:string:validate_integrity
+action.success_add_reference = redirect_to_action:string:edit
+action.failure = traverse_to_action:string:edit
+action.next_schemata = traverse_to_action:string:edit

Added: zope-atextensions/trunk/skins/at_extensions/record_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/record_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/record_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,129 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+  <head><title></title></head>
+  <body>
+
+    <!-- Record Widgets -->
+   <metal:view_macro define-macro="view">
+       <div tal:define="values_dict python:field.getAccessor(here)();
+                      innerJoin field/innerJoin;
+		      outerJoin field/outerJoin">
+             
+       <table>
+           <tal:keys tal:repeat="key field/getSubfields">
+               <tr tal:condition="python:field.testSubfieldCondition(key,here,portal,template)">
+                <tal:value tal:define="value python:values_dict.get(key)"
+                     tal:condition="value">
+                      <td valign=top> 
+                        <span class="label" 
+                            tal:replace="python:field.getSubfieldLabel(key)+':'">Label</span></td>
+                       <td>    
+                        <span tal:replace="structure python:here.getDisplayView(value,'<br />')">value</span>
+                       </td>
+               </tal:value></tr>
+           </tal:keys></table>
+       </div>
+    </metal:view_macro>
+    <metal:define define-macro="edit">
+      <metal:use use-macro="here/widgets/field/macros/edit">
+       <table metal:fill-slot="widget_body"
+        tal:define="values python:field.getEditAccessor(here)();
+                    session_values python:here.session_restore_value(fieldName, values);
+                    cached_values python:request.get(fieldName,session_values);
+                    values python:cached_values or values">
+        <tal:keys tal:repeat="key python:field.getSubfields()">
+        <tr tal:condition="python:field.testSubfieldCondition(key,here,portal,template)">
+	<td class="field">
+        <label i18n:translate=""
+               tal:content="python:field.getSubfieldLabel(key)"
+               tal:attributes="for string:${fieldName}_${key}">
+        </label>
+	<span class="fieldRequired"
+              tal:condition="python:field.isRequired(key)"
+              title="Required"> #
+	</span>
+	</td>
+	<td tal:define="type python: field.getSubfieldType(key)">
+	<tal:string condition="python: type == 'string' and not field.isSelection(key)">
+	<input type="text"
+	       name=""
+	       value=""
+	       size="30"
+	       tabindex="#"
+	       tal:attributes="name string:${fieldName}.${key}:record:ignore_empty;
+	       value python:values.get(key);
+	       size python:field.getSubfieldSize(key);
+	       maxlength python:field.getSubfieldMaxlength(key); 
+	       tabindex tabindex/next;"
+	       />
+	</tal:string>
+	<tal:selection condition="python: field.isSelection(key)">
+	<tal:block tal:define="value python:values.get(key);
+                               vocab python:field.getVocabularyFor(key, here)">
+	   <select tal:attributes="name string:${fieldName}.${key}:record:ignore_empty; 
+                    id fieldName;
+		    tabindex tabindex/next;">
+	      <option tal:repeat="item vocab"
+		      tal:attributes="value item;
+		      selected python:test(here.checkSelected(item, value), 'selected', None);"
+		      tal:content="python: vocab.getValue(item)"
+		      i18n:translate=""
+		      />
+	    </select>
+	  </tal:block>
+	</tal:selection>
+	<tal:text condition="python:type == 'lines'">
+	<textarea name=""
+		  cols="30"
+		  rows="4"
+		  tabindex="#"
+		  tal:attributes="name string:${fieldName}.${key}:record:lines:ignore_empty;
+        	                  tabindex tabindex/next;"
+		  tal:content="python:'\n'.join(values.get(key,[]))" >
+	</textarea>
+	</tal:text>
+	<tal:date condition="python:type == 'datetime'">
+	<div tal:define="id string:${fieldName}_${key};
+               value python:values.get(key);
+	       inputname  string:${fieldName}.${key}:record;
+	       formname   string:edit_form;
+	       inputvalue python: test(value, value, '');
+	       tabindex tabindex/next;"
+	       tal:omit-tag="">
+	    <div metal:use-macro="here/calendar_macros/macros/calendarDatePickerBox|here/calendar_slot/macros/calendarDatePickerBox">
+	      a calendar, hopefully
+	    </div>
+	</div>
+	</tal:date>
+	<tal:number condition="python: type in ['float', 'int', 'long']">
+	<input type="text"
+	       name=""
+	       value=""
+	       size="10"
+	       tabindex="#"
+	       tal:attributes="name string:${fieldName}.${key}:${type}:record;
+	       value python:values.get(key);
+	       size python:field.getSubfieldSize(key);
+	       maxlength python:field.getSubfieldMaxlength(key); 
+	       tabindex tabindex/next;"
+	       />
+	</tal:number>
+	</td>
+	</tr>
+        </tal:keys> <!-- the condition-->
+       </table>
+      </metal:use>
+    </metal:define>
+
+    <div metal:define-macro="search">
+      <div metal:use-macro="here/widgets/string/macros/edit">
+      </div>
+    </div>
+
+  </body>
+
+</html>
+

Added: zope-atextensions/trunk/skins/at_extensions/records_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/records_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/records_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,156 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+  <head><title></title></head>
+  <body>
+
+    <!-- Records Widgets -->
+    <metal:view_macro define-macro="view">
+      <tal:block tal:repeat="idx python:range(field.getSize(here))"
+       tal:define="outerJoin field/outerJoin;
+                   innerJoin field/innerJoin;"><span 
+       tal:define="subfieldValues python:[field.getViewFor(here,idx,key,innerJoin) for key in field.getSubfields()]"
+       tal:replace="structure python:innerJoin.join(subfieldValues)" /><span 
+       tal:replace="structure outerJoin"
+       tal:condition="not: repeat/idx/end" />
+      </tal:block>
+    </metal:view_macro>
+
+    <metal:define define-macro="edit">
+      <metal:use use-macro="here/widgets/field/macros/edit">
+       <tal:block metal:fill-slot="widget_body">
+       <fieldset tal:define="values python:field.getEditAccessor(here)();
+                             session_values python:here.session_restore_value(fieldName, values);
+                             cached_values python:request.get(fieldName,session_values);
+                             values python:cached_values or values">
+       <table>
+        <tr> 
+	<tal:block repeat="key python:field.getSubfields()">
+	<td tal:condition="python:field.testSubfieldCondition(key,here,portal,template)"
+            tal:content="python: here.translate(domain=field.widget.i18n_domain, msgid=field.getSubfieldLabel(key), default=field.getSubfieldLabel(key))">
+	Label
+	</td>
+	</tal:block>
+	<td>
+            <span i18n:translate="delete_this_entry"
+                  id="delete_this_entry">
+             delete</span>
+	</td>
+	</tr>
+	<tr tal:repeat="idx python:range(field.getEditSize(here))">
+	<tal:keys tal:repeat="key python:field.getSubfields()">
+        <td tal:condition="python:field.testSubfieldCondition(key,here,portal,template)">
+	<tal:block define="type python:field.getSubfieldType(key)">
+	<tal:string condition="python: type == 'string' and not field.isSelection(key)">
+	<input type="text"
+	       name=""
+	       value=""
+	       size="30"
+	       tabindex="#"
+	       tal:attributes="name string:${fieldName}.${key}:records:ignore_empty;
+	       value python:field.getSubfieldValue(values, idx, key);
+	       size python:field.getSubfieldSize(key);
+	       maxlength python:field.getSubfieldMaxlength(key); 
+	       tabindex tabindex/next;"
+	       />
+	</tal:string>
+	<tal:selection condition="python: field.isSelection(key)">
+	<tal:block tal:define="value python:field.getSubfieldValue(values, idx, key);
+                               vocab python:field.getVocabularyFor(key, here)">
+	   <select tal:attributes="name string:${fieldName}.${key}:records:ignore_empty; 
+                                   id fieldName;
+		                   tabindex tabindex/next;"
+                   i18n:domain="python: field.widget.i18n_domain">
+	      <option tal:repeat="item vocab"
+		      tal:attributes="value item;
+		      selected python:test(here.checkSelected(item, value), 'selected', None);"
+		      tal:content="python: vocab.getValue(item)"
+		      i18n:translate=""
+		      />
+	    </select>
+	  </tal:block>
+	</tal:selection>
+	<tal:text condition="python: type == 'lines'">
+	<textarea name=""
+		  cols="30"
+		  rows="5"
+		  tabindex="#"
+		  tal:attributes="name string:${fieldName}.${key}:records:ignore_empty:lines;
+        	                  tabindex tabindex/next;"
+		  tal:content="python:'\n'.join(field.getSubfieldValue(values, idx, key, []))" >
+	</textarea>
+	</tal:text>
+	<tal:date condition="python:type == 'datetime'">
+	<div tal:define="id string:${fieldName}_${key}_${idx};
+               value python:field.getSubfieldValue(values, idx, key);
+	       inputname  string:${fieldName}.${key}:ignore_empty:records;
+	       formname   string:edit_form;
+	       inputvalue python: test(value, value, '');
+	       tabindex tabindex/next;"
+	       tal:omit-tag="">
+	    <div metal:use-macro="here/calendar_macros/macros/calendarDatePickerBox|here/calendar_slot/macros/calendarDatePickerBox">
+	      a pop-up calendar
+        </div>
+	</div>
+	</tal:date>
+	<tal:number condition="python: type in ['float', 'int', 'long']">
+	<input type="text"
+	       name=""
+	       value=""
+	       size="10"
+	       tabindex="#"
+	       tal:attributes="name string:${fieldName}.${key}:${type}:records:ignore_empty;
+	       value python:field.getSubfieldValue(values, idx, key);
+	       size python:field.getSubfieldSize(key);
+	       maxlength python:field.getSubfieldMaxlength(key); 
+	       tabindex tabindex/next;"
+	       />
+	</tal:number>
+	</tal:block>
+	</td>
+  </tal:keys>
+	<td>
+	    <input class="noborder"
+                   type="checkbox"
+                   value="off"
+                   tabindex=""
+                   tal:attributes="tabindex tabindex/next;
+                       name string:${fieldName}_delete_${idx};
+                       id string:${fieldName}_checkbox_${idx};"
+		     />
+	</td>
+	</tr>
+       </table>
+       <input type="submit" name="form.button.more" class="context"
+       tal:condition="python: field.showMore(values)"
+       tal:attributes="tabindex tabindex/next" 
+       i18n:attributes="value more_entries" />
+          <div tal:condition="values">
+            <input class="noborder"
+                   type="checkbox"
+                   value="off"
+                   tabindex=""
+                   tal:attributes="tabindex tabindex/next;
+                       name string:${fieldName}_delete;
+                       id string:${fieldName}_checkbox;"
+		     />
+            <span i18n:translate="delete_all_entries"
+                  id="delete_all_entries">
+                  delete all entries</span>
+          </div> 
+       </fieldset>
+       </tal:block>
+      </metal:use>
+    </metal:define>
+
+    <div metal:define-macro="search">
+      <div metal:use-macro="here/widgets/string/macros/edit">
+      </div>
+    </div>
+
+  </body>
+
+</html>
+

Added: zope-atextensions/trunk/skins/at_extensions/remotetext_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/remotetext_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/remotetext_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,93 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+
+<head>
+    <title></title>
+</head>
+
+<body>
+
+    <!-- Selection Widgets -->
+
+    <div metal:define-macro="view">
+        <div metal:use-macro="here/widgets/rich/macros/view" />
+        <div class="discreet"
+             tal:condition="python: not field.is_customized(here)">
+            Mirroring <a href="#"
+                         tal:define="url python:field.getSourcePath(here)"
+                         tal:attributes="href url"
+                         tal:content="url">
+                     #</a>
+        </div> 
+    </div>
+
+    <div metal:define-macro="edit">
+      <div tal:condition="python: not field.is_customized(here)">
+        <metal:use use-macro="field_macro | here/widgets/field/macros/edit">
+            <metal:fill fill-slot="widget_body"
+                        tal:define="spath python:field.getSourcePath(here);
+                                    stype python:field.getSourceType(here);
+                                    vocab field/getSourceTypeVocabulary">
+                <br />
+		<span class="label">Source path:</span>
+		    <input type="text"
+		           name=""
+			   value=""
+			   size="60"
+			   tabindex="#"
+			   tal:attributes="name string:${fieldName}_source_path;
+			   value spath;
+			   tabindex tabindex/next;"
+			   />
+                <br /> <br />
+		<span class="label">Source type:</span>
+                    <tal:radios repeat="item vocab">
+
+                        <input class="noborder"
+                               tabindex=""
+                               type="radio"
+                               tal:define="tabindex tabindex/next"
+                               tal:attributes="name string:${fieldName}_source_type;
+                                               id string:${fieldName}_source_type_${tabindex};
+                                               checked python:here.checkSelected(item, stype);
+                                               value item;
+                                               tabindex tabindex;"
+                               />
+
+                        <label tal:content="python:here.translate(vocab.getMsgId(item), default=vocab.getValue(item))"
+                               tal:attributes="for string:${fieldName}_source_type_${tabindex/pos}" />
+
+                    </tal:radios>
+                <br /> <br />
+       <input type="hidden" name="customize_field" value=""
+              tal:attributes="value string:${fieldName}" />
+       <input type="submit" name="form.button.customize" class="context"
+       tal:attributes="value python:' Customize ';
+                       tabindex tabindex/next" />
+
+            </metal:fill>
+        </metal:use>
+      </div>
+
+        <div tal:condition="python:field.is_customized(here)">
+          <div metal:use-macro="here/widgets/rich/macros/edit" />
+             <input type="hidden" name="uncustomize_field" value=""
+                    tal:attributes="value string:${fieldName}" />
+            <input type="submit" 
+                   name="form.button.uncustomize" 
+                   class="context"
+                   tal:attributes="value python:' Undo Customization ';
+                       tabindex tabindex/next" />
+        </div>
+    </div>
+
+    <div metal:define-macro="search">
+        <div metal:use-macro="here/widgets/rich/macros/search" />
+    </div>
+
+</body>
+
+</html>

Added: zope-atextensions/trunk/skins/at_extensions/uncustomize_remotefield.cpy
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/uncustomize_remotefield.cpy	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/uncustomize_remotefield.cpy	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,18 @@
+## Controller Python Script "uncustomize_remotefield"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind state=state
+##bind subpath=traverse_subpath
+##parameters=
+##title=
+##
+field_id = context.REQUEST.get('uncustomize_field', None)
+
+if field_id is not None:
+    field = context.getField(field_id)
+    field.uncustomize(context)
+
+# Always make sure to return the ControllerState object
+return state

Added: zope-atextensions/trunk/skins/at_extensions/url_widget.pt
===================================================================
--- zope-atextensions/trunk/skins/at_extensions/url_widget.pt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/skins/at_extensions/url_widget.pt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,41 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone">
+  <head><title></title></head>
+  <body>
+
+    <!-- Url Widgets -->
+    <metal:view_macro define-macro="view"
+                      tal:define="url accessor">
+	<a href=""
+	tal:attributes="href url"
+	tal:content="url">#</a>
+    </metal:view_macro>
+
+    <metal:define define-macro="edit">
+      <metal:use use-macro="here/widgets/field/macros/edit">
+        <input metal:fill-slot="widget_body"
+               type="text"
+               name=""
+               value=""
+               size="30"
+               tabindex="#"
+               tal:attributes="name fieldName;
+               value value;
+               size widget/size;
+               maxlength widget/maxlength;
+               tabindex tabindex/next;"
+               />
+      </metal:use>
+    </metal:define>
+
+    <div metal:define-macro="search">
+      <div metal:use-macro="here/widgets/string/macros/edit">
+      </div>
+    </div>
+
+  </body>
+
+</html>

Added: zope-atextensions/trunk/validator/__init__.py
===================================================================
--- zope-atextensions/trunk/validator/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/validator/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1 @@
+# blub, I'm a package

Added: zope-atextensions/trunk/validator/isPartialUrl.py
===================================================================
--- zope-atextensions/trunk/validator/isPartialUrl.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/validator/isPartialUrl.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,19 @@
+import re
+from Products.validation.interfaces import ivalidator
+
+class PartialUrlValidator:
+    __implements__ = (ivalidator,)
+    def __init__(self, name):
+        self.name = name
+    def __call__(self, value, *args, **kwargs):
+        # field gets passed in via kwargs
+        if '://' not in value:
+            widget = kwargs.get('field').widget
+            default_p = getattr(widget,'default_protocol','http')
+            value = default_p + '://' + value
+        pattern = re.compile(r'(ht|f)tps?://[^\s\r\n]+')
+        m = pattern.match(value)
+        if not m:
+            return ("Validation failed(%s): value is %s"%(self.name,
+                repr(value)))
+        return 1

Added: zope-atextensions/trunk/version.txt
===================================================================
--- zope-atextensions/trunk/version.txt	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/version.txt	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1 @@
+0.8.0

Added: zope-atextensions/trunk/widget/__init__.py
===================================================================
--- zope-atextensions/trunk/widget/__init__.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/__init__.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,13 @@
+# import the individual widgets
+
+from combo import ComboWidget
+from datetime import DateTimeWidget
+from deadlines import DeadlinesWidget
+from record import RecordWidget
+from records import RecordsWidget
+from url import UrlWidget
+from email import EmailWidget 
+from formattablename import FormattableNameWidget
+from formattablenames import FormattableNamesWidget
+from comment import CommentWidget
+from remotetext import RemoteTextWidget

Added: zope-atextensions/trunk/widget/combo.py
===================================================================
--- zope-atextensions/trunk/widget/combo.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/combo.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,39 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import SelectionWidget
+from Products.Archetypes.Registry import registerWidget
+
+
+class ComboWidget(SelectionWidget):
+    _properties = SelectionWidget._properties.copy()
+    _properties.update({
+        'type' : 'combobox',
+        'macro' : "combo_widget",
+        })
+    security = ClassSecurityInfo()
+
+    security.declarePublic('process_form')
+    def process_form(self, instance, field, form, empty_marker=None,
+                     emptyReturnsMarker=False):
+        """
+        if no value or value equals 'other'
+        replace it with the optionally entered one
+        """
+        value = form.get(field.getName(), empty_marker)
+        if not value or value.lower() in ('other', u'other'):
+            # XXX is this generic enough?
+            other_name = "%s_other" % field.getName()
+            value = form.get(other_name, empty_marker)
+        if value is empty_marker:
+            return empty_marker
+        if emptyReturnsMarker and value == '':
+            return empty_marker
+        return value, {}
+
+InitializeClass(ComboWidget)
+
+registerWidget(ComboWidget,
+               title='ComboBox',
+               description="Select box together with a string field.",
+               used_for=('Products.Archetypes.Field.StringField',)
+               )

Added: zope-atextensions/trunk/widget/comment.py
===================================================================
--- zope-atextensions/trunk/widget/comment.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/comment.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,23 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import StringWidget
+from Products.Archetypes.Registry import registerWidget
+
+class CommentWidget(StringWidget):
+    _properties = StringWidget._properties.copy()
+    _properties.update({
+        'macro' : "comment_widget",
+        'i18n_domain': "plone",
+        'visible' : {'edit' : 'visible',
+                     'view' : 'invisible',
+                     }
+        })
+    security = ClassSecurityInfo()
+    
+InitializeClass(CommentWidget)
+
+registerWidget(CommentWidget,
+               title='Comment',
+               description="Renders a comment in 'base_edit'.",
+               used_for=('Products.ATExtensions.fields.CommentField',)
+               )

Added: zope-atextensions/trunk/widget/datetime.py
===================================================================
--- zope-atextensions/trunk/widget/datetime.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/datetime.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,31 @@
+# -*- coding: UTF-8 -*-
+
+##################################################
+#                                                #
+#    Copyright (C), 2004, Thomas Förster         #
+#    <t.foerster at biologie.hu-berlin.de>          #
+#                                                #
+#    Humboldt University of Berlin               #
+#                                                #
+##################################################
+
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import CalendarWidget
+from Products.Archetypes.Registry import registerWidget
+
+class DateTimeWidget(CalendarWidget):
+    _properties = CalendarWidget._properties.copy()
+    _properties.update({
+        'macro' : "datetime_widget",
+        })
+
+    security = ClassSecurityInfo()
+
+InitializeClass(DateTimeWidget)
+
+registerWidget(DateTimeWidget,
+               title='DateTime',
+               description="An improved DateTime Widget.",
+               used_for=('Products.ATExtensions.fields.DateTimeField',)
+               )

Added: zope-atextensions/trunk/widget/deadlines.py
===================================================================
--- zope-atextensions/trunk/widget/deadlines.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/deadlines.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,21 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Widget import TypesWidget
+from Products.Archetypes.Registry import registerWidget
+
+## rr: this has gotten nowhere so far
+
+class DeadlinesWidget(TypesWidget):
+    _properties = TypesWidget._properties.copy()
+    _properties.update({
+        'macro' : "deadlines_widget",
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(DeadlinesWidget)
+
+registerWidget(DeadlinesWidget,
+               title='Deadlines',
+               description="Manage a list of deadlines.",
+               ## rr: ?? used_for=('Products.ATExtensions.fields.UrlField',)
+               )

Added: zope-atextensions/trunk/widget/email.py
===================================================================
--- zope-atextensions/trunk/widget/email.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/email.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,22 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import StringWidget
+from Products.Archetypes.Registry import registerWidget
+from Products.Archetypes.Registry import registerPropertyType
+
+class EmailWidget(StringWidget):
+    _properties = StringWidget._properties.copy()
+    _properties.update({
+        'macro_view' : "email_widget",
+        'at_mask' : '(at)',
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(EmailWidget)
+
+registerWidget(EmailWidget,
+               title='Email',
+               description="Renders an email address (masked for anonymous).",
+               used_for=('Products.ATExtensions.fields.EmailField',)
+               )
+registerPropertyType('at_mask', 'string', EmailWidget)

Added: zope-atextensions/trunk/widget/formattablename.py
===================================================================
--- zope-atextensions/trunk/widget/formattablename.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/formattablename.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,37 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Registry import registerWidget
+from record import RecordWidget
+
+class FormattableNameWidget(RecordWidget):
+    _properties = RecordWidget._properties.copy()
+    _properties.update({
+        'macro_view' : "formattablename_view",
+        'format' : '%T %F %M %P %L %S',
+        'formatter' : {},
+        'abbr' : {},
+        'join_abbr' : '',
+        'withURL' : False,
+        })
+    _format_properties = ['format', 'formatter', 'abbr',
+                          'join_abbr', 'withURL']
+    
+    security = ClassSecurityInfo()
+
+    security.declarePublic('getKwArgs')
+    def getKwArgs(self):
+        """mapping of all non-empty formatting properties"""
+        result = {}
+        for property in self._format_properties:
+            value = getattr(self, property, None)
+            if value:
+                result[property] = value
+        return result
+
+InitializeClass(FormattableNameWidget)
+
+registerWidget(FormattableNameWidget,
+               title='FormattableName',
+               description="Renders a formattable name.",
+               used_for=('Products.ATExtensions.fields.FormattableNameField',)
+               )

Added: zope-atextensions/trunk/widget/formattablenames.py
===================================================================
--- zope-atextensions/trunk/widget/formattablenames.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/formattablenames.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,31 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.Registry import registerWidget
+from records import RecordsWidget
+from formattablename import FormattableNameWidget
+
+class FormattableNamesWidget(RecordsWidget, FormattableNameWidget):
+    _properties = RecordsWidget._properties.copy()
+    _properties.update({
+        'macro_view' : "formattablename_view",
+        'format' : '%T %F %M %P %L %S',
+        'formatter' : {},
+        'abbr' : {},
+        'join_abbr' : '',
+        'withURL' : False,
+        'sep' : '',
+        'lastsep' : '',
+        'empty_marker' : '',
+        })
+    _format_properties = ['format', 'formatter', 'abbr', 'join_abbr',
+                          'withURL', 'sep', 'lastsep', 'empty_marker']
+    security = ClassSecurityInfo()
+
+
+InitializeClass(FormattableNamesWidget)
+
+registerWidget(FormattableNamesWidget,
+               title='FormattableNames',
+               description="Renders formattable names.",
+               used_for=('Products.ATExtensions.fields.FormattableNamesField',)
+               )

Added: zope-atextensions/trunk/widget/record.py
===================================================================
--- zope-atextensions/trunk/widget/record.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/record.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,19 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import StringWidget
+from Products.Archetypes.Registry import registerWidget
+
+class RecordWidget(StringWidget):
+    _properties = StringWidget._properties.copy()
+    _properties.update({
+        'macro' : "record_widget",
+        })
+    security = ClassSecurityInfo()
+
+InitializeClass(RecordWidget)
+
+registerWidget(RecordWidget,
+               title='Record',
+               description="Renders a group of subfields.",
+               used_for=('Products.ATExtensions.fields.RecordField',)
+               )

Added: zope-atextensions/trunk/widget/records.py
===================================================================
--- zope-atextensions/trunk/widget/records.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/records.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,47 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import StringWidget
+from Products.Archetypes.Registry import registerWidget
+
+from Products.ATExtensions.widget import RecordWidget
+
+class RecordsWidget(RecordWidget):
+    _properties = RecordWidget._properties.copy()
+    _properties.update({
+        'macro' : "records_widget",
+        })
+    security = ClassSecurityInfo()
+
+    security.declarePublic('process_form')
+    def process_form(self, instance, field, form, empty_marker=None,
+                     emptyReturnsMarker=False):
+        """
+        Basic impl for form processing in a widget plus clearing
+        of the value if the 'delete all entries' flag is set
+        """
+        ## check to see if the delete all entries flag was selected
+        delete = form.get('%s_delete' % field.getName(), empty_marker)
+        if delete is not empty_marker: return {}, {}
+        
+        value = form.get(field.getName(), empty_marker)
+        if value is empty_marker:
+            return empty_marker
+        if emptyReturnsMarker and value == '':
+            return empty_marker
+
+        ## now check to see if we need to delete an individual item
+        for idx in range(len(value)-1,-1,-1):
+            flag_name = '%s_delete_%s' % (field.getName(),idx)
+            delete = form.get(flag_name, None)
+            if delete:
+                del value[idx]
+                del form[flag_name]
+        return value, {}    
+
+InitializeClass(RecordsWidget)
+
+registerWidget(RecordsWidget,
+               title='Records',
+               description="Renders a list of records.",
+               used_for=('Products.ATExtensions.fields.RecordsField',)
+               )

Added: zope-atextensions/trunk/widget/remotetext.py
===================================================================
--- zope-atextensions/trunk/widget/remotetext.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/remotetext.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,50 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import RichWidget
+from Products.Archetypes.Registry import registerWidget
+
+class RemoteTextWidget(RichWidget):
+    _properties = RichWidget._properties.copy()
+    _properties.update({
+        'macro' : 'remotetext_widget',
+        })
+
+    # what to handle now where?
+    # customization; triggering upload
+    # all in process_form?
+    # or do we customize via an extra button with custom action?
+    # wouldn't an empty value be sufficient for the non-customized case?
+    security = ClassSecurityInfo()
+
+    security.declarePublic('process_form')
+    def process_form(self, instance, field, form, empty_marker=None,
+                     emptyReturnsMarker=False):
+        """
+        Basic impl for form processing in a widget plus setting
+        additional values used by our associated field
+        """
+        value = form.get(field.getName(), empty_marker)
+        if value is empty_marker:
+            return empty_marker
+        if emptyReturnsMarker and value == '':
+            return empty_marker
+
+        ## now check to see if we need to take care of some
+        ## extra values
+        stname = "%s_source_type" % field.getName()
+        spname = "%s_source_path" % field.getName()
+        stype = form.get(stname, None)
+        spath = form.get(spname, None)
+        if stype:
+            field.setSourceType(instance, stype)
+        if spath:
+            field.setSourcePath(instance, spath)
+        return value, {}    
+
+InitializeClass(RemoteTextWidget)
+
+registerWidget(RemoteTextWidget,
+               title="Remote Text Widget",
+               description="Populate a field from a remote source",
+               used_for="Products.ATExtensions.field.remotetext.RemoteTextField",
+               )

Added: zope-atextensions/trunk/widget/url.py
===================================================================
--- zope-atextensions/trunk/widget/url.py	2007-03-12 06:24:31 UTC (rev 713)
+++ zope-atextensions/trunk/widget/url.py	2007-03-21 14:32:46 UTC (rev 714)
@@ -0,0 +1,37 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from Products.Archetypes.public import StringWidget
+from Products.Archetypes.Registry import registerWidget
+from Products.Archetypes.Registry import registerPropertyType
+
+class UrlWidget(StringWidget):
+    _properties = StringWidget._properties.copy()
+    _properties.update({
+        'macro' : "url_widget",
+        'default_protocol' : 'http',
+        })
+    security = ClassSecurityInfo()
+    
+    security.declarePublic('process_form')
+    def process_form(self, instance, field, form, empty_marker=None,
+                     emptyReturnsMarker=False):
+        """
+        add the default protocol if there is none
+        """
+        value = form.get(field.getName(), empty_marker)
+        if value is empty_marker:
+            return empty_marker
+        if emptyReturnsMarker and value == '':
+            return empty_marker
+        if value and '://' not in value:
+            value = self.default_protocol + '://' + value
+        return value, {}
+
+InitializeClass(UrlWidget)
+
+registerWidget(UrlWidget,
+               title='Url',
+               description="Renders a URL in an anchor tag.",
+               used_for=('Products.ATExtensions.fields.UrlField',)
+               )
+registerPropertyType('default_protocol', 'string', UrlWidget)




More information about the pkg-zope-commits mailing list