"""This contains the class MPIWG Projects for organizing and maintaining the different project pages """ #TODO: mechanismus fur links to personen ueberarbeiten, da jetzt alle e_mails als members auftauchen unabhaengig vom status publish_the_data from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PageTemplates.PageTemplate import PageTemplate from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate from Products.ZSQLExtend.ZSQLExtend import ZSQLExtendFolder from Products.ZCatalog.CatalogPathAwareness import CatalogAware from OFS.Image import Image from Globals import package_home import urllib import MPIWGStaff import string import re import os from types import * import zLOG import xmlhelper # Methoden zur Verwaltung der projekt xml from OFS.SimpleItem import SimpleItem from OFS.Folder import Folder from Products.ZSQLMethods.SQL import SQLConnectionIDs from AccessControl import ClassSecurityInfo from bibliography import * import time import xml.dom.minidom import sys 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'] checkFields = ['xdata_01'] def getTextFromNode(nodename): nodelist=nodename.childNodes rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data return rc def sortF(x,y): try: return cmp(x[1],y[1]) except: try: return cmp(str(x[1]),str(y[1])) except: return 0 def sortI(x,y): xsplit=x[1].split(".") ysplit=y[1].split(".") xret="" yret="" try: for i in range(5): try: yret=yret+"%04i"%int(xsplit[i]) except: yret=yret+"%04i"%0 try: xret=xret+"%04i"%int(ysplit[i]) except: xret=xret+"%04i"%0 return cmp(int(yret),int(xret)) except: return cmp(x[1],y[1]) class MPIWGLink(SimpleItem): """create a symbolic link to another page""" meta_type="MPIWGLink" def isActiveItem(self): """teste ob ausgewaehlt""" #url1 beim link anstelle von url1, da link jeweils index_html als url hat. if self.absolute_url()==self.REQUEST['URL1']: return "aktiv" else: return "" def __init__(self,id,link,title='',weight=''): """init mpiwglink""" self.id=id self.link=link self.title=title self.weight=weight def getObj(self): """bekomme original""" ## objpath=self.link.replace("/",".") ## if objpath[0]!=".": ## objpath="."+objpath ## print objpath ## return eval("self"+objpath) splitted=self.link.lstrip().split("/") obj=self for x in splitted: if not x=="": obj=getattr(obj,x) return obj def getWeight(self): if self.linkWeight and linkWeight!="": return self.linkWeight else: return self.getObj().weight manage_options=SimpleItem.manage_options+( {'label':'main config','action':'changeLinkForm'}, ) def changeLinkForm(self): """change MPIWG link""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','MPIWGLinkChange.zpt')).__of__(self) return pt() def changeLink(self,link,title,weight,RESPONSE=None): """change links""" self.link=link self.title=title self.weight=weight if RESPONSE is not None: RESPONSE.redirect('manage') def index_html(self): """index""" return self.getObj().pt_render(extra_context={'here':self}) def manage_addMPIWGLinkForm(self): """Form for adding link""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt/AddMPIWGLink.zpt')).__of__(self) return pt() def manage_addMPIWGLink(self,id,link,title,weight,RESPONSE=None): """add link""" newObj=MPIWGLink(id,link,title,weight) self._setObject(id,newObj) if RESPONSE is not None: RESPONSE.redirect('manage_main') class MPIWGTemplate(ZopePageTemplate): """Create a layout Template for different purposes""" meta_type="MPIWGTemplate" manage_options=ZopePageTemplate.manage_options+( {'label':'Change Weight','action':'changeWeightForm'}, ) def changeWeightForm(self): """change weight form""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','MPIWGTemplateChangeWeight.zpt')).__of__(self) return pt() def changeWeight(self,weight,RESPONSE=None): """change weight""" self.weight=weight if RESPONSE is not None: RESPONSE.redirect('manage') def __init__(self, id, text=None, content_type=None,MPIWGType=None): self.id = str(id) self.ZBindings_edit(self._default_bindings) if text is None: self._default_content_fn = os.path.join(package_home(globals()), 'zpt/MPIWG_%s_template_standard.zpt'%MPIWGType) text = open(self._default_content_fn).read() self.pt_edit(text, content_type) """change form""" def isActiveItem(self): """teste ob ausgewaehlt""" if self.absolute_url()==self.REQUEST['URL']: return "aktiv" else: return "" def manage_addMPIWGTemplateForm(self): """Form for adding""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt/AddMPIWGTemplate.zpt')).__of__(self) return pt() from urllib import quote def manage_addMPIWGTemplate(self, MPIWGType,id, title=None,weight=0,REQUEST=None): "Add a Page Template with optional file content." if type(MPIWGType)==StringType: MPIWGTypes=[MPIWGType] else: MPIWGTypes=MPIWGType for singleType in MPIWGTypes: if REQUEST is None: self._setObject(id, MPIWGTemplate(id, text,EchoType=singleType)) ob = getattr(self, id) if title: ob.pt_setTitle(title) return ob else: file = REQUEST.form.get('file') headers = getattr(file, 'headers', None) if headers is None or not file.filename: zpt = MPIWGTemplate(id,MPIWGType=singleType) else: zpt = MPIWGTemplate(id, file, headers.get('content_type')) self._setObject(id, zpt) ob = getattr(self, id) if title: ob.pt_setTitle(title) try: u = self.DestinationURL() except AttributeError: u = REQUEST['URL1'] ob = getattr(self, id) ob.weight=weight REQUEST.RESPONSE.redirect(u+'/manage_main') return '' class MPIWGRoot(ZSQLExtendFolder): """Stammordner für den Web-Server""" fieldLabels={'WEB_title':'WEB_Title','xdata_01':'Responsible Scientists','xdata_02':'Department', 'xdata_03':'Historical Persons','xdata_04':'Time period', 'xdata_05':'Sorting number','xdata_06':'Keywords','xdata_07':'Short title', 'xdata_08':'Other involved scholars' ,'xdata_09':'Disciplines','xdata_10':'Themes', 'xdata_11':'Object Digitallibrary','xdata_12':'Cooperation partners', 'xdata_13':'Funding institutions','WEB_project_header':'WEB_project_header', 'WEB_project_description':'WEB_project_description','WEB_related_pub':'WEB_related_pub'} folders=['MPIWGProject','Folder','ECHO_Navigation'] meta_type='MPIWGRoot' def versionHeaderEN(self): """version header text""" date= self.REQUEST.get('date',None) if date: txt="""

