Esempio n. 1
0
    def copy_gebruiksrechten(self, source_object: Gebruiksrechten,
                             destination_folder: Folder) -> Gebruiksrechten:
        """Copy a gebruiksrechten to a folder

        :param source_object: Gebruiksrechten, the gebruiksrechten to copy
        :param destination_folder: Folder, the folder in which to place the copied gebruiksrechten
        :return: the copied object
        """
        # copy the properties from the source object
        properties = {
            property_name: property_details["value"]
            for property_name, property_details in
            source_object.properties.items() if "cmis:" not in property_name
        }

        file_name = get_random_string()

        properties.update(
            **{
                "cmis:objectTypeId": source_object.objectTypeId,
                "cmis:name": file_name,
                mapper("kopie_van", type="gebruiksrechten"): source_object.
                objectId,  # Keep tack of where this is copied from.
                mapper("uuid", type="gebruiksrechten"): str(uuid.uuid4()),
            })

        data = create_json_request_body(destination_folder, properties)
        logger.debug("CMIS_ADAPTER: copy_gebruiksrechten: request data: %s",
                     data)

        json_response = self.post_request(self.root_folder_url, data=data)
        logger.debug("CMIS_ADAPTER: copy_gebruiksrechten: response data: %s",
                     json_response)

        return Gebruiksrechten(json_response)
Esempio n. 2
0
    def build_properties(cls, data: dict, new: bool = True) -> dict:

        props = {}
        for key, value in data.items():
            prop_name = mapper(key, type="document")
            if not prop_name:
                logger.debug(
                    "CMIS_ADAPTER: No property name found for key '%s'", key)
                continue
            props[prop_name] = value

        # For documents that are not new, the uuid shouldn't be written
        props.pop(mapper("uuid"), None)

        if new:
            # increase likelihood of uniqueness of title by appending a random string
            title, suffix = data.get("titel"), get_random_string()
            if title is not None:
                props["cmis:name"] = f"{title}-{suffix}"

            # For new documents, the uuid needs to be set
            new_uuid = str(uuid.uuid4())
            props[mapper("uuid", type="document")] = new_uuid

            # The identification needs to be set ONLY for newly created documents.
            # identificatie is immutable once the document is created
            if not props.get(mapper("identificatie")):
                prop_name = mapper("identificatie")
                props[prop_name] = new_uuid

        return props
Esempio n. 3
0
    def create_content_object(
            self,
            data: dict,
            object_type: str,
            destination_folder: Folder = None) -> CMISContentObject:
        """Create a Gebruiksrechten or a ObjectInformatieObject

        :param data: dict, properties of the object to create
        :param object_type: string, either "gebruiksrechten" or "oio"
        :param destination_folder: Folder, a folder where to create the object. If not provided,
            the object will be placed in a temporary folder.
        :return: Either a Gebruiksrechten or ObjectInformatieObject
        """
        assert object_type in [
            "gebruiksrechten",
            "oio",
        ], "'object_type' can be only 'gebruiksrechten' or 'oio'"

        if destination_folder is None:
            other_folder = self.get_or_create_other_folder()
            destination_folder = self.get_or_create_folder(
                "Related data", other_folder)

        properties = {
            mapper(key, type=object_type): value
            for key, value in data.items() if mapper(key, type=object_type)
        }

        json_data = {
            "objectId": destination_folder.objectId,
            "cmisaction": "createDocument",
            "propertyId[0]": "cmis:name",
            "propertyValue[0]": get_random_string(),
            "propertyId[1]": f"drc:{object_type}__uuid",
            "propertyValue[1]": str(uuid.uuid4()),
        }

        json_data["propertyId[2]"] = "cmis:objectTypeId"
        if "cmis:objectTypeId" in properties.keys():
            json_data["propertyValue[2]"] = properties.pop("cmis:objectTypeId")
        else:
            json_data[
                "propertyValue[2]"] = f"{self.get_object_type_id_prefix(object_type)}drc:{object_type}"

        prop_count = 3
        for prop_key, prop_value in properties.items():
            if isinstance(prop_value, datetime.date):
                prop_value = prop_value.strftime("%Y-%m-%dT%H:%M:%S.000Z")

            json_data[f"propertyId[{prop_count}]"] = prop_key
            json_data[f"propertyValue[{prop_count}]"] = prop_value
            prop_count += 1

        logger.debug("CMIS_ADAPTER: create_content_object: request data: %s",
                     json_data)
        json_response = self.post_request(self.root_folder_url, data=json_data)
        logger.debug("CMIS_ADAPTER: create_content_object: response data: %s",
                     json_response)

        if object_type == "gebruiksrechten":
            return Gebruiksrechten(json_response)
        elif object_type == "oio":
            return ObjectInformatieObject(json_response)
