utils.py
branchtls-sprint
changeset 708 b4728112625f
parent 643 616191014b8b
child 709 b21ee900c732
equal deleted inserted replaced
707:21a59b468f1a 708:b4728112625f
       
     1 """Some utilities for CubicWeb server/clients.
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from md5 import md5
       
    10 from time import time
       
    11 from random import randint, seed
       
    12 
       
    13 # initialize random seed from current time
       
    14 seed()
       
    15 
       
    16 def make_uid(key):
       
    17     """forge a unique identifier"""
       
    18     msg = str(key) + "%.10f"%time() + str(randint(0, 1000000))
       
    19     return md5(msg).hexdigest()
       
    20 
       
    21 def working_hours(mxdate):
       
    22     """
       
    23     Predicate returning True is the date's hour is in working hours (8h->20h)
       
    24     """
       
    25     if mxdate.hour > 7 and mxdate.hour < 21:
       
    26         return True
       
    27     return False
       
    28     
       
    29 def date_range(begin, end, incr=1, include=None):
       
    30     """yields each date between begin and end
       
    31     :param begin: the start date
       
    32     :param end: the end date
       
    33     :param incr: the step to use to iterate over dates. Default is
       
    34                  one day.                 
       
    35     :param include: None (means no exclusion) or a function taking a
       
    36                     date as parameter, and returning True if the date
       
    37                     should be included.
       
    38     """
       
    39     date = begin
       
    40     while date <= end:
       
    41         if include is None or include(date): 
       
    42             yield date
       
    43         date += incr
       
    44 
       
    45 
       
    46 def dump_class(cls, clsname):
       
    47     """create copy of a class by creating an empty class inheriting
       
    48     from the given cls.
       
    49 
       
    50     Those class will be used as place holder for attribute and relation
       
    51     description
       
    52     """
       
    53     # type doesn't accept unicode name
       
    54     # return type.__new__(type, str(clsname), (cls,), {})
       
    55     # __autogenerated__ attribute is just a marker
       
    56     return type(str(clsname), (cls,), {'__autogenerated__': True})
       
    57 
       
    58 
       
    59 def merge_dicts(dict1, dict2):
       
    60     """update a copy of `dict1` with `dict2`"""
       
    61     dict1 = dict(dict1)
       
    62     dict1.update(dict2)
       
    63     return dict1
       
    64                 
       
    65 
       
    66 class SizeConstrainedList(list):
       
    67     """simple list that makes sure the list does not get bigger
       
    68     than a given size.
       
    69 
       
    70     when the list is full and a new element is added, the first
       
    71     element of the list is removed before appending the new one
       
    72 
       
    73     >>> l = SizeConstrainedList(2)
       
    74     >>> l.append(1)
       
    75     >>> l.append(2)
       
    76     >>> l
       
    77     [1, 2]
       
    78     >>> l.append(3)
       
    79     [2, 3]
       
    80     """
       
    81     def __init__(self, maxsize):
       
    82         self.maxsize = maxsize
       
    83 
       
    84     def append(self, element):
       
    85         if len(self) == self.maxsize:
       
    86             del self[0]
       
    87         super(SizeConstrainedList, self).append(element)
       
    88 
       
    89     def extend(self, sequence):
       
    90         super(SizeConstrainedList, self).extend(sequence)
       
    91         keepafter = len(self) - self.maxsize
       
    92         if keepafter > 0:
       
    93             del self[:keepafter]
       
    94 
       
    95     __iadd__ = extend
       
    96 
       
    97 
       
    98 class UStringIO(list):
       
    99     """a file wrapper which automatically encode unicode string to an encoding
       
   100     specifed in the constructor
       
   101     """
       
   102 
       
   103     def __nonzero__(self):
       
   104         return True
       
   105     
       
   106     def write(self, value):
       
   107         assert isinstance(value, unicode), u"unicode required not %s : %s"\
       
   108                                      % (type(value).__name__, repr(value))
       
   109         self.append(value)
       
   110         
       
   111     def getvalue(self):
       
   112         return u''.join(self)
       
   113 
       
   114     def __repr__(self):
       
   115         return '<%s at %#x>' % (self.__class__.__name__, id(self))
       
   116 
       
   117 
       
   118 class HTMLHead(UStringIO):
       
   119     """wraps HTML header's stream
       
   120 
       
   121     Request objects use a HTMLHead instance to ease adding of
       
   122     javascripts and stylesheets
       
   123     """
       
   124     js_unload_code = u'jQuery(window).unload(unloadPageData);'
       
   125 
       
   126     def __init__(self):
       
   127         super(HTMLHead, self).__init__()
       
   128         self.jsvars = []
       
   129         self.jsfiles = []
       
   130         self.cssfiles = []
       
   131         self.ie_cssfiles = []
       
   132         self.post_inlined_scripts = []
       
   133         self.pagedata_unload = False
       
   134 
       
   135 
       
   136     def add_raw(self, rawheader):
       
   137         self.write(rawheader)
       
   138 
       
   139     def define_var(self, var, value):
       
   140         self.jsvars.append( (var, value) )
       
   141 
       
   142     def add_post_inline_script(self, content):
       
   143         self.post_inlined_scripts.append(content)
       
   144 
       
   145     def add_onload(self, jscode):
       
   146         self.add_post_inline_script(u"""jQuery(document).ready(function () {
       
   147  %s
       
   148  });""" % jscode)
       
   149         
       
   150     
       
   151     def add_js(self, jsfile):
       
   152         """adds `jsfile` to the list of javascripts used in the webpage
       
   153 
       
   154         This function checks if the file has already been added
       
   155         :param jsfile: the script's URL
       
   156         """
       
   157         if jsfile not in self.jsfiles:
       
   158             self.jsfiles.append(jsfile)
       
   159 
       
   160     def add_css(self, cssfile, media):
       
   161         """adds `cssfile` to the list of javascripts used in the webpage
       
   162 
       
   163         This function checks if the file has already been added
       
   164         :param cssfile: the stylesheet's URL
       
   165         """
       
   166         if (cssfile, media) not in self.cssfiles:
       
   167             self.cssfiles.append( (cssfile, media) )
       
   168 
       
   169     def add_ie_css(self, cssfile, media='all'):
       
   170         """registers some IE specific CSS"""
       
   171         if (cssfile, media) not in self.ie_cssfiles:
       
   172             self.ie_cssfiles.append( (cssfile, media) )
       
   173 
       
   174     def add_unload_pagedata(self):
       
   175         """registers onunload callback to clean page data on server"""
       
   176         if not self.pagedata_unload:
       
   177             self.post_inlined_scripts.append(self.js_unload_code)
       
   178             self.pagedata_unload = True
       
   179 
       
   180     def getvalue(self, skiphead=False):
       
   181         """reimplement getvalue to provide a consistent (and somewhat browser
       
   182         optimzed cf. http://stevesouders.com/cuzillion) order in external
       
   183         resources declaration
       
   184         """
       
   185         w = self.write
       
   186         # 1/ variable declaration if any
       
   187         if self.jsvars:
       
   188             from simplejson import dumps
       
   189             w(u'<script type="text/javascript">\n')
       
   190             for var, value in self.jsvars:
       
   191                 w(u'%s = %s;\n' % (var, dumps(value)))
       
   192             w(u'</script>\n')
       
   193         # 2/ css files
       
   194         for cssfile, media in self.cssfiles:
       
   195             w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
       
   196               (media, cssfile))
       
   197         # 3/ ie css if necessary
       
   198         if self.ie_cssfiles:
       
   199             w(u'<!--[if lt IE 8]>\n')
       
   200             for cssfile, media in self.ie_cssfiles:
       
   201                 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
       
   202                   (media, cssfile))
       
   203             w(u'<![endif]--> \n')
       
   204         # 4/ js files
       
   205         for jsfile in self.jsfiles:
       
   206             w(u'<script type="text/javascript" src="%s"></script>\n' % jsfile)
       
   207         # 5/ post inlined scripts (i.e. scripts depending on other JS files)
       
   208         if self.post_inlined_scripts:
       
   209             w(u'<script type="text/javascript">\n')
       
   210             w(u'\n\n'.join(self.post_inlined_scripts))
       
   211             w(u'\n</script>\n')
       
   212         header = super(HTMLHead, self).getvalue()
       
   213         if skiphead:
       
   214             return header
       
   215         return u'<head>\n%s</head>\n' % header
       
   216         
       
   217 
       
   218 class HTMLStream(object):
       
   219     """represents a HTML page.
       
   220 
       
   221     This is used my main templates so that HTML headers can be added
       
   222     at any time during the page generation.
       
   223     
       
   224     HTMLStream uses the (U)StringIO interface to be compliant with
       
   225     existing code.
       
   226     """
       
   227     
       
   228     def __init__(self, req):
       
   229         # stream for <head>
       
   230         self.head = req.html_headers
       
   231         # main stream
       
   232         self.body = UStringIO()
       
   233         self.doctype = u''
       
   234         # xmldecl and html opening tag
       
   235         self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
       
   236         self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
       
   237                        'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
       
   238                        'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
       
   239 
       
   240 
       
   241     def write(self, data):
       
   242         """StringIO interface: this method will be assigned to self.w
       
   243         """
       
   244         self.body.write(data)
       
   245 
       
   246     def getvalue(self):
       
   247         """writes HTML headers, closes </head> tag and writes HTML body"""
       
   248         return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
       
   249                                                  self.htmltag,
       
   250                                                  self.head.getvalue(),
       
   251                                                  self.body.getvalue())
       
   252 
       
   253 
       
   254 class AcceptMixIn(object):
       
   255     """Mixin class for vobjects defining the 'accepts' attribute describing
       
   256     a set of supported entity type (Any by default).
       
   257     """
       
   258     # XXX deprecated, no more necessary
       
   259 
       
   260 
       
   261 from logilab.common.deprecation import moved, class_moved
       
   262 rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid')
       
   263 ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url')
       
   264 
       
   265 import cubicweb
       
   266 Binary = class_moved(cubicweb.Binary)