This pages shows the project which existed at %s

"""%str(date) return txt return "" def versionHeaderDE(self): """version header text""" date= self.REQUEST.get('date',None) if date: txt="""

Auf dieser Seite finden Sie die Projekte mit Stand vom %s

"""%str(date) return "" def createOrUpdateId_raw(self): """create sequence to create ids for bibliography""" debug=None #suche groesste existierende id founds=self.ZSQLQuery("select id from bibliography") if founds: ids=[int(x.id[1:]) for x in founds] maximum=max(ids) id_raw=self.ZSQLQuery("select nextval('id_raw_test')",debug=debug) if id_raw: self.ZSQLQuery("drop sequence id_raw_test",debug=debug) self.ZSQLQuery("create sequence id_raw_test start %i"%(maximum+1),debug=debug) def queryLink(self,link): """append querystring to the link""" return "%s?%s"%(link,self.REQUEST.get('QUERY_STRING','')) def getKategory(self,url): """kategorie""" splitted=url.split("/") return splitted[4] def generateUrlProject(self,url,project=None): """erzeuge aus absoluter url, relative des Projektes""" if project: splitted=url.split("/") length=len(splitted) short=splitted[length-2:length] base=self.REQUEST['URL3']+"/"+"/".join(short) else: findPart=url.find("/projects/") base=self.REQUEST['URL1']+"/"+url[findPart:] return base def isNewCapital(self,text=None,reset=None): if reset: self.REQUEST['capital']="A" return True else: if len(text)>0 and not (text[0]==self.REQUEST['capital']): self.REQUEST['capital']=text[0] return True else: return False def subNav(self,obj): """return subnav elemente""" def sortWeight(x,y): x1=int(getattr(x[1],'weight','0')) y1=int(getattr(y[1],'weight','0')) return cmp(x1,y1) #if obj.meta_type in ['MPIWGTemplate','MPIWGLink']: # id=obj.aq_parent.getId() #else: #id=obj.getId() #suche die zweite ebene if not obj.aq_parent.getId() in ['de','en']: obj=obj.aq_parent while not self.ZopeFind(self,obj_ids=[obj.getId()]): obj=obj.aq_parent if hasattr(self,obj.getId()): subs=self.ZopeFind(getattr(self,obj.getId()),obj_metatypes=['MPIWGTemplate','MPIWGLink']) subret=[] for x in subs: if not(x[1].title==""): subret.append(x) subret.sort(sortWeight) return subret else: return None def isActive(self,name): """teste ob subnavigation aktiv""" for part in self.REQUEST['URL'].split("/"): if part==name: return True return False def upDateSQL(self,fileName): """updates SQL databases using fm.jar""" fmJarPath=os.path.join(package_home(globals()), 'updateSQL/fm.jar') xmlPath=os.path.join(package_home(globals()), "updateSQL/%s"%fileName) zLOG.LOG("MPIWG Web",zLOG.INFO,"java -classpath %s -Djava.awt.headless=true Convert %s"%(fmJarPath,xmlPath)) ret=os.popen("java -classpath %s -Djava.awt.headless=true Convert %s"%(fmJarPath,xmlPath),"r").read() zLOG.LOG("MPIWG Web",zLOG.INFO,"result convert: %s"%ret) return 1 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("

%s

\n"%project[0]) def replaceNotEmpty(self,format,field): """replace not empty""" if field and (not field.lstrip()==''): return format%field else: return "" def isActual(self,project): """checke if project is actual""" actualTime=time.localtime() obj= project.getObject() if getattr(obj,'archiveTime',actualTime)< actualTime: return False else: return True def redirectIndex_html(self,request): #return request['URL1']+'/index_html' return urllib.urlopen(request['URL1']+'/index_html').read() def formatBibliography(self,here,found): """format""" return formatBibliography(here,found) def getValue(self,fieldStr): """Inhalt des Feldes""" if type(fieldStr)==StringType: field=fieldStr else: field=fieldStr[0] try: if field[len(field)-1]==";": field=field[0:len(field)-1] except: """nothing""" field=re.sub(r';([^\s])','; \g<1>',field) return field.encode('utf-8') def sortedNames(self,list): """sort names""" def sortLastName(x_c,y_c): try: x=urllib.unquote(x_c).encode('utf-8','ignore') except: x=urllib.unquote(x_c) try: y=urllib.unquote(y_c).encode('utf-8','ignore') except: x=urllib.unquote(y_c) try: last_x=x.split()[len(x.split())-1] last_y=y.split()[len(y.split())-1] except: last_x="" last_y="" if last_xlast_y: return -1 else: return 0 list.sort(sortLastName) list.reverse() return list def __init__(self, id, title): """init""" self.id=id self.title=title def urlQuote(self,str): """quote""" return urllib.quote(str) def urlUnQuote(self,str): """quote""" return urllib.unquote(str) def harvestHistoricalPersons(self): """erstelle liste aller erwaehnten actors""" def normalize(str): """loesche fuhrendes space""" if (len(str)>1) and (str[0]==" "): ret=str[1:] else: ret=str return ret list={} projects=self.ZopeFind(self.projects,obj_metatypes=['MPIWGProject']) for project in projects: lg=len(project[1].xdata_03[0])-1 if (lg>1) and (project[1].xdata_03[0][lg]==";"): project[1].xdata_03[0]=project[1].xdata_03[0][0:lg] try: if len(project[1].xdata_03[0].split(";"))>1: # guess if separator is ; for person in project[1].xdata_03[0].split(";"): personNormal=normalize(person) if personNormal in list.keys(): list[urllib.quote(personNormal)][1].append(project[1]) else: list[urllib.quote(personNormal)]=(personNormal,[project[1]]) else: #guess , is sepeator for person in project[1].xdata_03[0].split(","): personNormal=normalize(person) if urllib.quote(personNormal) in list.keys(): list[urllib.quote(personNormal)][1].append(project[1]) else: list[urllib.quote(personNormal)]=(personNormal,[project[1]]) except: zLOG.LOG("MPIWG Web (harvestHistoricalPerson)",zLOG.ERROR,"cannot annalyize: %s"%repr(project)) return list def storeHistoricalPersons(self,RESPONSE=None): """store persons""" self.personDict={} personDict=self.harvestHistoricalPersons() for person in personDict.keys(): for project in personDict[person][1]: if person in self.personDict.keys(): self.personDict[person][1].append((project.absolute_url(),project.WEB_title[0],project.xdata_01[0])) else: self.personDict[person]=(personDict[person][0],[(project.absolute_url(),project.WEB_title[0],project.xdata_01[0])]) if RESPONSE is not None: RESPONSE.redirect("showHistoricalPersons") def getPersonDict(self,name): """name von dict""" try: return self.personDict[name][0].encode('utf-8') except: return self.personDict[name][0] return self.personDict[name][0].decode('latin-1').encode('utf-8') def showHistoricalPersons(self): """show persons""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','showHistoricalPersons')).__of__(self) return pt() def editHistoricalPersonsForm(self): """edit historical persons for consistency""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','editHistoricalPersonsForm')).__of__(self) return pt() def getProjectsByFieldContent(self,fieldName,fieldContentsEntry, date=None): """gib alle Projekte aus mit Value von field mit fieldName enthält ein Element der Liste fieldContents""" def sort(x,y): return cmp(x.WEB_title[0],y.WEB_title[0]) if type(fieldContentsEntry) is StringType: fieldContentsTmp=[fieldContentsEntry] else: fieldContentsTmp=fieldContentsEntry fieldContents=[] for x in fieldContentsTmp: fieldContents.append(" AND ".join(x.split())) projects=self.ProjectCatalog({fieldName:string.join(fieldContents,' AND')}) #print projects #ret=[x for x in projects] ret=[] for x in projects: obj=x.getObject() obj=obj.getActualVersion(date) if obj and (not getattr(obj,'invisible',None)): #if not (x in ret): ret.append(x) ret.sort(sort) return ret def changeMPIWGRootForm(self): """edit""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','changeMPIWGRootForm')).__of__(self) return pt() def changeMPIWGRoot(self,title,disciplineList,themesList,connection_id,RESPONSE=None): """change""" self.title=title self.connection_id=connection_id self.disciplineList=disciplineList self.themesList=themesList if RESPONSE is not None: RESPONSE.redirect('manage_main') def getDisciplineList(self): """get disciplines as list""" list= self.disciplineList.split("\n") return [x.rstrip().lstrip() for x in list] def getThemeList(self): """get themes as list""" list= self.themesList.split("\n") return [x.rstrip().lstrip() for x in list] def test(self): """test""" return self.getProjectsByFieldContent('xdata_09',['biology'])[0].absolute_url def getContexts(self,childs=None,parents=None,depth=None,date=None): """childs alle childs, alle parents""" ret=[] if parents: splitted=parents.split(".") parentId=string.join(splitted[0:len(splitted)-1],".") for project in self.getProjectFields('xdata_05',sort='int',date=date): if project[1]==parentId: ret.append(project) if childs: for project in self.getProjectFields('xdata_05',sort='int',date=date): searchStr=childs+"(\..*)" if re.match(searchStr,project[1]): if depth: if int(depth)>=len(project[1].split("."))-len(childs.split(".")): ret.append(project) else: ret.append(project) return ret def getProjectFields(self,fieldName,date=None,folder=None,sort=None): """getListofFieldNames""" ret=[] objects=self.ZopeFind(self.projects,obj_metatypes=['MPIWGProject'],search_sub=0) for object in objects: obj=object[1] obj=obj.getActualVersion(date) if obj and (not getattr(obj,'invisible',None)): if fieldName=="WEB_title_or_short": if len(obj.getContent('xdata_07'))<3: # hack weil z.Z. manchmal noch ein Trennzeichen ; oder , im Feld statt leer fieldNameTmp="WEB_title" else: fieldNameTmp="xdata_07" else: fieldNameTmp=fieldName ret.append((obj,obj.getContent(fieldNameTmp))) if sort=="int": ret.sort(sortI) else: ret.sort(sortF) return ret def showNewProjects(self): projects=[] for objs in self.getProjectFields('WEB_title_or_short'): # Get all Projets if objs[0].xdata_05 and (objs[0].xdata_05[0] == ""): projects.append(objs) return projects manage_options = Folder.manage_options+( {'label':'Update Personal Homepages','action':'updateHomepages'}, {'label':'Main config','action':'changeMPIWGRootForm'}, {'label':'Edit Historical Persons','action':'editHistoricalPersonsForm'}, {'label':'Store Historical Persons','action':'storeHistoricalPersons'}, ) def importNamesForm(self): """Form""" pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','MPIWGNamesForm.zpt')).__of__(self) return pt() def importNames(self,fileupload,folderName,RESPONSE=None): """import komma-sep list email,lastName,firstName""" project=getattr(self,folderName) load=fileupload.read() for line in load.split('\r'): splitted=line.split(",") # print splitted if not (splitted[0]==""): newObj=MPIWGStaff.MPIWGStaff(splitted[0],splitted[1],splitted[2]) try: project._setObject(splitted[0],newObj) #print "done:",splitted[0] except: zLOG.LOG("MPIWG Web (importNames)",zLOG.INFO,"cannot import: %s"%splitted[0]) if RESPONSE is not None: RESPONSE.redirect('manage_main') def updatePublicationDB(self,personId=None): """updates the publication db, i.e. copy year into the main table""" if personId: founds = self.ZSQLInlineSearch(_table="publications",id_main=personId) else: founds = self.ZSQLInlineSearch(_table="publications") for found in founds: if found.id_institutsbibliographie and (not found.id_institutsbibliographie ==""): entries = self.ZSQLInlineSearch(_table="institutsbiblio",id=found.id_institutsbibliographie) for entry in entries: self.ZSQLChange(_table='publications',_identify='oid=%s' % found.oid,year=entry.year,referencetype=entry.reference_type) if found.id_gen_bib and (not found.id_gen_bib ==""): entries = self.ZSQLInlineSearch(_table="bibliography",id=found.id_gen_bib) for entry in entries: self.ZSQLChange(_table='publications',_identify='oid=%s' % found.oid,year=entry.year,referencetype=entry.reference_type) return True def updateHomepage_neu(self,RESPONSE=None): """ update""" RESPONSE.write("\n") url="http://itgroup.mpiwg-berlin.mpg.de:8050/FMPro?-db=personal-www&-format=-dso_xml&-lay=sql_export&-max=10000&-findall" fh=urllib.urlopen(url) dom=xml.dom.minidom.parse(fh) memberFolder=getattr(self,'members') members=memberFolder.ZopeFind(memberFolder,obj_metatypes=["MPIWGStaff"]) memberList=[x[0] for x in members] for row in dom.getElementsByTagName('ROW'): username=getTextFromNode(row.getElementsByTagName('username')[0]) id=getTextFromNode(row.getElementsByTagName('ID')[0]) name=getTextFromNode(row.getElementsByTagName('Name')[0]) vorname=getTextFromNode(row.getElementsByTagName('Vorname')[0]) title=getTextFromNode(row.getElementsByTagName('Title')[0]) e_mail=getTextFromNode(row.getElementsByTagName('e_mail')[0]) e_mail_p=getTextFromNode(row.getElementsByTagName('e_mail_p')[0]) date_from=getTextFromNode(row.getElementsByTagName('Date_from')[0]) date_to=getTextFromNode(row.getElementsByTagName('Date_to')[0]) abteilung=getTextFromNode(row.getElementsByTagName('Abteilung')[0]) heimat_inst=getTextFromNode(row.getElementsByTagName('heimat_inst')[0]) funded_by=getTextFromNode(row.getElementsByTagName('funded_by')[0]) e_mail2=getTextFromNode(row.getElementsByTagName('e_mail2')[0]) publish_the_data=getTextFromNode(row.getElementsByTagName('publish_the_data')[0]) cwNode=row.getElementsByTagName('current_work.current')[0] cw=cwNode.getElementsByTagName('DATA') if cw: txt=getTextFromNode(cw[0]) else: txt="" cwNode=row.getElementsByTagName('current_work.publish')[0] cw=cwNode.getElementsByTagName('DATA') if cw: txt_p=getTextFromNode(cw[0]) else: txt_p="" project=getattr(self,'members') if not (username in memberList):#neuer eintrag try: newObj=MPIWGStaff.MPIWGStaff(str(username),name,vorname) memberFolder._setObject(str(username),newObj) RESPONSE.write("