Esempio n. 4
0
def make_soap_envelope(
    cmis_action: str,
    auth: Tuple[str, str],
    repository_id: Optional[str] = None,
    properties: Optional[dict] = None,
    statement: Optional[str] = None,
    object_id: Optional[str] = None,
    folder_id: Optional[str] = None,
    content_id: Optional[str] = None,
    content_filename: Optional[str] = None,
    major: Optional[str] = None,
    checkin_comment: Optional[str] = None,
    source_folder_id: Optional[str] = None,
    target_folder_id: Optional[str] = None,
    continue_on_failure: Optional[str] = None,
) -> minidom.Document:
    """Create SOAP envelope from data provided

    :param cmis_action: string, the cmis action to perform
    :param auth: tuple, (username, password) for the DMS
    :param repository_id: ID of the main repository (e.g. 8ca7d93b-2286-44b7-bfce-487211e6e9af)
    :param properties: dictionary, properties of the object to create/update
    :param statement: str, SQL statement used in query requests
    :param object_id: str, ID of the node on which to act (e.g.
        workspace://SpacesStore/2bdd4f3d-851f-499b-99ec-142b82ce3c0d)
    :param folder_id: str, ID of a folder (e.g. needed when creating documents)
    :param content_id: str, ID of the content of a document (as the content will be a MTOM attachment)
    :param content_filename: str, name of the file that will be a MTOM attachment. Includes files extension.
    :param major: str, true or false whether the document being checked in is a major version
    :param checkin_comment: str, comment when checking in a document
    :param source_folder_id: str, folder objectId from which to copy a document
    :param target_folder_id: str, folder objectId to which to copy a document
    :param continue_on_failure: str, whether to continue deleting after an error in the deleteTree call
    :return: minidom document
    """

    xml_doc = minidom.Document()

    # Main soap entry element
    entry_element = xml_doc.createElement("soapenv:Envelope")
    entry_element.setAttribute("xmlns:soapenv",
                               "http://schemas.xmlsoap.org/soap/envelope/")
    entry_element.setAttribute(
        "xmlns:ns", "http://docs.oasis-open.org/ns/cmis/messaging/200908/")
    entry_element.setAttribute(
        "xmlns:ns1", "http://docs.oasis-open.org/ns/cmis/core/200908/")
    xml_doc.appendChild(entry_element)

    # Creates the security header
    header_element = xml_doc.createElement("soapenv:Header")
    security_header = xml_doc.createElement("wsse:Security")
    security_header.setAttribute(
        "xmlns:wsse",
        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
    )
    security_header.setAttribute(
        "xmlns:wsu",
        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
    )
    security_header_id = uuid.uuid4().hex

    # Username token
    username_token = xml_doc.createElement("wsse:UsernameToken")
    username_token.setAttribute(
        "wsu:Id",
        f"UsernameToken-{security_header_id}",
    )
    username_tag = xml_doc.createElement("wsse:Username")
    username_text = xml_doc.createTextNode(auth[0])
    username_tag.appendChild(username_text)
    username_token.appendChild(username_tag)

    password_tag = xml_doc.createElement("wsse:Password")
    password_tag.setAttribute(
        "Type",
        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText",
    )
    password_text = xml_doc.createTextNode(auth[1])
    password_tag.appendChild(password_text)
    username_token.appendChild(password_tag)

    security_header.appendChild(username_token)

    header_element.appendChild(security_header)
    entry_element.appendChild(header_element)

    # Time stamp
    time_stamp_tag = xml_doc.createElement("wsu:Timestamp")
    time_stamp_tag.setAttribute(
        "wsu:Id",
        f"TS-{security_header_id}",
    )

    created_tag = xml_doc.createElement("wsu:Created")
    created_text = xml_doc.createTextNode(
        timezone.now().strftime("%Y-%m-%dT%H:%M:%SZ"))
    created_tag.appendChild(created_text)
    time_stamp_tag.appendChild(created_tag)

    expires_tag = xml_doc.createElement("wsu:Expires")
    expires_text = xml_doc.createTextNode(
        (timezone.now() + timedelta(1)).strftime("%Y-%m-%dT%H:%M:%SZ"))
    expires_tag.appendChild(expires_text)
    time_stamp_tag.appendChild(expires_tag)

    security_header.appendChild(time_stamp_tag)

    # Body of the document
    body_element = xml_doc.createElement("soapenv:Body")

    # The name of the next tag is the name of the CMIS action to perform (e.g. createFolder)
    action_element = xml_doc.createElement(f"ns:{cmis_action}")

    # Repository ID
    if repository_id is not None:
        repo_element = xml_doc.createElement("ns:repositoryId")
        repo_text = xml_doc.createTextNode(str(repository_id))
        repo_element.appendChild(repo_text)
        action_element.appendChild(repo_element)

    # All the properties
    if properties is not None:
        properties_element = xml_doc.createElement("ns:properties")
        for prop_name, prop_dict in properties.items():
            property_element = xml_doc.createElement(
                f"ns1:{prop_dict['type']}")
            property_element.setAttribute("propertyDefinitionId", prop_name)

            value_element = xml_doc.createElement("ns1:value")
            value_text = xml_doc.createTextNode(prop_dict["value"])
            value_element.appendChild(value_text)
            property_element.appendChild(value_element)

            properties_element.appendChild(property_element)
        action_element.appendChild(properties_element)

    # For query requests, there is a SQL statement
    if statement is not None:
        query_element = xml_doc.createElement("ns:statement")
        query_text = xml_doc.createTextNode(statement)
        query_element.appendChild(query_text)
        action_element.appendChild(query_element)

    body_element.appendChild(action_element)

    # Folder ID
    if folder_id is not None:
        folder_element = xml_doc.createElement("ns:folderId")
        folder_text = xml_doc.createTextNode(str(folder_id))
        folder_element.appendChild(folder_text)
        action_element.appendChild(folder_element)

    # ObjectId
    if object_id is not None:
        object_id_element = xml_doc.createElement("ns:objectId")
        object_id_text = xml_doc.createTextNode(str(object_id))
        object_id_element.appendChild(object_id_text)
        action_element.appendChild(object_id_element)

    # File content
    if content_id is not None:
        filename = content_filename or get_random_string()
        mimetype, _encoding = mimetypes.guess_type(filename)

        content_element = xml_doc.createElement("ns:contentStream")
        mimetype_element = xml_doc.createElement("ns:mimeType")
        mimetype_txt = xml_doc.createTextNode(mimetype
                                              or "application/octet-stream")
        mimetype_element.appendChild(mimetype_txt)
        content_element.appendChild(mimetype_element)

        stream_element = xml_doc.createElement("ns:stream")
        include_element = xml_doc.createElement("inc:Include")
        include_element.setAttribute("xmlns:inc",
                                     "http://www.w3.org/2004/08/xop/include")
        include_element.setAttribute("href", f"cid:{content_id}")
        stream_element.appendChild(include_element)
        content_element.appendChild(stream_element)

        filename_element = xml_doc.createElement("ns:filename")
        filename_text = xml_doc.createTextNode(filename)
        filename_element.appendChild(filename_text)
        content_element.appendChild(filename_element)

        action_element.appendChild(content_element)

    if major is not None:
        major_element = xml_doc.createElement("ns:major")
        major_text = xml_doc.createTextNode(major)
        major_element.appendChild(major_text)
        action_element.appendChild(major_element)

    if checkin_comment is not None:
        comment_element = xml_doc.createElement("ns:checkinComment")
        comment_text = xml_doc.createTextNode(checkin_comment)
        comment_element.appendChild(comment_text)
        action_element.appendChild(comment_element)

    if source_folder_id is not None:
        source_folder_element = xml_doc.createElement("ns:sourceFolderId")
        source_folder_text = xml_doc.createTextNode(source_folder_id)
        source_folder_element.appendChild(source_folder_text)
        action_element.appendChild(source_folder_element)

    if target_folder_id is not None:
        target_folder_element = xml_doc.createElement("ns:targetFolderId")
        target_folder_text = xml_doc.createTextNode(target_folder_id)
        target_folder_element.appendChild(target_folder_text)
        action_element.appendChild(target_folder_element)

    if continue_on_failure is not None:
        continue_element = xml_doc.createElement("ns:continueOnFailure")
        continue_text = xml_doc.createTextNode(continue_on_failure)
        continue_element.appendChild(continue_text)
        action_element.appendChild(continue_element)

    entry_element.appendChild(body_element)

    return xml_doc
