示例#1
0
    def identify(self, arguments, **httpkwargs):
        responseDate = zuluTime()
        if arguments.keys() != ['verb']:
            additionalMessage = 'Argument(s) %s is/are illegal.' % ", ".join('"%s"' % key for key in arguments.keys() if key != 'verb')
            yield oaiError('badArgument',
                    additionalMessage=additionalMessage,
                    arguments=arguments,
                    requestUrl=self._repository.requestUrl(**httpkwargs),
                    **httpkwargs)
            return

        repositoryIdentifier = self._repository.identifier
        descriptionRepositoryIdentifier = '' if not repositoryIdentifier else DESCRIPTION_REPOSITORY_IDENTIFIER % {'repositoryIdentifier': escapeXml(repositoryIdentifier)}

        values = {
            'repositoryName': escapeXml(self._repository.name),
            'baseURL': escapeXml(self._repository.requestUrl(**httpkwargs)),
            'adminEmails': ''.join([ADMIN_EMAIL % escapeXml(email) for email in [self._repository.adminEmail]]),
            'deletedRecord': self.call.getDeletedRecordType(),
        }
        values.update(hardcoded_values)
        yield oaiHeader(self, responseDate)
        yield oaiRequestArgs(arguments, requestUrl=self._repository.requestUrl(**httpkwargs), **httpkwargs)
        yield '<Identify>'
        yield IDENTIFY % values
        yield descriptionRepositoryIdentifier
        yield TOOLKIT_DESCRIPTION
        yield self.all.description()
        yield '</Identify>'
        yield oaiFooter()
示例#2
0
 def description(self):
     yield """<description xmlns="http://www.openarchives.org/OAI/2.0/">"""
     yield """<branding xmlns="http://www.openarchives.org/OAI/2.0/branding/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/branding/
                             http://www.openarchives.org/OAI/2.0/branding.xsd">"""
     yield "<collectionIcon>"
     yield "     <url>%s</url>" % escapeXml(self._url)
     yield "     <link>%s</link>" % escapeXml(self._link) if self._link else ""
     yield "     <title>%s</title>" % self._title if self._title else ""
     yield "</collectionIcon>"
     yield "</branding>"
     yield "</description>"
示例#3
0
    def _getHumanStartPage(self, lxmlNode):

        didl_hsp_item = lxmlNode.xpath(
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/humanStartPage"]',
            namespaces=self._nsMap)
        if len(didl_hsp_item) == 0:
            didl_hsp_item = lxmlNode.xpath(
                '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/humanStartPage"]',
                namespaces=self._nsMap)
            if len(didl_hsp_item) > 0:
                self.do.logMsg(self._uploadid, LOGGER9, prefix=STR_DIDL)
            if len(didl_hsp_item) == 0:
                didl_hsp_item = lxmlNode.xpath(
                    '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/humanStartPage"]',
                    namespaces=self._nsMap)
                if len(didl_hsp_item) > 0:
                    self.do.logMsg(self._uploadid, LOGGER10, prefix=STR_DIDL)
                if len(didl_hsp_item) == 0:
                    self.do.logMsg(self._uploadid, LOGGER11, prefix=STR_DIDL)
                    return ""

        uriref = didl_hsp_item[0].xpath(
            'self::didl:Item/didl:Component/didl:Resource/@ref',
            namespaces=self._nsMap)
        mimetype = didl_hsp_item[0].xpath(
            'self::didl:Item/didl:Component/didl:Resource/@mimeType',
            namespaces=self._nsMap)

        if len(mimetype) == 0:
            self.do.logMsg(self._uploadid, LOGGER13, prefix=STR_DIDL)

        if len(mimetype) > 0 and not comm.isMimeType(mimetype[0]):
            self.do.logMsg(self._uploadid,
                           LOGGER12 + mimetype[0],
                           prefix=STR_DIDL)

        if len(uriref) == 0 or not comm.isURL(uriref[0]):
            raise ValidateException(
                formatExceptionLine(EXCEPTION11, prefix=STR_DIDL))

        return """<didl:Item>
                    <didl:Descriptor>
                        <didl:Statement mimeType="application/xml">
                            <rdf:type rdf:resource="info:eu-repo/semantics/humanStartPage"/>
                        </didl:Statement>
                    </didl:Descriptor>
                    <didl:Component>
                        <didl:Resource ref="%s" mimeType="%s"/>
                    </didl:Component>
                </didl:Item>""" % (escapeXml(comm.urlQuote(
            uriref[0].strip())), escapeXml(mimetype[0]))
示例#4
0
 def description(self):
     yield """<description xmlns="http://www.openarchives.org/OAI/2.0/">"""
     yield """<branding xmlns="http://www.openarchives.org/OAI/2.0/branding/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/branding/
                             http://www.openarchives.org/OAI/2.0/branding.xsd">"""
     yield "<collectionIcon>"
     yield "     <url>%s</url>" % escapeXml(self._url)
     yield "     <link>%s</link>" % escapeXml(
         self._link) if self._link else ""
     yield "     <title>%s</title>" % self._title if self._title else ""
     yield "</collectionIcon>"
     yield "</branding>"
     yield "</description>"
示例#5
0
    def handleRequest(self, Body=b'', **kwargs):
        contentLength = kwargs['Headers'].get('Content-Length')
        if contentLength:
            Body = Body[:int(contentLength)]

        yield '\r\n'.join([
            'HTTP/1.0 200 Ok', 'Content-Type: text/xml; charset=utf-8\r\n', ''
        ])
        try:
            updateRequest = XML(Body)
            recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()')
            action = xpathFirst(updateRequest, 'ucp:action/text()')
            if self._allInvalid and action == "info:srw/action/1/replace":
                if 'oai:record:02' in recordId:
                    raise InvalidDataException()
                raise InvalidDataException('Invalid data')
            if recordId in self._raiseExceptionOnIds:
                raise Exception("ERROR")
            self._number += 1
            filename = '%05d_%s.updateRequest' % (self._number,
                                                  action.rsplit('/')[-1])
            with open(join(self._dumpdir, filename), 'w') as f:
                stdout.flush()
                f.write(lxmltostring(updateRequest, pretty_print=True))
            answer = RESPONSE_XML % {
                "operationStatus": "success",
                "diagnostics": ""
            }
        except InvalidDataException as e:
            answer = RESPONSE_XML % {
                "operationStatus": "fail",
                "diagnostics": DIAGNOSTIC_XML % {
                    'uri': 'info:srw/diagnostic/12/12',
                    'details': escapeXml(str(e)),
                    'message': 'Invalid data:  record rejected'
                }
            }
        except Exception as e:
            answer = RESPONSE_XML % {
                "operationStatus": "fail",
                "diagnostics": DIAGNOSTIC_XML % {
                    'uri': 'info:srw/diagnostic/12/1',
                    'details': escapeXml(format_exc()),
                    'message': 'Invalid component:  record rejected'
                }
            }
        import sys
        sys.stdout.flush()
        yield answer
    def _deleteRecord(self, recordId):
        print 'DELETING', recordId
        body = """<ucp:updateRequest xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version xmlns:srw="http://www.loc.gov/zing/srw/">1.0</srw:version>
    <ucp:action>info:srw/action/1/delete</ucp:action>
    <ucp:recordIdentifier>%s</ucp:recordIdentifier>
</ucp:updateRequest>""" % escapeXml(recordId)
        s = socket()
        s.connect(self._sruUpdateHostAndPort)
        s.send("POST /internal/update HTTP/1.0\r\n")
        s.send("Content-Type: text/xml\r\n")
        s.send("Content-Length: %s\r\n" % len(body))
        s.send("\r\n")
        s.sendall(body)

        # Receive the answer from the server and print it to stdout.
        response = s.recv(1024)
        while True:
            part = s.recv(1024)
            if part == "":
                break
            response += part
        s.close()
        if 'success' in response:
            return
        raise ValueError(response)
