# -*- coding: utf-8 -*-"""default templates for CubicWeb web client:organization: Logilab:copyright: 2001-2010 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"fromlogilab.mtconverterimportxml_escapefromlogilab.common.deprecationimportclass_renamedfromcubicweb.appobjectimportobjectify_selectorfromcubicweb.selectorsimportmatch_kwargsfromcubicweb.viewimportView,MainTemplate,NOINDEX,NOFOLLOWfromcubicweb.utilsimportUStringIO,can_do_pdf_conversionfromcubicweb.schemaimportdisplay_namefromcubicweb.webimportcomponent,formfieldsasff,formwidgetsasfwfromcubicweb.web.viewsimportforms# main templates ##############################################################classLogInOutTemplate(MainTemplate):defcall(self):self.set_request_content_type()w=self.wself.write_doctype()self.template_header('text/html',self._cw._('login_action'))w(u'<body>\n')self.content(w)w(u'</body>')deftemplate_header(self,content_type,view=None,page_title='',additional_headers=()):w=self.whead# explictly close the <base> tag to avoid IE 6 bugs while browsing DOMw(u'<base href="%s"></base>'%xml_escape(self._cw.base_url()))w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'%(content_type,self._cw.encoding))w(NOINDEX)w(NOFOLLOW)w(u'\n'.join(additional_headers)+u'\n')self.wview('htmlheader',rset=self.cw_rset)w(u'<title>%s</title>\n'%xml_escape(page_title))classLogInTemplate(LogInOutTemplate):__regid__='login'title='log in'defcontent(self,w):self.wview('logform',rset=self.cw_rset,id='loginBox',klass='')classLoggedOutTemplate(LogInOutTemplate):__regid__='loggedout'title='logged out'defcontent(self,w):# FIXME Deprecated code ?msg=self._cw._('you have been logged out')w(u'<h2>%s</h2>\n'%msg)ifself._cw.vreg.config['anonymous-user']:indexurl=self._cw.build_url('view',vid='index',__message=msg)w(u'<p><a href="%s">%s</a><p>'%(xml_escape(indexurl),self._cw._('go back to the index page')))@objectify_selectordeftemplatable_view(cls,req,rset,*args,**kwargs):view=kwargs.pop('view',None)ifviewisNone:return1ifview.binary:return0ifreq.form.has_key('__notemplate'):return0returnview.templatableclassNonTemplatableViewTemplate(MainTemplate):"""main template for any non templatable views (xml, binaries, etc.)"""__regid__='main-template'__select__=~templatable_view()defcall(self,view):view.set_request_content_type()view.set_stream()if(self._cw.form.has_key('__notemplate')andview.templatableandview.content_type==self._cw.html_content_type()):view.w(self._cw.document_surrounding_div())view.render()view.w(u'</div>')else:view.render()# have to replace our stream by view's stream (which may be a binary# stream)self._stream=view._streamclassTheMainTemplate(MainTemplate):"""default main template : - call header / footer templates """__regid__='main-template'__select__=templatable_view()defcall(self,view):self.set_request_content_type()self.template_header(self.content_type,view)w=self.ww(u'<div id="pageContent">\n')vtitle=self._cw.form.get('vtitle')ifvtitle:w(u'<h1 class="vtitle">%s</h1>\n'%xml_escape(vtitle))# display entity type restriction componentetypefilter=self._cw.vreg['components'].select_or_none('etypenavigation',self._cw,rset=self.cw_rset)ifetypefilterandetypefilter.cw_propval('visible'):etypefilter.render(w=w)self.nav_html=UStringIO()ifview:view.paginate(w=self.nav_html.write)w(_(self.nav_html.getvalue()))w(u'<div id="contentmain">\n')view.render(w=w)w(u'</div>\n')# close id=contentmainw(_(self.nav_html.getvalue()))w(u'</div>\n')# closes id=pageContentself.template_footer(view)deftemplate_header(self,content_type,view=None,page_title='',additional_headers=()):page_title=page_titleorview.page_title()additional_headers=additional_headersorview.html_headers()self.template_html_header(content_type,page_title,additional_headers)self.template_body_header(view)deftemplate_html_header(self,content_type,page_title,additional_headers=()):w=self.wheadlang=self._cw.langself.write_doctype()# explictly close the <base> tag to avoid IE 6 bugs while browsing DOMw(u'<base href="%s"></base>'%xml_escape(self._cw.base_url()))w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'%(content_type,self._cw.encoding))w(u'\n'.join(additional_headers)+u'\n')self.wview('htmlheader',rset=self.cw_rset)ifpage_title:w(u'<title>%s</title>\n'%xml_escape(page_title))deftemplate_body_header(self,view):w=self.ww(u'<body>\n')self.wview('header',rset=self.cw_rset,view=view)w(u'<div id="page"><table width="100%" border="0" id="mainLayout"><tr>\n')self.nav_column(view,'left')w(u'<td id="contentcol">\n')components=self._cw.vreg['components']rqlcomp=components.select_or_none('rqlinput',self._cw,rset=self.cw_rset)ifrqlcomp:rqlcomp.render(w=self.w,view=view)msgcomp=components.select_or_none('applmessages',self._cw,rset=self.cw_rset)ifmsgcomp:msgcomp.render(w=self.w)self.content_header(view)deftemplate_footer(self,view=None):self.content_footer(view)self.w(u'</td>\n')self.nav_column(view,'right')self.w(u'</tr></table></div>\n')self.wview('footer',rset=self.cw_rset)self.w(u'</body>')defnav_column(self,view,context):boxes=list(self._cw.vreg['boxes'].poss_visible_objects(self._cw,rset=self.cw_rset,view=view,context=context))ifboxes:self.w(u'<td class="navcol"><div class="navboxes">\n')forboxinboxes:box.render(w=self.w,view=view)self.w(u'</div></td>\n')defcontent_header(self,view=None):"""by default, display informal messages in content header"""self.wview('contentheader',rset=self.cw_rset,view=view)defcontent_footer(self,view=None):self.wview('contentfooter',rset=self.cw_rset,view=view)classErrorTemplate(TheMainTemplate):"""fallback template if an internal error occured during displaying the main template. This template may be called for authentication error, which means that req.cnx and req.user may not be set. """__regid__='error-template'defcall(self):"""display an unexpected error"""self.set_request_content_type()self._cw.reset_headers()view=self._cw.vreg['views'].select('error',self._cw,rset=self.cw_rset)self.template_header(self.content_type,view,self._cw._('an error occured'),[NOINDEX,NOFOLLOW])view.render(w=self.w)self.template_footer(view)deftemplate_header(self,content_type,view=None,page_title='',additional_headers=()):w=self.wheadlang=self._cw.langself.write_doctype()w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'%(content_type,self._cw.encoding))w(u'\n'.join(additional_headers))self.wview('htmlheader',rset=self.cw_rset)w(u'<title>%s</title>\n'%xml_escape(page_title))self.w(u'<body>\n')deftemplate_footer(self,view=None):self.w(u'</body>')classSimpleMainTemplate(TheMainTemplate):__regid__='main-no-top'deftemplate_header(self,content_type,view=None,page_title='',additional_headers=()):page_title=page_titleorview.page_title()additional_headers=additional_headersorview.html_headers()whead=self.wheadlang=self._cw.langself.write_doctype()whead(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'%(content_type,self._cw.encoding))whead(u'\n'.join(additional_headers)+u'\n')self.wview('htmlheader',rset=self.cw_rset)w=self.ww(u'<title>%s</title>\n'%xml_escape(page_title))w(u'<body>\n')w(u'<div id="page">')w(u'<table width="100%" height="100%" border="0"><tr>\n')w(u'<td class="navcol">\n')self.topleft_header()boxes=list(self._cw.vreg['boxes'].poss_visible_objects(self._cw,rset=self.cw_rset,view=view,context='left'))ifboxes:w(u'<div class="navboxes">\n')forboxinboxes:box.render(w=w)self.w(u'</div>\n')w(u'</td>')w(u'<td id="contentcol" rowspan="2">')w(u'<div id="pageContent">\n')vtitle=self._cw.form.get('vtitle')ifvtitle:w(u'<h1 class="vtitle">%s</h1>'%xml_escape(vtitle))deftopleft_header(self):logo=self._cw.vreg['components'].select_or_none('logo',self._cw,rset=self.cw_rset)iflogoandlogo.cw_propval('visible'):self.w(u'<table id="header"><tr>\n')self.w(u'<td>')logo.render(w=self.w)self.w(u'</td>\n')self.w(u'</tr></table>\n')ifcan_do_pdf_conversion():try:fromxml.etree.cElementTreeimportElementTreeexceptImportError:#python2.4fromelementtreeimportElementTreefromsubprocessimportPopenassubfromStringIOimportStringIOfromtempfileimportNamedTemporaryFilefromcubicweb.ext.xhtml2foimportReportTransformerclassPdfViewComponent(component.EntityVComponent):__regid__='pdfview'context='ctxtoolbar'defcell_call(self,row,col,view):entity=self.cw_rset.get_entity(row,col)url=entity.absolute_url(vid=view.__regid__,__template='pdf-main-template')iconurl=self._cw.build_url('data/pdf_icon.gif')label=self._cw._('Download page as pdf')self.w(u'<a href="%s" title="%s" class="toolbarButton"><img src="%s" alt="%s"/></a>'%(xml_escape(url),label,xml_escape(iconurl),label))classPdfMainTemplate(TheMainTemplate):__regid__='pdf-main-template'defcall(self,view):"""build the standard view, then when it's all done, convert xhtml to pdf """super(PdfMainTemplate,self).call(view)section=self._cw.form.pop('section','contentmain')pdf=self.to_pdf(self._stream,section)self._cw.set_content_type('application/pdf',filename='report.pdf')self.binary=Trueself.w=Noneself.set_stream()# pylint needs helpself.w(pdf)defto_pdf(self,stream,section):# XXX see ticket/345282stream=stream.getvalue().replace(' ',' ').encode('utf-8')xmltree=ElementTree()xmltree.parse(StringIO(stream))foptree=ReportTransformer(section).transform(xmltree)foptmp=NamedTemporaryFile()pdftmp=NamedTemporaryFile()foptree.write(foptmp)foptmp.flush()fopproc=sub(['/usr/bin/fop',foptmp.name,pdftmp.name])fopproc.wait()pdftmp.seek(0)pdf=pdftmp.read()returnpdf# page parts templates ########################################################classHTMLHeader(View):"""default html headers"""__regid__='htmlheader'defcall(self,**kwargs):self.favicon()self.stylesheets()self.javascripts()self.alternates()deffavicon(self):favicon=self._cw.external_resource('FAVICON',None)iffavicon:self.whead(u'<link rel="shortcut icon" href="%s"/>\n'%favicon)defstylesheets(self):req=self._cwadd_css=req.add_cssforcssinreq.external_resource('STYLESHEETS'):add_css(css,localfile=False)forcssinreq.external_resource('STYLESHEETS_PRINT'):add_css(css,u'print',localfile=False)forcssinreq.external_resource('IE_STYLESHEETS'):add_css(css,localfile=False,ieonly=True)defjavascripts(self):forjscriptinself._cw.external_resource('JAVASCRIPTS'):self._cw.add_js(jscript,localfile=False)defalternates(self):urlgetter=self._cw.vreg['components'].select_or_none('rss_feed_url',self._cw,rset=self.cw_rset)ifurlgetterisnotNone:self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n'%xml_escape(urlgetter.feed_url()))classHTMLPageHeader(View):"""default html page header"""__regid__='header'main_cell_components=('appliname','breadcrumbs')defcall(self,view,**kwargs):self.main_header(view)self.w(u''' <div id="stateheader">''')self.state_header()self.w(u''' </div> ''')defmain_header(self,view):"""build the top menu with authentification info and the rql box"""self.w(u'<table id="header"><tr>\n')self.w(u'<td id="firstcolumn">')logo=self._cw.vreg['components'].select_or_none('logo',self._cw,rset=self.cw_rset)iflogoandlogo.cw_propval('visible'):logo.render(w=self.w)self.w(u'</td>\n')# appliname and breadcrumbsself.w(u'<td id="headtext">')forcidinself.main_cell_components:comp=self._cw.vreg['components'].select_or_none(cid,self._cw,rset=self.cw_rset)ifcompandcomp.cw_propval('visible'):comp.render(w=self.w)self.w(u'</td>')# logged user and helpself.w(u'<td>\n')comp=self._cw.vreg['components'].select_or_none('loggeduserlink',self._cw,rset=self.cw_rset)ifcompandcomp.cw_propval('visible'):comp.render(w=self.w)self.w(u'</td>')# lastcolumnself.w(u'<td id="lastcolumn">')self.w(u'</td>\n')self.w(u'</tr></table>\n')ifself._cw.cnx.anonymous_connection:self.wview('logform',rset=self.cw_rset,id='popupLoginBox',klass='hidden',title=False,showmessage=False)defstate_header(self):state=self._cw.search_stateifstate[0]=='normal':return_=self._cw._value=self._cw.view('oneline',self._cw.eid_rset(state[1][1]))msg=' '.join((_("searching for"),display_name(self._cw,state[1][3]),_("to associate with"),value,_("by relation"),'"',display_name(self._cw,state[1][2],state[1][0]),'"'))returnself.w(u'<div class="stateMessage">%s</div>'%msg)classHTMLPageFooter(View):"""default html page footer: include footer actions """__regid__='footer'defcall(self,**kwargs):req=self._cwself.w(u'<div class="footer">')actions=self._cw.vreg['actions'].possible_actions(self._cw,rset=self.cw_rset)footeractions=actions.get('footer',())fori,actioninenumerate(footeractions):self.w(u'<a href="%s">%s</a>'%(action.url(),self._cw._(action.title)))ifi<(len(footeractions)-1):self.w(u' | ')self.w(u'</div>')classHTMLContentHeader(View):"""default html page content header: * include message component if selectable for this request * include selectable content navigation components """__regid__='contentheader'defcall(self,view,**kwargs):"""by default, display informal messages in content header"""components=self._cw.vreg['contentnavigation'].poss_visible_objects(self._cw,rset=self.cw_rset,view=view,context='navtop')ifcomponents:self.w(u'<div id="contentheader">')forcompincomponents:comp.render(w=self.w,view=view)self.w(u'</div><div class="clear"></div>')classHTMLContentFooter(View):"""default html page content footer: include selectable content navigation components """__regid__='contentfooter'defcall(self,view,**kwargs):components=self._cw.vreg['contentnavigation'].poss_visible_objects(self._cw,rset=self.cw_rset,view=view,context='navbottom')ifcomponents:self.w(u'<div id="contentfooter">')forcompincomponents:comp.render(w=self.w,view=view)self.w(u'</div>')classLogForm(forms.FieldsForm):__regid__='logform'domid='loginForm'needs_css=('cubicweb.login.css',)# XXX have to recall fields name since python is mangling __login/__password__login=ff.StringField('__login',widget=fw.TextInput({'class':'data'}))__password=ff.StringField('__password',label=_('password'),widget=fw.PasswordSingleInput({'class':'data'}))form_buttons=[fw.SubmitButton(label=_('log in'),attrs={'class':'loginButton'})]@propertydefaction(self):returnxml_escape(login_form_url(self._cw))classLogFormView(View):__regid__='logform'__select__=match_kwargs('id','klass')title='log in'defcall(self,id,klass,title=True,showmessage=True):self.w(u'<div id="%s" class="%s">'%(id,klass))iftitle:stitle=self._cw.property_value('ui.site-title')ifstitle:stitle=xml_escape(stitle)else:stitle=u' 'self.w(u'<div id="loginTitle">%s</div>'%stitle)self.w(u'<div id="loginContent">\n')ifshowmessageandself._cw.message:self.w(u'<div class="simpleMessage">%s</div>\n'%self._cw.message)ifself._cw.vreg.config['auth-mode']!='http':# Cookie authenticationself.login_form(id)self.w(u'</div></div>\n')deflogin_form(self,id):cw=self._cwform=cw.vreg['forms'].select('logform',cw)ifcw.vreg.config['allow-email-login']:label=cw._('login or email')else:label=cw._('login')form.field_by_name('__login').label=labelself.w(form.render(table_class='',display_progress_div=False))cw.html_headers.add_onload('jQuery("#__login:visible").focus()')LogFormTemplate=class_renamed('LogFormTemplate',LogFormView)deflogin_form_url(req):ifreq.https:returnreq.url()httpsurl=req.vreg.config.get('https-url')ifhttpsurl:returnreq.url().replace(req.base_url(),httpsurl)returnreq.url()