Esempio n. 5
0
    def create_content_object(
            self,
            data: dict,
            object_type: str,
            destination_folder: Folder = None) -> CMISContentObject:
        """Create a Gebruiksrechten or a ObjectInformatieObject

        :param data: dict, properties of the object to create
        :param object_type: string, either "gebruiksrechten" or "oio"
        :param destination_folder: Folder, the folder in which to place the object
        :return: Either a Gebruiksrechten or ObjectInformatieObject
        """
        assert object_type in [
            "gebruiksrechten",
            "oio",
        ], "'object_type' can be only 'gebruiksrechten' or 'oio'"

        if object_type == "oio":
            return_type = ObjectInformatieObject
            data_class = Oio
        elif object_type == "gebruiksrechten":
            return_type = Gebruiksrechten
            data_class = GebruiksRechtDoc

        if destination_folder is None:
            other_folder = self.get_or_create_other_folder()
            destination_folder = self.get_or_create_folder(
                "Related data", other_folder)

        properties = return_type.build_properties(data)

        properties.setdefault(
            "cmis:objectTypeId",
            {
                "value":
                f"{self.get_object_type_id_prefix(object_type)}drc:{object_type}",
                "type": "propertyId",
            },
        )
        properties.setdefault("cmis:name", {
            "value": get_random_string(),
            "type": "propertyString"
        })
        properties.setdefault(
            mapper("uuid", type=object_type),
            {
                "value": str(uuid.uuid4()),
                "type": get_cmis_type(data_class, "uuid")
            },
        )

        soap_envelope = make_soap_envelope(
            auth=(self.user, self.password),
            repository_id=self.main_repo_id,
            folder_id=destination_folder.objectId,
            properties=properties,
            cmis_action="createDocument",
        )

        logger.debug(soap_envelope.toprettyxml())

        soap_response = self.request(
            "ObjectService",
            soap_envelope=soap_envelope.toxml(),
        )

        xml_response = extract_xml_from_soap(soap_response)
        logger.debug(pretty_xml(xml_response))

        extracted_data = extract_object_properties_from_xml(
            xml_response, "createDocument")[0]
        new_object_id = extracted_data["properties"]["objectId"]["value"]

        # Request all the properties of the newly created object
        soap_envelope = make_soap_envelope(
            auth=(self.user, self.password),
            repository_id=self.main_repo_id,
            object_id=new_object_id,
            cmis_action="getObject",
        )

        logger.debug(soap_envelope.toprettyxml())

        soap_response = self.request("ObjectService",
                                     soap_envelope=soap_envelope.toxml())

        xml_response = extract_xml_from_soap(soap_response)
        logger.debug(pretty_xml(xml_response))

        extracted_data = extract_object_properties_from_xml(
            xml_response, "getObject")[0]

        return return_type(extracted_data)
