das-cli 1.2.33__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {das_cli-1.2.33/das_cli.egg-info → das_cli-1.3.0}/PKG-INFO +44 -1
- {das_cli-1.2.33 → das_cli-1.3.0}/README.md +43 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/cli.py +43 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/managers/digital_objects_manager.py +37 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/digital_objects.py +58 -1
- {das_cli-1.2.33 → das_cli-1.3.0/das_cli.egg-info}/PKG-INFO +44 -1
- {das_cli-1.2.33 → das_cli-1.3.0}/pyproject.toml +1 -1
- {das_cli-1.2.33 → das_cli-1.3.0}/LICENSE +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/MANIFEST.in +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/__init__.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/ai/plugins/dasai.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/ai/plugins/entries/entries_plugin.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/app.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/authentication/auth.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/authentication/oauth.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/authentication/secure_input.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/common/api.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/common/config.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/common/entry_fields_constants.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/common/enums.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/common/file_utils.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/managers/__init__.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/managers/download_manager.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/managers/entries_manager.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/managers/search_manager.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/attributes.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/cache.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/downloads.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/entries.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/entry_fields.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/hangfire.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/search.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/service_base.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das/services/users.py +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das_cli.egg-info/SOURCES.txt +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das_cli.egg-info/dependency_links.txt +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das_cli.egg-info/entry_points.txt +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das_cli.egg-info/requires.txt +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/das_cli.egg-info/top_level.txt +0 -0
- {das_cli-1.2.33 → das_cli-1.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: das-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: DAS api client.
|
|
5
5
|
Author: Royal Netherlands Institute for Sea Research
|
|
6
6
|
License-Expression: MIT
|
|
@@ -185,6 +185,16 @@ das entry update --attribute <AttributeName> [--code CODE] <file_path>
|
|
|
185
185
|
das entry upload-digital-object --entry-code ENT001 --type Dataset --description "CTD raw" c:\data\ctd.zip
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
+
#### Direct download a digital object
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Download by entry and digital object codes
|
|
192
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\Downloads\
|
|
193
|
+
|
|
194
|
+
# Download by IDs
|
|
195
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\Downloads\file.bin
|
|
196
|
+
```
|
|
197
|
+
|
|
188
198
|
#### Link or unlink digital objects
|
|
189
199
|
|
|
190
200
|
```bash
|
|
@@ -925,6 +935,39 @@ digital_object_id = digital_objects_manager.upload_digital_object(
|
|
|
925
935
|
)
|
|
926
936
|
```
|
|
927
937
|
|
|
938
|
+
#### Direct Download Digital Object
|
|
939
|
+
|
|
940
|
+
**CLI:**
|
|
941
|
+
```bash
|
|
942
|
+
# Download by entry and digital object codes
|
|
943
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\Downloads\
|
|
944
|
+
|
|
945
|
+
# Download by IDs
|
|
946
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\Downloads\file.bin
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Manager Layer:**
|
|
950
|
+
```python
|
|
951
|
+
from das.managers.digital_objects_manager import DigitalObjectsManager
|
|
952
|
+
|
|
953
|
+
digital_objects_manager = DigitalObjectsManager()
|
|
954
|
+
|
|
955
|
+
# Download by codes
|
|
956
|
+
saved_path = digital_objects_manager.direct_download_digital_object(
|
|
957
|
+
entry_code="ENT001",
|
|
958
|
+
digital_object_code="DO001",
|
|
959
|
+
output_path="C:\\Downloads\\"
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
# Download by IDs
|
|
963
|
+
saved_path = digital_objects_manager.direct_download_digital_object(
|
|
964
|
+
entry_id="e1987f3e-7db7-11f0-a7e8-0edcca226f3d",
|
|
965
|
+
attribute_id=126,
|
|
966
|
+
digital_object_id="cf335b17-80e3-4151-aa80-40b69fe0567e",
|
|
967
|
+
output_path="C:\\Downloads\\file.bin"
|
|
968
|
+
)
|
|
969
|
+
```
|
|
970
|
+
|
|
928
971
|
#### Link/Unlink Digital Objects
|
|
929
972
|
|
|
930
973
|
**CLI:**
|
|
@@ -162,6 +162,16 @@ das entry update --attribute <AttributeName> [--code CODE] <file_path>
|
|
|
162
162
|
das entry upload-digital-object --entry-code ENT001 --type Dataset --description "CTD raw" c:\data\ctd.zip
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
#### Direct download a digital object
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Download by entry and digital object codes
|
|
169
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\Downloads\
|
|
170
|
+
|
|
171
|
+
# Download by IDs
|
|
172
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\Downloads\file.bin
|
|
173
|
+
```
|
|
174
|
+
|
|
165
175
|
#### Link or unlink digital objects
|
|
166
176
|
|
|
167
177
|
```bash
|
|
@@ -902,6 +912,39 @@ digital_object_id = digital_objects_manager.upload_digital_object(
|
|
|
902
912
|
)
|
|
903
913
|
```
|
|
904
914
|
|
|
915
|
+
#### Direct Download Digital Object
|
|
916
|
+
|
|
917
|
+
**CLI:**
|
|
918
|
+
```bash
|
|
919
|
+
# Download by entry and digital object codes
|
|
920
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\Downloads\
|
|
921
|
+
|
|
922
|
+
# Download by IDs
|
|
923
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\Downloads\file.bin
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
**Manager Layer:**
|
|
927
|
+
```python
|
|
928
|
+
from das.managers.digital_objects_manager import DigitalObjectsManager
|
|
929
|
+
|
|
930
|
+
digital_objects_manager = DigitalObjectsManager()
|
|
931
|
+
|
|
932
|
+
# Download by codes
|
|
933
|
+
saved_path = digital_objects_manager.direct_download_digital_object(
|
|
934
|
+
entry_code="ENT001",
|
|
935
|
+
digital_object_code="DO001",
|
|
936
|
+
output_path="C:\\Downloads\\"
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
# Download by IDs
|
|
940
|
+
saved_path = digital_objects_manager.direct_download_digital_object(
|
|
941
|
+
entry_id="e1987f3e-7db7-11f0-a7e8-0edcca226f3d",
|
|
942
|
+
attribute_id=126,
|
|
943
|
+
digital_object_id="cf335b17-80e3-4151-aa80-40b69fe0567e",
|
|
944
|
+
output_path="C:\\Downloads\\file.bin"
|
|
945
|
+
)
|
|
946
|
+
```
|
|
947
|
+
|
|
905
948
|
#### Link/Unlink Digital Objects
|
|
906
949
|
|
|
907
950
|
**CLI:**
|
|
@@ -724,6 +724,49 @@ def upload_digital_object(das_ctx, entry_code, digital_object_type, file_descrip
|
|
|
724
724
|
except Exception as e:
|
|
725
725
|
click.secho(f"Error: {e}", fg="red")
|
|
726
726
|
|
|
727
|
+
@entry.command("direct-download-digital-object")
|
|
728
|
+
@click.option('--entry-code', default=None, help='Entry code containing the digital object')
|
|
729
|
+
@click.option('--entry-id', default=None, help='Entry ID containing the digital object')
|
|
730
|
+
@click.option('--attribute-id', type=int, default=None, help='Attribute ID for the entry (required with --entry-id)')
|
|
731
|
+
@click.option('--digital-object-code', default=None, help='Digital object code to download')
|
|
732
|
+
@click.option('--digital-object-id', default=None, help='Digital object ID to download')
|
|
733
|
+
@click.option('--out', 'output_path', required=True, help='Output file path or output directory')
|
|
734
|
+
@pass_das_context
|
|
735
|
+
def direct_download_digital_object(das_ctx, entry_code, entry_id, attribute_id, digital_object_code, digital_object_id, output_path):
|
|
736
|
+
"""Directly download a digital object by codes or IDs.
|
|
737
|
+
|
|
738
|
+
Examples:
|
|
739
|
+
|
|
740
|
+
\b
|
|
741
|
+
# Download using entry and digital object codes
|
|
742
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\\Downloads\\
|
|
743
|
+
|
|
744
|
+
\b
|
|
745
|
+
# Download using IDs
|
|
746
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\\Downloads\\file.bin
|
|
747
|
+
"""
|
|
748
|
+
try:
|
|
749
|
+
das_ctx.get_client()
|
|
750
|
+
|
|
751
|
+
if not entry_code and not entry_id:
|
|
752
|
+
raise click.UsageError("Please provide either --entry-code or --entry-id")
|
|
753
|
+
if not digital_object_code and not digital_object_id:
|
|
754
|
+
raise click.UsageError("Please provide either --digital-object-code or --digital-object-id")
|
|
755
|
+
if entry_id and not attribute_id:
|
|
756
|
+
raise click.UsageError("--attribute-id is required when using --entry-id")
|
|
757
|
+
|
|
758
|
+
saved_path = das_ctx.digital_objects_manager.direct_download_digital_object(
|
|
759
|
+
entry_code=entry_code,
|
|
760
|
+
entry_id=entry_id,
|
|
761
|
+
attribute_id=attribute_id,
|
|
762
|
+
digital_object_id=digital_object_id,
|
|
763
|
+
digital_object_code=digital_object_code,
|
|
764
|
+
output_path=output_path,
|
|
765
|
+
)
|
|
766
|
+
click.secho(f"✓ Digital object saved to: {saved_path}", fg="green")
|
|
767
|
+
except Exception as e:
|
|
768
|
+
click.secho(f"Error: {e}", fg="red")
|
|
769
|
+
|
|
727
770
|
@entry.command("get")
|
|
728
771
|
@click.option('--code', default=None, help='Entry code')
|
|
729
772
|
@click.option('--id', type=int, default=None, help='Entry ID')
|
|
@@ -78,6 +78,43 @@ class DigitalObjectsManager:
|
|
|
78
78
|
|
|
79
79
|
return digital_object_id
|
|
80
80
|
|
|
81
|
+
|
|
82
|
+
def direct_download_digital_object(self,
|
|
83
|
+
entry_code: str = None,
|
|
84
|
+
entry_id: str = None,
|
|
85
|
+
attribute_id: int = None,
|
|
86
|
+
digital_object_id: str = None,
|
|
87
|
+
digital_object_code: str = None,
|
|
88
|
+
output_path: str = None):
|
|
89
|
+
"""Directly download a digital object from the digital object service."""
|
|
90
|
+
if not output_path:
|
|
91
|
+
raise ValueError("Output path is required")
|
|
92
|
+
if entry_code:
|
|
93
|
+
entry_response = self.entry_service.get_entry(entry_code)
|
|
94
|
+
if entry_response is None:
|
|
95
|
+
raise ValueError(f"Entry with code '{entry_code}' not found")
|
|
96
|
+
if entry_response.get('attributeId') is None:
|
|
97
|
+
raise ValueError(f"Entry with code '{entry_code}' has no attributeId")
|
|
98
|
+
entry_id = entry_response.get('entry').get('id')
|
|
99
|
+
attribute_id = entry_response.get('attributeId')
|
|
100
|
+
if digital_object_code:
|
|
101
|
+
digital_object_response = self.entry_service.get_entry(digital_object_code)
|
|
102
|
+
if digital_object_response is None:
|
|
103
|
+
raise ValueError(f"Digital object with code '{digital_object_code}' not found")
|
|
104
|
+
if digital_object_response.get('attributeId') is None:
|
|
105
|
+
raise ValueError(f"Digital object with code '{digital_object_code}' has no attributeId")
|
|
106
|
+
digital_object_id = digital_object_response.get('entry').get('id')
|
|
107
|
+
|
|
108
|
+
if not entry_id or not attribute_id or not digital_object_id:
|
|
109
|
+
raise ValueError("Entry ID, attribute ID, and digital object ID are required")
|
|
110
|
+
|
|
111
|
+
return self.digital_objects_service.direct_download_digital_object(
|
|
112
|
+
entry_id=entry_id,
|
|
113
|
+
attribute_id=attribute_id,
|
|
114
|
+
digital_object_id=digital_object_id,
|
|
115
|
+
output_path=output_path
|
|
116
|
+
)
|
|
117
|
+
|
|
81
118
|
|
|
82
119
|
if __name__ == "__main__":
|
|
83
120
|
digital_objects_manager = DigitalObjectsManager()
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
from math import ceil
|
|
3
3
|
from os.path import exists
|
|
4
4
|
import json
|
|
5
|
-
from base64 import b64encode
|
|
5
|
+
from base64 import b64encode, b64decode
|
|
6
6
|
from das.common.api import post_data
|
|
7
7
|
from das.common.config import load_verify_ssl
|
|
8
8
|
from das.services.service_base import ServiceBase
|
|
@@ -123,3 +123,60 @@ class DigitalObjectsService(ServiceBase):
|
|
|
123
123
|
raise ValueError(f"Error uploading file: {response.status_code} - {response.text}")
|
|
124
124
|
except requests.RequestException as e:
|
|
125
125
|
raise ValueError(f"Error uploading file: {str(e)}")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def direct_download_digital_object(self, entry_id: str, attribute_id: int, digital_object_id: str, output_path: str):
|
|
129
|
+
"""Directly download a digital object from the digital object service."""
|
|
130
|
+
headers = {**self.set_auth_headers(), "Content-Type": "application/json"}
|
|
131
|
+
|
|
132
|
+
url = f"{self.base_url}/GetDigitalObjectsFromEntry"
|
|
133
|
+
|
|
134
|
+
payload = {
|
|
135
|
+
"attributeId": attribute_id,
|
|
136
|
+
"entryId": entry_id,
|
|
137
|
+
"digitalObjectId": digital_object_id
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
response = post_data(url, data=payload, headers=headers)
|
|
141
|
+
if response.get("success") != True:
|
|
142
|
+
raise ValueError(response.get("error") or "Unknown error occurred")
|
|
143
|
+
|
|
144
|
+
result = response.get("result") or {}
|
|
145
|
+
digital_object_item = result.get("digitalObjectItem") or {}
|
|
146
|
+
data = digital_object_item.get("data")
|
|
147
|
+
display_name = digital_object_item.get("displayName")
|
|
148
|
+
|
|
149
|
+
if data is None:
|
|
150
|
+
raise ValueError("Digital object data was not returned by the API")
|
|
151
|
+
|
|
152
|
+
file_bytes = None
|
|
153
|
+
if isinstance(data, str):
|
|
154
|
+
try:
|
|
155
|
+
file_bytes = b64decode(data, validate=False)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
raise ValueError(f"Invalid base64 digital object data: {str(e)}")
|
|
158
|
+
elif isinstance(data, (bytes, bytearray)):
|
|
159
|
+
file_bytes = bytes(data)
|
|
160
|
+
else:
|
|
161
|
+
raise ValueError("Unsupported digital object data format")
|
|
162
|
+
|
|
163
|
+
is_directory_target = (
|
|
164
|
+
output_path.endswith(os.sep)
|
|
165
|
+
or output_path.endswith("/")
|
|
166
|
+
or output_path.endswith("\\")
|
|
167
|
+
or os.path.isdir(output_path)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if is_directory_target:
|
|
171
|
+
if not display_name:
|
|
172
|
+
raise ValueError("Digital object displayName was not returned by the API")
|
|
173
|
+
|
|
174
|
+
os.makedirs(output_path, exist_ok=True)
|
|
175
|
+
final_output_path = os.path.join(output_path, display_name)
|
|
176
|
+
else:
|
|
177
|
+
final_output_path = output_path
|
|
178
|
+
|
|
179
|
+
with open(final_output_path, "wb") as file_handle:
|
|
180
|
+
file_handle.write(file_bytes)
|
|
181
|
+
|
|
182
|
+
return final_output_path
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: das-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: DAS api client.
|
|
5
5
|
Author: Royal Netherlands Institute for Sea Research
|
|
6
6
|
License-Expression: MIT
|
|
@@ -185,6 +185,16 @@ das entry update --attribute <AttributeName> [--code CODE] <file_path>
|
|
|
185
185
|
das entry upload-digital-object --entry-code ENT001 --type Dataset --description "CTD raw" c:\data\ctd.zip
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
+
#### Direct download a digital object
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Download by entry and digital object codes
|
|
192
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\Downloads\
|
|
193
|
+
|
|
194
|
+
# Download by IDs
|
|
195
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\Downloads\file.bin
|
|
196
|
+
```
|
|
197
|
+
|
|
188
198
|
#### Link or unlink digital objects
|
|
189
199
|
|
|
190
200
|
```bash
|
|
@@ -925,6 +935,39 @@ digital_object_id = digital_objects_manager.upload_digital_object(
|
|
|
925
935
|
)
|
|
926
936
|
```
|
|
927
937
|
|
|
938
|
+
#### Direct Download Digital Object
|
|
939
|
+
|
|
940
|
+
**CLI:**
|
|
941
|
+
```bash
|
|
942
|
+
# Download by entry and digital object codes
|
|
943
|
+
das entry direct-download-digital-object --entry-code ENT001 --digital-object-code DO001 --out C:\Downloads\
|
|
944
|
+
|
|
945
|
+
# Download by IDs
|
|
946
|
+
das entry direct-download-digital-object --entry-id e1987f3e-7db7-11f0-a7e8-0edcca226f3d --attribute-id 126 --digital-object-id cf335b17-80e3-4151-aa80-40b69fe0567e --out C:\Downloads\file.bin
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Manager Layer:**
|
|
950
|
+
```python
|
|
951
|
+
from das.managers.digital_objects_manager import DigitalObjectsManager
|
|
952
|
+
|
|
953
|
+
digital_objects_manager = DigitalObjectsManager()
|
|
954
|
+
|
|
955
|
+
# Download by codes
|
|
956
|
+
saved_path = digital_objects_manager.direct_download_digital_object(
|
|
957
|
+
entry_code="ENT001",
|
|
958
|
+
digital_object_code="DO001",
|
|
959
|
+
output_path="C:\\Downloads\\"
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
# Download by IDs
|
|
963
|
+
saved_path = digital_objects_manager.direct_download_digital_object(
|
|
964
|
+
entry_id="e1987f3e-7db7-11f0-a7e8-0edcca226f3d",
|
|
965
|
+
attribute_id=126,
|
|
966
|
+
digital_object_id="cf335b17-80e3-4151-aa80-40b69fe0567e",
|
|
967
|
+
output_path="C:\\Downloads\\file.bin"
|
|
968
|
+
)
|
|
969
|
+
```
|
|
970
|
+
|
|
928
971
|
#### Link/Unlink Digital Objects
|
|
929
972
|
|
|
930
973
|
**CLI:**
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|