fix #343630 by getting explicitly TextInput widget for String, also instantiate field to get proper widget configuration
"""html calendar views: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"_=unicodefromdatetimeimportdatetime,date,timedeltafromlogilab.mtconverterimporthtml_escapefromcubicweb.interfacesimportICalendarablefromcubicweb.selectorsimportimplementsfromcubicweb.utilsimportstrptime,date_range,todate,todatetimefromcubicweb.viewimportEntityView# useful constants & functions ################################################ONEDAY=timedelta(1)WEEKDAYS=(_("monday"),_("tuesday"),_("wednesday"),_("thursday"),_("friday"),_("saturday"),_("sunday"))MONTHNAMES=(_('january'),_('february'),_('march'),_('april'),_('may'),_('june'),_('july'),_('august'),_('september'),_('october'),_('november'),_('december'))# Calendar views ##############################################################try:fromvobjectimportiCalendarclassiCalView(EntityView):"""A calendar view that generates a iCalendar file (RFC 2445) Does apply to ICalendarable compatible entities """__select__=implements(ICalendarable)need_navigation=Falsecontent_type='text/calendar'title=_('iCalendar')templatable=Falseid='ical'defcall(self):ical=iCalendar()foriinrange(len(self.rset.rows)):task=self.complete_entity(i)event=ical.add('vevent')event.add('summary').value=task.dc_title()event.add('description').value=task.dc_description()iftask.start:event.add('dtstart').value=task.startiftask.stop:event.add('dtend').value=task.stopbuff=ical.serialize()ifnotisinstance(buff,unicode):buff=unicode(buff,self.req.encoding)self.w(buff)exceptImportError:passclasshCalView(EntityView):"""A calendar view that generates a hCalendar file Does apply to ICalendarable compatible entities """id='hcal'__select__=implements(ICalendarable)need_navigation=Falsetitle=_('hCalendar')#templatable = Falsedefcall(self):self.w(u'<div class="hcalendar">')foriinrange(len(self.rset.rows)):task=self.complete_entity(i)self.w(u'<div class="vevent">')self.w(u'<h3 class="summary">%s</h3>'%html_escape(task.dc_title()))self.w(u'<div class="description">%s</div>'%task.dc_description(format='text/html'))iftask.start:self.w(u'<abbr class="dtstart" title="%s">%s</abbr>'%(task.start.isoformat(),self.format_date(task.start)))iftask.stop:self.w(u'<abbr class="dtstop" title="%s">%s</abbr>'%(task.stop.isoformat(),self.format_date(task.stop)))self.w(u'</div>')self.w(u'</div>')classCalendarItemView(EntityView):id='calendaritem'defcell_call(self,row,col,dates=False):task=self.complete_entity(row)task.view('oneline',w=self.w)ifdates:iftask.startandtask.stop:self.w('<br/>'%self.req._('from %(date)s'%{'date':self.format_date(task.start)}))self.w('<br/>'%self.req._('to %(date)s'%{'date':self.format_date(task.stop)}))self.w('<br/>to %s'%self.format_date(task.stop))classCalendarLargeItemView(CalendarItemView):id='calendarlargeitem'class_TaskEntry(object):def__init__(self,task,color,index=0):self.task=taskself.color=colorself.index=indexself.length=1defin_working_hours(self):"""predicate returning True is the task is in working hours"""iftodatetime(self.task.start).hour>7andtodatetime(self.task.stop).hour<20:returnTruereturnFalsedefis_one_day_task(self):task=self.taskreturntask.startandtask.stopandtask.start.isocalendar()==task.stop.isocalendar()classOneMonthCal(EntityView):"""At some point, this view will probably replace ampm calendars"""id='onemonthcal'__select__=implements(ICalendarable)need_navigation=Falsetitle=_('one month')defcall(self):self.req.add_js('cubicweb.ajax.js')self.req.add_css('cubicweb.calendar.css')# XXX: restrict courses directy with RQL_today=datetime.today()if'year'inself.req.form:year=int(self.req.form['year'])else:year=_today.yearif'month'inself.req.form:month=int(self.req.form['month'])else:month=_today.monthfirst_day_of_month=date(year,month,1)firstday=first_day_of_month-timedelta(first_day_of_month.weekday())ifmonth>=12:last_day_of_month=date(year+1,1,1)-timedelta(1)else:last_day_of_month=date(year,month+1,1)-timedelta(1)lastday=last_day_of_month+timedelta(6-last_day_of_month.weekday())month_dates=list(date_range(firstday,lastday))dates={}task_max=0forrowinxrange(self.rset.rowcount):task=self.rset.get_entity(row,0)iflen(self.rset[row])>1andself.rset.description[row][1]=='CWUser':user=self.rset.get_entity(row,1)else:user=Nonethe_dates=[]tstart=task.startiftstart:tstart=todate(task.start)iftstart>lastday:continuethe_dates=[tstart]tstop=task.stopiftstop:tstop=todate(tstop)iftstop<firstday:continuethe_dates=[tstop]iftstartandtstop:iftstart.isocalendar()==tstop.isocalendar():iffirstday<=tstart<=lastday:the_dates=[tstart]else:the_dates=date_range(max(tstart,firstday),min(tstop,lastday))ifnotthe_dates:continuefordinthe_dates:d_tasks=dates.setdefault((d.year,d.month,d.day),{})t_users=d_tasks.setdefault(task,set())t_users.add(user)iflen(d_tasks)>task_max:task_max=len(d_tasks)days=[]nrows=max(3,task_max)# colors here are class names defined in cubicweb.csscolors=["col%x"%iforiinrange(12)]next_color_index=0visited_tasks={}# holds a description of a tasktask_colors={}# remember a color assigned to a taskformdateinmonth_dates:d_tasks=dates.get((mdate.year,mdate.month,mdate.day),{})rows=[None]*nrows# every task that is "visited" for the first time# require a special treatment, so we put them in# 'postpone'postpone=[]fortaskind_tasks:iftaskinvisited_tasks:task_descr=visited_tasks[task]rows[task_descr.index]=task_descrelse:postpone.append(task)fortaskinpostpone:# to every 'new' task we must affect a color# (which must be the same for every user concerned# by the task)fori,tinenumerate(rows):iftisNone:iftaskintask_colors:color=task_colors[task]else:color=colors[next_color_index]next_color_index=(next_color_index+1)%len(colors)task_colors[task]=colortask_descr=_TaskEntry(task,color,i)rows[i]=task_descrvisited_tasks[task]=task_descrbreakelse:raiseRuntimeError("is it possible we got it wrong?")days.append(rows)curdate=first_day_of_monthself.w(u'<div id="onemonthcalid">')# build scheduleself.w(u'<table class="omcalendar">')prevlink,nextlink=self._prevnext_links(curdate)# XXXself.w(u'<tr><th><a href="%s"><<</a></th><th colspan="5">%s%s</th>'u'<th><a href="%s">>></a></th></tr>'%(html_escape(prevlink),self.req._(curdate.strftime('%B').lower()),curdate.year,html_escape(nextlink)))# output headerself.w(u'<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>'%tuple(self.req._(day)fordayinWEEKDAYS))# build calendarformdate,task_rowsinzip(month_dates,days):ifmdate.weekday()==0:self.w(u'<tr>')self._build_calendar_cell(mdate,task_rows,curdate)ifmdate.weekday()==6:self.w(u'</tr>')self.w(u'</table></div>')def_prevnext_links(self,curdate):prevdate=curdate-timedelta(31)nextdate=curdate+timedelta(31)rql=self.rset.printable_rql()prevlink=self.req.build_ajax_replace_url('onemonthcalid',rql,'onemonthcal',year=prevdate.year,month=prevdate.month)nextlink=self.req.build_ajax_replace_url('onemonthcalid',rql,'onemonthcal',year=nextdate.year,month=nextdate.month)returnprevlink,nextlinkdef_build_calendar_cell(self,celldate,rows,curdate):curmonth=curdate.monthclasses=""ifcelldate.month!=curmonth:classes+=" outOfRange"ifcelldate==date.today():classes+=" today"self.w(u'<td class="cell%s">'%classes)self.w(u'<div class="calCellTitle%s">'%classes)self.w(u'<div class="day">%s</div>'%celldate.day)iflen(self.rset.column_types(0))==1:etype=list(self.rset.column_types(0))[0]url=self.build_url(vid='creation',etype=etype,schedule=True,start=self.format_date(celldate),stop=self.format_date(celldate),__redirectrql=self.rset.printable_rql(),__redirectparams=self.req.build_url_params(year=curdate.year,month=curmonth),__redirectvid=self.id)self.w(u'<div class="cmd"><a href="%s">%s</a></div>'%(html_escape(url),self.req._(u'add')))self.w(u' ')self.w(u'</div>')self.w(u'<div class="cellContent">')fortask_descrinrows:iftask_descr:task=task_descr.taskself.w(u'<div class="task %s">'%task_descr.color)task.view('calendaritem',w=self.w)url=task.absolute_url(vid='edition',__redirectrql=self.rset.printable_rql(),__redirectparams=self.req.build_url_params(year=curdate.year,month=curmonth),__redirectvid=self.id)self.w(u'<div class="tooltip" ondblclick="stopPropagation(event); window.location.assign(\'%s\'); return false;">'%html_escape(url))task.view('tooltip',w=self.w)self.w(u'</div>')else:self.w(u'<div class="task">')self.w(u" ")self.w(u'</div>')self.w(u'</div>')self.w(u'</td>')classOneWeekCal(EntityView):"""At some point, this view will probably replace ampm calendars"""id='oneweekcal'__select__=implements(ICalendarable)need_navigation=Falsetitle=_('one week')defcall(self):self.req.add_js(('cubicweb.ajax.js','cubicweb.calendar.js'))self.req.add_css('cubicweb.calendar.css')# XXX: restrict directly with RQL_today=datetime.today()if'year'inself.req.form:year=int(self.req.form['year'])else:year=_today.yearif'week'inself.req.form:week=int(self.req.form['week'])else:week=_today.isocalendar()[1]# week - 1 since we get week number > 0 while we want it to start from 0first_day_of_week=todate(strptime('%s-%s-1'%(year,week-1),'%Y-%U-%w'))lastday=first_day_of_week+timedelta(6)firstday=first_day_of_weekdates=[[]foriinrange(7)]task_colors={}# remember a color assigned to a task# colors here are class names defined in cubicweb.csscolors=["col%x"%iforiinrange(12)]next_color_index=0done_tasks=[]forrowinxrange(self.rset.rowcount):task=self.rset.get_entity(row,0)iftaskindone_tasks:continuedone_tasks.append(task)the_dates=[]tstart=task.starttstop=task.stopiftstart:tstart=todate(tstart)iftstart>lastday:continuethe_dates=[tstart]iftstop:tstop=todate(tstop)iftstop<firstday:continuethe_dates=[tstop]iftstartandtstop:the_dates=date_range(max(tstart,firstday),min(tstop,lastday))ifnotthe_dates:continueiftasknotintask_colors:task_colors[task]=colors[next_color_index]next_color_index=(next_color_index+1)%len(colors)fordinthe_dates:day=d.weekday()task_descr=_TaskEntry(task,task_colors[task])dates[day].append(task_descr)self.w(u'<div id="oneweekcalid">')# build scheduleself.w(u'<table class="omcalendar" id="week">')prevlink,nextlink=self._prevnext_links(first_day_of_week)# XXXself.w(u'<tr><th class="transparent"></th>')self.w(u'<th><a href="%s"><<</a></th><th colspan="5">%s%s%s</th>'u'<th><a href="%s">>></a></th></tr>'%(html_escape(prevlink),first_day_of_week.year,self.req._(u'week'),first_day_of_week.isocalendar()[1],html_escape(nextlink)))# output headerself.w(u'<tr>')self.w(u'<th class="transparent"></th>')# column for hours_today=date.today()fori,dayinenumerate(WEEKDAYS):wdate=first_day_of_week+timedelta(i)ifwdate.isocalendar()==_today.isocalendar():self.w(u'<th class="today">%s<br/>%s</th>'%(self.req._(day),self.format_date(wdate)))else:self.w(u'<th>%s<br/>%s</th>'%(self.req._(day),self.format_date(wdate)))self.w(u'</tr>')# build week calendarself.w(u'<tr>')self.w(u'<td style="width:5em;">')# column for hoursextra=""forhinrange(8,20):self.w(u'<div class="hour" %s>'%extra)self.w(u'%02d:00'%h)self.w(u'</div>')self.w(u'</td>')fori,dayinenumerate(WEEKDAYS):wdate=first_day_of_week+timedelta(i)classes=""ifwdate.isocalendar()==_today.isocalendar():classes=" today"self.w(u'<td class="column %s" id="%s">'%(classes,day))iflen(self.rset.column_types(0))==1:etype=list(self.rset.column_types(0))[0]url=self.build_url(vid='creation',etype=etype,schedule=True,__redirectrql=self.rset.printable_rql(),__redirectparams=self.req.build_url_params(year=year,week=week),__redirectvid=self.id)extra=' ondblclick="addCalendarItem(event, hmin=8, hmax=20, year=%s, month=%s, day=%s, duration=2, baseurl=\'%s\')"'%(wdate.year,wdate.month,wdate.day,html_escape(url))else:extra=""self.w(u'<div class="columndiv"%s>'%extra)forhinrange(8,20):self.w(u'<div class="hourline" style="top:%sex;">'%((h-7)*8))self.w(u'</div>')ifdates[i]:self._build_calendar_cell(wdate,dates[i])self.w(u'</div>')self.w(u'</td>')self.w(u'</tr>')self.w(u'</table></div>')self.w(u'<div id="coord"></div>')self.w(u'<div id="debug"> </div>')def_build_calendar_cell(self,date,task_descrs):inday_tasks=[tfortintask_descrsift.is_one_day_task()andt.in_working_hours()]wholeday_tasks=[tfortintask_descrsifnott.is_one_day_task()]inday_tasks.sort(key=lambdat:t.task.start)sorted_tasks=[]fori,tinenumerate(wholeday_tasks):t.index=incols=len(wholeday_tasks)whileinday_tasks:t=inday_tasks.pop(0)fori,cinenumerate(sorted_tasks):ifnotcorc[-1].task.stop<=t.task.start:c.append(t)t.index=i+ncolsbreakelse:t.index=len(sorted_tasks)+ncolssorted_tasks.append([t])ncols+=len(sorted_tasks)ifncols==0:returninday_tasks=[]fortasklistinsorted_tasks:inday_tasks+=tasklistwidth=100.0/ncolsfortask_descinwholeday_tasks+inday_tasks:task=task_desc.taskstart_hour=8start_min=0stop_hour=20stop_min=0iftask.start:ifdate<todate(task.start)<date+ONEDAY:start_hour=max(8,task.start.hour)start_min=task.start.minuteiftask.stop:ifdate<todate(task.stop)<date+ONEDAY:stop_hour=min(20,task.stop.hour)ifstop_hour<20:stop_min=task.stop.minuteheight=100.0*(stop_hour+stop_min/60.0-start_hour-start_min/60.0)/(20-8)top=100.0*(start_hour+start_min/60.0-8)/(20-8)left=width*task_desc.indexstyle="height: %s%%; width: %s%%; top: %s%%; left: %s%%; "% \(height,width,top,left)self.w(u'<div class="task %s" style="%s">'% \(task_desc.color,style))task.view('calendaritem',dates=False,w=self.w)url=task.absolute_url(vid='edition',__redirectrql=self.rset.printable_rql(),__redirectparams=self.req.build_url_params(year=date.year,week=date.isocalendar()[1]),__redirectvid=self.id)self.w(u'<div class="tooltip" ondblclick="stopPropagation(event); window.location.assign(\'%s\'); return false;">'%html_escape(url))task.view('tooltip',w=self.w)self.w(u'</div>')iftask.startisNone:self.w(u'<div class="bottommarker">')self.w(u'<div class="bottommarkerline" style="margin: 0px 3px 0px 3px; height: 1px;">')self.w(u'</div>')self.w(u'<div class="bottommarkerline" style="margin: 0px 2px 0px 2px; height: 1px;">')self.w(u'</div>')self.w(u'<div class="bottommarkerline" style="margin: 0px 1px 0px 1px; height: 3ex; color: white; font-size: x-small; vertical-align: center; text-align: center;">')self.w(u'end')self.w(u'</div>')self.w(u'</div>')self.w(u'</div>')def_prevnext_links(self,curdate):prevdate=curdate-timedelta(7)nextdate=curdate+timedelta(7)rql=self.rset.printable_rql()prevlink=self.req.build_ajax_replace_url('oneweekcalid',rql,'oneweekcal',year=prevdate.year,week=prevdate.isocalendar()[1])nextlink=self.req.build_ajax_replace_url('oneweekcalid',rql,'oneweekcal',year=nextdate.year,week=nextdate.isocalendar()[1])returnprevlink,nextlink