changeset 48:f59bdd5f4890

Merge with 5c6ad316e1ceef48e323907ab81dd50e7ef743b2
author dwinter
date Mon, 29 Apr 2013 16:02:24 +0200
parents 225179dfd892 (current diff) 5c6ad316e1ce (diff)
children e40ff9829108 e718d9a72f19
files MPIWGStaff.py zpt/staff/member_index_html.zpt
diffstat 9 files changed, 519 insertions(+), 358 deletions(-) [+]
line wrap: on
line diff
--- a/HashTree.py	Mon Apr 29 16:01:24 2013 +0200
+++ b/HashTree.py	Mon Apr 29 16:02:24 2013 +0200
@@ -44,10 +44,7 @@
             else:
                 sub = [self.value]
                 
-            keys = self.children.keys()
-            keys.sort()
-            for k in keys:
-                #logging.debug("getSubtreeList: k=%s sub=%s"%(k,sub))
+            for k in sorted(self.children.keys()):
                 sub.extend(self.children.get(k).getSubtreeAsList())
                 
             return sub
@@ -59,29 +56,55 @@
             return "(%s:%s)"%(self.key, self.value)
         else:
             sub = "(%s:%s):["%(self.key, self.value)
-            keys = self.children.keys()
-            keys.sort()
-            for k in keys:
+            for k in sorted(self.children.keys()):
                 sub += self.children.get(k).getSubtreeAsText()
                 sub += ", "
                 
             return sub + "] "
 
+    
+
 
 class HashTree:
     """Tree using dictionaries"""
     
+    # root HashTreeNode
     root = None
+    # separator by which key strings are split into parts
     keySeparator = '.'
+    # function applied to key parts
     keyFn = None
     
     def __init__(self, keySeparator='.', keyFn=None):
-        """creates a HashTree"""
+        """creates a HashTree.
+        
+        @param keySeparator string by which key strings are split into parts
+        @param keyFn function applied to key parts (e.g. int if key parts are numbers)
+        """
         self.root = HashTreeNode()
         self.keySeparator = keySeparator
         self.keyFn = keyFn
     
 
+    def _splitkey(self, key):
+        """returns a list of key parts"""
+        if isinstance(key, basestring):
+            # its a string - split
+            keys = key.split(self.keySeparator)
+            if self.keyFn is not None:
+                keys = [self.keyFn(k) for k in keys]
+                
+        elif isinstance(key, list):
+            # its a list - keep
+            keys = key
+            
+        else:
+            # not a list - wrap in list
+            keys = [key]
+            
+        return keys
+
+
     def getNode(self, key):
         """gets node under key from the tree.
         key can be sequence of key string or a single string using keySeparator.
@@ -90,22 +113,11 @@
         if key is None:
             return self.root
 
-        if isinstance(key, basestring):
-            keys = key.split(self.keySeparator)
-            if self.keyFn is not None:
-                keys = [self.keyFn(k) for k in keys]
-                
-        elif isinstance(key, list):
-            keys = key
-            
-        else:
-            keys = [key]
+        keys = self._splitkey(key)
             
         node = self.root
         for k in keys:
-            #logging.debug("getNode node=%s k=%s"%(node, repr(k)))
             node = node.getNode(k)
-            #logging.debug("getNode got:%s"%(node))
             if node is None:
                 return None
             
@@ -127,16 +139,7 @@
         """adds value under key to the tree.
         key can be sequence of key string or a single string using keySeparator.
         """
-        #logging.debug("add(key=%s, value=%s)"%(repr(key), value))
-        if isinstance(key, basestring):
-            keys = key.split(self.keySeparator)
-            if self.keyFn is not None:
-                keys = [self.keyFn(k) for k in keys]
-        elif isinstance(key, list):
-            keys = key
-        else:
-            keys = [key]
-            
+        keys = self._splitkey(key)
         node = self.root
         for k in keys:
             nextnode = node.getNode(k)
@@ -147,6 +150,34 @@
             node = nextnode
             
         node.value = value
+
         
-    
+    def getChildrenOf(self, key):
+        """returns the list of child (values) of the node under key."""
+        node = self.getNode(key)
+        if node.children is None:
+            return []
+        else:
+            # sort children by key
+            childlist = sorted(node.children.items(), key=lambda x:x[0])
+            # and return the values
+            return [n.value for (k, n) in childlist if n.value is not None]
+
+
+    def getAncestorsOf(self, key):
+        """returns the list of ancestor (values) of the node under key.
+        ordered from root up."""
+        keys = self._splitkey(key)
+        node = self.root
+        ancestors = []
+        for k in keys:
+            if node.value is not None:
+                ancestors.append(node.value)
+                
+            node = node.getNode(k)
+            if node is None:
+                return ancestors
+            
+        return ancestors
+
         
--- a/MPIWGDepartment.py	Mon Apr 29 16:01:24 2013 +0200
+++ b/MPIWGDepartment.py	Mon Apr 29 16:02:24 2013 +0200
@@ -91,12 +91,12 @@
         """returns the name of the director"""
         return self.director
     
-    def getDirectorId(self):
-        """returns the id of the director"""
+    def getDirectorUsername(self):
+        """returns the username of the director"""
         return self.director_id
 
-    def getProjectId(self):
-        """return the project id"""
+    def getProjectNumber(self):
+        """return the project number"""
         return self.project_id
     
     def getUrl(self, baseUrl=None):
@@ -141,8 +141,9 @@
 
     def getProject(self):
         """returns the default project"""
-        proj = self.getProjects(count=1, onlyActive=1, onlyArchived=1)
-        return getAt(proj, 0, None) 
+        pf = self.getMPIWGRoot().getProjectFolder()
+        proj = pf.getProject(projectNumber=self.getProjectNumber())
+        return proj
 
     def getProjects(self, count=0, onlyActive=0, onlyArchived=0):
         """returns a list of projects of this department.
@@ -155,8 +156,8 @@
         onlyArchived = 1 : current projects
         onlyArchived = 2 : archived projects
         """
-        pf = self.en.getProjectFolder()
-        projects = pf.getProjectsAsList(self.getProjectId(), active=onlyActive, archived=onlyArchived)
+        pf = self.getMPIWGRoot().getProjectFolder()
+        projects = pf.getProjectsAsList(self.getProjectNumber(), active=onlyActive, archived=onlyArchived)
         #logging.debug("getProjects projects=%s"%repr(projects))
         if count > 0:
             return projects[:count]
@@ -168,7 +169,7 @@
         self.weight=weight
         if RESPONSE is not None:
             RESPONSE.redirect('manage_main')
-
+            
     def changeMPIWGDepartment(self, title=None, weight=None, project_id=None, director=None, director_id=None, 
                               type_en=None, type_de=None, title_en=None, title_de=None, RESPONSE=None):
         """change everything"""
--- a/MPIWGProjects.py	Mon Apr 29 16:01:24 2013 +0200
+++ b/MPIWGProjects.py	Mon Apr 29 16:02:24 2013 +0200
@@ -1,7 +1,7 @@
 """This contains the class MPIWG Projects
 for organizing and maintaining the different project pages
 
