# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1234907155 -3600 # Node ID b4728112625f878c73cc57c9869e5e0eb6562c3a # Parent 21a59b468f1a1f552d269461004f568a66fd9d22 move utils from cw.common to cw diff -r 21a59b468f1a -r b4728112625f appobject.py --- a/appobject.py Tue Feb 17 22:37:59 2009 +0100 +++ b/appobject.py Tue Feb 17 22:45:55 2009 +0100 @@ -20,7 +20,7 @@ from cubicweb import Unauthorized from cubicweb.vregistry import VObject from cubicweb.selectors import yes -from cubicweb.common.utils import UStringIO +from cubicweb.utils import UStringIO from cubicweb.common.uilib import html_escape, ustrftime from cubicweb.common.registerers import yes_registerer, priority_registerer diff -r 21a59b468f1a -r b4728112625f common/uilib.py --- a/common/uilib.py Tue Feb 17 22:37:59 2009 +0100 +++ b/common/uilib.py Tue Feb 17 22:45:55 2009 +0100 @@ -17,7 +17,6 @@ from cStringIO import StringIO from copy import deepcopy -import simplejson from mx.DateTime import DateTimeType, DateTimeDeltaType @@ -258,6 +257,7 @@ elif vid: params.append(repr(vid)) if extraparams: + import simplejson params.append(simplejson.dumps(extraparams)) if swap: params.append('true') diff -r 21a59b468f1a -r b4728112625f common/utils.py --- a/common/utils.py Tue Feb 17 22:37:59 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,266 +0,0 @@ -"""Some utilities for CubicWeb server/clients. - -:organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -""" -__docformat__ = "restructuredtext en" - -from md5 import md5 -from time import time -from random import randint, seed - -# initialize random seed from current time -seed() - -def make_uid(key): - """forge a unique identifier""" - msg = str(key) + "%.10f"%time() + str(randint(0, 1000000)) - return md5(msg).hexdigest() - -def working_hours(mxdate): - """ - Predicate returning True is the date's hour is in working hours (8h->20h) - """ - if mxdate.hour > 7 and mxdate.hour < 21: - return True - return False - -def date_range(begin, end, incr=1, include=None): - """yields each date between begin and end - :param begin: the start date - :param end: the end date - :param incr: the step to use to iterate over dates. Default is - one day. - :param include: None (means no exclusion) or a function taking a - date as parameter, and returning True if the date - should be included. - """ - date = begin - while date <= end: - if include is None or include(date): - yield date - date += incr - - -def dump_class(cls, clsname): - """create copy of a class by creating an empty class inheriting - from the given cls. - - Those class will be used as place holder for attribute and relation - description - """ - # type doesn't accept unicode name - # return type.__new__(type, str(clsname), (cls,), {}) - # __autogenerated__ attribute is just a marker - return type(str(clsname), (cls,), {'__autogenerated__': True}) - - -def merge_dicts(dict1, dict2): - """update a copy of `dict1` with `dict2`""" - dict1 = dict(dict1) - dict1.update(dict2) - return dict1 - - -class SizeConstrainedList(list): - """simple list that makes sure the list does not get bigger - than a given size. - - when the list is full and a new element is added, the first - element of the list is removed before appending the new one - - >>> l = SizeConstrainedList(2) - >>> l.append(1) - >>> l.append(2) - >>> l - [1, 2] - >>> l.append(3) - [2, 3] - """ - def __init__(self, maxsize): - self.maxsize = maxsize - - def append(self, element): - if len(self) == self.maxsize: - del self[0] - super(SizeConstrainedList, self).append(element) - - def extend(self, sequence): - super(SizeConstrainedList, self).extend(sequence) - keepafter = len(self) - self.maxsize - if keepafter > 0: - del self[:keepafter] - - __iadd__ = extend - - -class UStringIO(list): - """a file wrapper which automatically encode unicode string to an encoding - specifed in the constructor - """ - - def __nonzero__(self): - return True - - def write(self, value): - assert isinstance(value, unicode), u"unicode required not %s : %s"\ - % (type(value).__name__, repr(value)) - self.append(value) - - def getvalue(self): - return u''.join(self) - - def __repr__(self): - return '<%s at %#x>' % (self.__class__.__name__, id(self)) - - -class HTMLHead(UStringIO): - """wraps HTML header's stream - - Request objects use a HTMLHead instance to ease adding of - javascripts and stylesheets - """ - js_unload_code = u'jQuery(window).unload(unloadPageData);' - - def __init__(self): - super(HTMLHead, self).__init__() - self.jsvars = [] - self.jsfiles = [] - self.cssfiles = [] - self.ie_cssfiles = [] - self.post_inlined_scripts = [] - self.pagedata_unload = False - - - def add_raw(self, rawheader): - self.write(rawheader) - - def define_var(self, var, value): - self.jsvars.append( (var, value) ) - - def add_post_inline_script(self, content): - self.post_inlined_scripts.append(content) - - def add_onload(self, jscode): - self.add_post_inline_script(u"""jQuery(document).ready(function () { - %s - });""" % jscode) - - - def add_js(self, jsfile): - """adds `jsfile` to the list of javascripts used in the webpage - - This function checks if the file has already been added - :param jsfile: the script's URL - """ - if jsfile not in self.jsfiles: - self.jsfiles.append(jsfile) - - def add_css(self, cssfile, media): - """adds `cssfile` to the list of javascripts used in the webpage - - This function checks if the file has already been added - :param cssfile: the stylesheet's URL - """ - if (cssfile, media) not in self.cssfiles: - self.cssfiles.append( (cssfile, media) ) - - def add_ie_css(self, cssfile, media='all'): - """registers some IE specific CSS""" - if (cssfile, media) not in self.ie_cssfiles: - self.ie_cssfiles.append( (cssfile, media) ) - - def add_unload_pagedata(self): - """registers onunload callback to clean page data on server""" - if not self.pagedata_unload: - self.post_inlined_scripts.append(self.js_unload_code) - self.pagedata_unload = True - - def getvalue(self, skiphead=False): - """reimplement getvalue to provide a consistent (and somewhat browser - optimzed cf. http://stevesouders.com/cuzillion) order in external - resources declaration - """ - w = self.write - # 1/ variable declaration if any - if self.jsvars: - from simplejson import dumps - w(u'\n') - # 2/ css files - for cssfile, media in self.cssfiles: - w(u'\n' % - (media, cssfile)) - # 3/ ie css if necessary - if self.ie_cssfiles: - w(u' \n') - # 4/ js files - for jsfile in self.jsfiles: - w(u'\n' % jsfile) - # 5/ post inlined scripts (i.e. scripts depending on other JS files) - if self.post_inlined_scripts: - w(u'\n') - header = super(HTMLHead, self).getvalue() - if skiphead: - return header - return u'\n%s\n' % header - - -class HTMLStream(object): - """represents a HTML page. - - This is used my main templates so that HTML headers can be added - at any time during the page generation. - - HTMLStream uses the (U)StringIO interface to be compliant with - existing code. - """ - - def __init__(self, req): - # stream for - self.head = req.html_headers - # main stream - self.body = UStringIO() - self.doctype = u'' - # xmldecl and html opening tag - self.xmldecl = u'\n' % req.encoding - self.htmltag = u'' % (req.lang, req.lang) - - - def write(self, data): - """StringIO interface: this method will be assigned to self.w - """ - self.body.write(data) - - def getvalue(self): - """writes HTML headers, closes tag and writes HTML body""" - return u'%s\n%s\n%s\n%s\n%s\n' % (self.xmldecl, self.doctype, - self.htmltag, - self.head.getvalue(), - self.body.getvalue()) - - -class AcceptMixIn(object): - """Mixin class for vobjects defining the 'accepts' attribute describing - a set of supported entity type (Any by default). - """ - # XXX deprecated, no more necessary - - -from logilab.common.deprecation import moved, class_moved -rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid') -ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url') - -import cubicweb -Binary = class_moved(cubicweb.Binary) diff -r 21a59b468f1a -r b4728112625f utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utils.py Tue Feb 17 22:45:55 2009 +0100 @@ -0,0 +1,266 @@ +"""Some utilities for CubicWeb server/clients. + +:organization: Logilab +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" +__docformat__ = "restructuredtext en" + +from md5 import md5 +from time import time +from random import randint, seed + +# initialize random seed from current time +seed() + +def make_uid(key): + """forge a unique identifier""" + msg = str(key) + "%.10f"%time() + str(randint(0, 1000000)) + return md5(msg).hexdigest() + +def working_hours(mxdate): + """ + Predicate returning True is the date's hour is in working hours (8h->20h) + """ + if mxdate.hour > 7 and mxdate.hour < 21: + return True + return False + +def date_range(begin, end, incr=1, include=None): + """yields each date between begin and end + :param begin: the start date + :param end: the end date + :param incr: the step to use to iterate over dates. Default is + one day. + :param include: None (means no exclusion) or a function taking a + date as parameter, and returning True if the date + should be included. + """ + date = begin + while date <= end: + if include is None or include(date): + yield date + date += incr + + +def dump_class(cls, clsname): + """create copy of a class by creating an empty class inheriting + from the given cls. + + Those class will be used as place holder for attribute and relation + description + """ + # type doesn't accept unicode name + # return type.__new__(type, str(clsname), (cls,), {}) + # __autogenerated__ attribute is just a marker + return type(str(clsname), (cls,), {'__autogenerated__': True}) + + +def merge_dicts(dict1, dict2): + """update a copy of `dict1` with `dict2`""" + dict1 = dict(dict1) + dict1.update(dict2) + return dict1 + + +class SizeConstrainedList(list): + """simple list that makes sure the list does not get bigger + than a given size. + + when the list is full and a new element is added, the first + element of the list is removed before appending the new one + + >>> l = SizeConstrainedList(2) + >>> l.append(1) + >>> l.append(2) + >>> l + [1, 2] + >>> l.append(3) + [2, 3] + """ + def __init__(self, maxsize): + self.maxsize = maxsize + + def append(self, element): + if len(self) == self.maxsize: + del self[0] + super(SizeConstrainedList, self).append(element) + + def extend(self, sequence): + super(SizeConstrainedList, self).extend(sequence) + keepafter = len(self) - self.maxsize + if keepafter > 0: + del self[:keepafter] + + __iadd__ = extend + + +class UStringIO(list): + """a file wrapper which automatically encode unicode string to an encoding + specifed in the constructor + """ + + def __nonzero__(self): + return True + + def write(self, value): + assert isinstance(value, unicode), u"unicode required not %s : %s"\ + % (type(value).__name__, repr(value)) + self.append(value) + + def getvalue(self): + return u''.join(self) + + def __repr__(self): + return '<%s at %#x>' % (self.__class__.__name__, id(self)) + + +class HTMLHead(UStringIO): + """wraps HTML header's stream + + Request objects use a HTMLHead instance to ease adding of + javascripts and stylesheets + """ + js_unload_code = u'jQuery(window).unload(unloadPageData);' + + def __init__(self): + super(HTMLHead, self).__init__() + self.jsvars = [] + self.jsfiles = [] + self.cssfiles = [] + self.ie_cssfiles = [] + self.post_inlined_scripts = [] + self.pagedata_unload = False + + + def add_raw(self, rawheader): + self.write(rawheader) + + def define_var(self, var, value): + self.jsvars.append( (var, value) ) + + def add_post_inline_script(self, content): + self.post_inlined_scripts.append(content) + + def add_onload(self, jscode): + self.add_post_inline_script(u"""jQuery(document).ready(function () { + %s + });""" % jscode) + + + def add_js(self, jsfile): + """adds `jsfile` to the list of javascripts used in the webpage + + This function checks if the file has already been added + :param jsfile: the script's URL + """ + if jsfile not in self.jsfiles: + self.jsfiles.append(jsfile) + + def add_css(self, cssfile, media): + """adds `cssfile` to the list of javascripts used in the webpage + + This function checks if the file has already been added + :param cssfile: the stylesheet's URL + """ + if (cssfile, media) not in self.cssfiles: + self.cssfiles.append( (cssfile, media) ) + + def add_ie_css(self, cssfile, media='all'): + """registers some IE specific CSS""" + if (cssfile, media) not in self.ie_cssfiles: + self.ie_cssfiles.append( (cssfile, media) ) + + def add_unload_pagedata(self): + """registers onunload callback to clean page data on server""" + if not self.pagedata_unload: + self.post_inlined_scripts.append(self.js_unload_code) + self.pagedata_unload = True + + def getvalue(self, skiphead=False): + """reimplement getvalue to provide a consistent (and somewhat browser + optimzed cf. http://stevesouders.com/cuzillion) order in external + resources declaration + """ + w = self.write + # 1/ variable declaration if any + if self.jsvars: + from simplejson import dumps + w(u'\n') + # 2/ css files + for cssfile, media in self.cssfiles: + w(u'\n' % + (media, cssfile)) + # 3/ ie css if necessary + if self.ie_cssfiles: + w(u' \n') + # 4/ js files + for jsfile in self.jsfiles: + w(u'\n' % jsfile) + # 5/ post inlined scripts (i.e. scripts depending on other JS files) + if self.post_inlined_scripts: + w(u'\n') + header = super(HTMLHead, self).getvalue() + if skiphead: + return header + return u'\n%s\n' % header + + +class HTMLStream(object): + """represents a HTML page. + + This is used my main templates so that HTML headers can be added + at any time during the page generation. + + HTMLStream uses the (U)StringIO interface to be compliant with + existing code. + """ + + def __init__(self, req): + # stream for + self.head = req.html_headers + # main stream + self.body = UStringIO() + self.doctype = u'' + # xmldecl and html opening tag + self.xmldecl = u'\n' % req.encoding + self.htmltag = u'' % (req.lang, req.lang) + + + def write(self, data): + """StringIO interface: this method will be assigned to self.w + """ + self.body.write(data) + + def getvalue(self): + """writes HTML headers, closes tag and writes HTML body""" + return u'%s\n%s\n%s\n%s\n%s\n' % (self.xmldecl, self.doctype, + self.htmltag, + self.head.getvalue(), + self.body.getvalue()) + + +class AcceptMixIn(object): + """Mixin class for vobjects defining the 'accepts' attribute describing + a set of supported entity type (Any by default). + """ + # XXX deprecated, no more necessary + + +from logilab.common.deprecation import moved, class_moved +rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid') +ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url') + +import cubicweb +Binary = class_moved(cubicweb.Binary) diff -r 21a59b468f1a -r b4728112625f view.py --- a/view.py Tue Feb 17 22:37:59 2009 +0100 +++ b/view.py Tue Feb 17 22:45:55 2009 +0100 @@ -16,8 +16,8 @@ nonempty_rset, none_rset) from cubicweb.selectors import require_group_compat, accepts_compat from cubicweb.appobject import AppRsetObject +from cubicweb.utils import UStringIO, HTMLStream from cubicweb.common.registerers import accepts_registerer, priority_registerer, yes_registerer -from cubicweb.common.utils import UStringIO, HTMLStream _ = unicode