diff --git a/fields/__init__.py b/fields/__init__.py
index 45b496e..a729579 100644
--- a/fields/__init__.py
+++ b/fields/__init__.py
@@ -356,7 +356,7 @@ class Field:
# represents the maximum number of chars that a given input field may
# accept (corresponds to HTML "maxlength" property). "None" means
# "unlimited".
- self.maxChars = maxChars
+ self.maxChars = maxChars or ''
# If the widget is in a group with multiple columns, the following
# attribute specifies on how many columns to span the widget.
self.colspan = colspan
diff --git a/fields/action.py b/fields/action.py
index f1385a2..4034316 100644
--- a/fields/action.py
+++ b/fields/action.py
@@ -28,20 +28,20 @@ class Action(Field):
pxView = pxCell = Px('''
''')
diff --git a/fields/boolean.py b/fields/boolean.py
index fc20b12..650d656 100644
--- a/fields/boolean.py
+++ b/fields/boolean.py
@@ -30,7 +30,7 @@ class Boolean(Field):
''')
pxEdit = Px('''
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
''')
@@ -96,4 +96,13 @@ class Boolean(Field):
if not self.isEmptyValue(value):
exec 'res = %s' % value
return res
+
+ def isChecked(self, obj, dbValue):
+ '''When rendering this field as a checkbox, must it be checked or
+ not?'''
+ rq = obj.REQUEST
+ # Get the value we must compare (from request or from database)
+ if rq.has_key(self.name):
+ return rq.get(self.name) in ('True', 1, '1')
+ return dbValue
# ------------------------------------------------------------------------------
diff --git a/fields/calendar.py b/fields/calendar.py
index 90f1273..d8007c1 100644
--- a/fields/calendar.py
+++ b/fields/calendar.py
@@ -16,35 +16,28 @@ class Calendar(Field):
# Month view for a calendar. Called by pxView, and directly from the UI,
# via Ajax, when the user selects another month.
pxMonthView = Px('''
-
-
-
+
-
@@ -139,14 +139,14 @@ class Date(Field):
/
-
+
-
@@ -248,4 +248,30 @@ class Date(Field):
return DateTime.DateTime(value)
def getIndexType(self): return 'DateIndex'
+
+ def isSelected(self, obj, fieldPart, dateValue, dbValue):
+ '''When displaying this field, must the particular p_dateValue be
+ selected in the sub-field p_fieldPart corresponding to the date
+ part?'''
+ # Get the value we must compare (from request or from database)
+ rq = obj.REQUEST
+ partName = '%s_%s' % (self.name, fieldPart)
+ if rq.has_key(partName):
+ compValue = rq.get(partName)
+ if compValue.isdigit():
+ compValue = int(compValue)
+ else:
+ compValue = dbValue
+ if compValue:
+ compValue = getattr(compValue, fieldPart)()
+ # Compare the value
+ return compValue == dateValue
+
+ def getJsInit(self, name, years):
+ '''Gets the Javascript init code for displaying a calendar popup for
+ this field, for an input named p_name (which can be different from
+ self.name if, ie, it is a search field).'''
+ return 'Calendar.setup({inputField: "%s", button: "%s_img", ' \
+ 'onSelect: onSelectDate, range:[%d,%d]});' % \
+ (name, name, years[0], years[-1])
# ------------------------------------------------------------------------------
diff --git a/fields/file.py b/fields/file.py
index e0504d0..1f6b05d 100644
--- a/fields/file.py
+++ b/fields/file.py
@@ -16,6 +16,7 @@
# ------------------------------------------------------------------------------
import time, os.path, mimetypes
+from appy import Object
from appy.fields import Field
from appy.px import Px
from appy.shared import utils as sutils
@@ -24,32 +25,32 @@ from appy.shared import utils as sutils
class File(Field):
pxView = pxCell = Px('''
-
-
- :info['filename']" -
- '%sKb' % (info['size'] / 1024)">
+
+ :info.filename -
+ '%sKb' % (info.size / 1024)">
-
+ -''')
pxEdit = Px('''
-
- :widget['pxView']
+ :field.pxView
-
+
@@ -57,14 +58,14 @@ class File(Field):
+ size=":field.width"/>
+
+
+ ''')
+
+ pxEdit = Px('''
+
+
+
+
+
+
+
+ :_('captcha_text', \
+ mapping=field.getCaptchaChallenge(req.SESSION))
+
+
+
+
+
+
+ ''')
+
+ pxCell = Px('''
+
+ :', '.join(value)">
+ :field.pxView
+ ''')
+
+ pxSearch = Px('''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ''')
+
# Some predefined functions that may also be used as validators
@staticmethod
def _MODULO_97(obj, value, complement=False):
@@ -352,14 +465,16 @@ class String(Field):
if res in self.emptyValuesCatalogIgnored: res = ' '
return res
- def getPossibleValues(self,obj,withTranslations=False,withBlankValue=False):
- '''Returns the list of possible values for this field if it is a
- selection field. If p_withTranslations is True,
- instead of returning a list of string values, the result is a list
- of tuples (s_value, s_translation). If p_withBlankValue is True, a
- blank value is prepended to the list, excepted if the type is
- multivalued.'''
- if not self.isSelect: raise 'This field is not a selection.'
+ def getPossibleValues(self, obj, withTranslations=False,
+ withBlankValue=False, className=None):
+ '''Returns the list of possible values for this field (only for fields
+ with self.isSelect=True). If p_withTranslations is True, instead of
+ returning a list of string values, the result is a list of tuples
+ (s_value, s_translation). If p_withBlankValue is True, a blank value
+ is prepended to the list, excepted if the type is multivalued. If
+ p_className is given, p_obj is the tool and, if we need an instance
+ of p_className, we will need to use obj.executeQuery to find one.'''
+ if not self.isSelect: raise Exception('This field is not a selection.')
if isinstance(self.validator, Selection):
# We need to call self.methodName for getting the (dynamic) values.
# If methodName begins with _appy_, it is a special Appy method:
@@ -381,6 +496,15 @@ class String(Field):
if methodName.startswith('tool:'):
obj = obj.getTool()
methodName = methodName[5:]
+ else:
+ # We must call on p_obj. But if we have something in
+ # p_className, p_obj is the tool and not an instance of
+ # p_className as required. So find such an instance.
+ if className:
+ brains = obj.executeQuery(className, maxResults=1,
+ brainsOnly=True)
+ if brains:
+ obj = brains[0].getObject()
# Do we need to call the method on the object or on the wrapper?
if methodName.startswith('_appy_'):
exec 'res = obj.%s(*args)' % methodName
@@ -503,4 +627,47 @@ class String(Field):
'''Generates a password (we recycle here the captcha challenge
generator).'''
return self.getCaptchaChallenge({})['text']
+
+ def getJsInit(self, obj):
+ '''Gets the Javascript init code for displaying a rich editor for this
+ field (rich field only).'''
+ # Define the attributes that will initialize the ckeditor instance for
+ # this field.
+ ckAttrs = {'toolbar': 'Appy',
+ 'format_tags': '%s' % ';'.join(self.styles)}
+ if self.width: ckAttrs['width'] = self.width
+ if self.allowImageUpload:
+ ckAttrs['filebrowserUploadUrl'] = '%s/upload' % obj.absolute_url()
+ ck = []
+ for k, v in ckAttrs.iteritems():
+ if isinstance(v, int): sv = str(v)
+ else: sv = '"%s"' % v
+ ck.append('%s: %s' % (k, sv))
+ return 'CKEDITOR.replace("%s", {%s})' % (name, ', '.join(ck))
+
+ def getJsInlineInit(self, obj):
+ '''Gets the Javascript init code for enabling inline edition of this
+ field (rich text only).'''
+ uid = obj.UID()
+ return "CKEDITOR.disableAutoInline = true;\n" \
+ "CKEDITOR.inline('%s_%s_ck', {on: {blur: " \
+ "function( event ) { var data = event.editor.getData(); " \
+ "askAjaxChunk('%s_%s','POST','%s','page','saveField', "\
+ "{'fieldName':'%s', 'fieldContent': encodeURIComponent(data)}, "\
+ "null, evalInnerScripts);}}});"% \
+ (uid, self.name, uid, self.name, obj.absolute_url(), self.name)
+
+ def isSelected(self, obj, vocabValue, dbValue):
+ '''When displaying a selection box (only for fields with a validator
+ being a list), must the _vocabValue appear as selected?'''
+ rq = obj.REQUEST
+ # Get the value we must compare (from request or from database)
+ if rq.has_key(self.name):
+ compValue = rq.get(self.name)
+ else:
+ compValue = dbValue
+ # Compare the value
+ if type(compValue) in sutils.sequenceTypes:
+ return vocabValue in compValue
+ return vocabValue == compValue
# ------------------------------------------------------------------------------
diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py
index 61b1abd..cd7273b 100644
--- a/gen/mixins/__init__.py
+++ b/gen/mixins/__init__.py
@@ -1393,24 +1393,10 @@ class BaseMixin:
def getPossibleValues(self, name, withTranslations, withBlankValue,
className=None):
- '''Gets the possible values for field named p_name. This field must be a
- String with isSelection()=True. If p_withTranslations is True,
- instead of returning a list of string values, the result is a list
- of tuples (s_value, s_translation). If p_withBlankValue is True, a
- blank value is prepended to the list. If no p_className is defined,
- the field is supposed to belong to self's class.'''
- appyType = self.getAppyType(name, className=className)
- if className:
- # We need an instance of className, but self can be an instance of
- # another class. So here we will search such an instance.
- brains = self.executeQuery(className, maxResults=1, brainsOnly=True)
- if brains:
- obj = brains[0].getObject()
- else:
- obj = self
- else:
- obj = self
- return appyType.getPossibleValues(obj, withTranslations, withBlankValue)
+ '''See docstring of String.getPossibleValues.'''
+ field = self.getAppyType(name, className=className)
+ return field.getPossibleValues(self, withTranslations, withBlankValue,
+ className=className)
def getCaptchaChallenge(self, name):
return self.getAppyType(name).getCaptchaChallenge(self.REQUEST.SESSION)