示例#7
0
 def handleRequest(self, Body='', **kwargs):
     yield '\r\n'.join(['HTTP/1.0 200 Ok', 'Content-Type: text/xml; charset=utf-8\r\n', ''])
     try:
         updateRequest = XML(Body)
         recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()')
         action = xpathFirst(updateRequest, 'ucp:action/text()')
         if self._allInvalid and action == "info:srw/action/1/replace":
             if 'oai:record:02' in recordId:
                 raise InvalidDataException()
             raise InvalidDataException('Invalid data')
         if recordId in self._raiseExceptionOnIds:
             raise Exception("ERROR")
         self._number +=1
         filename = '%05d_%s.updateRequest' %(self._number, action.rsplit('/')[-1])
         with open(join(self._dumpdir, filename), 'w') as f:
             stdout.flush()
             f.write(lxmltostring(updateRequest, pretty_print=True))
         answer = RESPONSE_XML % {
             "operationStatus": "success",
             "diagnostics": ""}
     except InvalidDataException, e:
         answer = RESPONSE_XML % {
             "operationStatus": "fail",
             "diagnostics": DIAGNOSTIC_XML % {
                 'uri': 'info:srw/diagnostic/12/12',
                 'details': escapeXml(str(e)),
                 'message': 'Invalid data:  record rejected'}}
示例#8
0
    def identify(self, arguments, **httpkwargs):
        responseDate = zuluTime()
        if list(arguments.keys()) != ['verb']:
            allArguments = sorted(list(arguments.keys()))
            additionalMessage = 'Argument(s) %s is/are illegal.' % ", ".join(
                '"%s"' % key for key in allArguments if key != 'verb')
            yield oaiError(
                'badArgument',
                additionalMessage=additionalMessage,
                arguments=arguments,
                requestUrl=self._repository.requestUrl(**httpkwargs),
                **httpkwargs)
            return

        repositoryIdentifier = self._repository.identifier
        descriptionRepositoryIdentifier = '' if not repositoryIdentifier else DESCRIPTION_REPOSITORY_IDENTIFIER % {
            'repositoryIdentifier': escapeXml(repositoryIdentifier)
        }

        values = {
            'repositoryName':
            escapeXml(self._repository.name),
            'baseURL':
            escapeXml(self._repository.requestUrl(**httpkwargs)),
            'adminEmails':
            ''.join([
                ADMIN_EMAIL % escapeXml(email)
                for email in [self._repository.adminEmail]
            ]),
            'deletedRecord':
            self.call.getDeletedRecordType(),
        }
        values.update(hardcoded_values)
        yield oaiHeader(self, responseDate)
        yield oaiRequestArgs(
            arguments,
            requestUrl=self._repository.requestUrl(**httpkwargs),
            **httpkwargs)
        yield '<Identify>'
        yield IDENTIFY % values
        yield descriptionRepositoryIdentifier
        yield TOOLKIT_DESCRIPTION
        yield self.all.description()
        yield '</Identify>'
        yield oaiFooter()
def _generateXml(fields):
    for (key, value) in fields:
        tags = key.split('.')
        for tag in tags:
            if not correctNameRe.match(tag):
                raise Fields2XmlException('Invalid key: "%s"' % key)
        yield ''.join("<%s>" % tag for tag in tags)
        yield escapeXml(value)
        yield ''.join("</%s>" % tag for tag in reversed(tags))
示例#10
0
def _generateXml(fields):
    for (key, value) in fields:
        tags = key.split('.')
        for tag in tags:
            if not correctNameRe.match(tag):                                
                raise Fields2XmlException('Invalid key: "%s"' % key)
        yield ''.join("<%s>" % tag for tag in tags)
        yield escapeXml(value)
        yield ''.join("</%s>" % tag for tag in reversed(tags))
示例#11
0
 def _getIdentifierDescriptor(self, lxmlNode):
     # Check geldige Identifier (feitelijk verplicht, hoewel vaak niet geimplemeteerd...)
     idee = lxmlNode.xpath(
         'self::didl:Item/didl:Descriptor/didl:Statement/dii:Identifier/text()',
         namespaces=self._nsMap)
     if len(idee) > 0:
         return descr_templ % ('<dii:Identifier>' + escapeXml(
             idee[0].strip()) + '</dii:Identifier>')
     else:
         return ''
示例#12
0
 def _findAndBindFirst(self, node, template, *xpaths):
     ## Will bind only the FIRST (xpath match/record) to the template. It will never return more than one templated element...
     items = []
     for p in xpaths:
         items += node.xpath(p, namespaces=self._nsMap)
         if len(items) > 1:
             break
     for item in items:
         return template % escapeXml(item)
     return ''
示例#13
0
    def _openSearchDescription(self, **kwargs):
        server = self._host if str(self._port) == "80" else "{}:{}".format(self._host, self._port)
        yield okXml
        yield """<?xml version="1.0" encoding="UTF-8"?>"""
        yield """<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
    <ShortName>%(shortname)s</ShortName>
    <Description>%(description)s</Description>
    <Url type="text/xml" method="get" template="http://%(server)s%(xmlTemplateQuery)s"/>
    %(htmlUrl)s
    <Url type="%(contentTypeSuggest)s" template="http://%(server)s%(path)s?%(templateQueryForSuggest)s"/>
</OpenSearchDescription>""" % {
            'contentTypeSuggest': CONTENT_TYPE_JSON_SUGGESTIONS,
            'shortname': self._shortname,
            'description': self._description,
            'server': server,
            'path': self._path,
            'xmlTemplateQuery': escapeXml(self._xmlTemplateQuery),
            'htmlUrl': '<Url type="text/html" method="get" template="http://{server}{template}"/>'.format(server=server, template=escapeXml(self._htmlTemplateQuery)) if self._htmlTemplateQuery else '',
            'templateQueryForSuggest': escapeXml(self._templateQueryForSuggest()),
        }
