# -*- coding: utf-8 -*- import string from DummyField import fields from DocumentTemplate.DT_Util import html_quote from DateTime import DateTime from cgi import escape import types from DocumentTemplate.ustr import ustr from urlparse import urljoin from Globals import get_request class Widget: """A field widget that knows how to display itself as HTML. """ property_names = ['title', 'description', 'default', 'css_class', 'alternate_name', 'hidden'] title = fields.StringField('title', title='Title', description=( "The title of this field. This is the title of the field that " "will appear in the form when it is displayed. Required."), default="", required=1) description = fields.TextAreaField('description', title='Description', description=( "Description of this field. The description property can be " "used to add a short description of what a field does; such as " "this one."), default="", width="20", height="3", required=0) css_class = fields.StringField('css_class', title='CSS class', description=( "The CSS class of the field. This can be used to style your " "formulator fields using cascading style sheets. Not required."), default="", required=0) alternate_name = fields.StringField('alternate_name', title='Alternate name', description=( "An alternative name for this field. This name will show up in " "the result dictionary when doing validation, and in the REQUEST " "if validation goes to request. This can be used to support names " "that cannot be used as Zope ids."), default="", required=0) hidden = fields.CheckBoxField('hidden', title="Hidden", description=( "This field will be on the form, but as a hidden field. The " "contents of the hidden field will be the default value. " "Hidden fields are not visible but will be validated."), default=0) # NOTE: for ordering reasons (we want extra at the end), # this isn't in the base class property_names list, but # instead will be referred to by the subclasses. extra = fields.StringField('extra', title='Extra', description=( "A string containing extra HTML code for attributes. This " "string will be literally included in the rendered field." "This property can be useful if you want " "to add an onClick attribute to use with JavaScript, for instance."), default="", required=0) def render(self, field, key, value, REQUEST): """Renders this widget as HTML using property values in field. """ return "[widget]" def render_hidden(self, field, key, value, REQUEST, render_prefix=None): """Renders this widget as a hidden field. """ try: extra = field.get_value('extra') except KeyError: # In case extra is not defined as in DateTimeWidget extra = '' result = '' # We must adapt the rendering to the type of the value # in order to get the correct type back if isinstance(value, (tuple, list)): for v in value: result += render_element("input", type="hidden", name="%s:list" % key, value=v, extra=extra) else: result = render_element("input", type="hidden", name=key, value=value, extra=extra) return result def render_view(self, field, value, REQUEST=None, render_prefix=None): """Renders this widget for public viewing. """ # default implementation if value is None: return '' return value render_pdf = render_view def render_html(self, *args, **kw): return self.render(*args, **kw) def render_htmlgrid(self, field, key, value, REQUEST, render_prefix=None): """ render_htmlgrid returns a list of tuple (title, html render) """ # XXX Calling _render_helper on the field is not optimized return ((field.get_value('title'), field._render_helper(key, value, REQUEST, render_prefix=render_prefix)),) def render_css(self, field, REQUEST): """ Default render css for widget - to be overwritten in field classes. Should return valid css code as string. The value returned by this method will be used as inline style for a field. """ pass def get_css_list(self, field, REQUEST): """ Return CSS needed by the widget - to be overwritten in field classes. Should return a list of CSS file names. These names will be appended to global css_list and included in a rendered page. """ return [] def get_javascript_list(self, field, REQUEST): """ Return JS needed by the widget - to be overwritten in field classes. Should return a list of javascript file names. These names will be appended to global js_list and included in a rendered page. """ return [] def render_dict(self, field, value): """ This is yet another field rendering. It is designed to allow code to understand field's value data by providing its type and format when applicable. """ return None class TextWidget(Widget): """Text widget """ property_names = Widget.property_names +\ ['display_width', 'display_maxwidth', 'extra'] default = fields.StringField('default', title='Default', description=( "You can place text here that will be used as the default " "value of the field, unless the programmer supplies an override " "when the form is being generated."), default="", required=0) display_width = fields.IntegerField('display_width', title='Display width', description=( "The width in characters. Required."), default=20, required=1) display_maxwidth = fields.IntegerField('display_maxwidth', title='Maximum input', description=( "The maximum input in characters that the widget will allow. " "Required. If set to 0 or is left empty, there is no maximum. " "Note that is client side behavior only."), default="", required=0) def render(self, field, key, value, REQUEST, render_prefix=None): """Render text input field. """ display_maxwidth = field.get_value('display_maxwidth') or 0 if display_maxwidth > 0: return render_element("input", type="text", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), maxlength=display_maxwidth, extra=field.get_value('extra')) else: return render_element("input", type="text", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), extra=field.get_value('extra')) def render_view(self, field, value, REQUEST=None, render_prefix=None): """Render text as non-editable. This renderer is designed to be type error resistant. in we get a non string value. It does escape the result and produces clean xhtml. Patch the render_view of TextField to enclose the value within <span> html tags if css class defined """ if value is None: return '' if isinstance(value, types.ListType) or isinstance(value, types.TupleType): old_value = value else: old_value = [str(value)] value = [] for line in old_value: value.append(escape(line)) value = '<br/>'.join(value) css_class = field.get_value('css_class') if css_class not in ('', None): # All strings should be escaped before rendering in HTML # except for editor field return "<span class='%s'>%s</span>" % (css_class, value) return value TextWidgetInstance = TextWidget() class PasswordWidget(TextWidget): def render(self, field, key, value, REQUEST, render_prefix=None): """Render password input field. """ display_maxwidth = field.get_value('display_maxwidth') or 0 if display_maxwidth > 0: return render_element("input", type="password", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), maxlength=display_maxwidth, extra=field.get_value('extra')) else: return render_element("input", type="password", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), extra=field.get_value('extra')) def render_view(self, field, value, REQUEST=None, render_prefix=None): return "[password]" PasswordWidgetInstance = PasswordWidget() class CheckBoxWidget(Widget): property_names = Widget.property_names + ['extra'] default = fields.CheckBoxField('default', title='Default', description=( "Default setting of the widget; either checked or unchecked. " "(true or false)"), default=0) def render(self, field, key, value, REQUEST, render_prefix=None): """Render checkbox. """ rendered = [render_element("input", type="hidden", name="default_%s:int" % (key, ), value="0") ] if value: rendered.append(render_element("input", type="checkbox", name=key, css_class=field.get_value('css_class'), checked=None, extra=field.get_value('extra')) ) else: rendered.append(render_element("input", type="checkbox", name=key, css_class=field.get_value('css_class'), extra=field.get_value('extra')) ) return "".join(rendered) def render_view(self, field, value, REQUEST=None, render_prefix=None): """Render checkbox in view mode. """ if value: return render_element("input", type="checkbox", css_class=field.get_value('css_class'), checked=1, extra=field.get_value('extra'), disabled='disabled') else: return render_element("input", type="checkbox", css_class=field.get_value('css_class'), extra=field.get_value('extra'), disabled='disabled') CheckBoxWidgetInstance = CheckBoxWidget() class TextAreaWidget(Widget): """Textarea widget """ property_names = Widget.property_names +\ ['width', 'height', 'extra'] default = fields.TextAreaField('default', title='Default', description=( "Default value of the text in the widget."), default="", width=20, height=3, required=0) width = fields.IntegerField('width', title='Width', description=( "The width (columns) in characters. Required."), default=40, required=1) height = fields.IntegerField('height', title="Height", description=( "The height (rows) in characters. Required."), default=5, required=1) def render(self, field, key, value, REQUEST, render_prefix=None): width = field.get_value('width', REQUEST=REQUEST) height = field.get_value('height', REQUEST=REQUEST) return render_element("textarea", name=key, css_class=field.get_value('css_class'), cols=width, rows=height, contents=html_quote(value), extra=field.get_value('extra')) def render_view(self, field, value, REQUEST, render_prefix=None): if value is None: return '' return value TextAreaWidgetInstance = TextAreaWidget() class LinesTextAreaWidget(TextAreaWidget): property_names = Widget.property_names +\ ['width', 'height', 'view_separator', 'extra'] default = fields.LinesField('default', title='Default', description=( "Default value of the lines in the widget."), default=[], width=20, height=3, required=0) view_separator = fields.StringField('view_separator', title='View separator', description=( "When called with render_view, this separator will be used to " "render individual items."), width=20, default='<br />\n', whitespace_preserve=1, required=1) def render(self, field, key, value, REQUEST, render_prefix=None): """ If type definition is missing for LinesField, the input text will be splitted into list like ['f', 'o', 'o'] with original Formulator's implementation. So explicit conversion to list is required before passing to LinesTextAreaWidget's render and render_view methods. """ if isinstance(value, (str, unicode)): value = [value] value = string.join(value, "\n") return TextAreaWidget.render(self, field, key, value, REQUEST) def render_view(self, field, value, REQUEST=None, render_prefix=None): if value is None: return '' elif isinstance(value, (str, unicode)): value = [value] return string.join(value, field.get_value('view_separator')) LinesTextAreaWidgetInstance = LinesTextAreaWidget() class FileWidget(TextWidget): def render(self, field, key, value, REQUEST, render_prefix=None): """Render text input field. """ display_maxwidth = field.get_value('display_maxwidth') or 0 if display_maxwidth > 0: return render_element("input", type="file", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), maxlength=display_maxwidth, extra=field.get_value('extra')) else: return render_element("input", type="file", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), extra=field.get_value('extra')) def render_view(self, field, value, REQUEST=None, render_prefix=None): return "[File]" FileWidgetInstance = FileWidget() class ItemsWidget(Widget): """A widget that has a number of items in it. """ items = fields.ListTextAreaField('items', title='Items', description=( "Items in the field. Each row should contain an " "item. Use the | (pipe) character to separate what is shown " "to the user from the submitted value. If no | is supplied, the " "shown value for the item will be identical to the submitted value. " "Internally the items property returns a list. If a list item " "is a single value, that will be used for both the display and " "the submitted value. A list item can also be a tuple consisting " "of two elements. The first element of the tuple should be a string " "that is name of the item that should be displayed. The second " "element of the tuple should be the value that will be submitted. " "If you want to override this property you will therefore have " "to return such a list."), default=[], width=20, height=5, required=0) # NOTE: for ordering reasons (we want extra at the end), # this isn't in the base class property_names list, but # instead will be referred to by the subclasses. extra_item = fields.StringField('extra_item', title='Extra per item', description=( "A string containing extra HTML code for attributes. This " "string will be literally included each of the rendered items of the " "field. This property can be useful if you want " "to add a disabled attribute to disable all contained items, for " "instance."), default="", required=0) class SingleItemsWidget(ItemsWidget): """A widget with a number of items that has only a single selectable item. """ default = fields.StringField('default', title='Default', description=( "The default value of the widget; this should be one of the " "elements in the list of items."), default="", required=0) first_item = fields.CheckBoxField('first_item', title="Select First Item", description=( "If checked, the first item will always be selected if " "no initial default value is supplied."), default=0) def render_items(self, field, key, value, REQUEST, render_prefix=None): # get items cell = getattr(REQUEST, 'cell', None) items = field.get_value('items', REQUEST=REQUEST, cell=cell) if not items: # single item widget should have at least one child in order to produce # valid XHTML; disable it so user can not select it return [self.render_item('', '', '', '', 'disabled="disabled"')] # check if we want to select first item if not value and field.get_value('first_item', REQUEST=REQUEST, cell=cell) and len(items) > 0: try: text, value = items[0] except ValueError: value = items[0] css_class = field.get_value('css_class') extra_item = field.get_value('extra_item') # if we run into multiple items with same value, we select the # first one only (for now, may be able to fix this better later) selected_found = 0 rendered_items = [] for item in items: try: item_text, item_value = item except ValueError: item_text = item item_value = item if item_value == value and not selected_found: rendered_item = self.render_selected_item(escape(ustr(item_text)), item_value, key, css_class, extra_item) selected_found = 1 else: rendered_item = self.render_item(escape(ustr(item_text)), item_value, key, css_class, extra_item) rendered_items.append(rendered_item) # XXX We want to make sure that we always have the current value in items. -yo if not selected_found and value: value = escape(ustr(value)) rendered_item = self.render_selected_item('??? (%s)' % value, value, key, css_class, extra_item) rendered_items.append(rendered_item) return rendered_items def render_view(self, field, value, REQUEST=None, render_prefix=None): """ This method is not as efficient as using a StringField in read only. Always consider to change the field in your Form. """ if value is None: return '' title_list = [x[0] for x in field.get_value("items", REQUEST=REQUEST) if x[1]==value] if len(title_list) == 0: return "??? (%s)" % escape(value) else: return title_list[0] return value render_pdf = render_view class MultiItemsWidget(ItemsWidget): """A widget with a number of items that has multiple selectable items. """ default = fields.LinesField('default', title='Default', description=( "The initial selections of the widget. This is a list of " "zero or more values. If you override this property from Python " "your code should return a Python list."), width=20, height=3, default=[], required=0) view_separator = fields.StringField('view_separator', title='View separator', description=( "When called with render_view, this separator will be used to " "render individual items."), width=20, default='<br />\n', whitespace_preserve=1, required=1) def render_items(self, field, key, value, REQUEST, render_prefix=None): # list is needed, not a tuple if isinstance(value, tuple): value = list(value) # need to deal with single item selects if not isinstance(value, list): value = [value] # XXX -yo selected_found = {} items = field.get_value('items', REQUEST=REQUEST, cell=getattr(REQUEST, 'cell', None)) # Added request from Products.ERP5Form.MultiLinkField import MultiLinkFieldWidget if not items and not isinstance(self, MultiLinkFieldWidget): # multi items widget should have at least one child in order to produce # valid XHTML; disable it so user can not select it. # This cannot be applied to MultiLinkFields, which are just some <a> # links return [self.render_item('', '', '', '', 'disabled="disabled"')] css_class = field.get_value('css_class') extra_item = field.get_value('extra_item') rendered_items = [] for item in items: try: item_text, item_value = item except ValueError: item_text = item item_value = item if item_value in value: rendered_item = self.render_selected_item( escape(ustr(item_text)), escape(ustr(item_value)), key, css_class, extra_item) # XXX -yo index = value.index(item_value) selected_found[index] = 1 else: rendered_item = self.render_item( escape(ustr(item_text)), escape(ustr(item_value)), key, css_class, extra_item) rendered_items.append(rendered_item) # XXX We want to make sure that we always have the current value in items. -yo for index in range(len(value)): v = value[index] if index not in selected_found and v: v = escape(v) rendered_item = self.render_selected_item('??? (%s)' % v, v, key, css_class, extra_item) rendered_items.append(rendered_item) # Moved marked field to Render # rendered_items.append(render_element('input', type='hidden', name="default_%s:int" % (key, ), value="0")) return rendered_items def render_items_view(self, field, value, REQUEST): if type(value) is not type([]): value = [value] items = field.get_value('items', REQUEST=REQUEST, cell=getattr(REQUEST, 'cell', None)) d = {} for item in items: try: item_text, item_value = item except ValueError: item_text = item item_value = item d[item_value] = item_text result = [] for e in value: result.append(d[e]) return result def render_view(self, field, value, REQUEST=None, render_prefix=None): if value is None: return '' return string.join(self.render_items_view(field, value, REQUEST), field.get_value('view_separator')) class ListWidget(SingleItemsWidget): """List widget. """ property_names = Widget.property_names +\ ['first_item', 'items', 'size', 'extra', 'extra_item'] size = fields.IntegerField('size', title='Size', description=( "The display size in rows of the field. If set to 1, the " "widget will be displayed as a drop down box by many browsers, " "if set to something higher, a list will be shown. Required."), default=5, required=1) def render(self, field, key, value, REQUEST, render_prefix=None): rendered_items = self.render_items(field, key, value, REQUEST) input_hidden = render_element('input', type='hidden', name="default_%s:int" % (key, ), value="0") list_widget = render_element( 'select', name=key, css_class=field.get_value('css_class', REQUEST=REQUEST), size=field.get_value('size', REQUEST=REQUEST), contents=string.join(rendered_items, "\n"), extra=field.get_value('extra', REQUEST=REQUEST)) return "\n".join([list_widget, input_hidden]) def render_item(self, text, value, key, css_class, extra_item): return render_element('option', contents=text, value=value, extra=extra_item) def render_selected_item(self, text, value, key, css_class, extra_item): return render_element('option', contents=text, value=value, selected=None, extra=extra_item) ListWidgetInstance = ListWidget() class MultiListWidget(MultiItemsWidget): """List widget with multiple select. """ property_names = Widget.property_names +\ ['items', 'size', 'view_separator', 'extra', 'extra_item'] size = fields.IntegerField('size', title='Size', description=( "The display size in rows of the field. If set to 1, the " "widget will be displayed as a drop down box by many browsers, " "if set to something higher, a list will be shown. Required."), default=5, required=1) def render(self, field, key, value, REQUEST, render_prefix=None): rendered_items = self.render_items(field, key, value, REQUEST) input_hidden = render_element('input', type='hidden', name="default_%s:int" % (key, ), value="0") multi_list = render_element( 'select', name=key, multiple=None, css_class=field.get_value('css_class', REQUEST=REQUEST), size=field.get_value('size', REQUEST=REQUEST), contents=string.join(rendered_items, "\n"), extra=field.get_value('extra', REQUEST=REQUEST)) return "\n".join([multi_list,input_hidden]) def render_item(self, text, value, key, css_class, extra_item): return render_element('option', contents=text, value=value, extra=extra_item) def render_selected_item(self, text, value, key, css_class, extra_item): return render_element('option', contents=text, value=value, selected=None, extra=extra_item) MultiListWidgetInstance = MultiListWidget() class RadioWidget(SingleItemsWidget): """radio buttons widget. """ property_names = Widget.property_names +\ ['first_item', 'items', 'orientation', 'extra_item'] orientation = fields.ListField('orientation', title='Orientation', description=( "Orientation of the radio buttons. The radio buttons will " "be drawn either vertically or horizontally."), default="vertical", required=1, size=1, items=[('Vertical', 'vertical'), ('Horizontal', 'horizontal')]) def render(self, field, key, value, REQUEST, render_prefix=None): input_hidden = render_element('input', type='hidden', name="default_%s" % (key, ), value="") rendered_items = self.render_items(field, key, value, REQUEST) rendered_items.append(input_hidden) orientation = field.get_value('orientation') if orientation == 'horizontal': return string.join(rendered_items, " ") else: return string.join(rendered_items, "<br />") def render_item(self, text, value, key, css_class, extra_item): return render_element('input', type="radio", css_class=css_class, name=key, value=value, extra=extra_item) + text def render_selected_item(self, text, value, key, css_class, extra_item): return render_element('input', type="radio", css_class=css_class, name=key, value=value, checked=None, extra=extra_item) + text RadioWidgetInstance = RadioWidget() class MultiCheckBoxWidget(MultiItemsWidget): """multiple checkbox widget. """ property_names = Widget.property_names +\ ['items', 'orientation', 'view_separator', 'extra_item'] orientation = fields.ListField('orientation', title='Orientation', description=( "Orientation of the check boxes. The check boxes will " "be drawn either vertically or horizontally."), default="vertical", required=1, size=1, items=[('Vertical', 'vertical'), ('Horizontal', 'horizontal')]) def render(self, field, key, value, REQUEST, render_prefix=None): rendered_items = self.render_items(field, key, value, REQUEST) rendered_items.append(render_element('input', type='hidden', name="default_%s:int" % (key, ), value="0")) orientation = field.get_value('orientation') if orientation == 'horizontal': return string.join(rendered_items, " ") else: return string.join(rendered_items, "<br />") def render_item(self, text, value, key, css_class, extra_item): return render_element('input', type="checkbox", css_class=css_class, name=key, value=value, extra=extra_item) + text def render_selected_item(self, text, value, key, css_class, extra_item): return render_element('input', type="checkbox", css_class=css_class, name=key, value=value, checked=None, extra=extra_item) + text MultiCheckBoxWidgetInstance = MultiCheckBoxWidget() class DateTimeWidget(Widget): """ Added support for key in every call to render_sub_field """ sql_format_year = '%Y' sql_format_month = '%m' sql_format_day = '%d' format_to_sql_format_dict = {'dmy': (sql_format_day , sql_format_month, sql_format_year), 'ymd': (sql_format_year , sql_format_month, sql_format_day ), 'mdy': (sql_format_month, sql_format_day , sql_format_year), 'my' : (sql_format_month, sql_format_year ), 'ym' : (sql_format_year , sql_format_month) } sql_format_default = format_to_sql_format_dict['ymd'] hide_day = fields.CheckBoxField('hide_day', title="Hide Day", description=( "The day will be hidden on the output. Instead the default" "Day will be taken"), default=0) hidden_day_is_last_day = fields.CheckBoxField('hidden_day_is_last_day', title="Hidden Day is last day of the Month", description=( "Defines wether hidden day means, you want the last day of the month" "Else it will be the first day"), default=0) timezone_style = fields.CheckBoxField('timezone_style', title="Display timezone", description=("Display timezone"), default=0) default = fields.DateTimeField('default', title="Default", description=("The default datetime."), default=None, display_style="text", display_order="ymd", input_style="text", required=0) default_now = fields.CheckBoxField('default_now', title="Default to now", description=( "Default date and time will be the date and time at showing of " "the form (if the default is left empty)."), default=0) date_separator = fields.StringField('date_separator', title='Date separator', description=( "Separator to appear between year, month, day."), default="/", required=0, display_width=2, display_maxwith=2, max_length=2) time_separator = fields.StringField('time_separator', title='Time separator', description=( "Separator to appear between hour and minutes."), default=":", required=0, display_width=2, display_maxwith=2, max_length=2) input_style = fields.ListField('input_style', title="Input style", description=( "The type of input used. 'text' will show the date part " "as text, while 'list' will use dropdown lists instead."), default="text", items=[("text", "text"), ("list", "list")], size=1) input_order = fields.ListField('input_order', title="Input order", description=( "The order in which date input should take place. Either " "year/month/day, day/month/year or month/day/year."), default="ymd", items=[("year/month/day", "ymd"), ("day/month/year", "dmy"), ("month/day/year", "mdy")], required=1, size=1) date_only = fields.CheckBoxField('date_only', title="Display date only", description=( "Display the date only, not the time."), default=0) ampm_time_style = fields.CheckBoxField('ampm_time_style', title="AM/PM time style", description=( "Display time in am/pm format."), default=0) property_names = Widget.property_names +\ ['default_now', 'date_separator', 'time_separator', 'input_style', 'input_order', 'date_only', 'ampm_time_style', 'timezone_style', 'hide_day', 'hidden_day_is_last_day'] def getInputOrder(self, field): input_order = field.get_value('input_order') if field.get_value('hide_day'): if input_order == 'ymd': input_order = 'ym' elif input_order in ('dmy', 'mdy'): input_order = 'my' return input_order def render_dict(self, field, value, render_prefix=None): """ This is yet another field rendering. It is designed to allow code to understand field's value data by providing its type and format when applicable. It returns a dict with 3 keys: type : Text representation of value's type. format: Type-dependant-formated formating information. This only describes the field format settings, not the actual format of provided value. query : Passthrough of given value. """ format_dict = self.format_to_sql_format_dict input_order = format_dict.get(self.getInputOrder(field), self.sql_format_default) if isinstance(value, unicode): value = value.encode(field.get_form_encoding()) return {'query': value, 'format': field.get_value('date_separator').join(input_order), 'type': 'date'} def render(self, field, key, value, REQUEST, render_prefix=None): use_ampm = field.get_value('ampm_time_style') use_timezone = field.get_value('timezone_style') # FIXME: backwards compatibility hack: if not hasattr(field, 'sub_form'): from StandardFields import create_datetime_text_sub_form field.sub_form = create_datetime_text_sub_form() # Is it still usefull to test the None value, # as DateTimeField should be considerer as the other field # and get an empty string as default value? # XXX hasattr(REQUEST, 'form') seems useless, # because REQUEST always has a form property if (value in (None, '')) and (field.get_value('default_now')) and \ ((REQUEST is None) or (not hasattr(REQUEST, 'form')) or \ (not REQUEST.form.has_key('subfield_%s_%s' % (key, 'year')))): value = DateTime() year = None month = None day = None hour = None minute = None ampm = None timezone = None if isinstance(value, DateTime): year = "%04d" % value.year() month = "%02d" % value.month() day = "%02d" % value.day() if use_ampm: hour = "%02d" % value.h_12() else: hour = "%02d" % value.hour() minute = "%02d" % value.minute() ampm = value.ampm() timezone = value.timezone() input_order = self.getInputOrder(field) if input_order == 'ymd': order = [('year', year), ('month', month), ('day', day)] elif input_order == 'dmy': order = [('day', day), ('month', month), ('year', year)] elif input_order == 'mdy': order = [('month', month), ('day', day), ('year', year)] elif input_order == 'my': order = [('month', month), ('year', year)] elif input_order == 'ym': order = [('year', year), ('month', month)] else: order = [('year', year), ('month', month), ('day', day)] result = [] for sub_field_name, sub_field_value in order: result.append(field.render_sub_field(sub_field_name, sub_field_value, REQUEST, key=key)) date_result = string.join(result, field.get_value('date_separator')) if not field.get_value('date_only'): time_result = (field.render_sub_field('hour', hour, REQUEST, key=key) + field.get_value('time_separator') + field.render_sub_field('minute', minute, REQUEST, key=key)) if use_ampm: time_result += ' ' + field.render_sub_field('ampm', ampm, REQUEST, key=key) if use_timezone: time_result += ' ' + field.render_sub_field('timezone', timezone, REQUEST, key=key) return date_result + ' ' + time_result else: return date_result def format_value(self, field, value, mode='html'): # Is it still usefull to test the None value, # as DateTimeField should be considerer as the other field # and get an empty string as default value? if value in (None, ''): return '' use_ampm = field.get_value('ampm_time_style') use_timezone = field.get_value('timezone_style') year = "%04d" % value.year() month = "%02d" % value.month() day = "%02d" % value.day() if use_ampm: hour = "%02d" % value.h_12() else: hour = "%02d" % value.hour() minute = "%02d" % value.minute() ampm = value.ampm() timezone = value.timezone() order = self.getInputOrder(field) if order == 'ymd': output = [year, month, day] elif order == 'dmy': output = [day, month, year] elif order == 'mdy': output = [month, day, year] elif order == 'my': output = [month, year] elif order == 'ym': output = [year, month] else: output = [year, month, day] date_result = string.join(output, field.get_value('date_separator')) if mode in ('html', ): space = ' ' else: space = ' ' if not field.get_value('date_only'): time_result = hour + field.get_value('time_separator') + minute if use_ampm: time_result += space + ampm if use_timezone: time_result += space + timezone return date_result + (space * 3) + time_result else: return date_result def render_view(self, field, value, REQUEST=None, render_prefix=None): return self.format_value(field, value, mode='html') def render_pdf(self, field, value, render_prefix=None): return self.format_value(field, value, mode='pdf') DateTimeWidgetInstance = DateTimeWidget() class LabelWidget(Widget): """Widget that is a label only. It simply returns its default value. """ property_names = ['title', 'description', 'default', 'css_class', 'hidden', 'extra'] default = fields.TextAreaField( 'default', title="Label text", description="Label text to render", default="", width=20, height=3, required=0) def render(self, field, key, value, REQUEST=None, render_prefix=None): return render_element("div", css_class=field.get_value('css_class'), contents=field.get_value('default')) # XXX should render view return the same information as render? def render_view(self, field, value, REQUEST=None, render_prefix=None): return field.get_value('default') LabelWidgetInstance = LabelWidget() def render_tag(tag, **kw): """Render the tag. Well, not all of it, as we may want to / it. """ attr_list = [] # special case handling for css_class if kw.has_key('css_class'): if kw['css_class'] != "": attr_list.append('class="%s"' % kw['css_class']) del kw['css_class'] # special case handling for extra 'raw' code if kw.has_key('extra'): extra = kw['extra'] # could be empty string but we don't care del kw['extra'] else: extra = "" # handle other attributes for key, value in kw.items(): if value == None: value = key attr_list.append('%s="%s"' % (key, html_quote(value))) attr_str = string.join(attr_list, " ") return "<%s %s %s" % (tag, attr_str, extra) def render_element(tag, **kw): if kw.has_key('contents'): contents = kw['contents'] del kw['contents'] return "%s>%s</%s>" % (apply(render_tag, (tag, ), kw), contents, tag) else: return apply(render_tag, (tag, ), kw) + " />" ############################################################################## # # Copyright (c) 2002-2009 Nexedi SA and Contributors. All Rights Reserved. # Jean Paul Smets <jp@nexedi.com> # Jerome Perrin <jerome@nexedi.com> # Yoshinori Okuji <yo@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## class IntegerWidget(TextWidget) : def render(self, field, key, value, REQUEST, render_prefix=None) : """Render an editable integer. """ if isinstance(value, float): value = int(value) display_maxwidth = field.get_value('display_maxwidth') or 0 if display_maxwidth > 0: return render_element("input", type="text", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), maxlength=display_maxwidth, extra=field.get_value('extra')) else: return render_element("input", type="text", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), extra=field.get_value('extra')) def render_view(self, field, value, REQUEST=None, render_prefix=None): """Render a non-editable interger.""" if isinstance(value, float): value = int(value) return TextWidget.render_view(self, field, value, REQUEST=REQUEST) IntegerWidgetInstance = IntegerWidget() class FloatWidget(TextWidget): property_names = TextWidget.property_names +\ ['input_style','precision'] input_style = fields.ListField('input_style', title="Input style", description=( "The type of float we should enter. "), default="-1234.5", items=[("-1234.5", "-1234.5"), ("-1 234.5", "-1 234.5"), ("-1 234,5", "-1 234,5"), ("-1.234,5", "-1.234,5"), ("-1,234.5", "-1,234.5"), ("-12.3%", "-12.3%"),], required=1, size=1) precision = fields.IntegerField('precision', title='Precision', description=( "Number of digits after the decimal point"), default='', required=0) def format_value(self, field, value): """Formats the value as requested""" if value not in (None,''): precision = field.get_value('precision') input_style = field.get_value('input_style') percent = 0 original_value = value if input_style.find('%')>=0: percent=1 try: value = float(value) * 100 except ValueError: return value try : float_value = float(value) if precision not in (None, ''): float_value = round(float_value, precision) value = str(float_value) except ValueError: return value else: if 'e' in value: # %f will not use exponential format value = '%f' % float(original_value) value_list = value.split('.') integer = value_list[0] decimal_separator = '' decimal_point = '.' if input_style == "-1234.5": decimal_point = '.' elif input_style == '-1 234.5': decimal_separator = ' ' decimal_point = '.' elif input_style == '-1 234,5': decimal_separator = ' ' decimal_point = ',' elif input_style == '-1.234,5': decimal_separator = '.' decimal_point = ',' elif input_style == '-1,234.5': decimal_separator = ',' decimal_point = '.' if input_style.find(decimal_separator) >= 0: integer = value_list[0] i = len(integer) % 3 value = integer[:i] while i != len(integer): value += decimal_separator + integer[i:i+3] i += 3 if value[0] == decimal_separator: value = value[1:] else: value = value_list[0] if precision != 0: value += decimal_point if precision not in (None,''): for i in range(0,precision): if i < len(value_list[1]): value += value_list[1][i] else: value += '0' else: value += value_list[1] if percent: value += '%' return value.strip() return '' def render(self, field, key, value, REQUEST, render_prefix=None): """Render Float input field """ value = self.format_value(field, value) display_maxwidth = field.get_value('display_maxwidth') or 0 extra_keys = {} if display_maxwidth > 0: extra_keys['maxlength'] = display_maxwidth return render_element( "input", type="text", name=key, css_class=field.get_value('css_class'), value=value, size=field.get_value('display_width'), extra=field.get_value('extra'), **extra_keys) def render_view(self, field, value, REQUEST=None, render_prefix=None): """ Render Float display field. This patch add: * replacement of spaces by unbreakable spaces if the content is float-like * support of extra CSS class when render as pure text """ value = self.format_value(field, value) float_value = None try: float_value = float(value.replace(' ', '')) except: pass if float_value != None: value = value.replace(' ', ' ') extra = field.get_value('extra') if extra not in (None, ''): value = "<div %s>%s</div>" % (extra, value) css_class = field.get_value('css_class') if css_class not in ('', None): return "<span class='%s'>%s</span>" % (css_class, value) return value def render_pdf(self, field, value, render_prefix=None): """Render the field as PDF.""" return self.format_value(field, value) def render_dict(self, field, value, render_prefix=None): """ This is yet another field rendering. It is designed to allow code to understand field's value data by providing its type and format when applicable. It returns a dict with 3 keys: type : Text representation of value's type. format: Type-dependant-formated formating information. This only describes the field format settings, not the actual format of provided value. query : Passthrough of given value. """ input_style = field.get_value('input_style') precision = field.get_value('precision') if precision not in (None, '') and precision != 0: for x in xrange(1, precision): input_style += '5' else: input_style = input_style.split('.')[0] if isinstance(value, unicode): value = value.encode(field.get_form_encoding()) return {'query': value, 'format': input_style, 'type': 'float'} FloatWidgetInstance = FloatWidget() class LinkWidget(TextWidget): def render_view(self, field, value, REQUEST=None, render_prefix=None): """Render link. """ link_type = field.get_value('link_type', REQUEST=REQUEST) if REQUEST is None: REQUEST = get_request() if link_type == 'internal': value = urljoin(REQUEST['BASE0'], value) elif link_type == 'relative': value = urljoin(REQUEST['URL1'], value) return '<a href="%s">%s</a>' % (value, field.get_value('title', cell=getattr(REQUEST,'cell',None))) LinkWidgetInstance = LinkWidget()