"""widgets for entity editionthose are in cubicweb.common since we need to know available widgets at schemaserialization time:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"fromdatetimeimportdatetimefromlogilab.mtconverterimportxml_escapefromyams.constraintsimportSizeConstraint,StaticVocabularyConstraintfromcubicweb.common.uilibimporttoggle_actionfromcubicweb.webimportINTERNAL_FIELD_VALUE,eid_paramdef_format_attrs(kwattrs):"""kwattrs is the dictionary of the html attributes available for the edited element """# sort for predictability (required for tests)returnu' '.join(sorted(u'%s="%s"'%itemforiteminkwattrs.iteritems()))def_value_from_values(values):# take care, value may be 0, 0.0...ifvalues:value=values[0]ifvalueisNone:value=u''else:value=u''returnvaluedef_eclass_eschema(eschema_or_eclass):try:returneschema_or_eclass,eschema_or_eclass.e_schemaexceptAttributeError:returnNone,eschema_or_eclassdefcheckbox(name,value,attrs='',checked=None):ifcheckedisNone:checked=valuechecked=checkedand'checked="checked"'or''returnu'<input type="checkbox" name="%s" value="%s" %s%s />'%(name,value,checked,attrs)defwidget(vreg,subjschema,rschema,objschema,role='object'):"""get a widget to edit the given relation"""ifrschema=='eid':# return HiddenWidget(vreg, subjschema, rschema, objschema)returnEidWidget(vreg,_eclass_eschema(subjschema)[1],rschema,objschema)returnwidget_factory(vreg,subjschema,rschema,objschema,role=role)classWidget(object):"""abstract widget class"""need_multipart=False# generate the "id" attribute with the same value as the "name" (html) attributeautoid=Truehtml_attributes=set(('id','class','tabindex','accesskey','onchange','onkeypress'))cubicwebns_attributes=set()def__init__(self,vreg,subjschema,rschema,objschema,role='subject',description=None,**kwattrs):self.vreg=vregself.rschema=rschemaself.subjtype=subjschemaself.objtype=objschemaself.role=roleself.name=rschema.typeself.description=descriptionself.attrs=kwattrs# XXX accesskey may not be uniquekwattrs['accesskey']=self.name[0]defcopy(self):"""shallow copy (useful when you need to modify self.attrs because widget instances are cached) """# brute force copy (subclasses don't have the# same __init__ prototype)widget=self.__new__(self.__class__)widget.__dict__=dict(self.__dict__)widget.attrs=dict(widget.attrs)returnwidget@staticmethoddefsize_constraint_attrs(attrs,maxsize):"""set html attributes in the attrs dict to consider maxsize"""passdefformat_attrs(self):"""return a string with html attributes available for the edit input"""# sort for predictability (required for tests)attrs=[]forname,valueinself.attrs.iteritems():# namespace attributes have priority over standard xhtml onesifnameinself.cubicwebns_attributes:attrs.append(u'cubicweb:%s="%s"'%(name,value))elifnameinself.html_attributes:attrs.append(u'%s="%s"'%(name,value))returnu' '.join(sorted(attrs))defrequired(self,entity):"""indicates if the widget needs a value to be filled in"""card=self.rschema.cardinality(self.subjtype,self.objtype,self.role)returncardin'1+'definput_id(self,entity):try:returnself.rnameexceptAttributeError:returneid_param(self.name,entity.eid)defrender_label(self,entity,label=None):"""render widget's label"""label=labelorself.rschema.display_name(entity.req,self.role)forid=self.input_id(entity)ifforid:forattr=' for="%s"'%foridelse:forattr=''ifself.required(entity):label=u'<label class="required"%s>%s</label>'%(forattr,label)else:label=u'<label%s>%s</label>'%(forattr,label)returnlabeldefrender_error(self,entity):"""return validation error for widget's field of the given entity, if any """errex=entity.req.data.get('formerrors')iferrexanderrex.eid==entity.eidandself.nameinerrex.errors:entity.req.data['displayederrors'].add(self.name)returnu'<span class="error">%s</span>'%errex.errors[self.name]returnu''defrender_help(self,entity):"""render a help message about the (edited) field"""req=entity.reqhelp=[u'<div class="helper">']descr=self.descriptionorself.rschema.rproperty(self.subjtype,self.objtype,'description')ifdescr:help.append(u'<span>%s</span>'%req._(descr))example=self.render_example(req)ifexample:help.append(u'<span>(%s: %s)</span>'%(req._('sample format'),example))help.append(u'</div>')returnu' '.join(help)defrender_example(self,req):returnu''defrender(self,entity):"""render the widget for a simple view"""ifnotentity.has_eid():returnu''returnentity.printable_value(self.name)defedit_render(self,entity,tabindex=None,includehelp=False,useid=None,**kwargs):"""render the widget for edition"""# this is necessary to handle multiple editionself.rname=eid_param(self.name,entity.eid)ifuseid:self.attrs['id']=useidelifself.autoid:self.attrs['id']=self.rnameiftabindexisnotNone:self.attrs['tabindex']=tabindexelse:self.attrs['tabindex']=entity.req.next_tabindex()output=self._edit_render(entity,**kwargs)ifincludehelp:output+=self.render_help(entity)returnoutputdef_edit_render(self,entity):"""do the actual job to render the widget for edition"""raiseNotImplementedErrordefcurrent_values(self,entity):"""return the value of the field associated to this widget on the given entity. always return a list of values, which'll have size equal to 1 if the field is monovalued (like all attribute fields, but not all non final relation fields """ifself.rschema.is_final():returnentity.attribute_values(self.name)elifentity.has_eid():return[row[0]forrowinentity.related(self.name,self.role)]return()defcurrent_value(self,entity):return_value_from_values(self.current_values(entity))defcurrent_display_values(self,entity):"""same as .current_values but consider values stored in session in case of validation error """values=entity.req.data.get('formvalues')ifvaluesisNone:returnself.current_values(entity)cdvalues=values.get(self.rname)ifcdvaluesisNone:returnself.current_values(entity)ifnotisinstance(cdvalues,(list,tuple)):cdvalues=(cdvalues,)returncdvaluesdefcurrent_display_value(self,entity):"""same as .current_value but consider values stored in session in case of validation error """return_value_from_values(self.current_display_values(entity))defhidden_input(self,entity,qvalue):"""return an hidden field which 1. indicates that a field is edited 2. hold the old value to easily detect if the field has been modified `qvalue` is the html quoted old value """ifself.role=='subject':editmark='edits'else:editmark='edito'ifqvalueisNoneornotentity.has_eid():qvalue=INTERNAL_FIELD_VALUEreturnu'<input type="hidden" name="%s-%s" value="%s"/>\n'%(editmark,self.rname,qvalue)classInputWidget(Widget):"""abstract class for input generating a <input> tag"""input_type=Nonehtml_attributes=Widget.html_attributes|set(('type','name','value'))def_edit_render(self,entity):value=self.current_value(entity)dvalue=self.current_display_value(entity)ifisinstance(value,basestring):value=xml_escape(value)ifisinstance(dvalue,basestring):dvalue=xml_escape(dvalue)returnu'%s<input type="%s" name="%s" value="%s" %s/>'%(self.hidden_input(entity,value),self.input_type,self.rname,dvalue,self.format_attrs())classHiddenWidget(InputWidget):input_type='hidden'autoid=Falsedef__init__(self,vreg,subjschema,rschema,objschema,role='subject',**kwattrs):InputWidget.__init__(self,vreg,subjschema,rschema,objschema,role='subject',**kwattrs)# disable access keydelself.attrs['accesskey']defcurrent_value(self,entity):value=InputWidget.current_value(self,entity)returnvalueorINTERNAL_FIELD_VALUEdefcurrent_display_value(self,entity):value=InputWidget.current_display_value(self,entity)returnvalueorINTERNAL_FIELD_VALUEdefrender_label(self,entity,label=None):"""render widget's label"""returnu''defrender_help(self,entity):returnu''defhidden_input(self,entity,value):"""no hidden input for hidden input"""return''classEidWidget(HiddenWidget):def_edit_render(self,entity):returnu'<input type="hidden" name="eid" value="%s" />'%entity.eidclassStringWidget(InputWidget):input_type='text'html_attributes=InputWidget.html_attributes|set(('size','maxlength'))@staticmethoddefsize_constraint_attrs(attrs,maxsize):"""set html attributes in the attrs dict to consider maxsize"""attrs['size']=min(maxsize,40)attrs['maxlength']=maxsizeclassAutoCompletionWidget(StringWidget):cubicwebns_attributes=(StringWidget.cubicwebns_attributes|set(('accesskey','size','maxlength')))attrs=()wdgtype='SuggestField'defcurrent_value(self,entity):value=StringWidget.current_value(self,entity)returnvalueorINTERNAL_FIELD_VALUEdef_get_url(self,entity):returnentity.req.build_url('json',fname=entity.autocomplete_initfuncs[self.rschema],pageid=entity.req.pageid,mode='remote')def_edit_render(self,entity):req=entity.reqreq.add_js(('cubicweb.widgets.js','jquery.autocomplete.js'))req.add_css('jquery.autocomplete.css')value=self.current_value(entity)dvalue=self.current_display_value(entity)ifisinstance(value,basestring):value=xml_escape(value)ifisinstance(dvalue,basestring):dvalue=xml_escape(dvalue)iid=self.attrs.pop('id')ifself.required(entity):cssclass=u' required'else:cssclass=u''dataurl=self._get_url(entity)return(u'%(hidden)s<input type="text" name="%(iid)s" value="%(value)s" cubicweb:dataurl="%(url)s" class="widget%(required)s" id="%(iid)s" 'u'tabindex="%(tabindex)s" cubicweb:loadtype="auto" cubicweb:wdgtype="%(wdgtype)s" %(attrs)s />'%{'iid':iid,'hidden':self.hidden_input(entity,value),'wdgtype':self.wdgtype,'url':xml_escape(dataurl),'tabindex':self.attrs.pop('tabindex'),'value':dvalue,'attrs':self.format_attrs(),'required':cssclass,})classStaticFileAutoCompletionWidget(AutoCompletionWidget):wdgtype='StaticFileSuggestField'def_get_url(self,entity):returnentity.req.datadir_url+entity.autocomplete_initfuncs[self.rschema]classRestrictedAutoCompletionWidget(AutoCompletionWidget):wdgtype='RestrictedSuggestField'classPasswordWidget(InputWidget):input_type='password'defrequired(self,entity):ifInputWidget.required(self,entity)andnotentity.has_eid():returnTruereturnFalsedefcurrent_values(self,entity):# on existant entity, show password field has non empty (we don't have# the actual valueifentity.has_eid():return(INTERNAL_FIELD_VALUE,)returnsuper(PasswordWidget,self).current_values(entity)def_edit_render(self,entity):html=super(PasswordWidget,self)._edit_render(entity)name=eid_param(self.name+'-confirm',entity.eid)returnu'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/> <span class="emphasis">(%s)</span>'%(html,self.input_type,name,name,entity.req.next_tabindex(),entity.req._('confirm password'))classTextWidget(Widget):html_attributes=Widget.html_attributes|set(('rows','cols'))@staticmethoddefsize_constraint_attrs(attrs,maxsize):"""set html attributes in the attrs dict to consider maxsize"""if256<maxsize<513:attrs['cols'],attrs['rows']=60,5else:attrs['cols'],attrs['rows']=80,10defrender(self,entity):ifnotentity.has_eid():returnu''returnentity.printable_value(self.name)def_edit_render(self,entity,with_format=True):req=entity.reqeditor=self._edit_render_textarea(entity,with_format)value=self.current_value(entity)ifisinstance(value,basestring):value=xml_escape(value)returnu'%s%s'%(self.hidden_input(entity,value),editor)def_edit_render_textarea(self,entity,with_format):self.attrs.setdefault('cols',80)self.attrs.setdefault('rows',20)dvalue=self.current_display_value(entity)ifisinstance(dvalue,basestring):dvalue=xml_escape(dvalue)ifentity.use_fckeditor(self.name):entity.req.fckeditor_config()ifwith_format:ifentity.has_eid():format=entity.attr_metadata(self.name,'format')else:format=''frname=eid_param(self.name+'_format',entity.eid)hidden=u'<input type="hidden" name="edits-%s" value="%s"/>\n'\'<input type="hidden" name="%s" value="text/html"/>\n'%(frname,format,frname)returnu'%s<textarea cubicweb:type="wysiwyg" onkeyup="autogrow(this)" name="%s" %s>%s</textarea>'%(hidden,self.rname,self.format_attrs(),dvalue)ifwith_formatandentity.e_schema.has_metadata(self.name,'format'):fmtwdg=entity.get_widget(self.name+'_format')fmtwdgstr=fmtwdg.edit_render(entity,tabindex=self.attrs['tabindex'])self.attrs['tabindex']=entity.req.next_tabindex()else:fmtwdgstr=''returnu'%s<br/><textarea onkeyup="autogrow(this)" name="%s" %s>%s</textarea>'%(fmtwdgstr,self.rname,self.format_attrs(),dvalue)classCheckBoxWidget(Widget):html_attributes=Widget.html_attributes|set(('checked',))def_edit_render(self,entity):value=self.current_value(entity)dvalue=self.current_display_value(entity)returnself.hidden_input(entity,value)+checkbox(self.rname,'checked',self.format_attrs(),dvalue)defrender(self,entity):ifnotentity.has_eid():returnu''ifgetattr(entity,self.name):returnentity.req._('yes')returnentity.req._('no')classYesNoRadioWidget(CheckBoxWidget):html_attributes=Widget.html_attributes|set(('disabled',))def_edit_render(self,entity):value=self.current_value(entity)dvalue=self.current_display_value(entity)attrs1=self.format_attrs()delself.attrs['id']# avoid duplicate id for xhtml complianceattrs2=self.format_attrs()ifdvalue:attrs1+=' checked="checked"'else:attrs2+=' checked="checked"'wdgs=[self.hidden_input(entity,value),u'<input type="radio" name="%s" value="1" %s/>%s<br/>'%(self.rname,attrs1,entity.req._('yes')),u'<input type="radio" name="%s" value="" %s/>%s<br/>'%(self.rname,attrs2,entity.req._('no'))]return'\n'.join(wdgs)classFileWidget(Widget):need_multipart=Truedef_file_wdg(self,entity):wdgs=[u'<input type="file" name="%s" %s/>'%(self.rname,self.format_attrs())]req=entity.reqif(entity.e_schema.has_metadata(self.name,'format')orentity.e_schema.has_metadata(self.name,'encoding')):divid='%s-%s-advanced'%(self.name,entity.eid)wdgs.append(u'<a href="%s" title="%s"><img src="%s" alt="%s"/></a>'%(xml_escape(toggle_action(divid)),req._('show advanced fields'),xml_escape(req.build_url('data/puce_down.png')),req._('show advanced fields')))wdgs.append(u'<div id="%s" class="hidden">'%divid)forextraattrin('_format','_encoding'):ifentity.e_schema.has_subject_relation('%s%s'%(self.name,extraattr)):ewdg=entity.get_widget(self.name+extraattr)wdgs.append(ewdg.render_label(entity))wdgs.append(ewdg.edit_render(entity,includehelp=True))wdgs.append(u'<br/>')wdgs.append(u'</div>')ifentity.has_eid():ifnotself.required(entity):# trick to be able to delete an uploaded filewdgs.append(u'<br/>')wdgs.append(checkbox(eid_param('__%s_detach'%self.rname,entity.eid),False))wdgs.append(req._('detach attached file %s'%entity.dc_title()))else:wdgs.append(u'<br/>')wdgs.append(req._('currently attached file: %s'%entity.dc_title()))return'\n'.join(wdgs)def_edit_render(self,entity):returnself.hidden_input(entity,None)+self._file_wdg(entity)classTextFileWidget(FileWidget):def_edit_msg(self,entity):ifentity.has_eid()andnotself.required(entity):msg=entity.req._('You can either submit a new file using the browse button above'', or choose to remove already uploaded file by checking the ''"detach attached file" check-box, or edit file content online ''with the widget below.')else:msg=entity.req._('You can either submit a new file using the browse button above'', or edit file content online with the widget below.')returnmsgdef_edit_render(self,entity):wdgs=[self._file_wdg(entity)]ifentity.attr_metadata(self.name,'format')in('text/plain','text/html','text/rest'):msg=self._edit_msg(entity)wdgs.append(u'<p><b>%s</b></p>'%msg)twdg=TextWidget(self.vreg,self.subjtype,self.rschema,self.objtype)twdg.rname=self.rnamedata=getattr(entity,self.name)ifdata:encoding=entity.attr_metadata(self.name,'encoding')try:entity[self.name]=unicode(data.getvalue(),encoding)exceptUnicodeError:passelse:wdgs.append(twdg.edit_render(entity,with_format=False))entity[self.name]=data# restore Binary valuewdgs.append(u'<br/>')return'\n'.join(wdgs)classComboBoxWidget(Widget):html_attributes=Widget.html_attributes|set(('multiple','size'))def__init__(self,vreg,subjschema,rschema,objschema,multiple=False,**kwattrs):super(ComboBoxWidget,self).__init__(vreg,subjschema,rschema,objschema,**kwattrs)ifmultiple:self.attrs['multiple']='multiple'ifnot'size'inself.attrs:self.attrs['size']='5'# disable access key (dunno why but this is not allowed by xhtml 1.0)delself.attrs['accesskey']defvocabulary(self,entity):raiseNotImplementedError()defform_value(self,entity,value,values):ifvalueinvalues:flag='selected="selected"'else:flag=''returnvalue,flagdef_edit_render(self,entity):values=self.current_values(entity)ifvalues:res=[self.hidden_input(entity,v)forvinvalues]else:res=[self.hidden_input(entity,INTERNAL_FIELD_VALUE)]dvalues=self.current_display_values(entity)res.append(u'<select name="%s" %s>'%(self.rname,self.format_attrs()))forlabel,valueinself.vocabulary(entity):ifvalueisNone:# handle separatorres.append(u'<optgroup label="%s"/>'%(labelor''))else:value,flag=self.form_value(entity,value,dvalues)res.append(u'<option value="%s" %s>%s</option>'%(value,flag,xml_escape(label)))res.append(u'</select>')return'\n'.join(res)classStaticComboBoxWidget(ComboBoxWidget):def__init__(self,vreg,subjschema,rschema,objschema,vocabfunc,multiple=False,sort=False,**kwattrs):super(StaticComboBoxWidget,self).__init__(vreg,subjschema,rschema,objschema,multiple,**kwattrs)self.sort=sortself.vocabfunc=vocabfuncdefvocabulary(self,entity):choices=self.vocabfunc(entity=entity)ifself.sort:choices=sorted(choices)ifself.rschema.rproperty(self.subjtype,self.objtype,'internationalizable'):returnzip((entity.req._(v)forvinchoices),choices)returnzip(choices,choices)classEntityLinkComboBoxWidget(ComboBoxWidget):"""to be used be specific forms"""defcurrent_values(self,entity):ifentity.has_eid():return[r[0]forrinentity.related(self.name,self.role)]defaultmeth='default_%s_%s'%(self.role,self.name)ifhasattr(entity,defaultmeth):returngetattr(entity,defaultmeth)()return()defvocabulary(self,entity):return[('',INTERNAL_FIELD_VALUE)]+entity.vocabulary(self.rschema,self.role)classRawDynamicComboBoxWidget(EntityLinkComboBoxWidget):defvocabulary(self,entity,limit=None):req=entity.req# first see if its specified by __linkto form parameterslinkedto=entity.linked_to(self.name,self.role)iflinkedto:entities=(req.eid_rset(eid).get_entity(0,0)foreidinlinkedto)return[(entity.view('combobox'),entity.eid)forentityinentities]# it isn't, check if the entity provides a method to get correct valuesifnotself.required(entity):res=[('',INTERNAL_FIELD_VALUE)]else:res=[]# vocabulary doesn't include current values, add themifentity.has_eid():rset=entity.related(self.name,self.role)relatedvocab=[(e.view('combobox'),e.eid)foreinrset.entities()]else:relatedvocab=[]returnres+entity.vocabulary(self.rschema,self.role)+relatedvocabclassDynamicComboBoxWidget(RawDynamicComboBoxWidget):defvocabulary(self,entity,limit=None):returnsorted(super(DynamicComboBoxWidget,self).vocabulary(entity,limit))classAddComboBoxWidget(DynamicComboBoxWidget):def_edit_render(self,entity):req=entity.reqreq.add_js(('cubicweb.ajax.js','jquery.js','cubicweb.widgets.js'))values=self.current_values(entity)ifvalues:res=[self.hidden_input(entity,v)forvinvalues]else:res=[self.hidden_input(entity,INTERNAL_FIELD_VALUE)]dvalues=self.current_display_values(entity)etype_from=entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0]res.append(u'<select class="widget" cubicweb:etype_to="%s" cubicweb:etype_from="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="AddComboBox" name="%s" %s>'%(entity.e_schema,etype_from,self.rname,self.format_attrs()))forlabel,valueinself.vocabulary(entity):ifvalueisNone:# handle separatorres.append(u'<optgroup label="%s"/>'%(labelor''))else:value,flag=self.form_value(entity,value,dvalues)res.append(u'<option value="%s" %s>%s</option>'%(value,flag,xml_escape(label)))res.append(u'</select>')res.append(u'<div id="newvalue">')res.append(u'<input type="text" id="newopt" />')res.append(u'<a href="javascript:noop()" id="add_newopt"> </a></div>')return'\n'.join(res)classIntegerWidget(StringWidget):def__init__(self,vreg,subjschema,rschema,objschema,**kwattrs):kwattrs['size']=5kwattrs['maxlength']=15StringWidget.__init__(self,vreg,subjschema,rschema,objschema,**kwattrs)defrender_example(self,req):return'23'classFloatWidget(StringWidget):def__init__(self,vreg,subjschema,rschema,objschema,**kwattrs):kwattrs['size']=5kwattrs['maxlength']=15StringWidget.__init__(self,vreg,subjschema,rschema,objschema,**kwattrs)defrender_example(self,req):formatstr=req.property_value('ui.float-format')returnformatstr%1.23defcurrent_values(self,entity):values=entity.attribute_values(self.name)ifvalues:formatstr=entity.req.property_value('ui.float-format')value=values[0]ifvalueisnotNone:value=float(value)else:return()return[formatstr%value]return()classDecimalWidget(StringWidget):def__init__(self,vreg,subjschema,rschema,objschema,**kwattrs):kwattrs['size']=5kwattrs['maxlength']=15StringWidget.__init__(self,vreg,subjschema,rschema,objschema,**kwattrs)defrender_example(self,req):return'345.0300'classDateWidget(StringWidget):format_key='ui.date-format'monthnames=('january','february','march','april','may','june','july','august','september','october','november','december')daynames=('monday','tuesday','wednesday','thursday','friday','saturday','sunday')@classmethoddefadd_localized_infos(cls,req):"""inserts JS variables defining localized months and days"""# import here to avoid dependancy from cubicweb-common to simplejson_=req._monthnames=[_(mname)formnameincls.monthnames]daynames=[_(dname)fordnameincls.daynames]req.html_headers.define_var('MONTHNAMES',monthnames)req.html_headers.define_var('DAYNAMES',daynames)def__init__(self,vreg,subjschema,rschema,objschema,**kwattrs):kwattrs.setdefault('size',10)kwattrs.setdefault('maxlength',10)StringWidget.__init__(self,vreg,subjschema,rschema,objschema,**kwattrs)defcurrent_values(self,entity):values=entity.attribute_values(self.name)ifvaluesandhasattr(values[0],'strftime'):formatstr=entity.req.property_value(self.format_key)return[values[0].strftime(str(formatstr))]returnvaluesdefrender_example(self,req):formatstr=req.property_value(self.format_key)returndatetime.now().strftime(str(formatstr))def_edit_render(self,entity):wdg=super(DateWidget,self)._edit_render(entity)cal_button=self.render_calendar_popup(entity)returnwdg+cal_buttondefrender_help(self,entity):"""calendar popup widget"""req=entity.reqhelp=[u'<div class="helper">']descr=self.rschema.rproperty(self.subjtype,self.objtype,'description')ifdescr:help.append('<span>%s</span>'%req._(descr))example=self.render_example(req)ifexample:help.append('<span>(%s: %s)</span>'%(req._('sample format'),example))help.append(u'</div>')returnu' '.join(help)defrender_calendar_popup(self,entity):"""calendar popup widget"""req=entity.reqself.add_localized_infos(req)req.add_js(('cubicweb.ajax.js','cubicweb.calendar.js',))req.add_css(('cubicweb.calendar_popup.css',))inputid=self.attrs.get('id',self.rname)helperid="%shelper"%inputid_today=datetime.now()year=int(req.form.get('year',_today.year))month=int(req.form.get('month',_today.month))return(u"""<a onclick="toggleCalendar('%s', '%s', %s, %s);" class="calhelper"><img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""%(helperid,inputid,year,month,req.external_resource('CALENDAR_ICON'),req._('calendar'),helperid))classDateTimeWidget(DateWidget):format_key='ui.datetime-format'def__init__(self,vreg,subjschema,rschema,objschema,**kwattrs):kwattrs['size']=16kwattrs['maxlength']=16DateWidget.__init__(self,vreg,subjschema,rschema,objschema,**kwattrs)defrender_example(self,req):formatstr1=req.property_value('ui.datetime-format')formatstr2=req.property_value('ui.date-format')returnreq._('%(fmt1)s, or without time: %(fmt2)s')%{'fmt1':datetime.now().strftime(str(formatstr1)),'fmt2':datetime.now().strftime(str(formatstr2)),}classTimeWidget(StringWidget):format_key='ui.time-format'def__init__(self,vreg,subjschema,rschema,objschema,**kwattrs):kwattrs['size']=5kwattrs['maxlength']=5StringWidget.__init__(self,vreg,subjschema,rschema,objschema,**kwattrs)classEmailWidget(StringWidget):defrender(self,entity):email=getattr(entity,self.name)ifnotemail:returnu''returnu'<a href="mailto:%s">%s</a>'%(email,email)classURLWidget(StringWidget):defrender(self,entity):url=getattr(entity,self.name)ifnoturl:returnu''url=xml_escape(url)returnu'<a href="%s">%s</a>'%(url,url)classEmbededURLWidget(StringWidget):defrender(self,entity):url=getattr(entity,self.name)ifnoturl:returnu''aurl=xml_escape(entity.build_url('embed',url=url))returnu'<a href="%s">%s</a>'%(aurl,url)defwidget_factory(vreg,subjschema,rschema,objschema,role='subject',**kwargs):"""return the most adapated widget to edit the relation 'subjschema rschema objschema' according to information found in the schema """ifrole=='subject':eclass,subjschema=_eclass_eschema(subjschema)else:eclass,objschema=_eclass_eschema(objschema)ifeclassisnotNoneandrschemaingetattr(eclass,'widgets',()):wcls=WIDGETS[eclass.widgets[rschema]]elifnotrschema.is_final():card=rschema.rproperty(subjschema,objschema,'cardinality')ifrole=='object':multiple=card[1]in'+*'else:#if role == 'subject':multiple=card[0]in'+*'returnDynamicComboBoxWidget(vreg,subjschema,rschema,objschema,role=role,multiple=multiple)else:wcls=Nonefactory=FACTORIES.get(objschema,_default_widget_factory)returnfactory(vreg,subjschema,rschema,objschema,wcls=wcls,role=role,**kwargs)# factories to find the most adapated widget according to a type and other constraintsdef_string_widget_factory(vreg,subjschema,rschema,objschema,wcls=None,**kwargs):w=Noneforcinrschema.rproperty(subjschema,objschema,'constraints'):ifisinstance(c,StaticVocabularyConstraint):# may have been set by a previous SizeConstraint but doesn't make sense# here (even doesn't have the same meaning on a combobox actually)kwargs.pop('size',None)return(wclsorStaticComboBoxWidget)(vreg,subjschema,rschema,objschema,vocabfunc=c.vocabulary,**kwargs)ifisinstance(c,SizeConstraint)andc.maxisnotNone:# don't return here since a StaticVocabularyConstraint may# followifwclsisNone:ifc.max<257:_wcls=StringWidgetelse:_wcls=TextWidgetelse:_wcls=wcls_wcls.size_constraint_attrs(kwargs,c.max)w=_wcls(vreg,subjschema,rschema,objschema,**kwargs)ifwisNone:w=(wclsorTextWidget)(vreg,subjschema,rschema,objschema,**kwargs)returnwdef_default_widget_factory(vreg,subjschema,rschema,objschema,wcls=None,**kwargs):ifwclsisNone:wcls=_WFACTORIES[objschema]returnwcls(vreg,subjschema,rschema,objschema,**kwargs)FACTORIES={'String':_string_widget_factory,'Boolean':_default_widget_factory,'Bytes':_default_widget_factory,'Date':_default_widget_factory,'Datetime':_default_widget_factory,'Float':_default_widget_factory,'Decimal':_default_widget_factory,'Int':_default_widget_factory,'Password':_default_widget_factory,'Time':_default_widget_factory,}# default widget by entity's type_WFACTORIES={'Boolean':YesNoRadioWidget,'Bytes':FileWidget,'Date':DateWidget,'Datetime':DateTimeWidget,'Int':IntegerWidget,'Float':FloatWidget,'Decimal':DecimalWidget,'Password':PasswordWidget,'String':StringWidget,'Time':TimeWidget,}# widgets registryWIDGETS={}defregister(widget_list):forobjinwidget_list:ifisinstance(obj,type)andissubclass(obj,Widget):ifobjisWidgetorobjisComboBoxWidget:continueWIDGETS[obj.__name__]=objregister(globals().values())