# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1240580299 -7200 # Node ID 716f0742ee7fa3165fcad06b84fb7881b2c13b09 # Parent 717dea3362c0236198106fe577364459fb97ce1f delete-trailing-whitespaces diff -r 717dea3362c0 -r 716f0742ee7f entity.py --- a/entity.py Fri Apr 24 15:38:11 2009 +0200 +++ b/entity.py Fri Apr 24 15:38:19 2009 +0200 @@ -62,7 +62,7 @@ except ImportError: AutomaticEntityForm = None - + def dispatch_rtags(*args): pass @@ -74,7 +74,7 @@ etype = getattr(base, 'id', None) if etype and etype != 'Any': return etype - + def _get_defs(attr, name, bases, classdict): try: yield name, classdict.pop(attr) @@ -86,7 +86,7 @@ yield base.__name__, value except AttributeError: continue - + class metaentity(type): """this metaclass sets the relation tags on the entity class and deals with the `widgets` attribute @@ -138,7 +138,7 @@ class Entity(AppRsetObject, dict): """an entity instance has e_schema automagically set on the class and instances has access to their issuing cursor. - + A property is set for each attribute and relation on each entity's type class. Becare that among attributes, 'eid' is *NEITHER* stored in the dict containment (which acts as a cache for other attributes dynamically @@ -151,7 +151,7 @@ :cvar rest_var: indicates which attribute should be used to build REST urls If None is specified, the first non-meta attribute will be used - + :type skip_copy_for: list :cvar skip_copy_for: a list of relations that should be skipped when copying this kind of entity. Note that some relations such @@ -162,14 +162,14 @@ __registry__ = 'etypes' __select__ = yes() - # class attributes that must be set in class definition + # class attributes that must be set in class definition id = None rest_attr = None fetch_attrs = None skip_copy_for = () # class attributes set automatically at registration time e_schema = None - + @classmethod def registered(cls, registry): """build class using descriptor at registration time""" @@ -178,7 +178,7 @@ if cls.id != 'Any': cls.__initialize__() return cls - + MODE_TAGS = set(('link', 'create')) CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata')) @classmethod @@ -210,7 +210,7 @@ if mixins: cls.__bases__ = tuple(mixins + [p for p in cls.__bases__ if not p is object]) cls.debug('plugged %s mixins on %s', mixins, etype) - + @classmethod def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X', settype=True, ordermethod='fetch_order'): @@ -231,7 +231,7 @@ rql += ' ORDERBY %s' % ','.join(orderby) rql += ' WHERE %s' % ', '.join(restrictions) return rql - + @classmethod def _fetch_restrictions(cls, mainvar, varmaker, fetchattrs, selection, orderby, restrictions, user, @@ -293,7 +293,7 @@ else: self.eid = None self._is_saved = True - + def __repr__(self): return '' % ( self.e_schema, self.eid, self.keys(), id(self)) @@ -308,11 +308,11 @@ """hook called by the repository before doing anything to add the entity (before_add entity hooks have not been called yet). This give the occasion to do weird stuff such as autocast (File -> Image for instance). - + This method must return the actual entity to be added. """ return self - + def set_eid(self, eid): self.eid = self['eid'] = eid @@ -333,7 +333,7 @@ saved in its source. """ return self.has_eid() and self._is_saved - + @cached def metainformation(self): res = dict(zip(('type', 'source', 'extid'), self.req.describe(self.eid))) @@ -349,7 +349,7 @@ def has_perm(self, action): return self.e_schema.has_perm(self.req, action, self.eid) - + def view(self, vid, __registry='views', **kwargs): """shortcut to apply a view on this entity""" return self.vreg.render(__registry, vid, self.req, rset=self.rset, @@ -452,9 +452,9 @@ trdata = TransformData(data, format, encoding, appobject=self) data = _engine.convert(trdata, target_format).decode() if format == 'text/html': - data = soup2xhtml(data, self.req.encoding) + data = soup2xhtml(data, self.req.encoding) return data - + # entity cloning ########################################################## def copy_relations(self, ceid): @@ -517,7 +517,7 @@ rset = ResultSet([(self.eid,)], 'Any X WHERE X eid %(x)s', {'x': self.eid}, [(self.id,)]) return self.req.decorate_rset(rset) - + def to_complete_relations(self): """by default complete final relations to when calling .complete()""" for rschema in self.e_schema.subject_relations(): @@ -533,7 +533,7 @@ all(matching_groups(es.get_groups('read')) for es in rschema.objects(self.e_schema)): yield rschema, 'subject' - + def to_complete_attributes(self, skip_bytes=True): for rschema, attrschema in self.e_schema.attribute_definitions(): # skip binary data by default @@ -548,7 +548,7 @@ self[attr] = None continue yield attr - + def complete(self, attributes=None, skip_bytes=True): """complete this entity by adding missing attributes (i.e. query the repository to fill the entity) @@ -618,7 +618,7 @@ else: rrset = self.req.eid_rset(value) self.set_related_cache(rtype, x, rrset) - + def get_value(self, name): """get value for the attribute relation , query the repository to get the value if necessary. @@ -654,7 +654,7 @@ def related(self, rtype, role='subject', limit=None, entities=False): """returns a resultset of related entities - + :param role: is the role played by 'self' in the relation ('subject' or 'object') :param limit: resultset's maximum size :param entities: if True, the entites are returned; if False, a result set is returned @@ -700,7 +700,7 @@ args = tuple(rql.split(' WHERE ', 1)) rql = '%s ORDERBY Z DESC WHERE X modification_date Z, %s' % args return rql - + # generic vocabulary methods ############################################## @obsolete('see new form api') @@ -708,7 +708,7 @@ """vocabulary functions must return a list of couples (label, eid) that will typically be used to fill the edition view's combobox. - + If `eid` is None in one of these couples, it should be interpreted as a separator in case vocabulary results are grouped """ @@ -717,7 +717,7 @@ form = EntityFieldsForm(self.req, entity=self) field = mock_object(name=rtype, role=role) return form.form_field_vocabulary(field, limit) - + def unrelated_rql(self, rtype, targettype, role, ordermethod=None, vocabconstraints=True): """build a rql to fetch `targettype` entities unrelated to this entity @@ -753,7 +753,7 @@ before, after = rql.split(' WHERE ', 1) rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after) return rql - + def unrelated(self, rtype, targettype, role='subject', limit=None, ordermethod=None): """return a result set of target type objects that may be related @@ -766,14 +766,14 @@ if self.has_eid(): return self.req.execute(rql, {'x': self.eid}) return self.req.execute(rql) - + # relations cache handling ################################################ - + def relation_cached(self, rtype, role): """return true if the given relation is already cached on the instance """ return '%s_%s' % (rtype, role) in self._related_cache - + def related_cache(self, rtype, role, entities=True, limit=None): """return values for the given relation if it's cached on the instance, else raise `KeyError` @@ -785,7 +785,7 @@ else: res = res.limit(limit) return res - + def set_related_cache(self, rtype, role, rset, col=0): """set cached values for the given relation""" if rset: @@ -805,7 +805,7 @@ else: related = [] self._related_cache['%s_%s' % (rtype, role)] = (rset, related) - + def clear_related_cache(self, rtype=None, role=None): """clear cached values for the given relation or the entire cache if no relation is given @@ -815,9 +815,9 @@ else: assert role self._related_cache.pop('%s_%s' % (rtype, role), None) - + # raw edition utilities ################################################### - + def set_attributes(self, **kwargs): assert kwargs relations = [] @@ -829,14 +829,14 @@ kwargs['x'] = self.eid self.req.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations), kwargs, 'x') - + def delete(self): assert self.has_eid(), self.eid self.req.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, {'x': self.eid}) - + # server side utilities ################################################### - + def set_defaults(self): """set default values according to the schema""" self._default_set = set() @@ -879,13 +879,13 @@ yield self else: yield self - + def get_words(self): """used by the full text indexer to get words to index this method should only be used on the repository side since it depends on the indexer package - + :rtype: list :return: the list of indexable word of this entity """ @@ -902,7 +902,7 @@ continue if value: words += tokenize(value) - + for rschema, role in self.e_schema.fulltext_relations(): if role == 'subject': for entity in getattr(self, rschema.type): @@ -947,7 +947,7 @@ raise AttributeError('%s cannot be only be accessed from instances' % self._rtype) return eobj.related(self._rtype, self._role, entities=True) - + def __set__(self, eobj, value): raise NotImplementedError @@ -955,7 +955,7 @@ class SubjectRelation(Relation): """descriptor that controls schema relation access""" _role = 'subject' - + class ObjectRelation(Relation): """descriptor that controls schema relation access""" _role = 'object' diff -r 717dea3362c0 -r 716f0742ee7f selectors.py --- a/selectors.py Fri Apr 24 15:38:11 2009 +0200 +++ b/selectors.py Fri Apr 24 15:38:19 2009 +0200 @@ -33,7 +33,7 @@ selectors.TRACED_OIDS = ('calendar',) self.view('calendar', myrset) selectors.TRACED_OIDS = () - + :organization: Logilab :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. @@ -97,11 +97,11 @@ >>> with traced_selection( ('oid1', 'oid2') ): ... # some code in which you want to debug selectors ... # for objects with id 'oid1' and 'oid2' - + """ def __init__(self, traced='all'): self.traced = traced - + def __enter__(self): global TRACED_OIDS TRACED_OIDS = self.traced @@ -139,7 +139,7 @@ class PartialSelectorMixIn(object): """convenience mix-in for selectors that will look into the containing class to find missing information. - + cf. `cubicweb.web.action.LinkToEntityAction` for instance """ def __call__(self, cls, *args, **kwargs): @@ -157,7 +157,7 @@ def __str__(self): return '%s(%s)' % (self.__class__.__name__, ','.join(str(s) for s in self.expected_ifaces)) - + def score_interfaces(self, cls_or_inst, cls): score = 0 vreg, eschema = cls_or_inst.vreg, cls_or_inst.e_schema @@ -187,7 +187,7 @@ """ def __init__(self, once_is_enough=False): self.once_is_enough = once_is_enough - + @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): if not rset: @@ -213,7 +213,7 @@ if etype in BASE_TYPES: return 0 return self.score_class(cls.vreg.etype_class(etype), req) - + def score_class(self, eclass, req): raise NotImplementedError() @@ -236,7 +236,7 @@ note: None values (resulting from some outer join in the query) are not considered. """ - + @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): if not rset and not kwargs.get('entity'): @@ -265,7 +265,7 @@ return self.score_entity(rset.get_entity(row, col)) except NotAnEntity: return 0 - + def score_entity(self, entity): raise NotImplementedError() @@ -302,7 +302,7 @@ if rset is not None and rset.rowcount: return 1 return 0 - + @objectify_selector @lltrace def empty_rset(cls, req, rset, *args, **kwargs): @@ -341,7 +341,7 @@ @lltrace def paginated_rset(cls, req, rset, *args, **kwargs): """accept result set with more lines than the page size. - + Page size is searched in (respecting order): * a page_size argument * a page_size form parameters @@ -442,7 +442,7 @@ class match_search_state(Selector): """accept if the current request search state is in one of the expected states given to the initializer - + :param expected: either 'normal' or 'linksearch' (eg searching for an object to create a relation with another) """ @@ -453,7 +453,7 @@ def __str__(self): return '%s(%s)' % (self.__class__.__name__, ','.join(sorted(str(s) for s in self.expected))) - + @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): try: @@ -467,7 +467,7 @@ class match_form_params(match_search_state): """accept if parameters specified as initializer arguments are specified in request's form parameters - + :param *expected: parameters (eg `basestring`) which are expected to be found in request's form parameters """ @@ -486,7 +486,7 @@ class match_kwargs(match_search_state): """accept if parameters specified as initializer arguments are specified in named arguments given to the selector - + :param *expected: parameters (eg `basestring`) which are expected to be found in named arguments (kwargs) """ @@ -502,7 +502,7 @@ class match_user_groups(match_search_state): """accept if logged users is in at least one of the given groups. Returned score is the number of groups in which the user is. - + If the special 'owners' group is given: * if row is specified check the entity at the given row/col is owned by the logged user @@ -512,7 +512,7 @@ :param *required_groups: name of groups (`basestring`) in which the logged user should be """ - + @lltrace def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs): user = req.user @@ -550,7 +550,7 @@ def __init__(self, registry, oid): self.registry = registry self.oid = oid - + def __call__(self, cls, req, rset, *args, **kwargs): try: cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs) @@ -572,7 +572,7 @@ or an entity type (e.g. `basestring`) in which case the associated class will be searched in the registry (at selection time) - + note: when interface is an entity class, the score will reflect class proximity so the most specific object'll be selected """ @@ -589,11 +589,11 @@ or an entity type (e.g. `basestring`) in which case the associated class will be searched in the registry (at selection time) - + note: when interface is an entity class, the score will reflect class proximity so the most specific object'll be selected """ - + @lltrace def __call__(self, cls, req, *args, **kwargs): try: @@ -617,10 +617,10 @@ or an entity type (e.g. `basestring`) in which case the associated class will be searched in the registry (at selection time) - + note: when interface is an entity class, the score will reflect class proximity so the most specific object'll be selected - """ + """ def score_entity(self, entity): return self.score_interfaces(entity, entity.__class__) @@ -655,7 +655,7 @@ return 0 score = super(relation_possible, self).__call__(cls, req, *args, **kwargs) return score - + def score_class(self, eclass, req): eschema = eclass.e_schema try: @@ -684,12 +684,12 @@ for this selector are: - `rtype`: same as `rtype` parameter of the `relation_possible` selector - + - `role`: this attribute will be passed to the `cubicweb.role` function to determine the role of class in the relation - `etype` (optional): the entity type on the other side of the relation - + :param action: a relation schema action (one of 'read', 'add', 'delete') which must be granted to the logged user, else a 0 score will be returned @@ -707,19 +707,19 @@ class may_add_relation(EntitySelector): """accept if the relation can be added to an entity found in the result set by the logged user. - + See `EntitySelector` documentation for behaviour when row is not specified. :param rtype: a relation type (`basestring`) :param role: the role of the result set entity in the relation. 'subject' or 'object', default to 'subject'. """ - + def __init__(self, rtype, role='subject', once_is_enough=False): super(may_add_relation, self).__init__(once_is_enough) self.rtype = rtype self.role = role - + def score_entity(self, entity): rschema = entity.schema.rschema(self.rtype) if self.role == 'subject': @@ -738,10 +738,10 @@ for this selector are: - `rtype`: same as `rtype` parameter of the `relation_possible` selector - + - `role`: this attribute will be passed to the `cubicweb.role` function to determine the role of class in the relation. - + :param action: a relation schema action (one of 'read', 'add', 'delete') which must be granted to the logged user, else a 0 score will be returned @@ -753,12 +753,12 @@ self.rtype = cls.rtype self.role = role(cls) - + class has_related_entities(EntitySelector): """accept if entity found in the result set has some linked entities using the specified relation (optionaly filtered according to the specified target type). Checks first if the relation is possible. - + See `EntitySelector` documentation for behaviour when row is not specified. :param rtype: a relation type (`basestring`) @@ -773,7 +773,7 @@ self.rtype = rtype self.role = role self.target_etype = target_etype - + def score_entity(self, entity): relpossel = relation_possible(self.rtype, self.role, self.target_etype) if not relpossel.score_class(entity.__class__, entity.req): @@ -792,12 +792,12 @@ for this selector are: - `rtype`: same as `rtype` parameter of the `relation_possible` selector - + - `role`: this attribute will be passed to the `cubicweb.role` function to determine the role of class in the relation. - `etype` (optional): the entity type on the other side of the relation - + :param action: a relation schema action (one of 'read', 'add', 'delete') which must be granted to the logged user, else a 0 score will be returned @@ -822,13 +822,13 @@ note: None values (resulting from some outer join in the query) are not considered. - + :param action: an entity schema action (eg 'read'/'add'/'delete'/'update') """ def __init__(self, action, once_is_enough=False): super(has_permission, self).__init__(once_is_enough) self.action = action - + @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): if rset is None: @@ -837,7 +837,7 @@ action = self.action if row is None: score = 0 - need_local_check = [] + need_local_check = [] geteschema = cls.schema.eschema for etype in rset.column_types(0): if etype in BASE_TYPES: @@ -862,7 +862,7 @@ score += 1 return score return self.score(req, rset, row, col) - + def score_entity(self, entity): if entity.has_perm(self.action): return 1 @@ -887,7 +887,7 @@ """accept if an arbitrary rql return some results for an eid found in the result set. Returned score is the number of items returned by the rql condition. - + See `EntitySelector` documentation for behaviour when row is not specified. :param expression: basestring containing an rql expression, which should use @@ -904,7 +904,7 @@ else: rql = 'Any X WHERE X eid %%(x)s, %s' % expression self.rql = rql - + def score(self, req, rset, row, col): try: return len(req.execute(self.rql, {'x': rset[row][col], @@ -912,32 +912,32 @@ except Unauthorized: return 0 - + class but_etype(EntitySelector): """accept if the given entity types are not found in the result set. See `EntitySelector` documentation for behaviour when row is not specified. - + :param *etypes: entity types (`basestring`) which should be refused """ def __init__(self, *etypes): super(but_etype, self).__init__() self.but_etypes = etypes - + def score(self, req, rset, row, col): if rset.description[row][col] in self.but_etypes: return 0 return 1 - + class score_entity(EntitySelector): """accept if some arbitrary function return a positive score for an entity found in the result set. - + See `EntitySelector` documentation for behaviour when row is not specified. :param scorefunc: callable expected to take an entity as argument and to - return a score >= 0 + return a score >= 0 """ def __init__(self, scorefunc, once_is_enough=False): super(score_entity, self).__init__(once_is_enough) @@ -1022,7 +1022,7 @@ rqlcondition_selector = deprecated_function(chainall(non_final_entity(), one_line_rset, _rql_condition, name='rql_condition')) - + def but_etype_selector(cls, req, rset, row=None, col=0, **kwargs): return but_etype(cls.etype)(cls, req, rset, row, col) but_etype_selector = deprecated_function(but_etype_selector) @@ -1079,7 +1079,7 @@ warn(msg, DeprecationWarning) return registered(cls, vreg) return _deprecate - + @unbind_method def require_group_compat(registered): def plug_selector(cls, vreg): @@ -1123,7 +1123,7 @@ cls.__select__ &= rql_condition(cls.condition) return cls return plug_selector - + @unbind_method def has_relation_compat(registered): def plug_selector(cls, vreg): diff -r 717dea3362c0 -r 716f0742ee7f web/formwidgets.py --- a/web/formwidgets.py Fri Apr 24 15:38:11 2009 +0200 +++ b/web/formwidgets.py Fri Apr 24 15:38:19 2009 +0200 @@ -20,7 +20,7 @@ # automatically set id and tabindex attributes ? setdomid = True settabindex = True - + def __init__(self, attrs=None, setdomid=None, settabindex=None): if attrs is None: attrs = {} @@ -38,7 +38,7 @@ form.req.add_js(self.needs_js) if self.needs_css: form.req.add_css(self.needs_css) - + def render(self, form, field): """render the widget for the given `field` of `form`. To override in concrete class @@ -63,10 +63,10 @@ class Input(FieldWidget): """abstract widget class for tag based widgets""" type = None - + def render(self, form, field): """render the widget for the given `field` of `form`. - + Generate one tag for each field's value """ self.add_media(form) @@ -88,7 +88,7 @@ -confirm as name) """ type = 'password' - + def render(self, form, field): self.add_media(form) name, values, attrs = self._render_attrs(form, field) @@ -109,20 +109,20 @@ class FileInput(Input): """""" type = 'file' - + def _render_attrs(self, form, field): # ignore value which makes no sense here (XXX even on form validation error?) name, values, attrs = super(FileInput, self)._render_attrs(form, field) return name, ('',), attrs - + class HiddenInput(Input): """""" type = 'hidden' setdomid = False # by default, don't set id attribute on hidden input settabindex = False - + class ButtonInput(Input): """ @@ -151,18 +151,18 @@ def __init__(self, *args, **kwargs): super(FCKEditor, self).__init__(*args, **kwargs) self.attrs['cubicweb:type'] = 'wysiwyg' - + def render(self, form, field): form.req.fckeditor_config() return super(FCKEditor, self).render(form, field) class Select(FieldWidget): - """, for field having a specific vocabulary""" def __init__(self, attrs=None, multiple=False): super(Select, self).__init__(attrs) self.multiple = multiple - + def render(self, form, field): name, curvalues, attrs = self._render_attrs(form, field) if not 'size' in attrs: @@ -183,9 +183,9 @@ class CheckBox(Input): """, for field having a specific vocabulary. One input will be generated for each possible value. - """ + """ type = 'checkbox' - + def render(self, form, field): name, curvalues, attrs = self._render_attrs(form, field) options = [] @@ -199,13 +199,13 @@ options.append(tag + label) return '
\n'.join(options) - + class Radio(Input): """, for field having a specific vocabulary. One input will be generated for each possible value. - """ + """ type = 'radio' - + def render(self, form, field): name, curvalues, attrs = self._render_attrs(form, field) domid = attrs.pop('id', None) @@ -235,7 +235,7 @@ needs_js = ('cubicweb.calendar.js',) needs_css = ('cubicweb.calendar_popup.css',) - + @classmethod def add_localized_infos(cls, req): """inserts JS variables defining localized months and days""" @@ -245,13 +245,13 @@ daynames = [_(dname) for dname in cls.daynames] req.html_headers.define_var('MONTHNAMES', monthnames) req.html_headers.define_var('DAYNAMES', daynames) - + def render(self, form, field): txtwidget = super(DateTimePicker, self).render(form, field) self.add_localized_infos(form.req) cal_button = self._render_calendar_popup(form, field) return txtwidget + cal_button - + def _render_calendar_popup(self, form, field): value = form.form_field_value(field) if not value: @@ -265,7 +265,7 @@ form.req.external_resource('CALENDAR_ICON'), form.req._('calendar'), helperid) ) - + # ajax widgets ################################################################ @@ -285,13 +285,13 @@ init_ajax_attributes(self.attrs, wdgtype) if inputid is not None: self.attrs['cubicweb:inputid'] = inputid - + def render(self, form, field): self.add_media(form) attrs = self._render_attrs(form, field)[-1] return tags.div(**attrs) - + class AutoCompletionWidget(TextInput): """ajax widget for StringField, proposing matching existing values as you type. @@ -300,7 +300,7 @@ needs_css = ('jquery.autocomplete.css',) wdgtype = 'SuggestField' loadtype = 'auto' - + def _render_attrs(self, form, field): name, values, attrs = super(AutoCompletionWidget, self)._render_attrs(form, field) if not values[0]: @@ -309,7 +309,7 @@ # XXX entity form specific attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity) return name, values, attrs - + def _get_url(self, entity): return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema], pageid=entity.req.pageid, mode='remote') @@ -318,14 +318,14 @@ class StaticFileAutoCompletionWidget(AutoCompletionWidget): """XXX describe me""" wdgtype = 'StaticFileSuggestField' - + def _get_url(self, entity): return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema] class RestrictedAutoCompletionWidget(AutoCompletionWidget): """XXX describe me""" - wdgtype = 'RestrictedSuggestField' + wdgtype = 'RestrictedSuggestField' class AddComboBoxWidget(Select): @@ -337,7 +337,7 @@ attrs['cubicweb:etype_to'] = entity.e_schema etype_from = entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0] attrs['cubicweb:etype_from'] = etype_from - + def render(self, form, field): return super(AddComboBoxWidget, self).render(form, field) + u'''
@@ -364,7 +364,7 @@ self.onclick = onclick self.cwaction = cwaction self.attrs.setdefault('klass', 'validateButton') - + def render(self, form, field=None): label = form.req._(self.label) attrs = self.attrs.copy() @@ -382,12 +382,12 @@ attrs['tabindex'] = form.req.next_tabindex() return tags.input(value=label, type=self.type, **attrs) - + class SubmitButton(Button): """, main button to submit a form""" type = 'submit' - + class ResetButton(Button): """, main button to reset a form. You usually don't want this. @@ -407,7 +407,7 @@ self.href = href self.imgressource = imgressource self.label = label - + def render(self, form, field=None): label = form.req._(self.label) imgsrc = form.req.external_resource(self.imgressource) @@ -415,7 +415,7 @@ '%(label)s%(label)s' % { 'label': label, 'imgsrc': imgsrc, 'domid': self.domid, 'href': self.href} - + - + # XXX EntityLinkComboBoxWidget, [Raw]DynamicComboBoxWidget