new:%s

\n"%username.encode('utf-8')) obj=getattr(memberFolder,username) obj.createNewDBEntry(publish_the_data,id,name, vorname,title,e_mail, e_mail_p,date_from,date_to, abteilung,heimat_inst,funded_by, e_mail2,txt,txt_p) except: RESPONSE.write("

ERROR:%s %s %s

\n"%(username.encode('utf-8'),name.encode('utf-8'),vorname.encode('utf-8'))) RESPONSE.write("

: %s %s"%sys.exc_info()[:2]) else: RESPONSE.write("

update:%s

\n"%username.encode('utf-8')) self.ZSQLChange(_table="personal_www",_identify="id=%s"%id,publish_the_data=publish_the_data, date_from=date_from, date_to=date_to) return True def updateHomepages(self,RESPONSE): """lege members an""" self.upDateSQL('personalwww.xml') founds=self.ZSQLInlineSearch(_table='personal_www',publish_the_data='yes') project=getattr(self,'members') for found in founds: if not (found.e_mail==""): newObj=MPIWGStaff.MPIWGStaff(found.username,found.name,found.vorname) try: project._setObject(found.username,newObj) RESPONSE.write("

new:%s

\n"%found.username) except: RESPONSE.write("

old:%s (%s,%s)

\n"%(found.username,found.name,found.vorname)) #delete non existing foundUserNames=[x.username for x in founds] for member in self.ZopeFind(self,obj_metatypes=["MPIWGStaff"],search_sub=1): if member[1].getId() not in foundUserNames: member[1].aq_parent.manage_delObjects(ids=[member[1].getId()]) RESPONSE.write("

deleted:%s

\n"%member[1].getId()) try: self.MembersCatalog.manage_catalogReindex(self.REQUEST,RESPONSE,self.REQUEST['URL1']) except: pass self.ProjectCatalog.manage_catalogReindex(self.REQUEST,RESPONSE,self.REQUEST['URL1']) self.updatePublicationDB() if RESPONSE is not None: RESPONSE.redirect('manage_main') def getAllMembers(self): """give list of all members""" ret=[] #for x in self.members.objectValues('MPIWGStaff'): #print x.title # ret.append(x.title.decode('utf-8')) for x in self.ZopeFind(self.members,obj_metatypes=['MPIWGStaff']): ret.append(x[1].title.decode('utf-8')) ret.sort() #print ret return ret def printAllMembers(self): """prin""" members=self.getAllMembers() ret="" for x in members: ret+="

%s

"%x return ret def makeList(self,entry): """makes a list out of one entry or repeat a list""" if type(entry) is StringType: return [entry] else: return entry def getTree(self,date=None): """generate Tree from project list""" returnList=[] for project in self.getProjectFields('xdata_05',sort="int",date=date): # get Projects sorted by xdata_05 for idNr in project[1].split(";"): # more than one number if not idNr=="": splittedId=idNr.split(".") depth=len(splittedId) nr=idNr #title=project[0].WEB_title title=[project[0].getContent('WEB_title')] #print title if idNr[0]!="x": returnList.append((depth,nr,title,project[0])) return returnList def formatElementsAsList(self,elements): """formatiere tree als liste""" actualDepth=0 ret="" for element in elements: if (element[0]>actualDepth): #fuege soviele ul ein wie unterschied in tiefe if element[0]==1: ret+="""
\n
\n""" if department==4: #hack department=3 ret+="""
\n