Skip to content

Commit 2838678

Browse files
committed
SharePoint API: file and list item versions improvements
1 parent 2a0a28f commit 2838678

File tree

13 files changed

+281
-54
lines changed

13 files changed

+281
-54
lines changed

examples/sharepoint/files/assign_file_permissions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
role_def = client.web.role_definitions.get_by_type(RoleType.Contributor)
1010

1111
# get user by email
12-
user = client.web.siteUsers.get_by_email(test_user_principal_name_alt)
12+
user = client.web.site_users.get_by_email(test_user_principal_name_alt)
1313

1414
# assign user with role to file
1515
target_file = client.web.get_file_by_server_relative_path(file_url)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import os
2+
import tempfile
3+
4+
from office365.sharepoint.client_context import ClientContext
5+
from office365.sharepoint.files.file_version import FileVersion
6+
from tests import test_team_site_url, test_client_credentials
7+
8+
ctx = ClientContext(test_team_site_url).with_credentials(test_client_credentials)
9+
file_url = "/sites/team/Shared Documents/report #123.csv"
10+
file_versions = ctx.web.get_file_by_server_relative_path(file_url).versions.get().execute_query()
11+
for version in file_versions: # type: FileVersion
12+
download_path = os.path.join(tempfile.mkdtemp(), version.version_label + "_" + os.path.basename(file_url))
13+
with open(download_path, "wb") as local_file:
14+
file = version.download(local_file).execute_query()
15+
print("[Ok] file version {0} has been downloaded into: {1}".format(version.url, download_path))
16+

office365/sharepoint/files/file.py

+18-13
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ def open_binary(ctx, server_relative_url):
295295
return response
296296

297297
def download(self, file_object):
298-
"""Download a file content
298+
"""
299+
Download a file content
300+
299301
:type file_object: typing.IO
300302
"""
301303

@@ -393,10 +395,7 @@ def length(self):
393395
394396
:rtype: int or None
395397
"""
396-
if self.is_property_available('Length'):
397-
return int(self.properties["Length"])
398-
else:
399-
return None
398+
return int(self.properties.get("Length", -1))
400399

401400
@property
402401
def exists(self):
@@ -459,10 +458,8 @@ def time_last_modified(self):
459458
def minor_version(self):
460459
"""
461460
Gets a value that specifies the minor version of the file.
462-
463-
:rtype: int or None
464461
"""
465-
return self.properties.get("MinorVersion", None)
462+
return int(self.properties.get("MinorVersion", -1))
466463

467464
@property
468465
def major_version(self):
@@ -471,7 +468,7 @@ def major_version(self):
471468
472469
:rtype: int or None
473470
"""
474-
return self.properties.get("MajorVersion", None)
471+
return int(self.properties.get("MajorVersion", -1))
475472

476473
@property
477474
def unique_id(self):
@@ -482,17 +479,25 @@ def unique_id(self):
482479
"""
483480
return self.properties.get("UniqueId", None)
484481

482+
def get_property(self, name, default_value=None):
483+
if default_value is None:
484+
property_mapping = {
485+
"LockedByUser": self.locked_by_user,
486+
"ModifiedBy": self.modified_by
487+
}
488+
default_value = property_mapping.get(name, None)
489+
return super(File, self).get_property(name, default_value)
490+
485491
def set_property(self, name, value, persist_changes=True):
486492
super(File, self).set_property(name, value, persist_changes)
487493
# fallback: create a new resource path
488494
if self._resource_path is None:
489495
if name == "ServerRelativeUrl":
490-
self._resource_path = ResourcePathServiceOperation(
491-
"GetFileByServerRelativeUrl", [value], ResourcePath("Web"))
496+
self._resource_path = ResourcePathServiceOperation("GetFileByServerRelativeUrl", [value],
497+
ResourcePath("Web"))
492498
elif name == "ServerRelativePath":
493499
self._resource_path = ResourcePathServiceOperation("getFolderByServerRelativePath", [value],
494500
ResourcePath("Web"))
495501
elif name == "UniqueId":
496-
self._resource_path = ResourcePathServiceOperation(
497-
"GetFileById", [value], ResourcePath("Web"))
502+
self._resource_path = ResourcePathServiceOperation("GetFileById", [value], ResourcePath("Web"))
498503
return self

office365/sharepoint/files/file_version.py