Esempio n. 6
0
    def copy_gebruiksrechten(self, source_object: Gebruiksrechten,
                             destination_folder: Folder) -> Gebruiksrechten:
        """Copy a gebruiksrechten to a folder

        :param source_object: Gebruiksrechten, the gebruiksrechten to copy
        :param destination_folder: Folder, the folder in which to place the copied gebruiksrechten
        :return: the copied object
        """

        # copy the properties from the source document
        drc_properties = {}
        drc_url_properties = {}
        for property_name, property_details in source_object.properties.items(
        ):
            if ("cmis:" not in property_name and property_details["value"]
                    is not None) or property_name == "cmis:objectTypeId":
                drc_property_name = reverse_mapper(property_name,
                                                   type="gebruiksrechten")

                # Urls are handled separately, because they are already in the 'short' form
                if get_type(GebruiksRechtDoc,
                            drc_property_name) == QueriableUrl:
                    drc_url_properties[property_name] = {
                        "value": property_details["value"],
                        "type": "propertyString",
                    }
                else:
                    drc_properties[drc_property_name] = property_details[
                        "value"]

        cmis_properties = Gebruiksrechten.build_properties(drc_properties)

        cmis_properties.update(
            **{
                "cmis:objectTypeId": {
                    "value": source_object.objectTypeId,
                    "type": "propertyId",
                },
                mapper("kopie_van", type="gebruiksrechten"): {
                    "value": source_object.objectId,
                    "type":
                    "propertyString",  # Keep tack of where this is copied from.
                },
                "cmis:name": {
                    "value": get_random_string(),
                    "type": "propertyString"
                },
                "drc:gebruiksrechten__uuid": {
                    "value": str(uuid.uuid4()),
                    "type": "propertyString",
                },
                **drc_url_properties,
            })

        # Create copy gebruiksrechten
        soap_envelope = make_soap_envelope(
            auth=(self.user, self.password),
            repository_id=self.main_repo_id,
            folder_id=destination_folder.objectId,
            properties=cmis_properties,
            cmis_action="createDocument",
        )
        logger.debug(soap_envelope.toprettyxml())

        soap_response = self.request(
            "ObjectService",
            soap_envelope=soap_envelope.toxml(),
        )

        # Creating the document only returns its ID
        xml_response = extract_xml_from_soap(soap_response)

        logger.debug(pretty_xml(xml_response))

        extracted_data = extract_object_properties_from_xml(
            xml_response, "createDocument")[0]
        copy_gebruiksrechten_id = extracted_data["properties"]["objectId"][
            "value"]

        # Request all the properties of the newly created object
        soap_envelope = make_soap_envelope(
            auth=(self.user, self.password),
            repository_id=self.main_repo_id,
            object_id=copy_gebruiksrechten_id,
            cmis_action="getObject",
        )

        logger.debug(soap_envelope.toprettyxml())

        soap_response = self.request("ObjectService",
                                     soap_envelope=soap_envelope.toxml())

        xml_response = extract_xml_from_soap(soap_response)
        logger.debug(pretty_xml(xml_response))

        extracted_data = extract_object_properties_from_xml(
            xml_response, "getObject")[0]

        return Gebruiksrechten(extracted_data)
