File:  [Repository] / OSA_system2 / OSAS_metadata.py
Revision 1.7: download - view: text, annotated - select for diffs - revision graph
Wed Apr 6 09:18:41 2005 UTC (19 years, 2 months ago) by dwinter
Branches: MAIN
CVS tags: HEAD
editing for metadata added

    1: """ Classes for managing metadata"""
    2: 
    3: from OFS.SimpleItem import SimpleItem
    4: from Globals import InitializeClass,package_home
    5: from OFS.Folder import Folder
    6: from AccessControl import ClassSecurityInfo
    7: from Products.PageTemplates.PageTemplateFile import PageTemplateFile
    8: import os.path
    9: import sys
   10: import xml.dom.minidom
   11: import xml.dom.pulldom
   12: import OSAS_helpers
   13: import zLOG
   14: import string
   15: import xml.xpath
   16: import xmlrpclib
   17: 
   18: from types import *
   19: 
   20: class OSAS_MetadataMapping(SimpleItem):
   21:     """Einfaches Mapping Object"""
   22: 
   23:     meta_type="OSAS_MetadataMapping__neu"
   24: 
   25:     def readFieldsFromParent(self):
   26:         """read all elements from root"""
   27:         
   28:         return self.aq_parent.fieldList
   29:     
   30:     def __init__(self,id,title,arglist):
   31:         """init
   32:         @param id: id
   33:         @param title: title fuer zope navigation
   34:         @param arglist: dictionary mit Namen der zugelassenen generische Metadaten als key und Tripel  als Werte (human readable, tag version,explanation
   35:         """
   36:         self.id=id
   37:         self.title=title
   38:         for fieldName in arglist.keys():
   39:             setattr(self,"md_"+fieldName,arglist[fieldName])
   40:         
   41:             
   42:     manage_options = SimpleItem.manage_options+(
   43:         {'label':'Main Config','action':'changeMetadataMappingForm'},
   44:         )
   45: 
   46: 
   47:     def showSetXML(self,RESPONSE=None):
   48:         """prints out the mapping as XML"""
   49:         ret="""<set name="%s">"""%self.title
   50:         for fieldName in self.readFieldsFromParent():
   51:             entry=getattr(self,"md_"+fieldName)
   52:             if entry[2]=="": # no explanation of this field
   53:                 ret+="""<entry genericName="%s" tag="%s" label="%s"/>"""%(fieldName,entry[0],entry[1])
   54:             else:
   55:                 ret+="""<entry genericName="%s" tag="%s" label="%s">%s</entry>"""%(fieldName,entry[0],entry[1],entry[2])
   56:         ret+="</set>"
   57: 
   58:         if not RESPONSE:
   59:             return ret
   60:         else:
   61:             self.REQUEST.RESPONSE.setHeader('Content-Type','text/xml')
   62:             return ret
   63: 
   64:     def getValue(self,fieldName):
   65:         """get md value
   66:         @param fieldName: Bezeichnung des gesuchten Metadatums
   67:         @retunr: Value des Metadatums"""
   68:         
   69:         ret= getattr(self,"md_"+fieldName,(None,None,None,None,None))
   70:         if len(ret)!= 4: # alte MD haben keine info ueber optional/required und listen werte
   71:             ret=ret+("","")
   72:         return ret
   73:     
   74: 
   75:     def isEmptyValue(self,fieldName):
   76:         """teste ob fielname in diesem Metadatenschema definiert ist"""
   77:         field=getattr(self,"md_"+fieldName,'')
   78:         if field[1]=='':
   79:             return 0
   80:         else:
   81:             return 1
   82:         
   83:     def generateMappingHash(self):
   84:         """erzeugen des dictionaries: generisches Feld -> Definition in diesem Schema"""
   85:         hash={}
   86:         for field in self.fieldList:
   87:             hash[field]=getattr(self,"md_"+field)
   88:         return hash
   89: 
   90:        
   91:     
   92:     def changeMetadataMappingForm(self):
   93:         """change"""
   94:         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','changeMetadataMapping.zpt')).__of__(self)
   95:         return pt()
   96:     
   97:     def changeMetadataMapping(self,titleOfObject,RESPONSE=None):
   98:         """change"""
   99: 
  100:         self.title=titleOfObject
  101:         arglist=self.REQUEST.form
  102:         
  103:         for fieldName in self.readFieldsFromParent():
  104:             setattr(self,"md_"+fieldName,(arglist[fieldName],arglist['label_'+fieldName],arglist['explanation_'+fieldName],arglist['status_'+fieldName],arglist['values_'+fieldName]))
  105:             
  106:             
  107:         if RESPONSE is not None:
  108:             RESPONSE.redirect('manage_main')
  109: 
  110:     
  111: 
  112: def manage_addMetadataMappingForm(self):
  113:     """interface for adding the OSAS_root"""
  114:     pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','addMetadataMappingForm.zpt')).__of__(self)
  115: 
  116:     return pt()
  117: 
  118: def manage_addMetadataMapping(self,idOfObject,titleOfObject,RESPONSE=None):
  119:     """add the OSAS_root"""
  120:     
  121:     argList={}
  122:     for arg in self.fieldList:
  123:         if not (arg in ['idOfObject','titleOfObject']):
  124:             argList[arg]=(self.REQUEST.form[arg],self.REQUEST.form['label_'+arg],self.REQUEST.form['explanation_'+arg],arglist['status_'+fieldName],arglist['values_'+fieldName])
  125:             
  126:     newObj=OSAS_MetadataMapping(idOfObject,titleOfObject,argList)
  127:     self._setObject(idOfObject,newObj)
  128:     if RESPONSE is not None:
  129:         RESPONSE.redirect('manage_main')
  130: 
  131:    
  132: 
  133: class OSAS_Metadata(Folder):
  134:     """Foldertype enthält methoden zur Halbautomatischen Erstellung von Metadatenfiles"""
  135:     security=ClassSecurityInfo()
  136:     
  137:     def __init__(self,id,shortDescription,description,fields):
  138:         """initialize a new instance"""
  139:         self.id = id
  140:         self.shortDescription =shortDescription #label fuer link auf add page
  141:         self.description=description #description of the method for link page
  142:         self.fieldList=fields.split(",")[0:]
  143:         
  144:     meta_type='OSAS_Metadata__neu'
  145: 
  146:     manage_options = Folder.manage_options+(
  147:         {'label':'Main Config','action':'changeMetadataForm'},
  148:         {'label':'Import XML Schema','action':'importXMLSchemaForm'},
  149:         {'label':'Select Fields for Display','action':'indicateDisplayFieldsForm'},
  150:         )
  151: 
  152:     def showGenericXML(self,RESPONSE=None):
  153:         """show generic fields as XML"""
  154:         ret="""<set name="%s">"""%"generic"
  155:         for field in self.fieldList:
  156:             ret+="""<entry genericName="%s"/>"""%field
  157: 
  158:         ret+="</set>"
  159:         
  160:         if not RESPONSE:
  161:             return ret
  162:         else:
  163:             self.REQUEST.RESPONSE.setHeader('Content-Type','text/xml')
  164:             return ret
  165:         
  166:     def showOverviewXML(self,RESPONSE):
  167:         """gives an overview over the Metadata stored in this folder"""
  168:         ret="""<metadata name="%s">"""%self.getId()
  169:         ret+=self.showGenericXML()
  170:         for entry in self.ZopeFind(self,obj_metatypes=['OSAS_MetadataMapping']):
  171:             ret+=entry[1].showSetXML()
  172: 
  173:         ret+="</metadata>"
  174: 
  175:         if not RESPONSE:
  176:             return ret
  177:         else:
  178:             self.REQUEST.RESPONSE.setHeader('Content-Type','text/xml')
  179:             return ret
  180:         
  181:     def generateMappingList(self):
  182:         """Erzeuge Mapping"""
  183:         mapping={}
  184:         for dict in self.__dict__:
  185:             #print dict
  186:             obj=getattr(self,dict)
  187:             if hasattr(obj,'meta_type'):
  188:                 if obj.meta_type=="OSAS_MetadataMapping":
  189:                     mapping[obj.getId()]=obj.generateMappingHash()
  190:         return mapping
  191:     
  192:     def generateMappingForType(self,type,clean="yes"):
  193:         """erzeuge spezifisches Mapping"""
  194:         hash=self.generateMappingList()
  195:         if hash.has_key(type):
  196:             if clean=="yes":
  197:                 temp={}
  198:                 for x in hash[type].keys():
  199:                     if not hash[type][x]=="":
  200:                         temp[x]=hash[type][x]
  201:                 return temp
  202:             else:
  203:                 return hash[type]
  204:         
  205:         else:
  206:             return {}
  207: 
  208:     def getFieldList(self):
  209:         """erzeuge string aus fields"""
  210:         try:
  211:             return string.join(self.fieldList,",")
  212:         except:
  213:             return ""
  214:         
  215:     def getFields(self):
  216:         """ausgabe der Felder"""
  217:         return self.fieldList
  218: 
  219:     def getTypeTitle(self,id):
  220:         """Title von ID"""
  221:         try:
  222:             obj=getattr(self,id)
  223:             return obj.title
  224:         except:
  225:             return id
  226: 
  227:     def getType(self,type):
  228:         """gib metadataobject type zurueck"""
  229: 
  230:         for obj in self.ZopeFind(self,obj_metatypes=['OSAS_MetadataMapping__neu']):
  231:             if obj[0]==type:
  232:                 return obj
  233:         return (self.id,self)
  234: 
  235:         
  236:         
  237:     def getStoredTypes(self):
  238:         """Gebe gespeicherte typen zurück"""
  239:         
  240:         types=[]
  241: 
  242:         for obj in self.ZopeFind(self,obj_metatypes=['OSAS_MetadataMapping_neu']):
  243:              if obj.title=="":
  244:                  title=obj.id
  245:              else:
  246:                  title=obj.title
  247:              types.append((obj.id,title,obj))
  248: 
  249:         return types
  250: 
  251:     def indicateDisplayFieldsForm(self):
  252:         """form zur Makierung der Felder die in der Browserumgebung angezeigt werden"""
  253:         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','indicateDisplayFieldsForm.zpt')).__of__(self)
  254:         return pt()
  255: 
  256:     def indicateDisplayFields(self,displayFields,RESPONSE=None):
  257:         """set Displayfields
  258:         @param displayFields: Liste von Felder die im Browserenvironment angezeigt werden
  259:         """
  260:         self.displayFields=OSAS_helpers.toList(displayFields)
  261:         if RESPONSE is not None:
  262:             RESPONSE.redirect('manage_main')
  263: 
  264:     def getDisplayFieldsAsStr(self,indexMeta):
  265:         ret=[]
  266: 	try:
  267:          if indexMeta and not (indexMeta==""):
  268:             dom=xml.dom.pulldom.parseString(indexMeta)
  269: 
  270:             for (event,node) in dom:
  271: 
  272:                 if event == xml.dom.pulldom.START_ELEMENT and node.tagName=="bib":
  273:                     dom.expandNode(node)
  274: 
  275:                     try:
  276:                         type=node.getAttribute('type')
  277:                         mapping=getattr(self,type).generateMappingHash()
  278:                     except:
  279:                         type='generic'
  280:                         mapping=getattr(self,type).generateMappingHash()
  281: 
  282:                     for field in self.displayFields:
  283:                         try:
  284:                             ret.append(OSAS_helpers.getText(node.getElementsByTagName(mapping[field][0])[0].childNodes))
  285:                         except:
  286:                             """nothing"""
  287: 
  288:                     return "; ".join(ret)
  289:          else:
  290:             return ""
  291: 	except:
  292: 	    return ""
  293:     def getDisplayFieldsAsStrOLD(self,indexMeta):
  294:         """Gebe display fields als string zurück
  295:         @param path: Pfad zum Object
  296:         """
  297:         ret=[]
  298:         try:
  299:             dom=xml.dom.minidom.parseString(indexMeta)
  300:         except:
  301:             zLOG.LOG("OSAS_metadata (getDisplayFieldsAsStr)",zLOG.INFO,"Cannot parse: %s"%indexMeta)
  302:         try:
  303:             bib = dom.getElementsByTagName("meta")[0].getElementsByTagName("bib")[0]
  304:         except:
  305:             return ""
  306:         try:
  307:             type=bib.getAttribute('type')
  308:             mapping=getattr(self,type).generateMappingHash()
  309:         except:
  310:             type='generic'
  311:             mapping=getattr(self,type).generateMappingHash()
  312: 
  313:         for field in self.displayFields:
  314:             try:
  315:                 ret.append(OSAS_helpers.getText(bib.getElementsByTagName(mapping[field][0])[0].childNodes))
  316:             except:
  317:                 """nothing"""
  318:                 
  319:         return "; ".join(ret)
  320: 
  321:     security.declarePublic('changeMetadataForm')
  322:     def changeMetadataForm(self):
  323:         """Main configuration"""
  324:         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','changeMetadata.zpt')).__of__(self)
  325:         return pt()
  326:     
  327:     security.declarePublic('changeMetadata')
  328:     def changeMetadata(self,shortDescription,description,fields,RESPONSE=None):
  329:         """Change Metadata"""
  330:         self.shortDescription=shortDescription
  331:         self.description=description
  332:         self.fieldList=fields.split(",")[0:]
  333:         if RESPONSE is not None:
  334:             RESPONSE.redirect('manage_main')
  335: 
  336:     security.declarePublic('index_html')
  337: 
  338:     def importXMLSchemaForm(self):
  339:         """form"""
  340:         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','importXMLSchemaForm.zpt')).__of__(self)
  341:         return pt()
  342: 
  343:     
  344:     def importXMLSchema(self,file,RESPONSE=None):
  345:        """import xmlschema, Metadatenschema wird eingelesen und entsprechende Metadatenmappings angelegt."""
  346: 
  347:        dom=xml.dom.minidom.parse(file)
  348:        sets=dom.getElementsByTagName('set')
  349:        #erster schritt: anlegen der fieldlist
  350:        for set in sets:
  351:            if set.getAttribute('name')=='generic':
  352:                list=[]
  353:                for entry in set.getElementsByTagName('entry'):
  354:                    list.append(entry.getAttribute('genericName'))
  355:                self.fieldList=list[0:]
  356: 
  357:        #zweiter schritt: anlegen der mapping
  358:        for set in sets:
  359:            id=set.getAttribute('name').encode('utf-8')
  360:            list=[]
  361:            argList={}
  362:            for entry in set.getElementsByTagName('entry'):
  363:                genericName=entry.getAttribute('genericName')
  364:                tag=entry.getAttribute('tag')
  365:                label=entry.getAttribute('label')
  366:                description=OSAS_helpers.getText(entry.childNodes)
  367:                argList[genericName]=(tag,label,description)
  368:            self._setObject(id,OSAS_MetadataMapping(id,id,argList))
  369:            if RESPONSE:
  370:                RESPONSE.write("Wrote: %s"%id)
  371: 
  372: 
  373:     def createMetadataFragment(self,type,path,prefix="",presets={}):
  374:         """fragment"""
  375:         self.REQUEST.SESSION['MDF_type']=type
  376:         self.REQUEST.SESSION['MDF_path']=path
  377:         self.REQUEST.SESSION['MDF_prefix']=prefix
  378:         self.REQUEST.SESSION['MDF_presets']=presets
  379: 
  380:         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','addMetadataForm_fragment.zpt')).__of__(self)
  381:         return pt()
  382: 
  383:     def createMetadataForm(self,type="",path=""):
  384:         """createMetadataForm"""
  385:         self.REQUEST.SESSION['MDF_type']=type
  386:         self.REQUEST.SESSION['MDF_path']=path
  387: 
  388:         
  389:         pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','addMetadataForm_template.zpt')).__of__(self)
  390:         return pt()
  391: 
  392:     def readMetadata(self,MDF_path,MDF_type,MDF_prefix="",MDF_xpathStart="", MDF_addPath=None,MDF_identifyFields=None,newdoc=None,actualNode=None):
  393: 
  394:         if type(MDF_path)==ListType:
  395:                 MDF_path=MDF_path[0]
  396: 
  397:         indexFile=os.path.join(MDF_path,'index.meta')
  398: 
  399:         server=xmlrpclib.Server(self.serverUrl)
  400:         
  401:         documentStr=server.getFile(indexFile)
  402:         
  403:         if documentStr:
  404:             newdoc=xml.dom.minidom.parseString(documentStr)
  405:             dom=newdoc.documentElement
  406:             actualNode=dom
  407:         else:
  408:             return {}
  409: 
  410:         if self.containerTag=="":
  411:             containerTag="doc"
  412:         else:
  413:             containerTag=self.containerTag
  414:             
  415:         if MDF_xpathStart=="":
  416:             dom=newdoc.documentElement
  417:             actualNode=dom
  418:         else:
  419:             #try to find xpath
  420:             if MDF_identifyFields:
  421:                 query=[]
  422:                 for field in MDF_identifyFields.keys():
  423: 
  424:                     query.append("""(%s="%s")"""%(field,MDF_identifyFields[field]))
  425:                 querystr=" and ".join(query)
  426:                 xpathStr=MDF_xpathStart+"[%s]"%querystr
  427:                 
  428:             else:
  429:                 xpathStr=MDF_xpathStart
  430: 
  431: 
  432:             xpathNodes=xml.xpath.Evaluate(xpathStr,actualNode)
  433:             
  434: 
  435:             if len(xpathNodes)>0:
  436:                 actualNode=xpathNodes[0]
  437:             else:
  438:                 return {}
  439: 
  440:         ret={}
  441:         for node in actualNode.childNodes:
  442:             name=node.tagName
  443:             text=OSAS_helpers.getText(node.childNodes)
  444:             ret[name]=text
  445: 
  446:         return ret
  447:         
  448:     def writeMetadata(self,MDF_path,MDF_type,form,MDF_prefix="",MDF_xpathStart="", MDF_addPath=None,MDF_identifyFields=None,newdoc=None,actualNode=None):
  449:         """writeMetadata"""
  450:         #MDF_path="/tmp/index.meta"
  451:         if type(MDF_path)==ListType:
  452:                 MDF_path=MDF_path[0]
  453: 
  454: 
  455:         indexFile=os.path.join(MDF_path,'index.meta')
  456:         
  457:         server=xmlrpclib.Server(self.serverUrl)
  458:         if newdoc:
  459:             if not actualNode: actualNode=newdoc
  460:             dom=newdoc
  461:         else:
  462:             documentStr=server.getFile(indexFile)
  463: 
  464:             if documentStr:
  465: 
  466:                 newdoc=xml.dom.minidom.parseString(documentStr)
  467:                 dom=newdoc.documentElement
  468:                 actualNode=dom
  469: 
  470: 
  471:             else:
  472: 
  473:                 impl=xml.dom.minidom.getDOMImplementation()
  474:                 newdoc=None
  475: 
  476:         
  477:         if self.containerTag=="":
  478:             containerTag="doc"
  479:         else:
  480:             containerTag=self.containerTag
  481: 
  482:         create=None
  483:         if MDF_xpathStart=="":
  484:             if not newdoc:
  485:                 newdoc=impl.createDocument(None,containerTag,None)
  486:             dom=newdoc.documentElement
  487:             actualNode=dom
  488:         else:
  489: 
  490:             #try to find xpath
  491:             if MDF_identifyFields:
  492:                 query=[]
  493:                 for field in MDF_identifyFields:
  494: 
  495:                     query.append("""(%s="%s")"""%(field,form[MDF_prefix+"MD_"+field]))
  496:                 querystr=" and ".join(query)
  497:                 xpathStr=MDF_xpathStart+"[%s]"%querystr
  498:                 
  499:             else:
  500:                 xpathStr=MDF_xpathStart
  501: 
  502: 
  503:             xpathNodes=xml.xpath.Evaluate(xpathStr,actualNode)
  504:             
  505: 
  506: 
  507: 
  508:             if len(xpathNodes)>0:
  509:                 actualNode=xpathNodes[0]
  510: 
  511:             else:
  512:                 #existiert nicht dann neue erzeugen
  513: 
  514:                 if len(xml.xpath.Evaluate(MDF_xpathStart,dom))>0:
  515: 
  516:                     create=True
  517:                 
  518:                 splitted=MDF_xpathStart.split("/")
  519:                 base=""
  520:                 for element in splitted:
  521: 
  522:                     if not (element=="") and not (element==containerTag):
  523:                         base="/".join([base,element])
  524:                         
  525:                         if not newdoc:
  526:                             newdoc=impl.createDocument(None,element,None)
  527:                             actualNode=newdoc.documentElement
  528:                             dom=actualNode
  529:                         else:
  530:                             changed=None
  531: 
  532:                             if not (MDF_addPath==base):
  533: 
  534: 
  535:                                 for childNode in actualNode.childNodes:
  536:                                     if getattr(childNode,'tagName','')==element:
  537:                                         actualNode=childNode
  538:                                         changed=1
  539: 
  540:                                         if (os.path.normpath("/".join(["",containerTag,base]))==MDF_xpathStart) and create:
  541:                                             actualNode=actualNode.parentNode
  542:                                             changed=None
  543: 
  544:                             if not changed:
  545:                                 namenode=newdoc.createElement(element)
  546: 
  547:                                 actualNode.appendChild(namenode)
  548:                                 actualNode=namenode
  549: 
  550: 
  551:             
  552:         
  553:         for name in self.REQUEST.form.keys():
  554:             length=len(MDF_prefix)
  555:             if MDF_type and not (MDF_type == ""):
  556:                 actualNode.setAttribute("type",MDF_type)
  557:             if name[0:3+length]==MDF_prefix+"MD_":
  558:                 tagName=name[3+length:]
  559: 
  560:                 #CHECK if element exists
  561:                 for childNode in actualNode.childNodes:
  562:                     if getattr(childNode,'tagName','')==tagName:
  563:                         actualNode.removeChild(childNode).unlink()
  564:                 
  565:                 namenode=newdoc.createElement(tagName)
  566:                 namenodetext=newdoc.createTextNode(self.REQUEST.form[name])
  567:                 namenode.appendChild(namenodetext)
  568:                 actualNode.appendChild(namenode)
  569:                 
  570:         ret=newdoc.toxml(encoding='utf-8')
  571: 
  572:         server.writeMetaDataFile(indexFile,ret)
  573: 
  574: 
  575:         return newdoc,actualNode
  576: 
  577:     def writeMetadataFile(self,MDF_path,MDF_type,MDF_xpathStart="",newdoc=None,actualNode=None):
  578:         """writeMetaFile"""
  579: 
  580:         return self.writeMetadata(MDF_path,MDF_type,self.REQUEST.form,MDF_xpathStart=MDF_xpathStart,newdoc=newdoc,actualNode=actualNode)
  581: 
  582:     
  583:     def isEmptyValue(self,fieldName):
  584:         """im generischen fall stets falsch"""
  585:         return 1
  586: 
  587:     def getValue(self,fieldName):
  588:         """im generischen fall gleich fieldname"""
  589:         return fieldName,fieldName,"","",""
  590: 
  591:     def getList(self,list):
  592:         """return list"""
  593: 
  594:         if list=="":
  595:             return None
  596:         listsplit=list.split("\n")
  597:         return listsplit
  598: 
  599:     def showHelp(self,refType,genericTag):
  600:         """helptext"""
  601:         for reference in self.ZopeFind(self):
  602:             if reference[1].title==refType:
  603:                 text=getattr(reference[1],'md_'+genericTag)[2]
  604:                 return text
  605:         return "NO EXPLANATION"
  606: 
  607:     def showHelpTag(self,url,reftype,item):
  608:         """gen javascript for showhelp"""
  609:         url2=url+'/showHelp?refType=%s&genericTag=%s'%(reftype,item)
  610:         ret="""javascript:wd=window.open(\'%s\',\'Help\',\'width=300,height=250\');void(\'\');wd.focus();"""%url2
  611:         return ret
  612: 
  613:         
  614: def manage_addMetadataForm(self):
  615:     """interface for adding the OSAS_add_Metadata"""
  616:     pt=PageTemplateFile(os.path.join(package_home(globals()),'zpt','addMetadataForm.zpt')).__of__(self)
  617:     return pt()
  618: 
  619: def manage_addMetadata(self,id,shortDescription,description,fields,RESPONSE=None):
  620:     """add the OSAS_root"""
  621:     newObj=OSAS_Metadata(id,shortDescription,description,fields)
  622:     self.Destination()._setObject(id,newObj)
  623:     if RESPONSE is not None:
  624:         RESPONSE.redirect('manage_main')
  625: 
  626:             
  627: InitializeClass(OSAS_Metadata)

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>