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)
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
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)
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
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)
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)
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