Changeset 56571d6 in indico


Ignore:
Timestamp:
03/03/10 19:02:10 (3 years ago)
Author:
Jose Benito <jose.benito.gonzalez@…>
Branches:
master, burotel, hello-world-walkthrough, ipv6, new-webex, prov-dual-interface, v0.97-series, v0.98-series, v0.98.2, v0.98.3, v0.98b1, v0.98b2, v0.99, 051b2622c51afb171a1dedb46a0df4fbb0cbd02e, 0da0c1403bae8e51d8229f460181c71b9e6dda72
Children:
bf522c
Parents:
2cc320
git-author:
David Martín Clavo <david.martin.clavo@…> (02/24/10 13:44:46)
git-committer:
Jose Benito <jose.benito.gonzalez@…> (03/03/10 19:02:10)
Message:

[FIX] Favourite exceptions and others

-fixes #146
-fix: removed raise ServiceError? when adding an already
present favourite or removing a favourite that was not
in the favourite basket.
-imp: included "_type" and "_fossil" in all fossilized objects.
Added restriction to fossil interface name: I*Fossil.
-imp: took the opportunity to implement 3 levels of Avatar fossils
-fix: properly make the "fossilize" function available in templates
-imp: used fossilized avatars in UserListBasket? service, and in lists of
users in plugin options.
-fix: check for "_type" == "Avatar" in avatars added to UserListWidget?
on widget build. If so, their key is "existingAv
*". This way they are
not duplicated when we add one that was already in the list on page load.
-imp: updated testFossilize.py to take into account "_type" and "_fossil"

Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • indico/MaKaC/common/TemplateExec.py

    rbd21e7 r56571d6  
    4040 
    4141class TemplateExecException( Exception ): 
    42      
     42 
    4343    def __init__( self, value ): 
    4444        self.value = value 
     
    4747        if hasattr(value, 'problematic_templates'): 
    4848            self.problematic_templates = value.problematic_templates 
    49              
     49 
    5050    def __str__(self): 
    5151        s = str( type(self.value).__name__) + ": " + str(self.value ) 
     
    5454            if hasattr(self, 'problematic_templates'): 
    5555                s += """ - - - -> {Python code generated from templates is available in %s/%s.tpl.error.py""" % ( ERROR_PATH, self.problematic_templates[0]) 
    56              
     56 
    5757                for template_name in self.problematic_templates[1:]: 
    5858                    s += ", %s/%s.py"%(ERROR_PATH, template_name) 
    5959                s += " file(s)}" 
    60                           
     60 
    6161        return s 
    6262 
    6363def declareTemplate(newTemplateStyle=False): 
    64      
     64 
    6565    objDict = globals()['_objDict'] 
    6666    objDict['newTemplateStyle'] = newTemplateStyle 
     
    7070    Use: 
    7171    <% includeTpl( 'TemplateName', paramA = 2, paramB = 3 ) %> 
    72     """  
     72    """ 
    7373 
    7474    # Resurrect dictionary from globals - ugly hack. 
    7575    # In the recurrent exec, we still need an old dictCopy. 
    76     # This old dictCopy is resurrected from the global scope.  
     76    # This old dictCopy is resurrected from the global scope. 
    7777    objDict = globals()['_dictCopy'] 
    7878    for k, v in kwargs.iteritems(): 
     
    8282    from MaKaC.webinterface import wcomponents 
    8383    result = wcomponents.WTemplated(tplFileName).getHTML(objDict) 
    84      
     84 
    8585    globals()['_objDict']["newTemplateStyle"] = True 
    86      
     86 
    8787    print( result.replace('%', '%%') ) 
    8888 
     
    9393    """ 
    9494    includeTpl( 'ContextHelp', helpId = helpId, imgSrc = Config.getInstance().getSystemIconURL( "help" ) ) 
    95      
     95 
    9696def inlineContextHelp( helpContent ): 
    9797    """ 
     
    9999    Help content passed as argument helpContent. 
    100100    """ 
    101     includeTpl( 'InlineContextHelp', helpContent = helpContent, imgSrc = Config.getInstance().getSystemIconURL( "help" ) )  
     101    includeTpl( 'InlineContextHelp', helpContent = helpContent, imgSrc = Config.getInstance().getSystemIconURL( "help" ) ) 
    102102 
    103103def escapeAttrVal( s ): 
     
    121121def verbose( s, default = "" ): 
    122122    """ 
    123     Purpose: avoid showing "None" to user; show default value instead.  
     123    Purpose: avoid showing "None" to user; show default value instead. 
    124124    """ 
    125125    if isinstance( s, bool ) and s != None: 
     
    128128        else: 
    129129            return "no" 
    130      
     130 
    131131    return s or default 
    132132 
     
    158158 
    159159def roomClass( room ): 
    160     if room.isReservable:  
     160    if room.isReservable: 
    161161        roomClass = "" 
    162     if not room.isReservable:  
    163         roomClass = "privateRoom"  
    164     if room.isReservable and room.resvsNeedConfirmation:  
     162    if not room.isReservable: 
     163        roomClass = "privateRoom" 
     164    if room.isReservable and room.resvsNeedConfirmation: 
    165165        roomClass = "moderatedRoom" 
    166166    return roomClass 
     
    186186        Author: David Martin Clavo 
    187187    """ 
    188      
     188 
    189189    # stringfy objects inside a list 
    190190    if isinstance(obj,list): 
    191191        for i in range(0, len(obj)): 
    192192            obj[i] = deepstr(obj[i]) 
    193              
     193 
    194194    #stringfy objects inside a dictionary 
    195195    if isinstance(obj,dict): 
     
    197197            del obj[k] #we delete the old key 
    198198            obj[deepstr(k)] = deepstr(v) 
    199                  
     199 
    200200    return str(obj) 
    201201 
     
    208208        See BeautifulHTMLList.tpl and BeautifulHTMLDict.tpl to see how they are used. 
    209209        In the CSS file, you should define classes like: ul.optionList1, ul.optionList2, ul.optionKey1 (the number is the level of recursivity) 
    210         -level: the level of recursivity.  
     210        -level: the level of recursivity. 
    211211    """ 
    212212    from MaKaC.webinterface import wcomponents 
     
    234234    else: 
    235235        return title 
    236      
     236 
    237237def escapeHTMLForJS(s): 
    238238    """ Replaces some restricted characters in JS strings. 
     
    247247        (backspace) -> \b 
    248248        (form feed) -> \f 
    249          
     249 
    250250        TODO: try to optimize this (or check if it's optimum already). 
    251251        translate() doesn't work, because we are replacing characters by couples of characters. 
     
    260260    """ 
    261261    Enables template execution. 
    262      
     262 
    263263    Template is a string mixing any text (HTML, e-mail, etc.) 
    264264    with Python code. 
     
    267267 
    268268    Every Python expression put between <%= %> will be EVALUATED 
    269     and result will be put in place of expression.  
    270      
     269    and result will be put in place of expression. 
     270 
    271271    Every Python statements put between <% %> will be EXECUTED. 
    272272    Standard output (stdout) will be put in place of these statements. 
    273273    (so you can do <% print %>) 
    274      
     274 
    275275    Every strings put between %()s will be SUBSTITUTED with the 
    276276    corresponding value from given dictionary. 
    277      
     277 
    278278    Examples of templates: 
    279      
     279 
    280280    # Example 1: 
    281281    <tr><td> <%= 2 + 5 %> </td> </tr> 
    282      
     282 
    283283    # Example 2: 
    284284    Hello <%= person.fistName %>! 
    285285    Your reservation for <%= period.startDT %> -- <%= period.endDT %> is confirmed. 
    286      
     286 
    287287    # Example 3: 
    288288    <html> 
     
    292292    <body> 
    293293        <h1> <%= room.name.capitalize() %> </h1> 
    294      
     294 
    295295        <% import os %> <!-- Not necessary here - just to show you can do this --> 
    296296        Let's skip odd columns. 
     
    300300                <% for column in xrange( 0, 6 ): %> 
    301301                    <% if column % 2 == 0: %> 
    302                         <td style="background-color:lightgray"> Row = <%= row %>, Column = <%= column %> </td>  
     302                        <td style="background-color:lightgray"> Row = <%= row %>, Column = <%= column %> </td> 
    303303                    <% end %> 
    304304                <% end %> 
     
    312312    Template is execution context => objects live through the hole template, 
    313313    and not only in their <% %> tag. 
    314      
     314 
    315315    You can include another templates via includeTpl( 'TemplateName' ). 
    316316    The child templates inherit execution context, so all parent objects 
    317     are available to it.         
     317    are available to it. 
    318318 
    319319    Restrictions: 
     
    321321       => you CAN NOT use strings like "foo:goo" 
    322322       => you CAN NOT initialize dictionaries dic = { a: 2, b: 3 } 
    323        => you can write statements like: for i in xrange( 0, 10 ):  
     323       => you can write statements like: for i in xrange( 0, 10 ): 
    324324    2) You CAN NOT put blok in the same line as statement: 
    325        => BAD:  
     325       => BAD: 
    326326          if a == 2: break 
    327327          Use instead: 
    328328          if a == 2: 
    329329              break 
    330      
     330 
    331331    These limitations are consequence of Python syntax-by-indentation rules. 
    332332    """ 
     
    344344 
    345345        Returns resulting string. 
    346          
     346 
    347347        tpl - template string 
    348348        objDict - context dictionary; insert objects and strings there. 
    349         """         
     349        """ 
    350350        # IMPLEMENTATION 
    351351        # General idea is to convert template into Python code and then execute it. 
    352352        # With includeTpl(), these may go recurrent. 
    353         #global recurrenceLevel  
     353        #global recurrenceLevel 
    354354        #recurrenceLevel += 1 
    355355 
     
    357357        lastIx = 0        # Index of last Python code in template 
    358358        indentLevel = 0   # Indentation level 
    359          
     359 
    360360        # Copy the dictionary, since it will be modified. 
    361361        # We do not want to mess up with the oryginal dict. 
    362         dictCopy = objDict.copy()         
     362        dictCopy = objDict.copy() 
    363363 
    364364        TemplateExec.__registerHelpers( dictCopy ) 
     
    371371        globals()['_dictCopy'] = dictCopy 
    372372        globals()['_objDict'] = objDict 
    373          
     373 
    374374        while True: 
    375375            ixSt, ixEnd, type, statement = TemplateExec.__nextPythonCode( tpl, lastIx ) 
    376              
     376 
    377377            # Human text between Python codes: convert to Python code (print) 
    378378            humanText = tpl[lastIx:ixSt] 
     
    380380                humanText = TemplateExec.__neutralizeLastChar( humanText ) 
    381381                pythonCode += '\n%ssys.stdout.write( """%s""" )' % ( TemplateExec.__indent( indentLevel ), humanText ) 
    382              
    383             if TemplateExec.__isBlockEnd( statement ):  
     382 
     383            if TemplateExec.__isBlockEnd( statement ): 
    384384                indentLevel -= 1 
    385              
     385 
    386386            # Deal with Python code 
    387             if statement == None:  
     387            if statement == None: 
    388388                break 
    389389 
     
    396396                pythonCode += "\n%s%s" % ( TemplateExec.__indent( indentLevel ),  statement ) 
    397397                # Try to find all trailing ':'  <== OVERSIMPLIFIED 
    398                 indentLevel += statement.count( ':' )  
     398                indentLevel += statement.count( ':' ) 
    399399            lastIx = ixEnd + 2  # Move after this Python code 
    400400 
    401         try:             
    402             newTpl = TemplateExec.__executePythonCode( pythonCode, dictCopy, tplFilename )             
     401        try: 
     402            newTpl = TemplateExec.__executePythonCode( pythonCode, dictCopy, tplFilename ) 
    403403        except TemplateExecException, e: 
    404                  
     404 
    405405            try: open( ERROR_PATH + "/" + tplFilename + ".tpl.py", "w" ).write( pythonCode ) 
    406406            except: pass 
    407407            raise TemplateExecException( e ) 
    408         except Exception, e:             
     408        except Exception, e: 
    409409            try: open( ERROR_PATH + "/" + tplFilename + ".tpl.error.py", "w" ).write( pythonCode ) 
    410410            except: pass 
    411411            raise TemplateExecException( e ) 
    412412        #except: 
    413         #    raise pythonCode         
    414  
    415          
     413        #    raise pythonCode 
     414 
     415 
    416416        try: 
    417417            if (not objDict.has_key( "isIncluded" ) and \ 
     
    444444        #recurrenceLevel -= 1 
    445445 
    446         #if recurrenceLevel == 0 and globals().has_key( '_dictCopy' ):  
     446        #if recurrenceLevel == 0 and globals().has_key( '_dictCopy' ): 
    447447            ## Clean up - we do not want to mess up with global scope 
    448448            #global _dictCopy 
     
    453453 
    454454    # PRIVATE ================================================================ 
    455      
     455 
    456456    @staticmethod 
    457457    def __saveDebugInfo(e, tplFilename): 
     
    469469    def __executePythonCode( pythonCode, objDict, tplFilename ): 
    470470        global recurrenceLevel 
    471         # Execute the pythonCode -------------------------  
     471        # Execute the pythonCode ------------------------- 
    472472 
    473473        # create file-like string to capture output 
     
    495495 
    496496            raise e 
    497          
     497 
    498498        sys.stdout = stdoutBackup 
    499499        sys.stderr = stderrBackup 
    500          
     500 
    501501        newTpl = "" 
    502          
     502 
    503503        s = codeErr.getvalue() 
    504504        if s: newTpl += "<font color='red'>%s</font>" % s 
    505          
     505 
    506506        s = codeOut.getvalue() 
    507507        if s: newTpl += s 
    508          
     508 
    509509        codeOut.close() 
    510         codeErr.close()         
    511          
     510        codeErr.close() 
     511 
    512512        return newTpl 
    513513 
     
    582582            except: 
    583583                # if the connection to the database is not started, there is no need to set the variable and 
    584                 # we avoid an error report.  
     584                # we avoid an error report. 
    585585                # THIS IS NEEDED, when using JSContent. JSContent does not need connection to the database 
    586586                # and this is causing an unexpected exception. 
     
    609609        # allow fossilization 
    610610        if not 'fossilize' in objDict: 
    611             from MaKaC.common import fossilize 
     611            from MaKaC.common.fossilize import fossilize 
    612612            objDict['fossilize'] = fossilize 
    613613        #if not 'minfo' in objDict: 
     
    630630        return INDENTATION * n 
    631631 
    632     @staticmethod         
     632    @staticmethod 
    633633    def __nextPythonCode( tpl, afterIx ): 
    634634        """ 
    635635        Returns a tuple: 
    636         (  
    637             start index,  
     636        ( 
     637            start index, 
    638638            end index, 
    639639            type, 
     
    655655            statement = tpl[ixSt + 3 : ixEnd].strip() 
    656656        else: 
    657             type = 'NonIndentBlock'             
     657            type = 'NonIndentBlock' 
    658658            statement = tpl[ixSt + 2 : ixEnd].strip() 
    659          
    660          
     659 
     660 
    661661        return ( ixSt, ixEnd, type, statement ) 
    662662 
     
    695695            <% for column in xrange( 0, 6 ): %> 
    696696                <% if column % 2 == 0: %> 
    697                     <td style="background-color:lightgray"> Row = <%= row %>, Column = <%= column %> </td>  
     697                    <td style="background-color:lightgray"> Row = <%= row %>, Column = <%= column %> </td> 
    698698                <% end %> 
    699699            <% end %> 
     
    705705</html> 
    706706""" 
    707          
     707 
    708708        dic = {} 
    709709        dic['title'] = "Templating example" 
  • indico/MaKaC/common/fossilize.py

    r046026 r56571d6  
    3535    pass 
    3636 
     37class InvalidFossilException(Exception): 
     38    pass 
     39 
    3740class IFossil(zope.interface.Interface): 
    3841    """ 
     
    4649    """ 
    4750 
    48     __nameRE = re.compile('^get(.*)$') 
     51    __fossilNameRE = re.compile('^I(.*)Fossil$') 
     52    __methodNameRE = re.compile('^get(.*)$') 
     53 
    4954 
    5055    @classmethod 
     
    5257        """ 'De-camelcase' the name """ 
    5358 
    54         m = cls.__nameRE.match(name) 
     59        m = cls.__methodNameRE.match(name) 
    5560 
    5661        if not m: 
     
    6570            return map(lambda elem: cls.__fossilizeIterable(elem, interface, **kwargs), obj) 
    6671        elif type(obj) == dict: 
    67             return dict((k,cls.__fossilizeIterable(v, interface, **kwargs)) for k,v in obj.iteritems()) 
     72            return dict((k, cls.__fossilizeIterable(v, interface, **kwargs)) for k,v in obj.iteritems()) 
    6873        else: 
    6974            return obj.fossilize(interface, **kwargs) 
     
    7984        if not interface.providedBy(self): 
    8085            raise WrongFossilTypeException("Interface '%s' not provided by '%s'" % (interface.__name__, self.__class__.__name__)) 
     86 
     87        fossilNameMatch = Fossilizable.__fossilNameRE.match(interface.getName()) 
     88        if fossilNameMatch is None: 
     89            raise InvalidFossilException("Invalid fossil name: %s. A fossil name should follow the pattern: I******Fossil." % interface.getName()) 
    8190 
    8291        result = {} 
     
    108117            result[attrName] = methodResult 
    109118 
     119        if "_type" in result or "_fossil" in result: 
     120            raise InvalidFossilException('"_type" or "_fossil" cannot be a fossil attribute name') 
     121        else: 
     122            result["_type"] = self.__class__.__name__ 
     123            innerFossilName = fossilNameMatch.group(1) 
     124            if innerFossilName: #we check that it's not an empty string 
     125                result["_fossil"] = innerFossilName[0].lower() + innerFossilName[1:] 
     126            else: 
     127                result["_fossil"] = "" 
     128 
    110129        return result 
    111130 
     
    128147            return map(lambda elem: fossilize(elem, interface, **kwargs), target) 
    129148        elif t is dict: 
    130             return dict((k, fossilize(v, interface, **kwargs)) for k,v in target.iteritems()) 
     149            return dict((k, fossilize(v, interface, **kwargs)) for k, v in target.iteritems()) 
    131150        else: 
    132151            raise NonFossilizableException() 
  • indico/MaKaC/services/implementation/user.py

    rf78b0c r56571d6  
    1 from MaKaC.services.implementation.base import ProtectedDisplayService 
     1# -*- coding: utf-8 -*- 
     2## 
     3## This file is part of CDS Indico. 
     4## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN. 
     5## 
     6## CDS Indico is free software; you can redistribute it and/or 
     7## modify it under the terms of the GNU General Public License as 
     8## published by the Free Software Foundation; either version 2 of the 
     9## License, or (at your option) any later version. 
     10## 
     11## CDS Indico is distributed in the hope that it will be useful, but 
     12## WITHOUT ANY WARRANTY; without even the implied warranty of 
     13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
     14## General Public License for more details. 
     15## 
     16## You should have received a copy of the GNU General Public License 
     17## along with CDS Indico; if not, write to the Free Software Foundation, Inc., 
     18## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 
     19 
    220from MaKaC.services.implementation.base import LoggedOnlyService, ProtectedService 
    321from MaKaC.services.implementation.base import ServiceBase 
    422 
    5 import MaKaC.conference as conference 
    623import MaKaC.user as user 
    724from MaKaC.services.interface.rpc.common import ServiceError 
     
    1128 
    1229import time 
     30from MaKaC.fossils.user import IAvatarAllDetailsFossil, IAvatarDetailedFossil,\ 
     31    IAvatarMinimalFossil 
     32from MaKaC.common.fossilize import fossilize 
    1333 
    1434class UserListEvents(LoggedOnlyService): 
     
    1737        LoggedOnlyService._checkParams(self) 
    1838 
    19         self._time = self._params.get('time',None) 
     39        self._time = self._params.get('time', None) 
    2040        self._target = self.getAW().getUser() 
    2141 
     
    6080                jsonData[event['id']] = event 
    6181 
    62         return jsonData; 
     82        return jsonData 
     83 
    6384 
    6485class UserAddToBasket(LoggedOnlyService): 
     86 
    6587    def _checkParams(self): 
    6688        LoggedOnlyService._checkParams(self) 
     
    83105 
    84106        for user in self._userList: 
    85             if (not self._target.getPersonalInfo().getBasket().addElement(user)): 
    86                 raise ServiceError("ERR-U1","Element already exists in list!") 
     107            #we do not care if the user is already in the favourites 
     108            self._target.getPersonalInfo().getBasket().addElement(user) 
     109 
    87110 
    88111class UserRemoveFromBasket(LoggedOnlyService): 
     
    101124            if (self._obj == None): 
    102125                raise ServiceError("ERR-U0","User does not exist!") 
    103             if (self._target.getPersonalInfo().getBasket().deleteElement(self._obj)): 
    104                 return 
    105             else: 
    106                 raise ServiceError("ERR-U2","Element not in list!") 
     126 
     127            #we do not care if the user is already out of the favourites 
     128            self._target.getPersonalInfo().getBasket().deleteElement(self._obj) 
     129 
    107130 
    108131class UserListBasket(ProtectedService): 
     
    117140 
    118141        self._target = self.getAW().getUser() 
     142        self._detailLevel = self._params.get("detailLevel", "max") 
    119143 
    120144    def _getAnswer( self): 
     
    122146        if self._target: 
    123147            users = self._target.getPersonalInfo().getBasket().getUsers().values() 
    124             return DictPickler.pickle(users) 
     148 
     149            if self._detailLevel == "min": 
     150                fossilToUse = IAvatarMinimalFossil 
     151            elif self._detailLevel == "medium": 
     152                fossilToUse = IAvatarDetailedFossil 
     153            elif self._detailLevel == "max": 
     154                fossilToUse = IAvatarAllDetailsFossil 
     155 
     156            return fossilize(users, fossilToUse) 
    125157        else: 
    126158            return None 
     
    137169    def _getAnswer( self): 
    138170        return DictPickler.pickle(self._target.getPersonalInfo()) 
     171 
    139172 
    140173class UserGetEmail(LoggedOnlyService): 
     
    151184            raise ServiceError("ERR-U4","User is not logged in") 
    152185 
     186 
    153187class UserSetPersonalInfo(LoggedOnlyService): 
    154188 
     
    157191 
    158192        self._target = self.getAW().getUser() 
    159         self._info = self._params.get("value",None) 
     193        self._info = self._params.get("value", None) 
    160194 
    161195    def _getAnswer( self): 
     
    169203        return DictPickler.pickle(pInfo) 
    170204 
     205 
    171206class UserGetTimezone(ServiceBase): 
    172207 
     
    178213        return tz 
    179214 
     215 
    180216class UserGetSessionTimezone(ServiceBase): 
    181217 
     
    184220        return tz 
    185221 
     222 
    186223class UserGetSessionLanguage(ServiceBase): 
    187224 
     
    192229            minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance() 
    193230            return minfo.getLang() 
     231 
    194232 
    195233methodMap = { 
  • indico/MaKaC/user.py

    ra0203b r56571d6  
    2020## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 
    2121 
    22 """Contains the classes that implement the user management subsystem 
    23 """ 
     22from MaKaC.fossils.user import IAvatarMinimalFossil, IAvatarDetailedFossil,\ 
     23    IAvatarAllDetailsFossil 
     24from MaKaC.common.fossilize import Fossilizable, fossilizes 
    2425from random import random 
    2526 
     
    5455from copy import copy 
    5556 
     57"""Contains the classes that implement the user management subsystem 
     58""" 
     59 
    5660class Group(Persistent): 
    5761    """ 
     
    102106            self.email = "" 
    103107        return self.email 
    104      
     108 
    105109    def addMember( self, newMember ): 
    106110        if newMember == self: 
     
    112116            newMember.linkTo(self, "member") 
    113117        self._p_changed = 1 
    114      
     118 
    115119    def removeMember( self, member ): 
    116120        if member == None or member not in self.members: 
     
    144148                continue 
    145149        return 0 
    146      
     150 
    147151    def canModify( self, aw ): 
    148152        return self.canUserModify( aw.getUser() ) 
     
    158162 
    159163class NiceGroup(Group): 
    160      
     164 
    161165    groupType = "Nice" 
    162      
     166 
    163167    def addMember( self, newMember ): 
    164168        pass 
    165      
     169 
    166170    def removeMember( self, member ): 
    167171        pass 
     
    169173    def getMemberList( self ): 
    170174        return [] 
    171      
     175 
    172176    def containsUser( self, avatar ): 
    173177        if avatar == None: 
     
    178182                return True 
    179183        return False 
    180      
     184 
    181185    def containsMember( self, member ): 
    182186        return 0 
    183187 
    184188class CERNGroup(Group): 
    185      
     189 
    186190    groupType = "CERN" 
    187      
     191 
    188192    def addMember( self, newMember ): 
    189193        pass 
    190      
     194 
    191195    def removeMember( self, member ): 
    192196        pass 
    193      
     197 
    194198    def getMemberList( self ): 
    195199        params = urllib.urlencode( { 'ListName': self.name } ) 
     
    209213        #print data 
    210214        conn.close() 
    211                  
     215 
    212216        try: 
    213217            doc = parseString( data ) 
     
    231235            avatarLists.append( lst ) 
    232236        return [ avList[0] for avList in avatarLists if avList ] 
    233      
     237 
    234238    def containsUser( self, avatar ): 
    235239        if avatar == None: 
     
    278282        response = conn.getresponse() 
    279283        data = response.read() 
    280         conn.close()                 
     284        conn.close() 
    281285        try: 
    282286            doc = parseString(data) 
     
    299303    _id="name" 
    300304 
    301     def satisfies(self,group):   
     305    def satisfies(self,group): 
    302306        for value in self._values: 
    303307            if value.strip() != "": 
     
    349353    def getLength( self ): 
    350354        return self.getIndex().getLength() 
    351          
     355 
    352356    def matchFirstLetter( self, letter, forceWithoutExtAuth=False ): 
    353357        result = [] 
     
    364368            pass 
    365369        return result 
    366      
     370 
    367371    def match(self,criteria,forceWithoutExtAuth=False, exact=False): 
    368372        crit={} 
     
    381385                    result.append(gr) 
    382386        return result 
    383      
     387 
    384388    def updateCERNGroupMatch(self, name, exact=False): 
    385389        if not exact: 
     
    408412                gr.setDescription( _("""_("Mapping of the Nice group") %s""")%name+"<br><br>\nMembers list: https://websvc02.cern.ch/WinServices/Services/GroupManager/GroupManager.aspx") 
    409413                self.add(gr) 
    410          
    411  
    412  
    413 class Avatar(Persistent): 
    414     """This class implements the representation of users inside the system.  
    415        Basically it contains personal data from them which is relevant for the  
     414 
     415 
     416 
     417class Avatar(Persistent, Fossilizable): 
     418    """This class implements the representation of users inside the system. 
     419       Basically it contains personal data from them which is relevant for the 
    416420       system. 
    417421    """ 
     422    fossilizes(IAvatarMinimalFossil, IAvatarDetailedFossil, IAvatarAllDetailsFossil) 
     423 
    418424    linkedToBase = {"category":{"creator":[], 
    419425                                "manager":[], 
     
    454460        """Class constructor. 
    455461           Attributes: 
    456                 userData -- dictionary containing user data to map into the  
    457                             avatar. Possible key values (those with * are  
     462                userData -- dictionary containing user data to map into the 
     463                            avatar. Possible key values (those with * are 
    458464                            multiple): 
    459465                                name, surname, title, organisation*, addess*, 
     
    474480        from MaKaC.common import utils 
    475481        self.key = utils.newKey() #key to activate the account 
    476         self.registrants = {}                 
    477          
     482        self.registrants = {} 
     483 
    478484        minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance() 
    479485        self._lang = minfo.getLang() 
     
    491497        #Fermi timezone awareness(end)  # 
    492498        ################################# 
    493          
     499 
    494500        self.resetLinkedTo() 
    495501        self.resetTimedLinkedEvents() 
    496          
     502 
    497503        self.personalInfo = PersonalInfo() 
    498          
     504 
    499505        if userData != None: 
    500506            if userData.has_key( "name" ): 
     
    542548            #Fermi timezone awareness(end) # 
    543549            ################################ 
    544      
     550 
    545551##    def __getattribute__(self, attr): 
    546552##        if object.__getattribute__(self, '_p_getattr')(attr): 
    547553##            return Persistent.__getattribute__(self, attr) 
    548 ##         
     554## 
    549555##        #attributs that always get from this instance 
    550556##        if attr in ["_mergeTo", "getId", "id", "mergeTo", "_mergeFrom", "isMerged", "getMergeTo", "mergeFrom", \ 
    551557##                    "unmergeFrom", "getMergeFromList"]: 
    552558##            return Persistent.__getattribute__(self,attr) 
    553 ##         
    554 ##        #if _mergeTo, get attributs from the _mergeTo  
     559## 
     560##        #if _mergeTo, get attributs from the _mergeTo 
    555561##        elif hasattr(self,"_mergeTo") and Persistent.__getattribute__(self,"_mergeTo") != None: 
    556562##            return Persistent.__getattribute__(self,"_mergeTo").__getattribute__(attr) 
    557563##        else: 
    558564##            return Persistent.__getattribute__(self,attr) 
    559 ##     
     565## 
    560566##    def __setattr__(self, attr, value): 
    561567##        if self._p_setattr(attr, value): 
    562568##            return Persistent.__setattr__(self, attr, value) 
    563 ##         
     569## 
    564570##        #attribute always set in this instance 
    565571##        if attr in ["_mergeTo", "id", "_mergeFrom"]: 
    566572##            Persistent.__setattr__(self, attr, value) 
    567 ##         
     573## 
    568574##        elif hasattr(self,"_mergeTo") and Persistent.__getattribute__(self,"_mergeTo") != None: 
    569575##            Persistent.__getattribute__(self,"_mergeTo").__setattr__(attr, value) 
    570576##        else: 
    571577##            Persistent.__setattr__(self, attr, value) 
    572      
     578 
    573579    def mergeTo(self, av): 
    574580        if av: 
     
    577583            self._mergeTo.unmergeFrom(self) 
    578584        self._mergeTo = av 
    579      
     585 
    580586    def getMergeTo(self): 
    581587        try: 
     
    584590            self._mergeTo = None 
    585591        return self._mergeTo 
    586      
     592 
    587593    def isMerged(self): 
    588594        if self.getMergeTo(): 
    589595            return True 
    590596        return False 
    591      
     597 
    592598    def mergeFrom(self, av): 
    593599        if not av in self.getMergeFromList(): 
    594600            self._mergeFrom.append(av) 
    595601            self._p_changed = 1 
    596      
     602 
    597603    def unmergeFrom(self, av): 
    598604        if av in self.getMergeFromList(): 
    599605            self._mergeFrom.remove(av) 
    600606            self._p_changed = 1 
    601      
     607 
    602608    def getMergeFromList(self): 
    603609        try: 
     
    606612            self._mergeFrom = [] 
    607613        return self._mergeFrom 
    608      
     614 
    609615    def getKey( self ): 
    610616        return self.key 
     
    613619        self.linkedTo = copy(self.linkedToBase) 
    614620        self._p_changed = 1 
    615      
     621 
    616622    def getLinkedTo(self): 
    617623        try: 
     
    620626            self.resetLinkedTo() 
    621627            return self.linkedTo 
    622      
     628 
    623629    def getTimedLinkedEvents(self): 
    624630        try: 
     
    627633            self.resetTimedLinkedEvents() 
    628634            return self.timedLinkedEvents 
    629          
     635 
    630636    def resetTimedLinkedEvents(self): 
    631          
     637 
    632638        ltt = self.getLinkedTo() 
    633          
     639 
    634640        self.timedLinkedEvents = TimedLinkedEvents() 
    635              
     641 
    636642        for registrantRole in ltt['registration']: 
    637643            for registrant in ltt['registration'][registrantRole]: 
    638644                self.timedLinkedEvents.addFuture(registrant.getConference(),registrantRole) 
    639              
    640         for confRole in ltt['conference']:             
     645 
     646        for confRole in ltt['conference']: 
    641647            for conf in ltt['conference'][confRole]: 
    642648                self.timedLinkedEvents.addFuture(conf,confRole) 
    643                      
     649 
    644650 
    645651    def updateLinkedTo(self): 
     
    651657                if role not in self.linkedTo[type].keys(): 
    652658                    self.linkedTo[type][role] = [] 
    653      
     659 
    654660    def linkTo(self, obj, role): 
    655661        self.updateLinkedTo() 
     
    661667                    self.linkedTo["category"][role].append(obj) 
    662668                    self._p_changed = 1 
    663          
     669 
    664670        elif isinstance(obj, MaKaC.conference.Conference): 
    665671            if not role in self.linkedTo["conference"].keys(): 
     
    668674                if not obj in self.linkedTo["conference"][role]: 
    669675                    self.linkedTo["conference"][role].append(obj) 
    670                      
     676 
    671677                    # add directly to the time-ordered list 
    672678                    self.getTimedLinkedEvents().addFuture(obj, role) 
    673                      
    674                     self._p_changed = 1 
    675              
     679 
     680                    self._p_changed = 1 
     681 
    676682        elif isinstance(obj, MaKaC.conference.Session): 
    677683            if not role in self.linkedTo["session"].keys(): 
     
    681687                    self.linkedTo["session"][role].append(obj) 
    682688                    self._p_changed = 1 
    683          
     689 
    684690        elif isinstance(obj, MaKaC.conference.Contribution): 
    685691            if not role in self.linkedTo["contribution"].keys(): 
     
    689695                    self.linkedTo["contribution"][role].append(obj) 
    690696                    self._p_changed = 1 
    691          
     697 
    692698        elif isinstance(obj, MaKaC.conference.Track): 
    693699            if not role in self.linkedTo["track"].keys(): 
     
    697703                    self.linkedTo["track"][role].append(obj) 
    698704                    self._p_changed = 1 
    699          
     705 
    700706        elif isinstance(obj, MaKaC.conference.Material): 
    701707            if not role in self.linkedTo["material"].keys(): 
     
    705711                    self.linkedTo["material"][role].append(obj) 
    706712                    self._p_changed = 1 
    707          
     713 
    708714        elif isinstance(obj, MaKaC.conference.Resource): 
    709715            if not role in self.linkedTo["resource"].keys(): 
     
    713719                    self.linkedTo["resource"][role].append(obj) 
    714720                    self._p_changed = 1 
    715          
     721 
    716722        elif isinstance(obj, MaKaC.review.Abstract): 
    717723            if not role in self.linkedTo["abstract"].keys(): 
     
    721727                    self.linkedTo["abstract"][role].append(obj) 
    722728                    self._p_changed = 1 
    723          
     729 
    724730        elif isinstance(obj, MaKaC.registration.Registrant): 
    725731            if not role in self.linkedTo["registration"].keys(): 
     
    728734                if not obj in self.linkedTo["registration"][role]: 
    729735                    self.linkedTo["registration"][role].append(obj) 
    730                      
     736 
    731737                     # add directly to the time-ordered list 
    732738                    self.getTimedLinkedEvents().addFuture(obj.getConference(), role) 
    733                      
    734                     self._p_changed = 1 
    735          
     739 
     740                    self._p_changed = 1 
     741 
    736742        elif isinstance(obj, MaKaC.common.timerExec.Alarm): 
    737743            if not role in self.linkedTo["alarm"].keys(): 
     
    741747                    self.linkedTo["alarm"][role].append(obj) 
    742748                    self._p_changed = 1 
    743          
     749 
    744750        elif isinstance(obj, MaKaC.user.Group): 
    745751            if not role in self.linkedTo["group"].keys(): 
     
    749755                    self.linkedTo["group"][role].append(obj) 
    750756                    self._p_changed = 1 
    751          
     757 
    752758        elif isinstance(obj, MaKaC.evaluation.Submission): 
    753759            if not role in self.linkedTo["evaluation"].keys(): 
     
    757763                    self.linkedTo["evaluation"][role].append(obj) 
    758764                    self._p_changed = 1 
    759          
     765 
    760766    def getLinkTo( self, type, role ): 
    761767        self.updateLinkedTo() 
     
    774780                    self.linkedTo["category"][role].remove(obj) 
    775781                    self._p_changed = 1 
    776          
     782 
    777783        elif isinstance(obj, MaKaC.conference.Conference): 
    778784            if not role in self.linkedTo["conference"].keys(): 
     
    781787                if obj in self.linkedTo["conference"][role]: 
    782788                    self.linkedTo["conference"][role].remove(obj) 
    783                      
     789 
    784790                    # remove from the time-ordered list 
    785791                    self.getTimedLinkedEvents().delete(obj, role) 
    786                      
    787                     self._p_changed = 1 
    788          
     792 
     793                    self._p_changed = 1 
     794 
    789795        elif isinstance(obj, MaKaC.conference.Session): 
    790796            if not role in self.linkedTo["session"].keys(): 
     
    794800                    self.linkedTo["session"][role].remove(obj) 
    795801                    self._p_changed = 1 
    796          
     802 
    797803        elif isinstance(obj, MaKaC.conference.Contribution): 
    798804            if not role in self.linkedTo["contribution"].keys(): 
     
    802808                    self.linkedTo["contribution"][role].remove(obj) 
    803809                    self._p_changed = 1 
    804          
     810 
    805811        elif isinstance(obj, MaKaC.conference.Track): 
    806812            if not role in self.linkedTo["track"].keys(): 
     
    810816                    self.linkedTo["track"][role].remove(obj) 
    811817                    self._p_changed = 1 
    812          
     818 
    813819        elif isinstance(obj, MaKaC.conference.Material): 
    814820            if not role in self.linkedTo["material"].keys(): 
     
    818824                    self.linkedTo["material"][role].remove(obj) 
    819825                    self._p_changed = 1 
    820          
     826 
    821827        elif isinstance(obj, MaKaC.review.Abstract): 
    822828            if not role in self.linkedTo["abstract"].keys(): 
     
    826832                    self.linkedTo["abstract"][role].remove(obj) 
    827833                    self._p_changed = 1 
    828          
     834 
    829835        elif isinstance(obj, MaKaC.registration.Registrant): 
    830836            if not role in self.linkedTo["registration"].keys(): 
     
    833839                if obj in self.linkedTo["registration"][role]: 
    834840                    self.linkedTo["registration"][role].remove(obj) 
    835                      
     841 
    836842                    # remove from the time-ordered list 
    837843                    self.getTimedLinkedEvents().delete(obj.getConference(), role) 
    838                      
    839                     self._p_changed = 1 
    840          
     844 
     845                    self._p_changed = 1 
     846 
    841847        elif isinstance(obj, MaKaC.common.timerExec.Alarm): 
    842848            if not role in self.linkedTo["alarm"].keys(): 
     
    846852                    self.linkedTo["alarm"][role].remove(obj) 
    847853                    self._p_changed = 1 
    848          
     854 
    849855        elif isinstance(obj, MaKaC.user.Group): 
    850856            if not role in self.linkedTo["group"].keys(): 
     
    854860                    self.linkedTo["group"][role].remove(obj) 
    855861                    self._p_changed = 1 
    856          
     862 
    857863        elif isinstance(obj, MaKaC.evaluation.Submission): 
    858864            if not role in self.linkedTo["evaluation"].keys(): 
     
    869875            self.status = "activated" 
    870876            return self.status 
    871      
     877 
    872878    def setStatus( self, status ): 
    873879        statIdx = indexes.IndexesHolder().getById("status") 
     
    876882        self._p_changed = 1 
    877883        statIdx.indexUser( self ) 
    878      
     884 
    879885    def activateAccount( self ): 
    880886        self.setStatus("activated") 
     
    882888    def disabledAccount( self ): 
    883889        self.setStatus("disabled") 
    884      
     890 
    885891    def isActivated( self ): 
    886892        return self.status == "activated" 
    887      
     893 
    888894    def isDisabled( self ): 
    889895        return self.status == "disabled" 
    890      
     896 
    891897    def isNotConfirmed( self ): 
    892898        return self.status == "Not confirmed" 
     
    914920 
    915921    def getSurName(self): 
    916         return self.surName  
     922        return self.surName 
    917923 
    918924    @Retrieves('MaKaC.user.Avatar', 'familyName') 
    919925    def getFamilyName(self): 
    920         return self.surName  
     926        return self.surName 
    921927 
    922928    def getFullName(self): 
     
    925931            surName = "%s, "%self.getSurName().upper() 
    926932        return "%s%s"%(surName, self.getName()) 
    927      
     933 
    928934    @Retrieves('MaKaC.user.Avatar', 'name') 
    929935    def getStraightFullName(self): 
     
    9971003    #Fermi timezone awareness(end)  # 
    9981004    ################################# 
    999      
     1005 
    10001006    def addAddress(self, newAddress): 
    10011007        self.address.append(newAddress) 
     
    10151021    def setEmail(self, email, item=0 ): 
    10161022        self.email = email.strip().lower() 
    1017      
     1023 
    10181024    def getEmails( self ): 
    10191025        return [self.email] + self.getSecondaryEmails() 
    1020      
     1026 
    10211027    @Retrieves('MaKaC.user.Avatar', 'email') 
    10221028    def getEmail( self ): 
    10231029        return self.email 
    1024      
     1030 
    10251031    def getSecondaryEmails(self): 
    10261032        try: 
     
    10291035            self.secondaryEmails = [] 
    10301036            return self.secondaryEmails 
    1031      
     1037 
    10321038    def addSecondaryEmail(self, email): 
    10331039        self.getSecondaryEmails() #create attribute if not exist 
    1034          
     1040 
    10351041        if not email in self.secondaryEmails: 
    10361042            self.secondaryEmails.append(email) 
    10371043            self._p_changed = 1 
    1038      
     1044 
    10391045    def removeSecondaryEmail(self, email): 
    10401046        self.getSecondaryEmails() #create attribute if not exist 
    1041          
     1047 
    10421048        if email in self.secondaryEmails: 
    10431049            self.secondaryEmails.remove(email) 
    10441050            self._p_changed = 1 
    1045      
     1051 
    10461052    def setSecondaryEmails(self, emailList): 
    10471053        self.secondaryEmails = emailList 
     
    10501056        l=[self.email] + self.getSecondaryEmails() 
    10511057        return email.lower().strip() in l 
    1052                  
     1058 
    10531059 
    10541060    def addTelephone(self, newTel ): 
     
    10661072    def getTelephones(self): 
    10671073        return self.telephone 
    1068      
     1074 
    10691075    def getSecondaryTelephones(self): 
    10701076        return self.telephone[1:] 
     
    10841090    def getFaxes(self): 
    10851091        return self.fax 
    1086      
     1092 
    10871093    def addIdentity(self, newId): 
    10881094        if newId != None and (newId not in self.identities): 
    10891095            self.identities.append( newId ) 
    10901096            self._p_changed = 1 
    1091      
     1097 
    10921098    def removeIdentity(self, Id): 
    10931099        if Id in self.identities: 
     
    11081114            self.getRegistrants()[ n.getConference().getId() ] = n 
    11091115            self._p_changed = 1 
    1110      
     1116 
    11111117    def removeRegistrant(self, r): 
    11121118        if self.getRegistrants().has_key(r.getConference().getId()): 
    1113              
     1119 
    11141120            # unlink registrant from user 
    11151121            self.unlinkTo(r,'registrant') 
    1116              
     1122 
    11171123            del self.getRegistrants()[r.getConference().getId()] 
    11181124            self._p_changed = 1 
     
    11391145            return True 
    11401146        return False 
    1141      
     1147 
    11421148    def hasSubmittedEvaluation(self, evaluation): 
    11431149        for submission in evaluation.getSubmissions(): 
     
    11451151                return True 
    11461152        return False 
    1147      
     1153 
    11481154    def containsUser( self, avatar ): 
    11491155        return avatar == self 
     
    11551161    def canUserModify( self, user ): 
    11561162        return user == self or (user in AdminList.getInstance().getList()) 
    1157      
     1163 
    11581164    def getLocator( self ): 
    11591165        d = Locator() 
     
    11631169    def delete(self): 
    11641170        TrashCanManager().add(self) 
    1165      
     1171 
    11661172    def recover(self): 
    11671173        TrashCanManager().remove(self) 
    11681174 
    11691175    # Room booking related 
    1170      
     1176 
    11711177    def isMemberOfSimbaList( self, simbaListName ): 
    11721178        try: 
     
    12011207            return True 
    12021208        return False 
    1203      
     1209 
    12041210    def getRooms( self ): 
    12051211        """ 
    1206         Returns list of rooms (RoomBase derived objects) this  
     1212        Returns list of rooms (RoomBase derived objects) this 
    12071213        user is responsible for. 
    12081214        """ 
    12091215        from MaKaC.rb_location import CrossLocationQueries 
    12101216        from MaKaC.rb_room import RoomBase 
    1211          
     1217 
    12121218        myRooms = CrossLocationQueries.getRooms( reallyAllFast = True ) 
    12131219        myRooms = [ room for room in myRooms if room.isOwnedBy( self ) ] 
     
    12161222    def getReservations(self): 
    12171223        """ 
    1218         Returns list of ALL reservations (ReservationBase  
     1224        Returns list of ALL reservations (ReservationBase 
    12191225        derived objects) this user has ever made. 
    12201226        """ 
     
    12221228#        resvs = [guid.getReservation() for guid in self.resvGuids] 
    12231229#        return resvs 
    1224          
     1230 
    12251231        from MaKaC.rb_location import CrossLocationQueries 
    12261232        from MaKaC.rb_reservation import ReservationBase 
    1227          
     1233 
    12281234        resvEx = ReservationBase() 
    12291235        resvEx.createdBy = str( self.id ) 
    1230         resvEx.isCancelled = None  
     1236        resvEx.isCancelled = None 
    12311237        resvEx.isRejected = None 
    12321238        resvEx.isArchival = None 
    1233          
     1239 
    12341240        myResvs = CrossLocationQueries.getReservations( resvExample = resvEx ) 
    12351241        return myResvs 
     
    12371243    def getReservationsOfMyRooms( self ): 
    12381244        """ 
    1239         Returns list of ALL reservations (ReservationBase  
     1245        Returns list of ALL reservations (ReservationBase 
    12401246        derived objects) this user has ever made. 
    12411247        """ 
     
    12431249#        resvs = [guid.getReservation() for guid in self.resvGuids] 
    12441250#        return resvs 
    1245          
     1251 
    12461252        from MaKaC.rb_location import CrossLocationQueries 
    12471253        from MaKaC.rb_reservation import ReservationBase 
    1248          
     1254 
    12491255        myRooms = self.getRooms() # Just to speed up 
    1250          
     1256 
    12511257        resvEx = ReservationBase() 
    12521258        resvEx.isCancelled = None 
    12531259        resvEx.isRejected = None 
    12541260        resvEx.isArchival = None 
    1255          
     1261 
    12561262        myResvs = CrossLocationQueries.getReservations( resvExample = resvEx, rooms = myRooms ) 
    12571263        return myResvs 
    1258      
    1259      
     1264 
     1265 
    12601266    def getPersonalInfo(self): 
    12611267        try: 
     
    12641270            self.personalInfo = PersonalInfo() 
    12651271            return self.personalInfo 
    1266          
     1272 
    12671273    def getLang(self): 
    12681274        try: 
     
    12721278            self._lang = minfo.getLang() 
    12731279            return self._lang 
    1274           
     1280 
    12751281    def setLang(self, lang): 
    1276         self._lang =lang   
     1282        self._lang =lang 
     1283 
    12771284 
    12781285class AvatarHolder( ObjectHolder ): 
    1279     """Specialised ObjectHolder dealing with user (avatar) objects. Objects of  
    1280        this class represent an access point to Avatars of the application and  
    1281        provides different methods for accessing and retrieving them in several  
     1286    """Specialised ObjectHolder dealing with user (avatar) objects. Objects of 
     1287       this class represent an access point to Avatars of the application and 
     1288       provides different methods for accessing and retrieving them in several 
    12821289       ways. 
    12831290    """ 
     
    12921299            self.invalidateRoomManagerIdList() 
    12931300        return root["roomManagerIdList"] 
    1294      
     1301 
    12951302    def invalidateRoomManagerIdList( self ): 
    12961303        from MaKaC.rb_location import CrossLocationQueries 
    12971304        root = DBMgr.getInstance().getDBConnection().root() 
    12981305        allRooms = CrossLocationQueries.getRooms() 
    1299          
     1306 
    13001307        idList = [ room.getResponsible().id for room in allRooms ] 
    1301          
     1308 
    13021309        for room in allRooms: 
    13031310            if room.customAtts.get( 'Simba List' ): 
     
    13081315                    avatars = groups[0].getMemberList() 
    13091316                    idList += [ avatar.id for avatar in avatars ] 
    1310          
     1317 
    13111318        root["roomManagerIdList"] = idList 
    13121319 
     
    13501357                    auth = NiceAuthentication.NiceAuthenticator() 
    13511358 
    1352                      
     1359 
    13531360                    for email in dict.keys(): 
    1354                         # TODO and TOSTUDY: result.keys should be replace it with  
     1361                        # TODO and TOSTUDY: result.keys should be replace it with 
    13551362                        # l=[]; for av in result.values(): l.append(av.getAllEmails()) 
    13561363                        if not email in result.keys(): 
     
    13701377                                    result[av.getEmail()] = av 
    13711378        return result.values() 
    1372      
     1379 
    13731380    def _userMatchCriteria(self, av, criteria, exact): 
    13741381        if criteria.has_key("organisation"): 
     
    13841391                if not lMatch: 
    13851392                    return False 
    1386          
     1393 
    13871394        if criteria.has_key("surName"): 
    13881395            if criteria["surName"]: 
     
    13931400                    if not criteria["surName"].lower() in av.getSurName().lower(): 
    13941401                        return False 
    1395          
     1402 
    13961403        if criteria.has_key("name"): 
    13971404            if criteria["name"]: 
     
    14021409                    if not criteria["name"].lower() in av.getName().lower(): 
    14031410                        return False 
    1404          
     1411 
    14051412        if criteria.has_key("email"): 
    14061413            if criteria["email"]: 
     
    14161423                    return False 
    14171424        return True 
    1418          
    1419          
    1420              
    1421      
     1425 
     1426 
     1427 
     1428 
    14221429    def getById(self, id): 
    14231430        try: 
     
    14381445        dict["authenticator"].add(identity) 
    14391446        av.activateAccount() 
    1440          
     1447 
    14411448        #try: 
    14421449        self.add(av) 
    1443          
     1450 
    14441451        #except: 
    14451452        #    av = self.match({'email': av.getEmail()}, exact=1, forceWithoutExtAuth=True)[0] 
    14461453        return av 
    1447          
     1454 
    14481455 
    14491456    def add(self,av): 
     
    14601467            indexes.IndexesHolder().getById(i).indexUser(av) 
    14611468        return id 
    1462      
    1463      
     1469 
     1470 
    14641471    def mergeAvatar(self, prin, merged): 
    14651472        #replace merged by prin in all object where merged is 
     
    14801487                            cat.revokeAccess(merged) 
    14811488                            cat.grantAccess(prin) 
    1482                  
     1489 
    14831490            elif objType == "conference": 
    14841491                for role in links[objType].keys(): 
     
    15021509                            conf.removeAuthorizedSubmitter(merged) 
    15031510                            conf.addAuthorizedSubmitter(prin) 
    1504                  
     1511 
    15051512            if objType == "session": 
    15061513                for role in links[objType].keys(): 
     
    15171524                            ses.removeCoordinator(merged) 
    15181525                            ses.addCoordinator(prin) 
    1519                  
     1526 
    15201527            if objType == "contribution": 
    15211528                for role in links[objType].keys(): 
     
    15321539                            contrib.revokeSubmission(merged) 
    15331540                            contrib.grantSubmission(prin) 
    1534                  
     1541 
    15351542            if objType == "track": 
    15361543                for role in links[objType].keys(): 
     
    15391546                            track.removeCoordinator(merged) 
    15401547                            track.addCoordinator(prin) 
    1541                  
     1548 
    15421549            if objType == "material": 
    15431550                for role in links[objType].keys(): 
     
    15461553                            mat.revokeAccess(merged) 
    15471554                            mat.grantAccess(prin) 
    1548                  
     1555 
    15491556            if objType == "file": 
    15501557                for role in links[objType].keys(): 
     
    15531560                            mat.revokeAccess(merged) 
    15541561                            mat.grantAccess(prin) 
    1555                  
     1562 
    15561563            if objType == "abstract": 
    15571564                for role in links[objType].keys(): 
     
    15591566                        for abstract in links[objType][role]: 
    15601567                            abstract.setSubmitter(prin) 
    1561                  
     1568 
    15621569            if objType == "registration": 
    15631570                for role in links[objType].keys(): 
     
    15651572                        for reg in links[objType][role]: 
    15661573                            reg.setAvatar(prin) 
    1567                  
     1574 
    15681575            if objType == "alarm": 
    15691576                for role in links[objType].keys(): 
     
    15721579                            alarm.removeToUser(merged) 
    15731580                            alarm.addToUser(prin) 
    1574                  
     1581 
    15751582            if objType == "group": 
    15761583                for role in links[objType].keys(): 
     
    15791586                            group.removeMember(merged) 
    15801587                            group.addMember(prin) 
    1581                  
     1588 
    15821589            if objType == "evaluation": 
    15831590                for role in links[objType].keys(): 
     
    15901597                                #prin ditn't answered to the same evaluation as merger's. 
    15911598                                submission.setSubmitter(prin) 
    1592          
     1599 
    15931600        # remove merged from holder 
    15941601        self.remove(merged) 
     
    15981605        name = idxs.getById( 'name' ) 
    15991606        surName = idxs.getById( 'surName' ) 
    1600          
     1607 
    16011608        org.unindexUser(merged) 
    16021609        email.unindexUser(merged) 
    16031610        name.unindexUser(merged) 
    16041611        surName.unindexUser(merged) 
    1605          
     1612 
    16061613        # add merged email and logins to prin and merge users 
    16071614        for mail in merged.getEmails(): 
     
    16101617            id.setUser(prin) 
    16111618            prin.addIdentity(id) 
    1612          
     1619 
    16131620        merged.mergeTo(prin) 
    1614          
     1621 
    16151622        # reindex prin email 
    16161623        email.unindexUser(prin) 
    16171624        email.indexUser(prin) 
    1618      
     1625 
    16191626    def unmergeAvatar(self, prin, merged): 
    16201627        if not merged in prin.getMergeFromList(): 
    16211628            return False 
    16221629        merged.mergeTo(None) 
    1623          
     1630 
    16241631        idxs = indexes.IndexesHolder() 
    16251632        org = idxs.getById( 'organisation' ) 
     
    16271634        name = idxs.getById( 'name' ) 
    16281635        surName = idxs.getById( 'surName' ) 
    1629          
    1630          
     1636 
     1637 
    16311638        email.unindexUser(prin) 
    16321639        for mail in merged.getEmails(): 
    16331640            prin.removeSecondaryEmail(mail) 
    1634          
     1641 
    16351642        for id in merged.getIdentityList(): 
    16361643            prin.removeIdentity(id) 
    16371644            id.setUser(merged) 
    1638          
     1645 
    16391646        self.add(merged) 
    1640          
     1647 
    16411648        org.indexUser(merged) 
    16421649        email.indexUser(merged) 
    16431650        name.indexUser(merged) 
    16441651        surName.indexUser(merged) 
    1645          
     1652 
    16461653        email.indexUser(prin) 
    16471654        return True 
    1648      
     1655 
    16491656 
    16501657 
    16511658# ToDo: This class should ideally derive from TreeHolder as it is thought to 
    16521659#   be a index over the "Principal" objects i.e. it will be a top indexing of 
    1653 #   the contents of AvatarHolder and GroupHolder. This will allow to  
     1660#   the contents of AvatarHolder and GroupHolder. This will allow to 
    16541661#   transparently access to Principal objects from its id. To transparently 
    1655 #   index all the objects AvatarHolder and GroupHolder must override the  
     1662#   index all the objects AvatarHolder and GroupHolder must override the 
    16561663#   "add" method and, apart from their normal operation, include an adding call 
    16571664#   for the PrincipalHolder. 
    16581665# The problem is that I have experienced some troubles (it seems not to perform 
    16591666#   the adding of objects) while adding an object both to the AvatarHolder and 
    1660 #   to this one; so, for the time being, I will implement it in a "dirty" and  
     1667#   to this one; so, for the time being, I will implement it in a "dirty" and 
    16611668#   non-optimal way to be able to continue working, but the trouble must be 
    16621669#   investigated and a better solution found. 
    1663 # I'll keep the ObjectHolder interface so it will be easier afterwards to  
     1670# I'll keep the ObjectHolder interface so it will be easier afterwards to 
    16641671#   implement a more optimised solution (just this object needs to be modified) 
    16651672class PrincipalHolder: 
     
    16671674        self.__gh = GroupHolder() 
    16681675        self.__ah = AvatarHolder() 
    1669      
     1676 
    16701677    def getById( self, id ): 
    16711678        try: 
     
    16801687 
    16811688class LoginInfo: 
    1682      
     1689 
    16831690    def __init__(self, login, password): 
    16841691        self.setLogin( login ) 
     
    17011708 
    17021709class TimedLinkedEvents(Persistent): 
    1703      
     1710 
    17041711    def __init__(self): 
    17051712        self._past = [] 
    17061713        self._present = [] 
    17071714        self._future = [] 
    1708      
     1715 
    17091716    def addPast(self, event, relation): 
    17101717        self._past.append((event, relation)) 
    17111718        self._p_changed = 1 
    1712      
     1719 
    17131720    def addPresent(self, event, relation): 
    17141721        self._present.append((event, relation)) 
    17151722        self._p_changed = 1 
    1716          
     1723 
    17171724    def addFuture(self, event, relation): 
    17181725        self._future.append((event, relation)) 
    17191726        self._p_changed = 1 
    1720          
     1727 
    17211728    def getPast(self): 
    17221729        return self._past 
    1723      
     1730 
    17241731    def getPresent(self): 
    17251732        return self._present 
    1726          
     1733 
    17271734    def getFuture(self): 
    17281735        return self._future 
    1729          
     1736 
    17301737    def sync(self): 
    1731          
     1738 
    17321739        now = datetime.now() 
    1733          
     1740 
    17341741        # Let's check past events 
    1735          
     1742 
    17361743        for elem in self._past: 
    17371744            if elem[0].getEndDate() > now: 
     
    17391746                self._past.remove(elem) 
    17401747                self._p_changed = 1 
    1741                  
     1748 
    17421749        # pass "future" events to present and past 
    17431750        for elem in self._future[:]: 
    1744             # play consistent, iterate over a copy             
     1751            # play consistent, iterate over a copy 
    17451752            if elem[0].getStartDate() < now: 
    17461753                self.addPresent(elem[0],elem[1]) 
    17471754                self._future.remove(elem) 
    17481755                self._p_changed = 1 
    1749          
     1756 
    17501757        # pass present events to past or future 
    17511758        for elem in self._present[:]: 
     
    17591766                self._present.remove(elem) 
    17601767                self._p_changed = 1 
    1761                  
    1762  
    1763      
     1768 
     1769 
     1770 
    17641771    def deleteFromList(self, list, elem, role): 
    1765              
     1772 
    17661773            for tuple in list[:]: 
    17671774                if (tuple == (elem, role)): 
     
    17701777                    return True 
    17711778            return False 
    1772      
     1779 
    17731780    def delete(self, elem, role): 
    1774          
     1781 
    17751782        if (self.deleteFromList(self._past, elem, role) or 
    17761783            self.deleteFromList(self._present, elem, role) or 
     
    17801787 
    17811788        return False 
    1782      
     1789 
    17831790class PersonalInfo(Persistent): 
    1784      
     1791 
    17851792    def __init__(self): 
    17861793        self._basket = PersonalBasket() 
    17871794        self._tabAdvancedMode = False # Basic set of tabs 
    17881795        self._p_changed = 1 
    1789      
     1796 
    17901797    @Retrieves('MaKaC.user.PersonalInfo', 'tabAdvancedMode') 
    17911798    def getTabAdvancedMode( self ): 
     
    17971804            self.setTabAdvancedMode(False) # (default) 
    17981805        return self._tabAdvancedMode 
    1799           
     1806 
    18001807    @Updates('MaKaC.user.PersonalInfo', 'tabAdvancedMode') 
    1801     def setTabAdvancedMode( self, mode ):         
     1808    def setTabAdvancedMode( self, mode ): 
    18021809        self._tabAdvancedMode = mode 
    18031810        self._p_changed = 1 
    18041811 
    1805          
     1812 
    18061813    def getBasket(self): 
    18071814        return self._basket 
    1808      
     1815 
    18091816class PersonalBasket(Persistent): 
    18101817 
     
    18181825        self._userGroups = {} 
    18191826        self._p_changed = 1 
    1820      
     1827 
    18211828    def __findAdequateDict(self, element): 
    1822          
     1829 
    18231830        if (type(element) == MaKaC.conference.Conference): 
    18241831            return self._events 
     
    18331840        else: 
    18341841            raise Exception( _("Unknown Element Type")) 
    1835      
    1836     def addElement(self, element):              
     1842 
     1843    def addElement(self, element): 
    18371844        dict = self.__findAdequateDict(element) 
    18381845        if (not dict.has_key(element.getId())): 
     
    18411848            return True 
    18421849        return False 
    1843              
     1850 
    18441851    def deleteElement(self, element): 
    18451852        res = self.__findAdequateDict(element).pop(element.getId(),None) 
    1846          
     1853 
    18471854        if res == None: 
    18481855            return False 
    1849          
     1856 
    18501857        self._p_changed = 1 
    18511858        return True 
    1852          
    1853     def hasElement(self, element):         
     1859 
     1860    def hasElement(self, element): 
    18541861        return self.__findAdequateDict(element).has_key(element.getId()) 
    1855      
     1862 
    18561863    def hasUserId(self, id): 
    18571864        return self._users.has_key(id) 
    1858      
     1865 
    18591866    def getUsers(self): 
    18601867        return self._users 
    1861          
    1862          
    1863          
     1868 
     1869 
     1870 
  • indico/MaKaC/webinterface/tpls/AdminPluginsOptionList.tpl

    rfd0c10 r56571d6  
    1 <% from MaKaC.common.PickleJar import DictPickler %> 
     1<% from MaKaC.fossils.user import IAvatarDetailedFossil %> 
    22 
    33    <% if ObjectType == "PluginType" :%> 
     
    77    <form method="post" action="<%= urlHandlers.UHAdminPluginsSaveOptions.getURL(Object, subtab = Index) %>" > 
    88    <% end %> 
    9      
     9 
    1010    <table> 
    1111        <% for option in Object.getOptionList(sorted = True, includeOnlyEditable = True, includeOnlyVisible = True): %> 
     
    2121                    <% name = Object.getOwner().getName() + '.' + Object.getName() + "." + option.getName() %> 
    2222                <% end %> 
    23                  
     23 
    2424                <% if option.getType() == "users": %> 
    2525                    <div id="userList<%=name%>"> 
    2626                    </div> 
    27                      
     27 
    2828                    <script type="text/javascript"> 
    2929                        var newPersonsHandler = function(userList, setResult) { 
     
    6161                            ); 
    6262                        } 
    63                          
     63 
    6464                        var uf = new UserListField('PluginPeopleListDiv', 'PluginPeopleList', 
    65                                                    <%= jsonEncode(DictPickler.pickle(option.getValue())) %>, 
     65                                                   <%= jsonEncode(fossilize(option.getValue(), IAvatarDetailedFossil)) %>, 
    6666                                                   null, 
    6767                                                   <%=jsonEncode(Favorites)%>, 
     
    130130                        <% value = str(option.getValue()) %> 
    131131                    <% end %> 
    132                                      
     132 
    133133                    <% if option.getType() == bool: %> 
    134134                        <% checked = '' %> 
     
    141141                    <input name="<%= name %>" type="text" size="50" value="<%= value %>"> 
    142142                    <% end %> 
    143                      
     143 
    144144                    <% if option.hasActions(): %> 
    145145                        <% for action in option.getActions(): %> 
     
    169169        </tr> 
    170170        <% end %> 
    171          
     171 
    172172        <% for option in Object.getOptionList(sorted = True, includeOnlyNonEditable = True, includeOnlyVisible = True): %> 
    173173        <tr> 
     
    185185        </tr> 
    186186        <% end %> 
    187          
     187 
    188188        <% if Object.hasAnyActions(includeOnlyNonAssociated = True): %> 
    189189        <tr> 
  • indico/MaKaC/webinterface/tpls/UserBaskets.tpl

    r342258 r56571d6  
    77</div> 
    88 
    9 <script type="text/javascript">  
     9<script type="text/javascript"> 
    1010 
    11     var userList = <%= offlineRequest(self._rh, 'user.favorites.listUsers', {}) %>;             
     11    var userList = <%= offlineRequest(self._rh, 'user.favorites.listUsers', {"detailLevel": "medium"}) %>; 
    1212    var removeUser = function(user, setResult){ 
    13          
     13 
    1414        jsonRpc(Indico.Urls.JsonRpcService, "user.favorites.removeUser", 
    1515        {value: [{'id': user.get('id')}]}, function(result, error){ 
     
    4545                    true, false, false, 
    4646                    addUsers, userListNothing, removeUser) 
    47      
     47 
    4848    $E('basketContainer').set(uf.draw()); 
    4949 
  • indico/htdocs/js/indico/Management/Users.js

    r57b5d5 r56571d6  
    506506                    each(newUserIds, function(newUserId) { 
    507507                        var newUser = self.allOptions.get(newUserId).clone(); 
    508                         if (self.userList.get("existingAv"+newUserId)) { 
    509                             self.userList.set("existingAv"+newUserId, null); 
    510                         } 
     508 
    511509                        self.newProcess([newUser], function(result) { 
    512510                            if (result) { 
     
    570568 
    571569         each(initialUsers, function(user){ 
    572              if (any(user.isAvatar, false)) { 
     570             if (any(user._type, null) === 'Avatar') { 
    573571                 self.userList.set('existingAv' + user.id, $O(user)); 
    574572             } else { 
  • tests/MaKaC_tests/common_tests/testFossilize.py

    r626e2c r56571d6  
    66    pass 
    77 
    8 class ISimpleFossil1(IFossil): 
     8class ISimpleFossil1Fossil(IFossil): 
    99    def getB(self): 
    1010        pass 
     
    1414    getC.convert = lambda x: x.upper() 
    1515 
    16 class ISimpleFossil2(IFossil): 
     16class ISimpleFossil2Fossil(IFossil): 
    1717    def getA(self): 
    1818        pass 
     
    2222    getC.convert = lambda x: x.title() 
    2323 
    24 class IComplexFossil1(IFossil): 
     24class IComplexFossil1Fossil(IFossil): 
    2525    def getSimpleInstance(self): 
    2626        pass 
    27     getSimpleInstance.result = ISimpleFossil2 
     27    getSimpleInstance.result = ISimpleFossil2Fossil 
    2828 
    29 class IComplexFossil2(IFossil): 
     29class IComplexFossil2Fossil(IFossil): 
    3030    def getSimpleInstance(self): 
    3131        pass 
    32     getSimpleInstance.result = ISimpleFossil2 
    33     getSimpleInstance.convert = lambda x: list(x.values()) 
     32    getSimpleInstance.result = ISimpleFossil2Fossil 
     33    getSimpleInstance.convert = lambda x: sorted(x.values()) 
    3434    getSimpleInstance.name = 'mySimpleInstance' 
    35      
     35 
    3636class IDynamicFossil(IFossil): 
    3737    def getA(self): 
     
    4444class SimpleClass(Fossilizable): 
    4545 
    46     fossilizes(ISimpleFossil1, ISimpleFossil2) 
    47      
     46    fossilizes(ISimpleFossil1Fossil, ISimpleFossil2Fossil) 
     47 
    4848    def __init__(self, a, b, c): 
    4949        self.a = a 
     
    6565class ComplexClass(Fossilizable): 
    6666 
    67     fossilizes(IComplexFossil1, IComplexFossil2) 
     67    fossilizes(IComplexFossil1Fossil, IComplexFossil2Fossil) 
    6868 
    6969    def getSimpleInstance(self): 
    7070        return SimpleClass(1,'foo','bar') 
    71      
    72      
     71 
     72 
    7373class ConversionClass(object): 
    7474    @classmethod 
    7575    def multiply(cls, number, factor): 
    7676        return number * factor 
    77      
     77 
    7878def sum(number, numberToBeAdded): 
    7979    return number + numberToBeAdded 
    8080 
    8181 
    82 class IFossilWithConversion2(IFossil): 
     82class IWithConversion2Fossil(IFossil): 
    8383    def getA(self): 
    8484        pass 
    8585    getA.convert = sum 
    8686 
    87 class IFossilWithConversion(IFossil): 
     87class IWithConversionFossil(IFossil): 
    8888    def getA(self): 
    8989        pass 
     
    9191    def getList(self): 
    9292        pass 
    93     getList.result = IFossilWithConversion2 
     93    getList.result = IWithConversion2Fossil 
    9494 
    9595class AnotherClass(Fossilizable): 
    96     fossilizes(IFossilWithConversion) 
     96    fossilizes(IWithConversionFossil) 
    9797    def __init__(self, a , list = []): 
    9898        self.a = a 
     
    104104 
    105105class AnotherChildrenClass(Fossilizable): 
    106     fossilizes(IFossilWithConversion2) 
     106    fossilizes(IWithConversion2Fossil) 
    107107    def __init__(self, a): 
    108108        self.a = a 
     
    136136 
    137137    def testFossilizingSimpleClass(self): 
    138         self.assertEquals(self.s.fossilize(ISimpleFossil1), 
    139                           {"b": "a", "c":"FOO"}) 
    140         self.assertEquals(self.s.fossilize(ISimpleFossil2), 
    141                           {"a":1, "c":"Foo"}) 
     138        self.assertEquals(self.s.fossilize(ISimpleFossil1Fossil), 
     139                          {'_type':'SimpleClass', '_fossil':'simpleFossil1', "b": "a", "c":"FOO"}) 
     140        self.assertEquals(self.s.fossilize(ISimpleFossil2Fossil), 
     141                          {'_type':'SimpleClass', '_fossil':'simpleFossil2', "a":1, "c":"Foo"}) 
    142142 
    143143    def testFossilizingComplexClass(self): 
    144         self.assertEquals(self.c.fossilize(IComplexFossil1), 
    145                           {'simpleInstance': {'a': 1, 'c':'Bar'}}) 
     144        self.assertEquals(self.c.fossilize(IComplexFossil1Fossil), 
     145                          {'_type':'ComplexClass', '_fossil':'complexFossil1', 'simpleInstance': {'_type':'SimpleClass', '_fossil':'simpleFossil2', 'a': 1, 'c':'Bar'}}) 
    146146 
    147147    def testFossilizingComplexClassWithConversion(self): 
    148         self.assertEquals(self.c.fossilize(IComplexFossil2), 
    149                           {'mySimpleInstance': [1,'Bar']}) 
     148        self.assertEquals(self.c.fossilize(IComplexFossil2Fossil), 
     149                          {'_type':'ComplexClass', '_fossil':'complexFossil2', 'mySimpleInstance': [1, 'Bar', 'SimpleClass', 'simpleFossil2']}) 
    150150 
    151151    def testFossilizingWithInheritance(self): 
    152152        d = DerivedClass(2,'b','bar') 
    153         self.assertEquals(d.fossilize(ISimpleFossil1), 
    154                           {"b": "b", "c":"BAR"}) 
    155          
     153        self.assertEquals(d.fossilize(ISimpleFossil1Fossil), 
     154                          {"_type": "DerivedClass", "_fossil": "simpleFossil1", "b": "b", "c":"BAR"}) 
     155 
    156156    def testFossilizeList(self): 
    157157        d1 = SimpleClass(1, 'a', 'aaa') 
    158158        d2 = SimpleClass(2, 'b', 'bbb') 
    159159        l = [d1, d2] 
    160         self.assertEquals(fossilize(l, ISimpleFossil1), 
    161                           [{'b': 'a', 'c': 'AAA'}, {'b': 'b', 'c': 'BBB'}] ) 
    162          
     160        self.assertEquals(fossilize(l, ISimpleFossil1Fossil), 
     161                          [{'_type':'SimpleClass', '_fossil':'simpleFossil1', 'b': 'a', 'c': 'AAA'}, {'_type':'SimpleClass', '_fossil':'simpleFossil1', 'b': 'b', 'c': 'BBB'}] ) 
     162 
    163163    def testFossilizingWithDynamicFossil(self): 
    164164        d = DerivedClass(2, 'bababa', 'bar') 
    165         self.assertEquals(d.fossilize(ISimpleFossil1), 
    166                           {"b": "bababa", "c":"BAR"}) 
     165        self.assertEquals(d.fossilize(ISimpleFossil1Fossil), 
     166                          {'_type':'DerivedClass', '_fossil':'simpleFossil1', "b": "bababa", "c":"BAR"}) 
    167167        self.assertEquals(d.fossilize(IDynamicFossil), 
    168                           {"a": 2, "b":"BABABA"}) 
    169          
     168                          {'_type':'DerivedClass', '_fossil':'dynamic', "a": 2, "b":"BABABA"}) 
     169 
    170170    def testFossilizingWithConversion(self): 
    171171        a1 = AnotherChildrenClass(10) 
    172172        a2 = AnotherChildrenClass(20) 
    173173        aFather = AnotherClass(1000, [a1, a2]) 
    174         self.assertEquals(aFather.fossilize(IFossilWithConversion, factor = 10, numberToBeAdded = 1), 
    175                           {'a': 10000, 'list': [{'a': 11}, {'a': 21}]}) 
     174        self.assertEquals(aFather.fossilize(IWithConversionFossil, factor = 10, numberToBeAdded = 1), 
     175                          {'_type':'AnotherClass', '_fossil':'withConversion', 'a': 10000, 'list': [{'_type': 'AnotherChildrenClass', '_fossil': 'withConversion2', 'a': 11}, {'_type': 'AnotherChildrenClass', '_fossil': 'withConversion2', 'a': 21}]}) 
    176176 
    177177 
Note: See TracChangeset for help on using the changeset viewer.