示例#14
0
    def _openSearchDescription(self, **kwargs):
        server = self._host if str(self._port) == "80" else "{}:{}".format(self._host, self._port)
        yield okXml
        yield """<?xml version="1.0" encoding="UTF-8"?>"""
        yield """<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
    <ShortName>%(shortname)s</ShortName>
    <Description>%(description)s</Description>
    <Url type="text/xml" method="get" template="http://%(server)s%(xmlTemplateQuery)s"/>
    %(htmlUrl)s
    <Url type="%(contentTypeSuggest)s" template="http://%(server)s%(path)s?%(templateQueryForSuggest)s"/>
</OpenSearchDescription>""" % {
            "contentTypeSuggest": CONTENT_TYPE_JSON_SUGGESTIONS,
            "shortname": self._shortname,
            "description": self._description,
            "server": server,
            "path": self._path,
            "xmlTemplateQuery": escapeXml(self._xmlTemplateQuery),
            "htmlUrl": '<Url type="text/html" method="get" template="http://{server}{template}"/>'.format(
                server=server, template=escapeXml(self._htmlTemplateQuery)
            )
            if self._htmlTemplateQuery
            else "",
            "templateQueryForSuggest": escapeXml(self._templateQueryForSuggest()),
        }
 def description(self):
     yield """<description xmlns="http://www.openarchives.org/OAI/2.0/">"""
     yield """<Service xmlns="https://www.openaire.eu/cerif-profile/1.1/">"""
     yield """<Compatibility xmlns="https://www.openaire.eu/cerif-profile/vocab/OpenAIRE_Service_Compatibility">https://www.openaire.eu/cerif-profile/vocab/OpenAIRE_Service_Compatibility#1.1</Compatibility>"""
     yield "<Acronym>%s</Acronym>" % escapeXml(self._acronym)
     yield """<Name xml:lang="en">%s</Name>""" % escapeXml(self._name)
     yield """<Description xml:lang="en">%s</Description>""" % escapeXml(
         self._description)
     yield "<WebsiteURL>%s</WebsiteURL>" % escapeXml(self._website)
     yield "<OAIPMHBaseURL>%s</OAIPMHBaseURL>" % escapeXml(self._baseurl)
     yield "<SubjectHeadingsURL>%s</SubjectHeadingsURL>" % escapeXml(
         self._subjectheading)
     yield "<Owner>"
     yield """    <OrgUnit id="%s">""" % escapeXml(self._orgunitid)
     yield "        <Acronym>%s</Acronym>" % escapeXml(self._owneracronym)
     yield "    </OrgUnit>"
     yield "</Owner>"
     yield "</Service>"
     yield "</description>"
示例#16
0
def main(reactor, port, directory):
    dumpdir = join(directory, 'dump')
    isdir(dumpdir) or makedirs(dumpdir)
    dump = Dump(dumpdir)
    oaiStorage = MultiSequentialStorage(join(directory, 'storage'))
    oaiJazz = OaiJazz(join(directory, 'oai'))
    server = be(
        (Observable(),
         (ObservableHttpServer(reactor, port), (PathFilter("/dump"), (dump, )),
          (PathFilter("/control"), (
              Control(),
              (dump, ),
              (Log(), ),
          )), (PathFilter('/oai'), (Log(), (
              OaiPmh(repositoryName="Oai Test Server",
                     adminEmail="*****@*****.**",
                     batchSize=10),
              (oaiStorage, ),
              (oaiJazz, ),
          ))), (PathFilter('/badoai'), (Log(), (BadOai(), ))),
          (PathFilter("/log"), (RetrieveLog(), (Log(), ))),
          (PathFilter("/ready"), (StringServer('yes',
                                               ContentTypePlainText), )))))
    list(compose(server.once.observer_init()))
    oaiJazz.updateMetadataFormat(
        prefix="oai_dc",
        schema="http://www.openarchives.org/OAI/2.0/oai_dc.xsd",
        namespace="http://www.openarchives.org/OAI/2.0/oai_dc/")
    for i in range(1, 16):
        if i == 2:
            identifier = 'oai:record:02/&gkn'
        else:
            identifier = 'oai:record:%02d' % i
        oaiStorage.addData(
            identifier=identifier,
            name='oai_dc',
            data=bytes(
                '''<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd"><dc:identifier>%s</dc:identifier><dc:title>Title is √</dc:title></oai_dc:dc>'''
                % escapeXml(identifier),
                encoding='utf-8'))
        oaiJazz.addOaiRecord(identifier=identifier,
                             metadataPrefixes=['oai_dc'])
        if i in [3, 6]:
            list(compose(oaiJazz.delete(identifier=identifier)))
示例#17
0
 def asXml(self):
     return """<{username}>{xml}</{username}>""".format(
         username=self.username,
         xml=''.join("<{tag}>{value}</{tag}>".format(
             tag=tag,
             value=escapeXml(getattr(self, tag, ''))) for tag in self.ATTRIBUTES))