-$author dwinter   - last change  26.06.2008
+$author dwinter 26.06.2008
 
 """
 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
@@ -51,25 +51,7 @@
 import transaction
 
 
-# TODO: fix this
-definedFields = ['WEB_title', 
-                 'xdata_01', 
-                 'xdata_02', 
-                 'xdata_03', 
-                 'xdata_04', 
-                 'xdata_05', 
-                 'xdata_06', 
-                 'xdata_07', 
-                 'xdata_08', 
-                 'xdata_09', 
-                 'xdata_10', 
-                 'xdata_11', 
-                 'xdata_12', 
-                 'xdata_13', 
-                 'WEB_project_header', 
-                 'WEB_project_description', 
-                 'WEB_related_pub']
-
+# TODO: better names for the fields
 fieldLabels = {'WEB_title':'WEB_Title',
          'xdata_01':'Responsible Scientists',
          'xdata_02':'Department',
@@ -88,6 +70,8 @@
          'WEB_project_description':'WEB_project_description',
          'WEB_related_pub':'WEB_related_pub'}
 
+definedFields = fieldLabels.keys() # TODO: should this be sorted?
+
 checkFields = ['xdata_01']
 
 
@@ -146,7 +130,6 @@
             logging.debug("MPIWGProject_publication - end FALSE ")
             self._v_hasLinkToBookPage = True
             return False
-        
     
     
     def getImageUrls(self, mode="not_cached"):
@@ -175,6 +158,7 @@
             return []
         self._v_imageUrls = ret[0:]
         return ret
+
         
     def editPublication(self, text=None, image1=None, image2=None, description=None, link=None, RESPONSE=None):
         """edit a publication"""
@@ -206,12 +190,12 @@
             else:
                 nO = Image('publicationImage2', '', image2)
                 self._setObject('publicationImage2', nO)
-                   
-
+ 
         self.ZCacheable_invalidate()
         if RESPONSE:
             self.redirect(RESPONSE, "../managePublications")
 
+
 class MPIWGProject_relatedProject(Folder):
     """publications object fuer project"""
 
@@ -237,10 +221,6 @@
         if (not link):
             pt = PageTemplateFile(os.path.join(package_home(globals()), 'zpt', 'edit_relatedProjectForm.zpt')).__of__(self)
             return pt()
-
-       
-       
-      
         
         # hole die id des projektes
         splitted = link.split("/")
@@ -254,10 +234,6 @@
         
         if object == None:
             self.redirect(RESPONSE, 'errorRelatedProjects?link=' + link)
-        
-      
-
-        
        
         self.orginallink = link[0:]
         self.objid = objid[0:]
@@ -270,6 +246,7 @@
         if RESPONSE:
             self.redirect(RESPONSE, "../manageRelatedProjects")
 
+
 class MPIWGProject_image(Image):
     """Images for Projects"""
 
@@ -329,6 +306,8 @@
     editForm = PageTemplateFile('zpt/project/edit_description', globals())
     edit_template = PageTemplateFile('zpt/project/edit_template', globals())
     project_template = PageTemplateFile('zpt/project/project_template', globals())
+    # TODO: this should go away
+    extendedBibliography = PageTemplateFile('zpt/project/extendedBibliography_template', globals())
 
     # TODO: compat
     edit_MPIWGProject_main = edit_template
@@ -434,23 +413,8 @@
         
         return ret;
     
-    def hasRelatedDigitalSources(self):
-        """test ob es digital sources gibt"""
-        
-        
-        ret = (self.getContent('xdata_11').lstrip().rstrip() == '')
-            
-      
-      
-        return not ret;
-            
-        
-    
-    
     def copyImageToMargin(self, RESPONSE=None):  
         """copy inline images to marginal images"""
-        
-        
         # getImages from WEB_project_description
         description = self.getContent('WEB_project_description')
         
@@ -480,12 +444,10 @@
                     tmp = splitted[1].split("</p>")
                     imageCaptions.append(tmp[0].encode('utf-8'))
 
-
                 else:
                     # keine caption
 
                     imageCaptions.append("")
-  
      
         # eintragen:
         for imageURL in imageURLs:
@@ -513,8 +475,8 @@
             obj.update_data(data)
         
         if RESPONSE:
-        
             self.redirect(RESPONSE, 'manageImages')
+
             
     def manageImages(self, imageName=None, op=None):
         """managage images"""
@@ -606,7 +568,6 @@
         """erzeuge erweiterte publications liste"""
         pl = BibliographyManager("publicationList", "", "institutsbiblio", self.connection_id)
         self._setObject("publicationList", pl)
-        
     
         zt = ZopePageTemplate('index.html')
         pl._setObject('index.html', zt)
@@ -614,12 +575,11 @@
                                               'zpt/showExtendedProjectBibliography.zpt')
         text = open(default_content_fn).read()
         zt.pt_edit(text, 'text/html')
-
     
         if RESPONSE:
             self.redirect(RESPONSE, "managePublications")
 
-            
+
     def getPublications(self):
         """get all Publications"""
         def sort_images(x, y):
@@ -727,7 +687,6 @@
             self.manage_delObjects([id])
             self.ZCacheable_invalidate()
             if RESPONSE:
-        
                 self.redirect(RESPONSE, 'managePublications')
               
     def deleteRelatedProject(self, id, RESPONSE=None):
@@ -735,7 +694,6 @@
             self.manage_delObjects([id])
             self.ZCacheable_invalidate()
             if RESPONSE:
-        
                 self.redirect(RESPONSE, 'manageRelatedProjects')
 
 
@@ -758,6 +716,20 @@
         else:
             return t
     
+    
+    def getLabel(self):
+        """returns label (or title) of this project"""
+        l = getattr(self, 'xdata_07', None)
+        if isinstance(l, list):
+            # compat with old lists
+            l = l[0]
+            
+        if l:
+            return l
+        else:
+            return self.getProjectTitle()
+
+    
     def getResponsibleScientists(self):
         """returns the responsible scientists as string"""
         t = getattr(self, 'xdata_01', None)
@@ -797,6 +769,26 @@
         self.responsibleScientistsList = scientistsList
         
               
+    def getInvolvedScholars(self):
+        """returns the other involved scholars"""
+        t = getattr(self, 'xdata_08', None)
+        if isinstance(t, list):
+            # compat with old lists
+            return t[0]
+        else:
+            return t
+
+        
+    def getCooperationPartners(self):
+        """returns the cooperation partners"""
+        t = getattr(self, 'xdata_12', None)
+        if isinstance(t, list):
+            # compat with old lists
+            return t[0]
+        else:
+            return t
+        
+              
     def getUrl(self, baseUrl=None):
         """returns URL to this Project"""
         if baseUrl is None:
@@ -824,10 +816,86 @@
               
     def getImageList(self):
         """returns the sorted list of images for this project"""
-        items = self.objectValues(spec='MPIWGProject_image')[:]
+        items = self.objectValues(spec='MPIWGProject_image')
+        # sort by place
+        return sorted(items, key=lambda x:int(getattr(x, 'place', 0)))
+
+
+    def getDepartment(self):
+        """returns the department of this project"""
+        num = self.getNumber()
+        pp = num.find('.')
+        if pp > 0:
+            num = num[:pp]
+            
+        return self.getMPIWGRoot().getDepartment(projectNumber=num)
+
+
+    def getDepartmentId(self):
+        """returns the id of the department of this project"""
+        dep = self.getDepartment()
+        if dep is not None:
+            return dep.getId()
+        
+        return None
+
+
+    def getDescription(self, filter=False):
+        """returns the project description"""
+        t = getattr(self, 'WEB_project_description', None)
+        if isinstance(t, list):
+            # compat with old lists
+            return t[0]
+        else:
+            return t
+        
+        
+    def getSuperProjects(self):
+        """returns a list of ancestor projects to the root"""
+        tree = self.getProjectTree()
+        return tree.getAncestorsOf(self.getNumber())
+    
+
+    def getSubProjects(self, active=1):
+        """returns a list of child projects"""
+        tree = self.getProjectTree()
+        return [p for p in tree.getChildrenOf(self.getNumber()) if p.checkActive(active)]
+    
+              
+    def getRelatedProjects(self):
+        """returns the list of related projects"""
+        items = self.objectValues(spec='MPIWGProject_relatedProject')
         # sort by place
         items.sort(key=lambda x:int(getattr(x, 'place', 0)))
         return items          
+        
+
+    def getRelatedPublications(self):
+        """returns the list of related publications"""
+        items = self.objectValues(spec='MPIWGProject_publication')
+        # sort by place
+        items.sort(key=lambda x:int(getattr(x, 'place', 0)))
+        return items          
+        
+    
+    def getRelatedDigitalSources(self):
+        """returns the related digital sources"""
+        t = getattr(self, 'xdata_11', None)
+        if isinstance(t, list):
+            # compat with old lists
+            return t[0]
+        else:
+            return t
+
+        
+    def getFundingInstitutions(self):
+        """returns the funding institutions"""
+        t = getattr(self, 'xdata_13', None)
+        if isinstance(t, list):
+            # compat with old lists
+            return t[0]
+        else:
+            return t
 
               
     def getImages(self):
@@ -864,6 +932,10 @@
                 self.manage_delObjects([id])
         except:
                 logging.error("ERROR MPIWG: %s %s" % sys.exc_info()[0:2])
+                
+        # invalidate thumbnail
+        self.projectThumb = None
+        
         if RESPONSE:
             self.redirect(RESPONSE, 'manageImages')
 
@@ -871,7 +943,6 @@
  
     def hasChildren(self, date=None, onlyActive=1, onlyArchived=1):
         """check if project has children"""
-        
         ct = self.getContexts(childs=self.getContent('xdata_05'),
                                  depth=1, date=date, onlyActive=onlyActive)
         
@@ -899,6 +970,9 @@
         obj.place = self.getLastImageNumber() + 1
         obj.id = filename
 
+        # invalidate thumbnail
+        self.projectThumb = None
+        
         if RESPONSE is not None:
             
             self.redirect(RESPONSE, 'manageImages')
@@ -921,7 +995,7 @@
             return cmp(x[1].archiveTime, y[1].archiveTime)
 
         if not date:
-            if self.isActual():
+            if self.isCurrentVersion():
                 return self
             else:
                 return None
@@ -954,16 +1028,12 @@
             return None
           
         
-    def isActual(self):
-        """gibt 1 zurueck wenn aktuell, 0 sonst"""
-        actualTime = time.localtime()
-        
+    def isCurrentVersion(self):
+        """Return if project is the current version."""
+        currentTime = time.localtime()
+        # print getattr(self,'archiveTime',currentTime)
+        return (getattr(self, 'archiveTime', currentTime) >= currentTime)
         
-        # print getattr(self,'archiveTime',actualTime)
-        if getattr(self, 'archiveTime', actualTime) < actualTime:
-            return 0
-        else:
-            return 1
         
     def copyObjectToArchive(self):
         """kopiere aktuelles objekt ins archiv"""
@@ -1005,43 +1075,8 @@
             self.redirect(RESPONSE, 'manage_main')
 
    
-    def crossLinker(self):
-        """experimental crosslinker"""
-        splitted = self.WEB_project_description[0].split()
-        new = []
-        for split in splitted:
-            try:
-                found = self.DescriptionCatalog({'fulltext':split})
-          
-                if len(found) > 1:
-             
-                    new.append("<a href=%s>%s</a>" % (split, split))
-                else:
-                    new.append(split)
-            except:
-                new.append(split)
-        return " ".join(new)
-            
-            
-
-
-    def generateTemplate(self, RESPONSE=None):
-        """Erzeuge Template fuer defined fields not_used"""
-
-        id = "index_html"
-        title = id
-        if self._getOb('index_html'):
-            self._delObject('index_html')
-
-        
-        newObj = ZopePageTemplate(id, 'TEXT')
-        self._setObject(id, newObj)
-        # self.manage_addPageTemplate(id,title)
-        if RESPONSE is not None:
-            self.redirect(RESPONSE, 'manage_main')
-            
     def isActiveProject(self):
-        """check if the project is still active, default is true, set to false is the project is accomplished"""
+        """check if the project is still active, default is true."""
         return getattr(self, 'isActiveFlag', True)
  
     def checkActive(self, active):
@@ -1102,19 +1137,10 @@
     def getCompletedAt(self):
         """gibt das transformierte Datum zurueck, an dem das Projekt beendet wurde."""
         date = getattr(self, 'completedAt', '')
-      
         if date:
             return self.reTransformDate(date);
         else:
             return ""
-            # test ob parent abgeschlossen ist
-            try:  # TODO: ersetzte try except durch vernuenftige abfrage  
-                ct = self.getContexts(parents=self.getContent('xdata_05'), depth=1)
-                if (len(ct) > 0):  # is there are parent
-                        return ct[0][0].getCompletedAt()
-                return '';
-            except:
-                return '';
         
     def getStartedAt(self):
         """gibt das transformierte Datum zurueck, an dem Projekt begonnen wurde."""
@@ -1227,7 +1253,7 @@
         if not context:
             context = self
             
-        if self.isActiveProject() and self.isActual():
+        if self.isActiveProject() and self.isCurrentVersion():
              templates = self.en.getHarvestCache()
             
              ext = getattr(self, "harvest_main", None)
@@ -1337,7 +1363,6 @@
             
     def getDefinedFields(self):
         """show all defined fields"""
-        
         return definedFields
 
     def getAttribute(self, field):
@@ -1496,7 +1521,7 @@
         else:
             return style    
 
-    def getLabel(self):
+    def getLabel_old(self):
         """returns label (or title) of this project"""
         l = self.getContent('xdata_07')
         if l:
@@ -1537,7 +1562,6 @@
 
     def getRootProject(self):
         """returns the root (=top level) project of the current project"""
-        
         ct = self.getContexts(parents=self.getContent('xdata_05'))
         if len(ct) > 0:
             return ct[-1][0]
@@ -1723,7 +1747,7 @@
         checkedScientists = {}
         names = {}
         keys = {}
-        for key in formdata.keys(): 
+        for key in formdata: 
             # gehe durch das Formular
             keyParts = key.split("_")
             if keyParts[0] == "responsibleScientist": 
@@ -1734,20 +1758,22 @@
                 elif keyParts[1] == "key":
                     keys[nr] = formdata[key]
          
-        for nr in names.keys():
+        for nr in names:
             name = names[nr]
             key = keys.get(nr, None)
             username = None
             if key:
                 # get username from db
-                member = self.executeZSQL("select * from personal_www where lower(key) = %s", [key.lower()])
-                if len(member) > 0:
-                    username = re.sub('@mpiwg-berlin\.mpg\.de', '', member[0].e_mail)
+                member = self.getMPIWGRoot().getStaffFolder().getMember(key=key)
+                if member is not None:
+                    username = member.getUsername()
                     
             # schreibe keys und namen in die Liste
             checkedScientists[names[nr]] = {'name' : name, 'key' : key, 'username' : username}
              
+        # update responsibleScientistsList
         self.setResponsibleScientistsList(checkedScientists)
+        self.updateProjectMembers()
         
         if fromEdit and (RESPONSE is not None):
             return self.editBasic()
@@ -1841,7 +1867,7 @@
             # create responsibleScientistsList automatically
             newScientists = {}
             names = p.identifyNames(p.getResponsibleScientists())
-            for name in names.keys():
+            for name in names:
                 logging.debug("updateAllProjectMembers: name=%s" % repr(name))
                 members = names[name]
                 if len(members) > 0:
@@ -1861,33 +1887,10 @@
         if len(memberlist) == 0:
             return
          
-        # update old format responsibleScientistsList
-        if isinstance(memberlist[0], tuple):
-            logging.debug("updateAllProjectMembers: updating memberlist for project %s" % self)
-            newScientists = {}
-            for m in memberlist:
-                name = m[0]
-                key = m[1] 
-                username = None
-                if key:
-                    if isinstance(key, list):
-                        key = key[0]
-                        
-                    # get username from db
-                    member = self.executeZSQL("select * from personal_www where lower(key) = %s", [key.lower()])
-                    if len(member) > 0:
-                        username = re.sub('@mpiwg-berlin\.mpg\.de', '', member[0].e_mail)
-                        
-                newScientists[name] = {'name': name, 'key' : key, 'username' : username}
-                
-            # set and re-read new list
-            self.setResponsibleScientistsList(newScientists)
-            memberlist = self.getResponsibleScientistsList()
-        
         # fill projects_members table
         self.executeZSQL("delete from projects_members where project_number = %s", [pNum])
         for m in memberlist:
-            memberKey = m.get('key')
+            memberKey = m.get('key', None)
             if not memberKey or not isinstance(memberKey, basestring):
                 logging.error("updateProjectMembers: not a valid member key: %s" % repr(memberKey))
                 continue
@@ -1934,12 +1937,12 @@
 
 
     def getProjectTree(self):
-        """returns the project hierarchy tree (and caches it).
+        """Return the project hierarchy tree (and cache it).
         
-        returns HashTree instance."""
+        Returns HashTree instance."""
         tree = self._v_projectTree 
         if tree is None:
-            tree = HashTree(keySeparator='.', keyFn=lambda x:getInt(x))
+            tree = HashTree(keySeparator='.', keyFn=getInt)
             for p in self.objectValues(spec='MPIWGProject'):
                 tree.add(p.getNumber(), p)
                 
@@ -1950,7 +1953,7 @@
     
     
     def getProjectsAsList(self, start, active=1, archived=1):
-        """returns flattened list of projects, starting from start.
+        """Return flattened list of projects, starting from start.
 
         active = 0 : all projects
         active = 1 : active projects
@@ -1970,9 +1973,18 @@
         # return filtered list
         return [p for p in pl if (p.checkActive(active) and p.checkArchived(archived))]     
     
+
+    def getProject(self, projectNumber=None):
+        """Return the matching project"""
+        tree = self.getProjectTree()
+        if projectNumber is not None:
+            return tree.get(projectNumber)
+        
+        return None
+
     
     def getProjectsOfMember(self, key, active=1, archived=1):
-        """returns a list of all projects of a member.
+        """Return a list of all projects of a member.
     
         @param key: member's key
         active = 0 : all projects
@@ -1989,15 +2001,17 @@
         # find projects in tree
         for r in res:
             p = tree.get(r.project_number)
+            # check if active
             if p is not None and p.checkActive(active) and p.checkArchived(archived):
                 projects.append(p)
             
+        projects.sort(key=lambda p:[int(n) for n in p.getNumber().split('.')])
         return projects        
         
         
     security.declareProtected('View management screens', 'updateAllProjectMembers')
     def updateAllProjectMembers(self, updateResponsibleScientistsList=False):
-        """re-creates responsibleScientistsLists and projects_members table from all current projects"""
+        """Re-create responsibleScientistsLists and projects_members table from all current projects."""
         # empty table
         self.executeZSQL('truncate table projects_members')
         cnt = 0
@@ -2010,6 +2024,66 @@
 
         return "updated %s projects!" % cnt
 
+
+    security.declareProtected('View management screens', 'updateAllProjects')
+    def updateAllProjects(self, updateResponsibleScientistsList=False):
+        """Patch all current projects for legacy problems."""
+        cnt = 0
+        # go through all projects
+        for (id, project) in self.ZopeFind(self, obj_metatypes=['MPIWGProject'], search_sub=1):
+            cnt += 1
+            #
+            # hasRelatedPublicationsOldVersion
+            #
+            if project.hasRelatedPublicationsOldVersion():
+                logging.debug("updateAllProjects(%s): update relatedPublicationsOldVersion!"%project.getId())
+                project.copyPublicationsToList()
+                                
+            #
+            # old format responsibleScientistsList
+            #
+            memberlist = project.getResponsibleScientistsList()
+            if len(memberlist) > 0 and isinstance(memberlist[0], tuple):
+                logging.debug("updateAllProjects(%s): updating memberlist" % project.getId())
+                newScientists = {}
+                for m in memberlist:
+                    name = m[0]
+                    key = m[1] 
+                    username = None
+                    if key:
+                        if isinstance(key, list):
+                            key = key[0]
+                            
+                        # get username from db
+                        member = self.getMPIWGRoot().getStaffFolder().getMember(key=key)
+                        if member is not None:
+                            username = member.getUsername()
+                            
+                    newScientists[name] = {'name': name, 'key' : key, 'username' : username}
+                    
+                # set new list
+                project.setResponsibleScientistsList(newScientists)
+
+            #
+            # remove old attributes
+            #
+            if hasattr(project, 'definedFields'):
+                logging.debug("updateAllProjects(%s): has definedFields!"%project.getId())
+                delattr(project, 'definedFields')
+                
+            #
+            # update extended bibliography
+            #
+            if hasattr(project, 'publicationList'):
+                logging.debug("updateAllProjects(%s): has publicationList!"%project.getId())
+                extpub = project.publicationList
+                if hasattr(extpub, 'connection_id'):
+                    logging.debug("updateAllProjects(%s): extended publication %s has connection_id=%s!"%(project.getId(),extpub.getId(),extpub.connection_id))
+            
+                
+        return "updated %s projects!" % cnt
+                
+
         
 def manage_addMPIWGProjectFolderForm(self):
     """form for adding a MPIWGProjectFolder"""
--- a/MPIWGRoot.py	Mon Apr 29 16:01:24 2013 +0200
+++ b/MPIWGRoot.py	Mon Apr 29 16:02:24 2013 +0200
@@ -273,28 +273,48 @@
         items.sort(key=lambda x:int(x.weight))
         return items
 
+    def getDepartment(self, projectNumber=None):
+        """returns a Department object"""
+        dir = getattr(self, 'departments', None)
+        if dir is None:
+            return None
+
+        if projectNumber is not None:
+            for dep in dir.objectValues(spec='MPIWGDepartment'):
+                if dep.getProjectNumber() == projectNumber:
+                    return dep
+                
+        return None
+
+
     def getProjectFolder(self):
         """returns the MPIWGProjectFolder"""
         dir = getattr(self, 'projects', None)
         return dir
 
+
+    def getStaffFolder(self):
+        """returns the MPIWGStaffFolder"""
+        dir = getattr(self, 'members_test', None) # TODO: fix the test
+        return dir
+
+
     def getMPIWGRoot(self):
         """returns the MPIWG root"""
         return self
 
+
     def MPIWGrootURL(self):
         """returns the URL to the root"""
         return self.absolute_url()
-        
-    #WTF?
-    def patchProjects(self,RESPONSE):
-        """patch"""
-        projects=self.ZopeFind(self.projects,obj_metatypes=['MPIWGProject'])
-        for project in projects:
-                tmp=project[1].WEB_project_description[0].replace("/CD/projects/","")[0:]
-                setattr(project[1],'WEB_project_description',[tmp[0:]])
-                RESPONSE.write("<p>%s</p>\n"%project[0])
-            
+
+    
+    # TODO: make obsolete
+    def decode(self, s):
+        """unicodify"""
+        return unicodify(s)
+    
+    # TODO: remove
     def replaceNotEmpty(self,format,field):
         """replace not empty"""
         if field and (not field.lstrip()==''):
@@ -304,6 +324,7 @@
             return ""
         
 
+    # TODO: remove
     def isActiveMember(self,key):
         """tested ob Mitarbeiter key ist aktiv"""
         key=utf8ify(key)
@@ -312,12 +333,13 @@
                                             _op_publish_the_data='eq',
                                             publish_the_data='yes'), 0)
         
-        logging.info("ACTIVE_MEMBER  %s"%ret)
+        logging.info("MPIWGROOT ACTIVE_MEMBER  %s"%ret)
         if ret:
             return True
         else:
             return False
         
+    # TODO: remove
     def isActual(self,project):
         """checke if project is actual"""
         actualTime=time.localtime()
@@ -447,18 +469,13 @@
         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','changeMPIWGRootForm')).__of__(self)
         return pt()
 
-    def changeMPIWGRoot(self,title,connection_id,coneServiceURL,harvestPort,harvestServer,lang=None,autocommit=None,RESPONSE=None):
+    def changeMPIWGRoot(self,title,connection_id,coneServiceURL,lang=None,autocommit=None,RESPONSE=None):
         """change"""
         self.title=title
         self.connection_id=connection_id
         #self.disciplineList=disciplineList
         #self.themesList=themesList
         self.coneServiceURL=coneServiceURL
-        self.harvestServer=harvestServer
-        try:
-            self.harvestPort=int(harvestPort)
-        except:
-            logging.error("couldn't change port!: no number:"+harvestPort)
         
         if lang is not None:
             self.lang = lang
--- a/MPIWGStaff.py	Mon Apr 29 16:01:24 2013 +0200
+++ b/MPIWGStaff.py	Mon Apr 29 16:02:24 2013 +0200
@@ -1093,13 +1093,20 @@
                 member = MPIWGStaffMember(self, dbresult=content)
         
         elif key is not None:
-            content = folder.executeZSQL("select * from personal_www where key = %s", [key])
+            # TODO: sometimes key is lowercased (e.g. responsibleScientistsList), we should fix the data
+            content = self.executeZSQL("select * from personal_www where lower(key) = %s", [key.lower()])
             if len(content) > 0:
                 member = MPIWGStaffMember(self, dbresult=content)             
          
         return member
     
 
+    def isActiveMember(self, key):
+        """returns if member key is active"""
+        res = self.executeZSQL("select * from personal_www where lower(key) = %s and publish_the_data = 'yes'", [key.lower()])
+        return len(res) > 0
+
+
     def sortPriority(self,list):
         def sort(x,y):
             try:
@@ -1578,7 +1585,7 @@
         REQUEST.response.redirect(self.REQUEST['HTTP_REFERER'])
  
    
-    def invalidate_chache(self):
+    def invalidate_cache(self):
         #TODO: How to invalidate the varnish cache from the member object
         pass;
         
--- a/zpt/changeMPIWGRootForm.zpt	Mon Apr 29 16:01:24 2013 +0200
+++ b/zpt/changeMPIWGRootForm.zpt	Mon Apr 29 16:02:24 2013 +0200
@@ -19,21 +19,6 @@
 <p>Cone Service URL:<br/>
 <input size="100" tal:attributes="value python:getattr(here,'coneServiceURL','http://virtuoso.mpiwg-berlin.mpg.de:8080/MetaDataManagerRestlet/cone/')" name="coneServiceURL"/>
 </p>
-<p>Harvester ZEO server:<br/>
-<input tal:attributes="value python:here.getHarvestCacheServer()" name="harvestServer"/>
-</p>
-<p>Harvester ZEO server port:<br/>
-<input tal:attributes="value python:here.getHarvestCachePort()" name="harvestPort"/>
-</p>
-<!--
-<p>Discipline list (seperated by CR)<br>
-<textarea name="disciplineList" cols=50 rows=20  tal:content="python:getattr(here,'disciplineList','')" ></textarea>
-</p>
-<p>Themes List (seperated by CR)<br>
-<textarea name="themesList" cols=50 rows=20 tal:content="python:getattr(here,'themesList','')"></textarea>
-</p>
--->
-
 
 <p>
 <div class="form-element">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zpt/project/extendedBibliography_template.zpt	Mon Apr 29 16:02:24 2013 +0200
@@ -0,0 +1,86 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html metal:define-macro="page">
+<metal:x metal:use-macro="here/main_template/macros/page">
+  <head>
+<tal:block metal:fill-slot="title">
+  <title tal:content="string:${here/getNumber} ${here/getProjectTitle}-Bibliography" />
+</tal:block>
+<tal:block metal:fill-slot="head"
+  tal:define="global proBaseUrl string:$root/${secmap/research}/projects;
+              global dept here/getDepartmentId;" />
+  </head>
+  <body>
+
+    <!-- middle column -->
+    <div class="center" metal:fill-slot="center">
+      <tal:block tal:replace="structure here/versionHeader" />
+      <tal:x tal:define="started here/getStartedAt" tal:condition="here/isArchivedProject">
+        <p>
+          (<span tal:condition="started" tal:content="string:$started-" /><span tal:condition="not:started">Completed:</span> <span
+            tal:content="here/getCompletedAt" />)
+        </p>
+      </tal:x>
+
+      <h1 tal:content="here/getProjectTitle">History of Scientific Objectivity, 18th-19th Cs</h1>
+      <p class="maintext_authors">
+        <tal:block tal:repeat="person here/getResponsibleScientistsList">
+          <a tal:omit-tag="python:not (person['username'] and here.isActiveMember(person['key']))"
+            tal:attributes="href string:$root/${secmap/staff}/members/${person/username}" tal:content="person/name"> Name of
+            responsible person</a>
+          <tal:block tal:condition="not:repeat/person/end">, </tal:block>
+        </tal:block>
+      </p>
+
+      <p class="maintext_more" tal:define="others here/getInvolvedScholars" tal:condition="others">
+        Other involved scholars: <span tal:content="structure others">Scholars </span>
+      </p>
+      <p class="maintext_more" tal:define="partners here/getCooperationPartners" tal:condition="partners">
+        Cooperation Partners: <span tal:content="structure partners">Partners</span>
+      </p>
+
+      <h2>Extended Bibliography</h2>
+
+      <tal:block
+        tal:define="
+             wgb python:here.getPublicationsOfTypes('workingGroupBook');
+             books python:here.getPublicationsOfTypes('book');
+             edited_books python:here.getPublicationsOfTypes('edited book');
+             articles python:here.getPublicationsOfTypes(['book','edited book'],neg=True);">
+
+        <tal:block tal:condition="wgb">
+          <h3>Working Group Books</h3>
+          <ul class="publicationlist">
+            <li tal:repeat="found3 wgb"><span tal:replace="structure python:here.decode(here.formatBibliography(found3))" /></li>
+          </ul>
+        </tal:block>
+        <tal:block tal:condition="books">
+          <h3>Books</h3>
+          <ul class="publicationlist">
+            <li tal:repeat="found3 books"><span tal:replace="structure python:here.decode(here.formatBibliography(found3))" />
+            </li>
+          </ul>
+        </tal:block>
+
+        <tal:block tal:condition="edited_books">
+          <h3>Edited Books</h3>
+          <ul class="publicationlist">
+            <li tal:repeat="found3 edited_books"><span
+              tal:replace="structure python:here.decode(here.formatBibliography(found3))" /></li>
+          </ul>
+        </tal:block>
+
+        <tal:block tal:condition="articles">
+          <h3>Articles and Chapters</h3>
+          <ul class="publicationlist">
+            <li tal:repeat="found3 articles"><span tal:replace="structure python:here.decode(here.formatBibliography(found3))" />
+            </li>
+          </ul>
+        </tal:block>
+      </tal:block>
+
+    </div>
+
+  </body>
+</metal:x>
+</html>
\ No newline at end of file
--- a/zpt/project/project_template.zpt	Mon Apr 29 16:01:24 2013 +0200
+++ b/zpt/project/project_template.zpt	Mon Apr 29 16:02:24 2013 +0200
@@ -4,155 +4,124 @@
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <tal:block metal:fill-slot="title">
-  <title tal:content="python:here.getContent('xdata_05')+'   '+here.getContent('WEB_title')" />
+  <title tal:content="string:${here/getNumber} ${here/getProjectTitle}" />
 </tal:block>
 <tal:block metal:fill-slot="head"
-  tal:define="global marginImages python:here.sortedByPlace('MPIWGProject_image');
-                           global dept python:here.getRootProject().getId();" />
+  tal:define="global proBaseUrl string:$root/${secmap/research}/projects;
+              global images here/getImageList;
+              global dept here/getDepartmentId;" />
 </head>
 <body>
 
   <!-- middle column -->
   <div class="center" metal:fill-slot="center">
-    <tal:block tal:replace="structure here/versionHeader" />
-    <tal:x tal:condition="here/isArchivedProject">
+    <h2  tal:condition="not:here/isCurrentVersion">
+      This is an outdated version of this project! For the current version, please refer to 
+      <a tal:define="parentUrl python:here.aq_parent.getUrl(baseUrl=proBaseUrl)" 
+        tal:attributes="href parentUrl" tal:content="parentUrl"/>
+    </h2>
+    <tal:x tal:define="started here/getStartedAt" tal:condition="here/isArchivedProject">
       <p>
-        (
-        <tal:x tal:condition="python:here.getStartedAt()!=''">
-          <span tal:content="python:here.getStartedAt()" />-</tal:x>
-        <tal:x tal:condition="not:python:here.getStartedAt()!=''">Completed:</tal:x>
-        <span tal:content="python:here.getCompletedAt()" />)
+        (<span tal:condition="started" tal:content="string:$started-"/><span tal:condition="not:started">Completed:</span>
+        <span tal:content="here/getCompletedAt"/>)
       </p>
     </tal:x>
 
     <h1 tal:content="here/getProjectTitle">History of Scientific Objectivity, 18th-19th Cs</h1>
     <p class="maintext_authors">
       <tal:block tal:repeat="person here/getResponsibleScientistsList">
-          <a tal:omit-tag="python:not (person['username'] and here.isActiveMember(person['key']))"
+          <a tal:omit-tag="python:not (person['username'] and here.getStaffFolder().isActiveMember(key=person['key']))"
             tal:attributes="href string:$root/${secmap/staff}/members/${person/username}" tal:content="person/name"> Name of
             responsible person</a><tal:block tal:condition="not:repeat/person/end">, </tal:block>
       </tal:block>
     </p>
 
-    <p class="maintext_more" tal:condition="not:python:here.getContent('xdata_08')==''">
-      Other involved scholars: <span tal:content="structure python:here.getContent('xdata_08')">Scholars </span>
+    <p class="maintext_more" tal:define="others here/getInvolvedScholars" tal:condition="others">
+      Other involved scholars: <span tal:content="structure others">Scholars </span>
     </p>
-    <p class="maintext_more" tal:condition="not:python:here.getContent('xdata_12')==''">
-      Cooperation Partners: <span tal:content="structure python:here.getContent('xdata_12')">Partners</span>
+    <p class="maintext_more" tal:define="partners here/getCooperationPartners" tal:condition="partners">
+      Cooperation Partners: <span tal:content="structure partners">Partners</span>
     </p>
 
     <!-- inline image -->
-    <div class="pic_inline_container" tal:condition="marginImages">
-      <tal:block tal:repeat="marginImage python:marginImages">
+    <div class="pic_inline_container" tal:condition="images">
+      <tal:block tal:repeat="image python:images">
         <div class="pic_inline">
-          <a tal:condition="python:marginImage[1].width"
-            tal:attributes="href python:here.REQUEST['URL1']+'/'+marginImage[1].getId()+'/showImage'" target="image"> <img
-            tal:replace="structure python:marginImage[1].tag(scale=min(220.0/marginImage[1].width,1), border='0')" />
-          </a>
-          <pre tal:condition="not:python:marginImage[1].width">empty image</pre>
+          <a tal:condition="image/width"
+            tal:attributes="href python:request['URL1']+'/'+image.getId()+'/showImage'" target="image"><img width="220"
+            tal:attributes="src image/absolute_url" alt=""/><!--  <img tal:replace="structure python:image.tag(scale=min(220.0/image.width,1), border='0')" /> 
+            --></a>
+          <pre tal:condition="not:image/width">empty image</pre>
         </div>
-        <div class="caption_inline" tal:content="structure python:marginImage[1].caption">J.-A.-D. Ingres: Mme Moitessier,
-          1856. Oel/Lw. 120 x 92,1 cm. London, National Gallery.</div>
+        <div class="caption_inline" tal:content="structure image/caption">
+          J.-A.-D. Ingres: Mme Moitessier, 1856. Oel/Lw. 120 x 92,1 cm. London, National Gallery.
+        </div>
       </tal:block>
     </div>
     <!-- inline image -->
 
-    <div tal:content="structure python:here.getContent('WEB_project_description',filter='yes')">Project description</div>
+    <div tal:content="structure python:here.getDescription()">Project description</div>
 
   </div>
 
   <!-- right column -->
   <div class="sidebar" metal:fill-slot="sidebar">
 
+    <!-- this project is part of -->
     <div class="sideblock"
-      tal:define="contexts python:here.getContexts(parents=here.getContent('xdata_05'),depth=1,date=here.REQUEST.get('date',None))"
-      tal:condition="contexts">
-      <!-- this project is part of -->
-
+      tal:define="parents here/getSuperProjects;" tal:condition="parents">
       <div class="project parent">
         Part of:
-        <tal:block tal:repeat="context contexts">
-          <tal:x tal:condition="not:here/isArchivedProject">
-            <a tal:content="python:context[0].getContent('WEB_title')"
-              tal:attributes="href python:here.generateUrlProject(context[0].absolute_url(),project='yes')+'/index.html'" />
-          </tal:x>
-          <tal:x tal:condition="here/isArchivedProject">
-            <a tal:content="python:context[0].getContent('WEB_title')"
-              tal:attributes="href python:here.generateUrlProject(context[0].absolute_url(),project='yes')+'/index.html?showArchive=yes'" />
-          </tal:x>
-        </tal:block>
+        <a tal:define="parent python:parents[-1]" tal:content="parent/getLabel"
+           tal:attributes="href python:parent.getUrl(baseUrl=proBaseUrl)+test(here.isArchivedProject(),'?showArchive=yes','')" />
       </div>
       <!-- end parent -->
     </div>
     <!-- sideblock -->
 
-    <tal:block tal:define="projects python:here.sortedByPlace('MPIWGProject_relatedProject');">
-      <div class="sideblock" tal:condition="python:len(projects)>0">
-        <h2>Related Projects</h2>
-        <div class="item" tal:repeat="project python:projects">
-          <a tal:attributes="href
-            python:root+'/'+secmap['research']+'/projects/'+project[1].objid"
-            tal:content="python:project[1].projectWEB_title" />
-        </div>
-        <!-- end item -->
+    <!-- related projects -->
+    <div class="sideblock" tal:define="projects here/getRelatedProjects" tal:condition="projects">
+      <h2>Related Projects</h2>
+      <div class="item" tal:repeat="project python:projects">
+        <a tal:attributes="href python:proBaseUrl+'/'+project.objid"
+          tal:content="python:project.projectWEB_title" />
       </div>
-      <!-- sideblock -->
-
-    </tal:block>
+      <!-- end item -->
+    </div>
+    <!-- sideblock -->
 
-    <!-- <\!-- related publications -\-> -->
-
-    <tal:block tal:define="publications python:here.sortedByPlace('MPIWGProject_publication');">
-      <div class="sideblock" tal:condition="python:here.hasRelatedPublicationsOldVersion()">
+    <!-- related publications -->
+    <tal:block tal:define="publications here/getRelatedPublications;">
+      <div class="sideblock" tal:condition="publications">
         <h2>Related Publications</h2>
-        <div class="item" tal:condition="python:getattr(here,'WEB_related_pub_copied',False)"
-          tal:define="pub python:here.getContent('WEB_related_pub')" tal:content="structure pub">
-          "Die Kultur der wissenschaftlichen Objektivit&auml;t," in: Michael Hagner, ed., Ansichten der Wissenschaftsgeschichte
-          (Frankfurt am Main: Fischer Taschenbuchverlag GmbH, 2001): 137-158. <br /> (with Peter Galison), "The Image of
-          Objectivity," Representations no. 40 (Fall 1992): 81-128. Translated into German (2002) <br /> "Objectivity and the Escape
-          from Perspective," Social Studies of Science 22(1992): 597-618
-        </div>
-        <!-- end item -->
-      </div>
-      <!-- sideblock -->
-
-      <div class="sideblock" tal:condition="python:len(publications)>0">
-        <h2>Related Publications</h2>
-        <div class="item" tal:repeat="publication python:publications">
-          <tal:y condition="not:python:publication[1].hasLinkToBookPage(mode='cached')">
-            <tal:x condition="python:hasattr(publication[1],'publicationImage1')">
-              <a target="_blank" tal:attributes="href  python:publication[1].publicationImage1.absolute_url()"> <img width="150"
-                tal:attributes="src python:publication[1].publicationImage1.absolute_url()" />
+        <div class="item" tal:repeat="publication publications">
+          <tal:y condition="not:python:publication.hasLinkToBookPage(mode='cached')">
+            <tal:x condition="python:hasattr(publication,'publicationImage1')">
+              <a target="_blank" tal:attributes="href  python:publication.publicationImage1.absolute_url()"> <img width="150"
+                tal:attributes="src python:publication.publicationImage1.absolute_url()" />
               </a>
-              <a tal:condition="python:hasattr(publication[1],'publicationImage2')" target="_blank"
-                tal:attributes="href python:publication[1].publicationImage2.absolute_url()"> <img width="150"
-                tal:condition="python:hasattr(publication[1],'publicationImage2')"
-                tal:attributes="src python:publication[1].publicationImage2.absolute_url()" />
+              <a tal:condition="python:hasattr(publication,'publicationImage2')" target="_blank"
+                tal:attributes="href python:publication.publicationImage2.absolute_url()"> <img width="150"
+                tal:condition="python:hasattr(publication,'publicationImage2')"
+                tal:attributes="src python:publication.publicationImage2.absolute_url()" />
               </a>
               <br />
             </tal:x>
           </tal:y>
-          <tal:y condition="python:publication[1].hasLinkToBookPage(mode='cached')"
-            tal:define="urls python:publication[1].getImageUrls(mode='cached')">
+          <tal:y condition="python:publication.hasLinkToBookPage(mode='cached')"
+            tal:define="urls python:publication.getImageUrls(mode='cached')">
             <tal:x condition="python:len(urls)>0">
-              <a target="_blank" tal:attributes="href python:urls[0]"> <img width="150" tal:attributes="src python:urls[0]" />
-              </a>
-              <a tal:condition="python:len(urls)>1" target="_blank" tal:attributes="href  python:urls[1]"> <img width="150"
-                tal:attributes="src python:urls[1]" />
-              </a>
+              <a target="_blank" tal:attributes="href python:urls[0]"><img width="150" tal:attributes="src python:urls[0]" /></a>
+              <a tal:condition="python:len(urls)>1" target="_blank" tal:attributes="href python:urls[1]"><img width="150"
+                tal:attributes="src python:urls[1]" /></a>
               <br />
             </tal:x>
           </tal:y>
-          <tal:y condition="python:getattr(publication[1],'link','')==''">
-            <tal:x tal:content="structure python:publication[1].text" />
-            <br />
-          </tal:y>
-          <tal:y condition="not:python:getattr(publication[1],'link','')==''">
-            <a tal:content="structure python:publication[1].text" tal:attributes="href python:getattr(publication[1],'link','')" />
-            <br />
-          </tal:y>
-          <tal:x tal:condition="python:hasattr(publication[1],'description')"
-            tal:content="structure python:publication[1].description" />
+          <a tal:define="link publication/link | nothing;" tal:omit-tag="not:link" tal:content="structure python:publication.text" 
+             tal:attributes="href link"/>
+          <br />
+          <tal:x tal:condition="python:hasattr(publication,'description')"
+            tal:content="structure python:publication.description" />
         </div>
         <!-- end item -->
       </div>
@@ -168,57 +137,50 @@
     <!-- related publications -->
 
     <!-- projects covered -->
-
     <div class="sideblock"
-      tal:define="showArchive python:here.REQUEST.get('showArchive','no')=='yes';
-               contexts python:here.getContexts(childs=here.getContent('xdata_05'),depth=1,date=here.REQUEST.get('date',None))"
-      tal:condition="contexts">
+         tal:define="showArchive python:(request.get('showArchive','no')=='yes');
+                     children here/getSubProjects;"
+         tal:condition="children">
       <h2>
-        Projects <span class="proj_state"> <tal:x
-            tal:condition="not:python:(not(here.isArchivedProject() or showArchive) and here.hasChildren(onlyArchived=2))">
-            <a href="?">current</a>
-          </tal:x> <tal:x tal:condition="python:(not(here.isArchivedProject() or showArchive) and here.hasChildren(onlyArchived=2))">
-          current
-        </tal:x> &nbsp; <tal:x tal:condition="python:(not(here.isArchivedProject() or showArchive) and here.hasChildren(onlyArchived=2))">
-            <a href="?showArchive=yes">completed</a>
-          </tal:x> <tal:x tal:condition="not:python:(not(here.isArchivedProject() or showArchive) and here.hasChildren(onlyArchived=2))">
-          completed
-        </tal:x>
+        Projects
+        <span class="proj_state">
+          <a href="?" tal:omit-tag="python:not (here.isArchivedProject() or showArchive)">current</a>
+          &nbsp; 
+          <a href="?showArchive=yes" tal:omit-tag="python:here.isArchivedProject() or showArchive">completed</a>
         </span>
       </h2>
-
-      <tal:y tal:repeat="context contexts">
-        <tal:x tal:condition="not:python:context[0].isArchivedProject() or  showArchive">
+      <tal:y tal:repeat="child children">
+        <tal:x tal:condition="python:not (child.isArchivedProject() or showArchive)">
           <div class="project">
-            <a tal:content="python:context[0].getContent('short_title')"
-              tal:attributes="href python:here.generateUrlProject(context[0].absolute_url(),project='yes')+'/index_html'" />
+            <a tal:content="child/getLabel"
+              tal:attributes="href python:child.getUrl(baseUrl=proBaseUrl)" />
           </div>
         </tal:x>
-        <tal:x tal:condition="python:context[0].isArchivedProject() and (here.isArchivedProject() or showArchive)">
-          <div class="project inactive">
-            <a tal:content="python:context[0].getContent('short_title')"
-              tal:attributes="href python:here.generateUrlProject(context[0].absolute_url(),project='yes')+'/index_html'" /> (
-            <tal:x tal:condition="python:context[0].getStartedAt()!=''">
-              <span tal:content="python:context[0].getStartedAt()" />-</tal:x>
-            <tal:x tal:condition="not:python:context[0].getStartedAt()!=''">Completed:</tal:x>
-            <span tal:content="python:context[0].getCompletedAt()" />)
+        <tal:x tal:condition="python:child.isArchivedProject() and (here.isArchivedProject() or showArchive)">
+          <div class="project inactive" tal:define="started child/getStartedAt">
+            <a tal:content="child/getLabel"
+              tal:attributes="href python:child.getUrl(baseUrl=proBaseUrl)" /> 
+            (<span tal:condition="started" tal:content="string:$started-"/><tal:x tal:condition="not:started">Completed: </tal:x><span tal:content="child/getCompletedAt"/>)
           </div>
         </tal:x>
       </tal:y>
     </div>
     <!-- projects covered -->
 
-
-    <div class="sideblock" tal:condition="python:here.hasRelatedDigitalSources()">
+    <!-- related digital sources -->
+    <div class="sideblock" tal:define="sources here/getRelatedDigitalSources" tal:condition="sources">
       <h2>Related digital sources</h2>
-      <div class="item" tal:content="structure python:here.getContent('xdata_11')">
-        Item 1 <br /> Item 2 <br /> Item 3 <br />
+      <div class="item" tal:content="structure sources">
+        digital sources
       </div>
     </div>
 
-    <div class="sideblock" tal:condition="not:python:here.getContent('xdata_13')==''">
+    <!-- funding institutions -->
+    <div class="sideblock" tal:define="funding here/getFundingInstitutions" tal:condition="funding">
       <h2>Funding Institutions</h2>
-      <div class="item" tal:content="structure python:here.getContent('xdata_13')">Funding</div>
+      <div class="item" tal:content="structure funding">
+        Funding
+      </div>
     </div>
     <!-- sideblock -->
 
--- a/zpt/staff/member_index_html.zpt	Mon Apr 29 16:01:24 2013 +0200
+++ b/zpt/staff/member_index_html.zpt	Mon Apr 29 16:02:24 2013 +0200
@@ -143,13 +143,11 @@
       </div>
     </div>
 
-    <div class="sideblock" tal:condition="python:here.getProjectsOfMember(key=key)">
+    <div class="sideblock" tal:define="projects python:here.getProjectFolder().getProjectsOfMember(key=key)" tal:condition="projects">
       <h2>Projects</h2>
-      <div class="project" tal:repeat="project python:here.getProjectsOfMember(key=key)">
-        <tal:x tal:condition="python:here.isActual(project)">
-          <a tal:attributes="href python:root+'/'+secmap['research']+'/projects/'+project.getId()+'/index.html'"
-            tal:content="python:here.decode(project.getContent('WEB_title'))" />
-        </tal:x>
+      <div class="project" tal:repeat="project projects">
+        <a tal:attributes="href python:project.getUrl(baseUrl=root+'/'+secmap['research']+'/projects/')"
+           tal:content="project/getLabel" />
       </div>
     </div>