Esempio n. 7
0
    def build_properties(cls, data: dict, new: bool = True) -> dict:
        """Construct property dictionary.

        The structure of the dictionary is (where ``property_name``, ``property_value``
        and ``property_type`` are the name, value and type of the property):

            .. code-block:: python

                properties = {
                    "property_name": {
                        "value": property_value,
                        "type": property_type,
                    }
                }
        """

        config = CMISConfig.objects.get()

        props = {}
        for key, value in data.items():
            prop_name = mapper(key, type="document")
            if not prop_name:
                logger.debug("CMIS_ADAPTER: No property name found for key '%s'", key)
                continue
            if value is not None:
                prop_type = get_cmis_type(EnkelvoudigInformatieObject, key)

                if (
                    settings.CMIS_URL_MAPPING_ENABLED
                    and get_type(EnkelvoudigInformatieObject, key) == QueriableUrl
                    and value != ""
                ):
                    value = shrink_url(value)
                elif isinstance(value, datetime.datetime):
                    value = value.astimezone(pytz.timezone(config.time_zone)).strftime(
                        "%Y-%m-%dT%H:%M:%S.000Z"
                    )
                elif isinstance(value, datetime.date):
                    # In CMIS, there is no propertyDate, only propertyDateTime.
                    # So dates need to be in the datetime format
                    value = value.strftime("%Y-%m-%dT00:00:00.000Z")
                elif isinstance(value, bool):
                    value = str(value).lower()

                props[prop_name] = {"value": str(value), "type": prop_type}
            # When a Gebruiksrechten object is deleted, the field in the Document needs to be None.
            elif key == "indicatie_gebruiksrecht":
                prop_type = get_cmis_type(EnkelvoudigInformatieObject, key)
                props[prop_name] = {"value": "", "type": prop_type}

        # For documents that are not new, the uuid shouldn't be written
        props.pop(mapper("uuid"), None)

        if new:
            # increase likelihood of uniqueness of title by appending a random string
            title, suffix = data.get("titel"), get_random_string()
            if title is not None:
                props["cmis:name"] = {
                    "value": f"{title}-{suffix}",
                    "type": get_cmis_type(EnkelvoudigInformatieObject, "name"),
                }

            # For new documents, the uuid needs to be set
            prop_name = mapper("uuid")
            prop_type = get_cmis_type(EnkelvoudigInformatieObject, "uuid")
            new_uuid = str(uuid.uuid4())
            props[prop_name] = {"value": new_uuid, "type": prop_type}

            # The identification needs to be set ONLY for newly created documents.
            # identificatie is immutable once the document is created
            prop_name = mapper("identificatie")
            if not props.get(prop_name, {}).get("value"):
                prop_type = get_cmis_type(EnkelvoudigInformatieObject, "identificatie")
                props[prop_name] = {"value": new_uuid, "type": prop_type}

        return props