示例#18
0
def main(reactor, port, directory):
    dumpdir = join(directory, 'dump')
    isdir(dumpdir) or makedirs(dumpdir)
    dump = Dump(dumpdir)
    oaiStorage = MultiSequentialStorage(join(directory, 'storage'))
    oaiJazz = OaiJazz(join(directory, 'oai'))
    server = be(
        (Observable(),
            (ObservableHttpServer(reactor, port),
                (PathFilter("/dump"),
                    (dump,)
                ),
                (PathFilter("/control"),
                    (Control(),
                        (dump,),
                        (Log(),),
                    )
                ),
                (PathFilter('/oai'),
                    (Log(),
                        (OaiPmh(repositoryName="Oai Test Server", adminEmail="*****@*****.**", batchSize=10),
                            (oaiStorage,),
                            (oaiJazz,),
                        )
                    )
                ),
                (PathFilter("/log"),
                    (RetrieveLog(),
                        (Log(),)
                    )
                ),
                (PathFilter("/ready"),
                    (StringServer('yes', ContentTypePlainText),)
                )
            )
        )
    )
    list(compose(server.once.observer_init()))
    for i in range(1,16):
        if i == 2:
            identifier = 'oai:record:02/&gkn'
        else:
            identifier = 'oai:record:%02d' % i
        oaiStorage.addData(identifier=identifier, name='oai_dc', data='''<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd"><dc:identifier>%s</dc:identifier></oai_dc:dc>''' % escapeXml(identifier))
        oaiJazz.addOaiRecord(identifier=identifier, metadataFormats=[('oai_dc', 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd', 'http://www.openarchives.org/OAI/2.0/oai_dc/')])
        if i in [3,6]:
            list(compose(oaiJazz.delete(identifier=identifier)))
示例#19
0
            else:
                raise ValueError("action value should refer to either 'create', 'replace' or 'delete'.")
            yield self._respond()
        except ValidateException, e:
            localLogCollector["invalid"] = recordId
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(
                diagnosticUri="info:srw/diagnostic/12/12",
                details=escapeXml(str(e)),
                message="Invalid data:  record rejected",
            )
        except Exception, e:
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(
                diagnosticUri="info:srw/diagnostic/12/1",
                details=escapeXml(format_exc()),
                message="Invalid component:  record rejected",
            )
        finally:
            self._collectLogForScope(sruRecordUpdate=localLogCollector)

    def _respond(self, diagnosticUri=None, details="", message=""):
        operationStatus = "success"
        diagnostics = ""
        if diagnosticUri:
            operationStatus = "fail"
            uri = diagnosticUri
            diagnostics = DIAGNOSTIC_XML % locals()
        yield RESPONSE_XML % locals()

    def _log(self, data, error=None, localLogCollector=None):
def _escapeXml(value):
    return escapeXml(value).replace('"', "&quot;")
示例#21
0
 def delete(self, identifier):
     path = self._path('update')
     yield self._send(path=path,
                      body="<delete><id>%s</id></delete>" %
                      escapeXml(identifier))
示例#22
0
    def _getObjectfiles(self, lxmlNode):
        of_container = ''
        objectfiles = lxmlNode.xpath(
            '//didl:DIDL/didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/objectFile"]',
            namespaces=self._nsMap)
        if len(objectfiles) == 0:
            objectfiles = lxmlNode.xpath(
                '//didl:DIDL/didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/objectFile"]',
                namespaces=self._nsMap)
            if len(objectfiles) > 0:
                self.do.logMsg(self._uploadid, LOGGER6, prefix=STR_DIDL)
        if len(objectfiles) == 0:
            objectfiles = lxmlNode.xpath(
                '//didl:DIDL/didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/objectFile"]',
                namespaces=self._nsMap)
            if len(objectfiles) > 0:
                self.do.logMsg(self._uploadid, LOGGER7, prefix=STR_DIDL)
        for objectfile in objectfiles:
            #1:Define correct ObjectFile descriptor:
            of_container += '<didl:Item><didl:Descriptor><didl:Statement mimeType="application/xml"><rdf:type rdf:resource="info:eu-repo/semantics/objectFile"/></didl:Statement></didl:Descriptor>'

            #2: Check geldige Identifier (feitelijk verplicht, hoewel vaak niet geimplemeteerd...)
            pi = objectfile.xpath(
                'self::didl:Item/didl:Descriptor/didl:Statement/dii:Identifier/text()',
                namespaces=self._nsMap)
            if len(pi) > 0:
                of_container += descr_templ % ('<dii:Identifier>' + escapeXml(
                    pi[0].strip()) + '</dii:Identifier>')

        #3: Check op geldige AccessRights:
            arights = objectfile.xpath(
                'self::didl:Item/didl:Descriptor/didl:Statement/dcterms:accessRights/text()',
                namespaces=self._nsMap)
            if len(arights) > 0:
                for key, value in accessRights.iteritems():
                    if arights[0].strip().lower().find(key) >= 0:
                        of_container += descr_templ % (
                            '<dcterms:accessRights>' + value +
                            '</dcterms:accessRights>')
                        break
                else:
                    raise ValidateException(
                        formatExceptionLine(arights[0] + EXCEPTION12,
                                            prefix=STR_DIDL))
            else:
                raise ValidateException(
                    formatExceptionLine(EXCEPTION8, prefix=STR_DIDL))

        #4: Check geldige datemodified (feitelijk verplicht, hoewel vaak niet geimplemeteerd...)
            modified = objectfile.xpath(
                'self::didl:Item/didl:Descriptor/didl:Statement/dcterms:modified/text()',
                namespaces=self._nsMap)
            if len(modified) > 0 and comm.isISO8601(modified[0]):
                of_container += descr_templ % ('<dcterms:modified>' +
                                               modified[0].strip() +
                                               '</dcterms:modified>')

        #5: Check for 'file' description:
            descr = objectfile.xpath(
                'self::didl:Item/didl:Descriptor/didl:Statement/dc:description/text()',
                namespaces=self._nsMap)
            if len(descr) > 0:
                of_container += descr_templ % ('<dc:description>' + escapeXml(
                    descr[0].strip()) + '</dc:description>')

        ## SKIPPING: Not in EduStandaard.
        #6.0: Check for embargo:
        #    embargo = objectfile.xpath('self::didl:Item/didl:Descriptor/didl:Statement/dcterms:available/text()', namespaces=self._nsMap)
        #    if len(embargo) > 0 and comm.isISO8601(embargo[0]):
        #        of_container += descr_templ % ('<dcterms:available>'+embargo[0].strip()+'</dcterms:available>')

        ## SKIPPING: Not in EduStandaard.
        #6.1: Check for dateSubmitted:
        #    dembargo = objectfile.xpath('self::didl:Item/didl:Descriptor/didl:Statement/dcterms:dateSubmitted/text()', namespaces=self._nsMap)
        #    if len(dembargo) > 0 and comm.isISO8601(dembargo[0]):
        #        of_container += descr_templ % ('<dcterms:dateSubmitted>'+dembargo[0].strip()+'</dcterms:dateSubmitted>')
        #    else:
        #        #6.2: Check for issued (depricated, normalize to dateSubmitted):
        #        issued = objectfile.xpath('self::didl:Item/didl:Descriptor/didl:Statement/dcterms:issued/text()', namespaces=self._nsMap)
        #        if len(issued) > 0 and comm.isISO8601(issued[0]):
        #            of_container += descr_templ % ('<dcterms:dateSubmitted>'+issued[0].strip()+'</dcterms:dateSubmitted>')

        #7: Check for published version(author/publisher):
            pubVersion = objectfile.xpath(
                'self::didl:Item/didl:Descriptor/didl:Statement/rdf:type/@rdf:resource',
                namespaces=self._nsMap)
            if len(
                    pubVersion
            ) > 0:  ## Both (author/publisher) may be available: we'll take the first one...
                for key, value in pubVersions.iteritems():
                    if pubVersion[0].strip().lower().find(key) >= 0:
                        of_container += descr_templ % (
                            '<rdf:type rdf:resource="' + value + '"/>')
                        break

        #8:Check for MANDATORY resources and mimetypes:
            didl_resources = objectfile.xpath(
                'self::didl:Item/didl:Component/didl:Resource[@mimeType and @ref]',
                namespaces=self._nsMap)
            resources = ''
            _url_list = []
            for resource in didl_resources:
                mimeType = resource.xpath('self::didl:Resource/@mimeType',
                                          namespaces=self._nsMap)
                uri = resource.xpath('self::didl:Resource/@ref',
                                     namespaces=self._nsMap)
                ## We need both mimeType and URI: (MIMETYPE is required by DIDL schema, @ref not).
                if len(mimeType) > 0 and len(uri) > 0:
                    if not comm.isMimeType(mimeType[0]):
                        self.do.logMsg(self._uploadid,
                                       LOGGER8 + mimeType[0],
                                       prefix=STR_DIDL)
                    if comm.isURL(uri[0].strip()):
                        resources += """<didl:Resource mimeType="%s" ref="%s"/>""" % (
                            escapeXml(mimeType[0].strip()),
                            escapeXml(comm.urlQuote(uri[0].strip())))
                        _url_list.append(
                            """<didl:Resource mimeType="%s" ref="%s"/>""" %
                            (escapeXml(mimeType[0].strip()),
                             escapeXml(comm.urlQuote(uri[0].strip()))))
                    else:
                        raise ValidateException(
                            formatExceptionLine(EXCEPTION9 + uri[0],
                                                prefix=STR_DIDL))

            if resources != '':
                of_container += """<didl:Component>
                %s
            </didl:Component>""" % (resources)
            else:
                raise ValidateException(
                    formatExceptionLine(EXCEPTION10, prefix=STR_DIDL))
            of_container += '</didl:Item>'
        return of_container
                yield self.all.delete(identifier=recordId)
            else:
                raise ValueError(
                    "action value should refer to either 'create', 'replace' or 'delete'."
                )
            yield self._respond()
        except ValidateException, e:
            localLogCollector['invalid'] = recordId
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(diagnosticUri='info:srw/diagnostic/12/12',
                                details=escapeXml(str(e)),
                                message='Invalid data:  record rejected')
        except Exception, e:
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(diagnosticUri='info:srw/diagnostic/12/1',
                                details=escapeXml(format_exc()),
                                message='Invalid component:  record rejected')
        finally:
            self._collectLogForScope(sruRecordUpdate=localLogCollector)

    def _respond(self, diagnosticUri=None, details='', message=''):
        operationStatus = "success"
        diagnostics = ""
        if diagnosticUri:
            operationStatus = "fail"
            uri = diagnosticUri
            diagnostics = DIAGNOSTIC_XML % locals()
        yield RESPONSE_XML % locals()

    def _log(self, data, error=None, localLogCollector=None):
        if not error is None:
def _escapeXml(value):
    return escapeXml(value).replace('"', "&quot;")
示例#25
0
    def _convertNODRecord2smods(self, lxmlNode):
        

        penvoerder, penvoerder_en, abstract, abstract_en, locatie, status, knaw_long = '', '', '', '', '', '', None
        # ORGANISATIE:
        if lxmlNode.xpath("boolean(count(//org:organisatie))", namespaces=namespaceMap):
            title = self._findAndBind(lxmlNode, '\n<titleInfo><title>%s</title></titleInfo>', '//org:naam_nl/text()')
            title_en = self._findAndBind(lxmlNode, '\n<titleInfo xml:lang="en"><title>%s</title></titleInfo>', '//org:naam_en/text()')
        
            taak = lxmlNode.xpath("//org:taak_nl/text()", namespaces=namespaceMap)
            if taak: abstract = '\n<abstract>%s</abstract>' % escapeXml(taak[0][:self._truncate_chars])
            taak = lxmlNode.xpath("//org:taak_en/text()", namespaces=namespaceMap)
            if taak: abstract_en = '\n<abstract xml:lang="en">%s</abstract>' % escapeXml(taak[0][:self._truncate_chars])
        
            # Locatie: In Dutch (NL) only!
            location = lxmlNode.xpath("//org:locatie/text()", namespaces=namespaceMap)
            if location: locatie = '\n<locatie>%s</locatie>' % escapeXml(location[0])
        
            knaw_long = '''<knaw_long xmlns="http://www.knaw.nl/narcis/1.0/long/"><metadata>%s%s<genre>organisation</genre>%s%s%s</metadata></knaw_long>''' % (
                title, title_en, locatie, abstract, abstract_en)
        # ACTIVITEIT:
        elif lxmlNode.xpath("boolean(count(//ond:activiteit))", namespaces=namespaceMap):
            title = self._findAndBind(lxmlNode, '\n<titleInfo><title>%s</title></titleInfo>', '//ond:title_nl/text()')
            title_en = self._findAndBind(lxmlNode, '\n<titleInfo xml:lang="en"><title>%s</title></titleInfo>',
                                         '//ond:title_en/text()')
        
            # Samenvatting:
            taak = lxmlNode.xpath("//ond:summary_nl/text()", namespaces=namespaceMap)
            if taak: abstract = '\n<abstract>%s</abstract>' % escapeXml(taak[0][:self._truncate_chars])
            taak = lxmlNode.xpath("//ond:summary_en/text()", namespaces=namespaceMap)
            if taak: abstract_en = '\n<abstract xml:lang="en">%s</abstract>' % escapeXml(taak[0][:self._truncate_chars])
        
            # Penvoerder:
            taak = lxmlNode.xpath("//ond:penvoerder/ond:naam[@xml:lang='nl']/text()", namespaces=namespaceMap)
            if taak: penvoerder = '\n<penvoerder xml:lang="nl">%s</penvoerder>' % escapeXml(taak[0])
            taak = lxmlNode.xpath("//ond:penvoerder/ond:naam[@xml:lang='en']/text()", namespaces=namespaceMap)
            if taak: penvoerder_en = '\n<penvoerder xml:lang="en">%s</penvoerder>' % escapeXml(taak[0])
        
            # Status onderzoek (C/D):
            taak = lxmlNode.xpath("//ond:status/text()", namespaces=namespaceMap)
            if taak: status = '\n<status>%s</status>' % escapeXml(taak[0])
        
            knaw_long = '''<knaw_long xmlns="http://www.knaw.nl/narcis/1.0/long/"><metadata>%s%s<genre>research</genre>%s%s%s%s%s</metadata></knaw_long>''' % (
                title, title_en, penvoerder, penvoerder_en, abstract, abstract_en, status)
        # PERSOON:
        elif lxmlNode.xpath("boolean(count(//prs:persoon))", namespaces=namespaceMap):
            # TODO: Moeten de expertise keywords wel in de abstract verschijnen?
            fullName = lxmlNode.xpath("//prs:fullName/text()", namespaces=namespaceMap)
            if not fullName:
                fullName.append('n.a.')
            else:
                fullName[0] = escapeXml(fullName[0])
            title = ('\n<titleInfo><title>%s</title></titleInfo>' % fullName[0])
            title_en = ('\n<titleInfo xml:lang="en"><title>%s</title></titleInfo>' % fullName[0])
        
            taak = lxmlNode.xpath("//prs:expertise_nl/text()", namespaces=namespaceMap)
            if taak: abstract = '\n<abstract>%s</abstract>' % escapeXml(taak[0][:self._truncate_chars])
            taak = lxmlNode.xpath("//prs:expertise_en/text()", namespaces=namespaceMap)
            if taak: abstract_en = '\n<abstract xml:lang="en">%s</abstract>' % escapeXml(taak[0][:self._truncate_chars])
        
            # Copy ALL nameIdentifiers + OLD <dai> tag ########
            str_name = '\n<name><type>personal</type><name>' + fullName[0] + '</name>'
            # Kijk of er een 'oude' dai-only tag aanwezig is:
            dai = self._findAndBind(lxmlNode, '\n<dai>info:eu-repo/dai/nl/%s</dai>', '//prs:persoon/prs:dai/text()')
            if dai: str_name += dai
        
            nids = lxmlNode.xpath("//prs:persoon/prs:nameIdentifier", namespaces=namespaceMap)
        
            for nid in nids:  # serialize complete tag and remove default namespace...
                nid_type = nid.xpath('self::prs:nameIdentifier/@type', namespaces=namespaceMap)
                nid_val = nid.xpath('self::prs:nameIdentifier/text()', namespaces=namespaceMap)
                if len(nid_type) > 0 and len(nid_val) > 0:
                    str_name += '''<nameIdentifier type="%s">%s</nameIdentifier>''' % (nid_type[0], nid_val[0])
            str_name += '</name>'
            # Einde nameIdentifier ########
        
            knaw_long = '''<knaw_long xmlns="http://www.knaw.nl/narcis/1.0/long/"><metadata>%s%s%s<genre>person</genre>%s%s</metadata></knaw_long>''' % (
                title, title_en, str_name, abstract, abstract_en)
        
        # We'll return a Lxml node here:
        parser = etree.XMLParser(remove_blank_text=True)
        if knaw_long is not None:
            try:
                return parse(StringIO(knaw_long), parser)
            except:
                print 'Error while parsing', knaw_long
                raise
        return knaw_long
示例#26
0
 def delete(self, identifier):
     path = self._path("update")
     yield self._send(path=path, body="<delete><id>%s</id></delete>" % escapeXml(identifier))
class SruRecordUpdate(Observable):
    def __init__(self,
                 name=None,
                 stderr=stderr,
                 sendRecordData=True,
                 logErrors=True,
                 enableCollectLog=False):
        Observable.__init__(self, name=name)
        self._stderr = stderr
        self._logErrors = logErrors
        self._sendRecordData = sendRecordData
        self._collectLogForScope = lambda **kwargs: None
        if enableCollectLog:
            self._collectLogForScope = collectLogForScope

    def handleRequest(self, Body="", **kwargs):
        yield okXml
        if not Body:
            yield self._respond(
                diagnosticUri='info:srw/diagnostic/12/9',
                details='Update request lacks a record in its body.',
                message='Missing mandatory element:  record rejected')
            return

        localLogCollector = dict()
        try:
            try:
                lxmlNode = parse(StringIO(Body))
            except XMLSyntaxError, e:
                self._log(Body, localLogCollector=localLogCollector)
                raise
            updateRequest = xpathFirst(lxmlNode,
                                       '/*[local-name()="updateRequest"]')
            recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()')
            if recordId is None or recordId.strip() == '':
                raise ValueError("recordIdentifier is mandatory.")
            recordId = str(recordId)
            action = xpathFirst(updateRequest, 'ucp:action/text()')
            action = action.partition("info:srw/action/1/")[-1]
            if action in ['create', 'replace']:
                record = xpathFirst(updateRequest, 'srw:record')
                lxmlNode = record
                if self._sendRecordData:
                    lxmlNode = xpathFirst(record, 'srw:recordData/child::*')
                recordSchema = xpathFirst(record, 'srw:recordSchema/text()')
                localLogCollector['add'] = recordId
                yield self.all.add(
                    identifier=recordId,
                    partname=recordSchema,
                    lxmlNode=ElementTree(lxmlElementUntail(lxmlNode)),
                )
            elif action == 'delete':
                localLogCollector['delete'] = recordId
                yield self.all.delete(identifier=recordId)
            else:
                raise ValueError(
                    "action value should refer to either 'create', 'replace' or 'delete'."
                )
            yield self._respond()
        except ValidateException, e:
            localLogCollector['invalid'] = recordId
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(diagnosticUri='info:srw/diagnostic/12/12',
                                details=escapeXml(str(e)),
                                message='Invalid data:  record rejected')
示例#28
0
    def _getTopItem(self, lxmlNode):
        ## Wrappers:
        pid, modified, mimetype, pidlocation = '', '', "application/xml", ''

        #1:     Get persistentIdentifier:
        pidlist = lxmlNode.xpath(
            '//didl:DIDL/didl:Item/didl:Descriptor/didl:Statement/dii:Identifier/text()',
            namespaces=self._nsMap)
        if len(pidlist) > 0:
            pid = pidlist[0].strip()
            if not comm.isURNNBN(pid):
                raise ValidateException(
                    formatExceptionLine(EXCEPTION0 + pid, prefix=STR_DIDL))
        else:
            raise ValidateException(
                formatExceptionLine(EXCEPTION1, prefix=STR_DIDL))

#2:     Get toplevel modificationDate: comm.isISO8601()
        tl_modified = lxmlNode.xpath(
            '//didl:DIDL/didl:Item/didl:Descriptor/didl:Statement/dcterms:modified/text()',
            namespaces=self._nsMap)
        ## Check op geldig/aanwezigheid tlModified, anders exception:
        if len(tl_modified) > 0 and not comm.isISO8601(tl_modified[0]):
            raise ValidateException(
                formatExceptionLine(EXCEPTION2 + tl_modified[0],
                                    prefix=STR_DIDL))
        elif len(tl_modified) == 0:
            raise ValidateException(
                formatExceptionLine(EXCEPTION3, prefix=STR_DIDL))

        ## Get all modified dates:
        all_modified = lxmlNode.xpath(
            '//didl:Item/didl:Descriptor/didl:Statement/dcterms:modified/text()',
            namespaces=self._nsMap)

        ## Get most recent date from all items, to add to toplevelItem:
        if len(all_modified) > 0:
            datedict = {}
            for date in all_modified:
                if comm.isISO8601(date.strip()):
                    #datedict[parseDate(date.strip())] = date.strip()
                    pd = parseDate(date.strip())
                    datedict["%s %s" %
                             (str(pd.date()), str(pd.time()))] = date.strip()

            ## Get first sorted key:
            for key in reversed(sorted(datedict.iterkeys())):
                modified = datedict[key]
                break
        if not tl_modified[0].strip() == modified:
            self.do.logMsg(self._uploadid, LOGGER1, prefix=STR_DIDL)

#3:     Get PidResourceMimetype
        mimetypelist = lxmlNode.xpath(
            '//didl:DIDL/didl:Item/didl:Component/didl:Resource/@mimeType',
            namespaces=self._nsMap)
        if len(mimetypelist) > 0:
            mimetype = mimetypelist[0].strip()
            if not comm.isMimeType(mimetype):
                self.do.logMsg(self._uploadid,
                               LOGGER2 + mimetype,
                               prefix=STR_DIDL)

#4:     Get PidResourceLocation:
        pidlocation = self._findAndBindFirst(
            lxmlNode,
            '%s',
            '//didl:DIDL/didl:Item/didl:Component/didl:Resource/@ref',
            '//didl:DIDL/didl:Item/didl:Component/didl:Resource/text()'
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/humanStartPage"]/didl:Component/didl:Resource/@ref',  #DIDL 3.0
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/humanStartPage"]/didl:Component/didl:Resource/@ref',  #DIDL 3.0, without @rdf:resource
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/humanStartPage"]/didl:Component/didl:Resource/@ref',  #fallback DIDL 2.3.1
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/objectFile"]/didl:Component/didl:Resource/@ref',  #fallback DIDL 3.0
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/objectFile"]/didl:Component/didl:Resource/@ref',  #fallback DIDL 3.0, without @rdf:resource
            '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/objectFile"]/didl:Component/didl:Resource/@ref'  #fallback DIDL 2.3.1
        ).strip()

        if pidlocation == '':
            raise ValidateException(
                formatExceptionLine(EXCEPTION4, prefix=STR_DIDL))
        if not comm.isURL(pidlocation):
            raise ValidateException(
                formatExceptionLine(EXCEPTION5 + pidlocation, prefix=STR_DIDL))

        return """<didl:Item>
        <didl:Descriptor><didl:Statement mimeType="application/xml"><dii:Identifier>%s</dii:Identifier></didl:Statement></didl:Descriptor>
        <didl:Descriptor><didl:Statement mimeType="application/xml"><dcterms:modified>%s</dcterms:modified></didl:Statement></didl:Descriptor>
        <didl:Component><didl:Resource mimeType="%s" ref="%s"/></didl:Component>""" % (
            escapeXml(pid), modified, escapeXml(mimetype),
            comm.urlQuote(pidlocation))
示例#29
0
 def _findAndBind(self, node, template, *xpaths):
     items = []
     for p in xpaths:
         items += node.xpath(p, namespaces=namespaceMap)
     return '\n'.join(template % escapeXml(item) for item in items)
示例#30
0
 def fieldStatement(key, value):
     return '<field name="%s">%s</field>' % (escapeXml(key),
                                             escapeXml(value))
示例#31
0
 def oaiRecord(record, metadataPrefix, fetchedRecords=None):
     yield '<mock:record xmlns:mock="uri:mock">%s/%s</mock:record>' % (escapeXml(record.identifier), escapeXml(metadataPrefix))
示例#32
0
    def handleRequest(self, Body="", **kwargs):
        if type(Body) is str:
            Body = bytes(Body, encoding="utf-8")

        yield okXml
        if not Body:
            yield self._respond(
                diagnosticUri='info:srw/diagnostic/12/9',
                details='Update request lacks a record in its body.',
                message='Missing mandatory element:  record rejected')
            return

        localLogCollector = dict()
        try:
            try:
                lxmlNode = parse(BytesIO(Body))
            except XMLSyntaxError as e:
                self._log(Body, localLogCollector=localLogCollector)
                raise
            updateRequest = xpathFirst(lxmlNode,
                                       '/*[local-name()="updateRequest"]')
            recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()')
            if recordId is None or recordId.strip() == '':
                raise ValueError("recordIdentifier is mandatory.")
            recordId = str(recordId)
            action = xpathFirst(updateRequest, 'ucp:action/text()')
            action = action.partition("info:srw/action/1/")[-1]
            if action in ['create', 'replace']:
                record = xpathFirst(updateRequest, 'srw:record')
                lxmlNode = record
                if self._sendRecordData:
                    lxmlNode = xpathFirst(record, 'srw:recordData/child::*')
                recordSchema = xpathFirst(record, 'srw:recordSchema/text()')
                localLogCollector['add'] = recordId
                yield self.all.add(
                    identifier=recordId,
                    partname=recordSchema,
                    lxmlNode=ElementTree(lxmlElementUntail(lxmlNode)),
                )
            elif action == 'delete':
                localLogCollector['delete'] = recordId
                if self._supportDeleteRecord:
                    yield self.all.deleteRecord(identifier=recordId,
                                                record=xpathFirst(
                                                    updateRequest,
                                                    'srw:record'))
                else:
                    yield self.all.delete(identifier=recordId)
            else:
                raise ValueError(
                    "action value should refer to either 'create', 'replace' or 'delete'."
                )
            yield self._respond()
        except ValidateException as e:
            localLogCollector['invalid'] = recordId
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(diagnosticUri='info:srw/diagnostic/12/12',
                                details=escapeXml(str(e)),
                                message='Invalid data:  record rejected')
        except Exception as e:
            self._log(Body, e, localLogCollector=localLogCollector)
            yield self._respond(diagnosticUri='info:srw/diagnostic/12/1',
                                details=escapeXml(format_exc()),
                                message='Invalid component:  record rejected')
        finally:
            self._collectLogForScope(sruRecordUpdate=localLogCollector)
示例#33
0
 def fieldStatement(key, value):
     return '<field name="%s">%s</field>' % (escapeXml(key), escapeXml(value))
示例#34
0
    def builder(sp,
                authnsign=False,
                wsign=False,
                valid_until=None,
                cache_duration=None,
                contacts=None,
                organization=None):
        """
        Builds the metadata of the SP

        :param sp: The SP data
        :type sp: string

        :param authnsign: authnRequestsSigned attribute
        :type authnsign: string

        :param wsign: wantAssertionsSigned attribute
        :type wsign: string

        :param valid_until: Metadata's expiry date
        :type valid_until: string|DateTime|Timestamp

        :param cache_duration: Duration of the cache in seconds
        :type cache_duration: int|string

        :param contacts: Contacts info
        :type contacts: dict

        :param organization: Organization info
        :type organization: dict
        """
        if valid_until is None:
            valid_until = int(time()) + OneLogin_Saml2_Metadata.TIME_VALID
        if not isinstance(valid_until, basestring):
            if isinstance(valid_until, datetimeClass):
                valid_until_time = valid_until.timetuple()
            else:
                valid_until_time = gmtime(valid_until)
            valid_until_str = strftime(r'%Y-%m-%dT%H:%M:%SZ', valid_until_time)
        else:
            valid_until_str = valid_until

        if cache_duration is None:
            cache_duration = OneLogin_Saml2_Metadata.TIME_CACHED
        if not isinstance(cache_duration, basestring):
            cache_duration_str = 'PT%sS' % cache_duration  # 'P'eriod of 'T'ime x 'S'econds
        else:
            cache_duration_str = cache_duration

        if contacts is None:
            contacts = {}
        if organization is None:
            organization = {}

        str_attribute_consuming_service = ''
        if 'attributeConsumingService' in sp and len(
                sp['attributeConsumingService']):
            attr_cs_desc_str = ''
            if "serviceDescription" in sp['attributeConsumingService']:
                attr_cs_desc_str = """            <md:ServiceDescription xml:lang="en">%s</md:ServiceDescription>
""" % sp['attributeConsumingService']['serviceDescription']

            requested_attribute_data = []
            for req_attribs in sp['attributeConsumingService'][
                    'requestedAttributes']:
                req_attr_nameformat_str = req_attr_friendlyname_str = req_attr_isrequired_str = ''
                req_attr_aux_str = ' />'

                if 'nameFormat' in req_attribs.keys(
                ) and req_attribs['nameFormat']:
                    req_attr_nameformat_str = " NameFormat=\"%s\"" % req_attribs[
                        'nameFormat']
                if 'friendlyName' in req_attribs.keys(
                ) and req_attribs['friendlyName']:
                    req_attr_nameformat_str = " FriendlyName=\"%s\"" % req_attribs[
                        'friendlyName']
                if 'isRequired' in req_attribs.keys(
                ) and req_attribs['isRequired']:
                    req_attr_isrequired_str = " isRequired=\"%s\"" % 'true' if req_attribs[
                        'isRequired'] else 'false'

                if 'attributeValue' in req_attribs.keys(
                ) and req_attribs['attributeValue']:
                    if isinstance(req_attribs['attributeValue'], basestring):
                        req_attribs['attributeValue'] = [
                            req_attribs['attributeValue']
                        ]

                    req_attr_aux_str = ">"
                    for attrValue in req_attribs['attributeValue']:
                        req_attr_aux_str += """
                <saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">%(attributeValue)s</saml:AttributeValue>""" % \
                            {
                                'attributeValue': attrValue
                            }
                    req_attr_aux_str += """
            </md:RequestedAttribute>"""

                requested_attribute = """            <md:RequestedAttribute Name="%(req_attr_name)s"%(req_attr_nameformat_str)s%(req_attr_friendlyname_str)s%(req_attr_isrequired_str)s%(req_attr_aux_str)s""" % \
                    {
                        'req_attr_name': req_attribs['name'],
                        'req_attr_nameformat_str': req_attr_nameformat_str,
                        'req_attr_friendlyname_str': req_attr_friendlyname_str,
                        'req_attr_isrequired_str': req_attr_isrequired_str,
                        'req_attr_aux_str': req_attr_aux_str
                    }

                requested_attribute_data.append(requested_attribute)

            str_attribute_consuming_service = """        <md:AttributeConsumingService index="1">
            <md:ServiceName xml:lang="en">%(service_name)s</md:ServiceName>
%(attr_cs_desc)s%(requested_attribute_str)s
        </md:AttributeConsumingService>
""" % \
                {
                    'service_name': sp['attributeConsumingService']['serviceName'],
                    'attr_cs_desc': attr_cs_desc_str,
                    'requested_attribute_str': '\n'.join(requested_attribute_data)
                }

        sls = ''
        if 'singleLogoutService' in sp and 'url' in sp['singleLogoutService']:
            sls = """        <md:SingleLogoutService Binding="%(binding)s"
                                Location="%(location)s" />\n""" % \
                {
                    'binding': sp['singleLogoutService']['binding'],
                    'location': sp['singleLogoutService']['url'],
                }

        str_authnsign = 'true' if authnsign else 'false'
        str_wsign = 'true' if wsign else 'false'

        str_organization = ''
        if len(organization) > 0:
            organization_names = []
            organization_displaynames = []
            organization_urls = []
            for (lang, info) in organization.items():
                organization_names.append(
                    """        <md:OrganizationName xml:lang="%s">%s</md:OrganizationName>"""
                    % (lang, info['name']))
                organization_displaynames.append(
                    """        <md:OrganizationDisplayName xml:lang="%s">%s</md:OrganizationDisplayName>"""
                    % (lang, info['displayname']))
                organization_urls.append(
                    """        <md:OrganizationURL xml:lang="%s">%s</md:OrganizationURL>"""
                    % (lang, info['url']))
            org_data = '\n'.join(organization_names) + '\n' + '\n'.join(
                organization_displaynames) + '\n' + '\n'.join(
                    organization_urls)
            str_organization = """    <md:Organization>
%(org)s
    </md:Organization>\n""" % {
                'org': org_data
            }

        str_contacts = ''
        if len(contacts) > 0:
            contacts_info = []
            for (ctype, info) in contacts.items():
                surName = info.get('surName')
                surNameXml = ''
                if surName:
                    surNameXml = '\n        <md:SurName>%s</md:SurName>' % escapeXml(
                        surName)

                contact = """    <md:ContactPerson contactType="%(type)s">
        <md:GivenName>%(name)s</md:GivenName>%(surNameXml)s
        <md:EmailAddress>%(email)s</md:EmailAddress>
    </md:ContactPerson>""" % \
                    {
                        'type': ctype,
                        'name': info['givenName'],
                        'surNameXml': surNameXml,
                        'email': info['emailAddress'],
                    }
                contacts_info.append(contact)
            str_contacts = '\n'.join(contacts_info) + '\n'

        metadata = u"""<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
                     %(valid)s
                     %(cache)s
                     entityID="%(entity_id)s">
    <md:SPSSODescriptor AuthnRequestsSigned="%(authnsign)s" WantAssertionsSigned="%(wsign)s" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
%(sls)s        <md:NameIDFormat>%(name_id_format)s</md:NameIDFormat>
        <md:AssertionConsumerService Binding="%(binding)s"
                                     Location="%(location)s"
                                     index="1" />
%(attribute_consuming_service)s    </md:SPSSODescriptor>
%(organization)s%(contacts)s</md:EntityDescriptor>""" % \
            {
                'valid': ('validUntil="%s"' % valid_until_str) if valid_until_str else '',
                'cache': ('cacheDuration="%s"' % cache_duration_str) if cache_duration_str else '',
                'entity_id': sp['entityId'],
                'authnsign': str_authnsign,
                'wsign': str_wsign,
                'name_id_format': sp['NameIDFormat'],
                'binding': sp['assertionConsumerService']['binding'],
                'location': sp['assertionConsumerService']['url'],
                'sls': sls,
                'organization': str_organization,
                'contacts': str_contacts,
                'attribute_consuming_service': str_attribute_consuming_service
            }
        return metadata