+56-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,56 @@
1+
from office365.runtime.client_result import ClientResult
2+
from office365.runtime.queries.service_operation_query import ServiceOperationQuery
3+
from office365.runtime.resource_path import ResourcePath
14
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
25
from office365.sharepoint.base_entity import BaseEntity
36

47

58
class FileVersion(BaseEntity):
69
"""Represents a version of a File object."""
710

11+
def download(self, file_object):
12+
"""Downloads the file version as a stream and save into a file.
13+
14+
:type file_object: typing.IO
15+
"""
16+
def _file_version_loaded():
17+
result = self.open_binary_stream()
18+
19+
def _process_response(response):
20+
"""
21+
:type response: requests.Response
22+
"""
23+
response.raise_for_status()
24+
file_object.write(result.value)
25+
self.context.after_execute(_process_response)
26+
self.ensure_property("ID", _file_version_loaded)
27+
return self
28+
29+
def open_binary_stream(self):
30+
"""Opens the file as a stream."""
31+
return_stream = ClientResult(self.context)
32+
qry = ServiceOperationQuery(self, "OpenBinaryStream", None, None, None, return_stream)
33+
self.context.add_query(qry)
34+
return return_stream
35+
36+
def open_binary_stream_with_options(self, open_options):
37+
"""Opens the file as a stream."""
38+
return_stream = ClientResult(self.context)
39+
qry = ServiceOperationQuery(self, "OpenBinaryStreamWithOptions", [open_options], None, None, return_stream)
40+
self.context.add_query(qry)
41+
return return_stream
42+
43+
@property
44+
def created_by(self):
45+
"""Gets the user that created the file version."""
46+
from office365.sharepoint.principal.user import User
47+
return self.properties.get("CreatedBy", User(self.context, ResourcePath("CreatedBy", self.resource_path)))
48+
49+
@property
50+
def id(self):
51+
"""Gets a file version identifier"""
52+
return int(self.properties.get("ID", -1))
53+
854
@property
955
def url(self):
1056
"""Gets a value that specifies the relative URL of the file version based on the URL for the site or subsite."""
@@ -25,11 +71,18 @@ def checkin_comment(self):
2571
"""Gets a value that specifies the check-in comment."""
2672
return self.properties.get("CheckInComment", None)
2773

74+
def get_property(self, name, default_value=None):
75+
if default_value is None:
76+
property_mapping = {
77+
"CreatedBy": self.created_by,
78+
}
79+
default_value = property_mapping.get(name, None)
80+
return super(FileVersion, self).get_property(name, default_value)
81+
2882
def set_property(self, name, value, persist_changes=True):
2983
super(FileVersion, self).set_property(name, value, persist_changes)
3084
if self._resource_path is None:
3185
if name == "ID":
3286
self._resource_path = ResourcePathServiceOperation(
33-
"GetById",
34-
[value],
35-
self._parent_collection.resource_path)
87+
"GetById", [value], self._parent_collection.resource_path)
88+
return self

office365/sharepoint/files/file_version_collection.py

+61
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from office365.runtime.queries.service_operation_query import ServiceOperationQuery
2+
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
13
from office365.sharepoint.base_entity_collection import BaseEntityCollection
24
from office365.sharepoint.files.file_version import FileVersion
35

