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(''' -
@@ -58,7 +51,7 @@ class Calendar(Field): + (q(ajaxHookId),q(objUrl),q(field.name),q(previousMonth))"/> + q(objUrl), q(field.name), q(nextMonth))"/> :_('month_%s' % monthDayOne.aMonth()) :month.split('/')[0]
@@ -82,11 +75,10 @@ class Calendar(Field): + var="rowHeight=int(field.height/float(len(grid)))"> - @@ -95,16 +87,13 @@ class Calendar(Field): + cssClasses=field.getCellStyle(contextObj, date, today)"> - @@ -164,13 +151,13 @@ class Calendar(Field):
:dayName
+ (q('del'), q(field.name), q(dayString), q(str(spansDays)))"/>
- :contextObj.callField(fieldName, \ - 'getEventName', contextObj, eventType)"> + :field.getEventName(contextObj, \ + eventType)">
-
:event['name']
- ::info
- - + (q('del'), q(ajaxHookId), q(field.name), q(objUrl))"/> @@ -235,13 +221,11 @@ class Calendar(Field): ''') pxView = pxCell = Px(''' - - :widget['pxMonthView']> - - ''') + :field.pxMonthView +
''') pxEdit = pxSearch = '' diff --git a/fields/computed.py b/fields/computed.py index ae9c620..6572129 100644 --- a/fields/computed.py +++ b/fields/computed.py @@ -24,15 +24,15 @@ class Computed(Field): # Ajax-called view content of a non sync Computed field. pxViewContent = Px(''' :widget['pxView']''') + sync=True">:field.pxView''') pxView = pxCell = pxEdit = Px(''' - :value - ::value> + :value + ::value>
@@ -45,12 +45,9 @@ class Computed(Field): pxSearch = Px(''' -
   - +
   +
''') def __init__(self, validator=None, multiplicity=(0,1), default=None, diff --git a/fields/date.py b/fields/date.py index 2a72735..0bc8f47 100644 --- a/fields/date.py +++ b/fields/date.py @@ -24,15 +24,15 @@ class Date(Field): pxView = pxCell = Px(''':value''') pxEdit = Px(''' - + @@ -41,50 +41,50 @@ class Date(Field): + selected="field.isSelected(contextObj, 'month', month, \ + rawValue)">:zMonth - + - + - - - : - + + + : + ''') pxSearch = 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)