common/uilib.py
changeset 350 f34ef2c64605
parent 277 a11a3c231050
child 362 a6a319f000c3
equal deleted inserted replaced
348:ebe40a8c7cc9 350:f34ef2c64605
    13 import decimal
    13 import decimal
    14 import locale
    14 import locale
    15 import re
    15 import re
    16 from urllib import quote as urlquote
    16 from urllib import quote as urlquote
    17 from cStringIO import StringIO
    17 from cStringIO import StringIO
    18 from xml.parsers.expat import ExpatError
    18 from xml.sax.saxutils import unescape
    19 from copy import deepcopy
    19 from copy import deepcopy
    20 
    20 
    21 import simplejson
    21 import simplejson
    22 
    22 
    23 from mx.DateTime import DateTimeType, DateTimeDeltaType
    23 from mx.DateTime import DateTimeType, DateTimeDeltaType
    24 
    24 
    25 from logilab.common.textutils import unormalize
    25 from logilab.common.textutils import unormalize
       
    26 from logilab.mtconverter import html_escape
    26 
    27 
    27 def ustrftime(date, fmt='%Y-%m-%d'):
    28 def ustrftime(date, fmt='%Y-%m-%d'):
    28     """like strftime, but returns a unicode string instead of an encoded
    29     """like strftime, but returns a unicode string instead of an encoded
    29     string which may be problematic with localized date.
    30     string which may be problematic with localized date.
    30     
    31     
   114 def safe_cut(text, length):
   115 def safe_cut(text, length):
   115     """returns a string of length <length> based on <text>, removing any html
   116     """returns a string of length <length> based on <text>, removing any html
   116     tags from given text if cut is necessary."""
   117     tags from given text if cut is necessary."""
   117     if text is None:
   118     if text is None:
   118         return u''
   119         return u''
   119     text_nohtml = remove_html_tags(text)
   120     noenttext = unescape(text)
       
   121     text_nohtml = remove_html_tags(noenttext)
   120     # try to keep html tags if text is short enough
   122     # try to keep html tags if text is short enough
   121     if len(text_nohtml) <= length:
   123     if len(text_nohtml) <= length:
   122         return text
   124         return text
   123     # else if un-tagged text is too long, cut it
   125     # else if un-tagged text is too long, cut it
   124     return text_nohtml[:length-3] + u'...'
   126     return html_escape(text_nohtml[:length] + u'...')
       
   127 
       
   128 fallback_safe_cut = safe_cut
   125 
   129 
   126 
   130 
   127 try:
   131 try:
   128     from lxml import etree
   132     from lxml import etree
   129 except (ImportError, AttributeError):
   133 except (ImportError, AttributeError):
   150             """returns an html document of length <length> based on <text>,
   154             """returns an html document of length <length> based on <text>,
   151             and cut is necessary.
   155             and cut is necessary.
   152             """
   156             """
   153             if text is None:
   157             if text is None:
   154                 return u''
   158                 return u''
   155             textParse = etree.HTML(text)
   159             dom = etree.HTML(text)
   156             compteur = 0
   160             curlength = 0
   157 
   161             add_ellipsis = False
   158             for element in textParse.iter():
   162             for element in dom.iter():
   159                 if compteur > length:
   163                 if curlength >= length:
   160                     parent = element.getparent()
   164                     parent = element.getparent()
   161                     parent.remove(element)
   165                     parent.remove(element)
       
   166                     if curlength == length and (element.text or element.tail):
       
   167                         add_ellipsis = True
   162                 else:
   168                 else:
   163                     if element.text is not None:
   169                     if element.text is not None:
   164                         text_resum = text_cut_letters(element.text,length)
   170                         element.text = cut(element.text, length - curlength)
   165                         len_text_resum = len(''.join(text_resum.split()))
   171                         curlength += len(element.text)
   166                         compteur = compteur + len_text_resum
       
   167                         element.text = text_resum
       
   168 
       
   169                     if element.tail is not None:
   172                     if element.tail is not None:
   170                         if compteur < length:
   173                         if curlength < length:
   171                             text_resum = text_cut_letters(element.tail,length)
   174                             element.tail = cut(element.tail, length - curlength)
   172                             len_text_resum = len(''.join(text_resum.split()))
   175                             curlength += len(element.tail)
   173                             compteur = compteur + len_text_resum
   176                         elif curlength == length:
   174                             element.tail = text_resum
   177                             element.tail = '...'
   175                         else:
   178                         else:
   176                             element.tail = ''
   179                             element.tail = ''
   177 
   180             text = etree.tounicode(dom[0])[6:-7] # remove wrapping <body></body>
   178             div = etree.HTML('<div></div>')[0][0]
   181             if add_ellipsis:
   179             listNode = textParse[0].getchildren()
   182                 return text + u'...'
   180             for node in listNode:
   183             return text
   181                 div.append(deepcopy(node))
   184         
   182             return etree.tounicode(div)
   185 def text_cut(text, nbwords=30):
       
   186     """from the given plain text, return a text with at least <nbwords> words,
       
   187     trying to go to the end of the current sentence.
       
   188 
       
   189     Note that spaces are normalized.
       
   190     """
       
   191     if text is None:
       
   192         return u''
       
   193     words = text.split()
       
   194     text = ' '.join(words) # normalize spaces
       
   195     minlength = len(' '.join(words[:nbwords]))
       
   196     textlength = text.find('.', minlength) + 1
       
   197     if textlength == 0: # no point found
       
   198         textlength = minlength 
       
   199     return text[:textlength]
       
   200 
       
   201 def cut(text, length):
       
   202     """returns a string of a maximum length <length> based on <text>
       
   203     (approximatively, since if text has been  cut, '...' is added to the end of the string,
       
   204     resulting in a string of len <length> + 3)
       
   205     """
       
   206     if text is None:
       
   207         return u''
       
   208     if len(text) <= length:
       
   209         return text
       
   210     # else if un-tagged text is too long, cut it
       
   211     return text[:length] + u'...'
       
   212 
   183 
   213 
   184     
   214     
   185 # HTML generation helper functions ############################################
   215 # HTML generation helper functions ############################################
   186 
       
   187 from logilab.mtconverter import html_escape
       
   188 
   216 
   189 def tooltipize(text, tooltip, url=None):
   217 def tooltipize(text, tooltip, url=None):
   190     """make an HTML tooltip"""
   218     """make an HTML tooltip"""
   191     url = url or '#'
   219     url = url or '#'
   192     return u'<a href="%s" title="%s">%s</a>' % (url, tooltip, text)
   220     return u'<a href="%s" title="%s">%s</a>' % (url, tooltip, text)
   218     if extraparams:
   246     if extraparams:
   219         params.append(simplejson.dumps(extraparams))
   247         params.append(simplejson.dumps(extraparams))
   220     if swap:
   248     if swap:
   221         params.append('true')
   249         params.append('true')
   222     return "javascript: replacePageChunk(%s);" % ', '.join(params)
   250     return "javascript: replacePageChunk(%s);" % ', '.join(params)
   223 
       
   224 def text_cut(text, nbwords=30):
       
   225     if text is None:
       
   226         return u''
       
   227     minlength = len(' '.join(text.split()[:nbwords]))
       
   228     textlength = text.find('.', minlength) + 1
       
   229     if textlength == 0: # no point found
       
   230         textlength = minlength 
       
   231     return text[:textlength]
       
   232 
       
   233 def text_cut_letters(text, nbletters):
       
   234     if text is None:
       
   235         return u''
       
   236     if len(''.join(text.split())) <= nbletters:
       
   237            return text
       
   238     else:
       
   239         text_nospace = ''.join(text.split())
       
   240         textlength=text.find('.') + 1
       
   241 
       
   242         if textlength==0:
       
   243            textlength=text.find(' ', nbletters+5)
       
   244            
       
   245         return text[:textlength] 
       
   246 
       
   247 def cut(text, length):
       
   248     """returns a string of length <length> based on <text>
       
   249     post:
       
   250       len(__return__) <= length
       
   251     """
       
   252     if text is None:
       
   253         return u''
       
   254     if len(text) <= length:
       
   255         return text
       
   256     # else if un-tagged text is too long, cut it
       
   257     return text[:length-3] + u'...'
       
   258 
   251 
   259 
   252 
   260 from StringIO import StringIO
   253 from StringIO import StringIO
   261 
   254 
   262 def ureport_as_html(layout):
   255 def ureport_as_html(layout):