@@ -6,3 +8,62 @@ class FileVersionCollection(BaseEntityCollection):
68
"""Represents a collection of FileVersion."""
79
def __init__(self, context, resource_path=None):
810
super(FileVersionCollection, self).__init__(context, FileVersion, resource_path)
11+
12+
def get_by_id(self, version_id):
13+
"""Gets the file version with the specified ID."""
14+
return FileVersion(self.context, ResourcePathServiceOperation("getById", [version_id], self.resource_path))
15+
16+
def delete_all(self):
17+
"""Deletes all the file version objects in the collection."""
18+
qry = ServiceOperationQuery(self, "DeleteAll")
19+
self.context.add_query(qry)
20+
return self
21+
22+
def delete_by_id(self, vid):
23+
"""Removes the file version object with the specified integer ID from the collection.
24+
25+
:param int vid: The file version to remove.
26+
"""
27+
qry = ServiceOperationQuery(self, "DeleteByID", {"vid": vid})
28+
self.context.add_query(qry)
29+
return self
30+
31+
def delete_by_label(self, label):
32+
"""
33+
Deletes the file version object with the specified version label.
34+
35+
:param str label: The file version to remove.
36+
"""
37+
qry = ServiceOperationQuery(self, "DeleteByLabel", {"versionlabel": label})
38+
self.context.add_query(qry)
39+
return self
40+
41+
def recycle_by_id(self, vid):
42+
"""
43+
Recycles a file version objects in the collection by version identifier.
44+
45+
:param int vid: The file version to remove.
46+
"""
47+
qry = ServiceOperationQuery(self, "RecycleByID", {"vid": vid})
48+
self.context.add_query(qry)
49+
return self
50+
51+
def recycle_by_label(self, label):
52+
"""
53+
Recycles the file version object with the specified version label.
54+
55+
:param str label: The file version to remove.
56+
"""
57+
qry = ServiceOperationQuery(self, "RecycleByLabel", {"versionlabel": label})
58+
self.context.add_query(qry)
59+
return self
60+
61+
def restore_by_label(self, label):
62+
"""
63+
Restores the file version object that has the specified version label.
64+
65+
:param str label: The file version to remove.
66+
"""
67+
qry = ServiceOperationQuery(self, "RestoreByLabel", {"versionlabel": label})
68+
self.context.add_query(qry)
69+
return self

office365/sharepoint/internal/download_file.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ def create_download_file_query(source_file, file_object):
1313

1414
def _process_response(response):
1515
"""
16-
:type response: RequestOptions
16+
:type response: requests.Response
1717
"""
18+
response.raise_for_status()
1819
file_object.write(response.content)
1920

2021
def _construct_download_query(request):
22+
"""
23+
:type request: office365.runtime.http.request_options.RequestOptions
24+
"""
2125
request.method = HttpMethod.Get
2226
source_file.context.after_execute(_process_response)
2327

Original file line numberDiff line numberDiff line change
@@ -1,5 +1,66 @@
1+
from office365.runtime.resource_path import ResourcePath
2+
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
13
from office365.sharepoint.base_entity import BaseEntity
24

35

46
class ListItemVersion(BaseEntity):
5-
pass
7+
"""Represents a version of a list item."""
8+
9+
@property
10+
def version_id(self):
11+
"""Gets the ID of the version."""
12+
return int(self.properties.get("VersionId", -1))
13+
14+
@property
15+
def version_label(self):
16+
"""Gets the version number of the item version."""
17+
return self.properties.get("VersionLabel")
18+
19+
@property
20+
def is_current_version(self):
21+
"""Gets a value that specifies whether the file version is the current version.
22+
23+
:rtype: bool
24+
"""
25+
return self.properties.get("IsCurrentVersion", None)
26+
27+
@property
28+
def created(self):
29+
"""Gets the creation date and time for the item version."""
30+
return self.properties.get("Created", None)
31+
32+
@property
33+
def created_by(self):
34+
"""Gets the user that created the item version."""
35+
from office365.sharepoint.principal.user import User
36+
return self.properties.get("CreatedBy", User(self.context, ResourcePath("CreatedBy", self.resource_path)))
37+
38+
@property
39+
def fields(self):
40+
"""Gets the collection of fields that are used in the list that contains the item version."""
41+
from office365.sharepoint.fields.field_collection import FieldCollection
42+
return self.properties.get("Fields", FieldCollection(self.context, ResourcePath("Fields", self.resource_path)))
43+
44+
@property
45+
def file_version(self):
46+
from office365.sharepoint.files.file_version import FileVersion
47+
return self.properties.get("FileVersion",
48+
FileVersion(self.context, ResourcePath("FileVersion", self.resource_path)))
49+
50+
def get_property(self, name, default_value=None):
51+
if default_value is None:
52+
property_mapping = {
53+
"CreatedBy": self.created_by,
54+
"FileVersion": self.file_version
55+
}
56+
default_value = property_mapping.get(name, None)
57+
return super(ListItemVersion, self).get_property(name, default_value)
58+
59+
def set_property(self, name, value, persist_changes=True):
60+
if self._resource_path is None:
61+
if name == "VersionId":
62+
self._resource_path = ResourcePathServiceOperation(
63+
"GetById", [value], self._parent_collection.resource_path)
64+
return super(ListItemVersion, self).set_property(name, value, persist_changes)
65+
66+

0 commit comments

Comments
 (0)