common/utils.py
changeset 1808 aa09e20dd8c0
parent 28 9b7067bfaa15
parent 1132 96752791c2b6
child 1977 606923dff11b
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     1 """Some utilities for CubicWeb server/clients.
     1 """pre 3.2 bw compat"""
     2 
     2 # pylint: disable-msg=W0614,W0401
     3 :organization: Logilab
     3 from warnings import warn
     4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     4 warn('moved to cubicweb.utils', DeprecationWarning, stacklevel=2)
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 from cubicweb.utils import *
     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):
       
   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         return u'<head>\n%s</head>\n' % super(HTMLHead, self).getvalue()
       
   213         
       
   214 
       
   215 class HTMLStream(object):
       
   216     """represents a HTML page.
       
   217 
       
   218     This is used my main templates so that HTML headers can be added
       
   219     at any time during the page generation.
       
   220     
       
   221     HTMLStream uses the (U)StringIO interface to be compliant with
       
   222     existing code.
       
   223     """
       
   224     
       
   225     def __init__(self, req):
       
   226         # stream for <head>
       
   227         self.head = req.html_headers
       
   228         # main stream
       
   229         self.body = UStringIO()
       
   230         self.doctype = u''
       
   231         # xmldecl and html opening tag
       
   232         self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
       
   233         self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
       
   234                        'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
       
   235                        'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
       
   236 
       
   237 
       
   238     def write(self, data):
       
   239         """StringIO interface: this method will be assigned to self.w
       
   240         """
       
   241         self.body.write(data)
       
   242 
       
   243     def getvalue(self):
       
   244         """writes HTML headers, closes </head> tag and writes HTML body"""
       
   245         return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
       
   246                                                  self.htmltag,
       
   247                                                  self.head.getvalue(),
       
   248                                                  self.body.getvalue())
       
   249 
       
   250 
       
   251 class AcceptMixIn(object):
       
   252     """Mixin class for vobjects defining the 'accepts' attribute describing
       
   253     a set of supported entity type (Any by default).
       
   254     """
       
   255     # XXX deprecated, no more necessary
       
   256 
       
   257 
       
   258 from logilab.common.deprecation import moved, class_moved
       
   259 rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid')
       
   260 ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url')
       
   261 
       
   262 import cubicweb
       
   263 Binary = class_moved(cubicweb.Binary)