Skip to content

Commit d254f9a

Browse files
committed
SharePoint API: get_user_effective_permissions method fix and example (#407) File and Folder properties
1 parent 2838678 commit d254f9a

File tree

12 files changed

+246
-44
lines changed

12 files changed

+246
-44
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from pprint import pprint
2+
3+
from office365.sharepoint.client_context import ClientContext
4+
from office365.sharepoint.permissions.permission_kind import PermissionKind
5+
from tests import test_team_site_url, test_user_principal_name_alt, test_user_credentials
6+
7+
client = ClientContext(test_team_site_url).with_credentials(test_user_credentials)
8+
file_url = "/sites/team/Shared Documents/report #123.csv"
9+
10+
# user = client.web.site_users.get_by_email(test_user_principal_name_alt).get().execute_query()
11+
target_user = client.web.site_users.get_by_email(test_user_principal_name_alt)
12+
target_file = client.web.get_file_by_server_relative_path(file_url)
13+
result = target_file.listItemAllFields.get_user_effective_permissions(target_user).execute_query()
14+
pprint(result.value.permission_levels) # print all permission levels
15+
16+
# verify whether user has Reader role to a file
17+
if result.value.has(PermissionKind.OpenItems):
18+
print("User has access to read a file")
19+

office365/sharepoint/contenttypes/content_type_collection.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ def add_available_content_type(self, contentTypeId):
5252
self.context.add_query(qry)
5353
return ct
5454

55-
def add_existing_content_type(self, contentType):
55+
def add_existing_content_type(self, content_type):
5656
"""Adds an existing content type to the collection. The name of the given content type MUST NOT be the same
5757
as any of the content types in the collection. A reference to the SP.ContentType that was added is returned.
5858
59-
:param ContentType contentType: Specifies the content type to be added to the collection
59+
:param ContentType content_type: Specifies the content type to be added to the collection
6060
6161
"""
6262
pass

office365/sharepoint/files/file.py

+107-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
from office365.runtime.queries.service_operation_query import ServiceOperationQuery
55
from office365.runtime.resource_path import ResourcePath
66
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
7+
from office365.sharepoint.base_entity_collection import BaseEntityCollection
8+
from office365.sharepoint.files.file_version_event import FileVersionEvent
79
from office365.sharepoint.internal.download_file import create_download_file_query
810
from office365.sharepoint.base_entity import BaseEntity
911
from office365.sharepoint.directory.user import User
1012
from office365.sharepoint.files.file_version_collection import FileVersionCollection
1113
from office365.sharepoint.listitems.listitem import ListItem
14+
from office365.sharepoint.permissions.information_rights_management_settings import InformationRightsManagementSettings
1215
from office365.sharepoint.webparts.limited_webpart_manager import LimitedWebPartManager
1316
from office365.sharepoint.types.resource_path import ResourcePath as SPResPath
1417

@@ -48,6 +51,38 @@ def from_url(abs_url):
4851
file = ctx.web.get_file_by_server_relative_url(file_relative_url)
4952
return file
5053

54+
def get_image_preview_uri(self, width, height, client_type=None):
55+
"""
56+
:param int width:
57+
:param int height:
58+
:param str client_type:
59+
"""
60+
result = ClientResult(self.context)
61+
payload = {
62+
"width": width,
63+
"height": height,
64+
"clientType": client_type
65+
}
66+
qry = ServiceOperationQuery(self, "GetImagePreviewUri", None, payload, None, result)
67+
self.context.add_query(qry)
68+
return result
69+
70+
def get_image_preview_url(self, width, height, client_type=None):
71+
"""
72+
:param int width:
73+
:param int height:
74+
:param str client_type:
75+
"""
76+
result = ClientResult(self.context)
77+
payload = {
78+
"width": width,
79+
"height": height,
80+
"clientType": client_type
81+
}
82+
qry = ServiceOperationQuery(self, "GetImagePreviewUrl", None, payload, None, result)
83+
self.context.add_query(qry)
84+
return result
85+
5186
def recycle(self):
5287
"""Moves the file to the Recycle Bin and returns the identifier of the new Recycle Bin item."""
5388

@@ -201,6 +236,51 @@ def get_limited_webpart_manager(self, scope):
201236
self.resource_path
202237
))
203238

239+
def open_binary_stream(self):
240+
"""Opens the file as a stream."""
241+
return_stream = ClientResult(self.context)
242+
qry = ServiceOperationQuery(self, "OpenBinaryStream", None, None, None, return_stream)
243+
self.context.add_query(qry)
244+
return return_stream
245+
246+
def save_binary_stream(self, stream):
247+
"""Saves the file."""
248+
qry = ServiceOperationQuery(self, "SaveBinaryStream", None, {"file": stream})
249+
self.context.add_query(qry)
250+
return self
251+
252+
def get_upload_status(self, upload_id):
253+
payload = {
254+
"uploadId": upload_id,
255+
}
256+
qry = ServiceOperationQuery(self, "GetUploadStatus", None, payload)
257+
self.context.add_query(qry)
258+
return self
259+
260+
def upload_with_checksum(self, upload_id, checksum, stream):
261+
"""
262+
:param str upload_id:
263+
:param str checksum:
264+
:param bytes stream:
265+
"""
266+
return_type = File(self.context)
267+
payload = {
268+
"uploadId": upload_id,
269+
"checksum": checksum,
270+
"stream": stream
271+
}
272+
qry = ServiceOperationQuery(self, "UploadWithChecksum", None, payload, None, return_type)
273+
self.context.add_query(qry)
274+
return return_type
275+
276+
def cancel_upload(self, upload_id):
277+
payload = {
278+
"uploadId": upload_id,
279+
}
280+
qry = ServiceOperationQuery(self, "CancelUpload", None, payload)
281+
self.context.add_query(qry)
282+
return self
283+
204284
def start_upload(self, upload_id, content):
205285
"""Starts a new chunk upload session and uploads the first fragment.
206286
@@ -324,6 +404,7 @@ def _construct_download_request(request):
324404
"""
325405
request.stream = True
326406
request.method = HttpMethod.Get
407+
327408
self.context.before_execute(_construct_download_request)
328409

329410
def _process_download_response(response):
@@ -337,13 +418,35 @@ def _process_download_response(response):
337418
if callable(chunk_downloaded):
338419
chunk_downloaded(bytes_read)
339420
file_object.write(chunk)
421+
340422
self.context.after_execute(_process_download_response)
341423

342424
self.context.add_query(qry)
343425

344426
self.ensure_property("ServerRelativeUrl", _download_as_stream)
345427
return self
346428

429+
@property
430+
def checked_out_by_user(self):
431+
"""Gets an object that represents the user who has checked out the file."""
432+
return self.properties.get('CheckedOutByUser',
433+
User(self.context, ResourcePath("CheckedOutByUser", self.resource_path)))
434+
435+
@property
436+
def version_events(self):
437+
return self.properties.get("VersionEvents",
438+
BaseEntityCollection(self.context,
439+
FileVersionEvent,
440+
ResourcePath("VersionEvents", self.resource_path)))
441+
442+
@property
443+
def information_rights_management_settings(self):
444+
return self.properties.get('InformationRightsManagementSettings',
445+
InformationRightsManagementSettings(self.context,
446+
ResourcePath(
447+
"InformationRightsManagementSettings",
448+
self.resource_path)))
449+
347450
@property
348451
def listItemAllFields(self):
349452
"""Gets a value that specifies the list item fields values for the list item corresponding to the file."""
@@ -387,7 +490,7 @@ def server_relative_path(self):
387490
"""Gets the server-relative Path of the list folder.
388491
:rtype: SPResPath or None
389492
"""
390-
return self.properties.get("ServerRelativePath", SPResPath(None))
493+
return self.properties.get("ServerRelativePath", SPResPath())
391494

392495
@property
393496
def length(self):
@@ -482,6 +585,9 @@ def unique_id(self):
482585
def get_property(self, name, default_value=None):
483586
if default_value is None:
484587
property_mapping = {
588+
"CheckedOutByUser": self.checked_out_by_user,
589+
"VersionEvents": self.version_events,
590+
"InformationRightsManagementSettings": self.information_rights_management_settings,
485591
"LockedByUser": self.locked_by_user,
486592
"ModifiedBy": self.modified_by
487593
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from office365.sharepoint.base_entity import BaseEntity
2+
3+
4+
class FileVersionEvent(BaseEntity):
5+
""""""
6+
pass

office365/sharepoint/folders/folder.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ def _move_folder():
189189

190190
@property
191191
def storage_metrics(self):
192+
""""""
192193
return self.properties.get("StorageMetrics",
193194
StorageMetrics(self.context, ResourcePath("StorageMetrics", self.resource_path)))
194195

@@ -227,6 +228,15 @@ def name(self):
227228
"""
228229
return self.properties.get("Name", None)
229230

231+
@property
232+
def is_wopi_enabled(self):
233+
return self.properties.get("IsWOPIEnabled", None)
234+
235+
@property
236+
def prog_id(self):
237+
"""Gets the identifier (ID) of the application in which the folder was created."""
238+
return self.properties.get("ProgID", None)
239+
230240
@property
231241
def unique_id(self):
232242
"""Gets the unique ID of the folder.
@@ -293,7 +303,8 @@ def get_property(self, name, default_value=None):
293303
"UniqueContentTypeOrder": self.unique_content_type_order,
294304
"ListItemAllFields": self.list_item_all_fields,
295305
"ParentFolder": self.parent_folder,
296-
"ServerRelativePath": self.server_relative_path
306+
"ServerRelativePath": self.server_relative_path,
307+
"StorageMetrics": self.storage_metrics
297308
}
298309
default_value = property_mapping.get(name, None)
299310
return super(Folder, self).get_property(name, default_value)

office365/sharepoint/forms/form.py

+18
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,21 @@
33

44
class Form(BaseEntity):
55
"""A form provides a display and editing interface for a single list item."""
6+
7+
@property
8+
def form_type(self):
9+
"""
10+
Gets the type of the form.
11+
12+
:rtype: str or None
13+
"""
14+
return self.properties.get("FormType", None)
15+
16+
@property
17+
def server_relative_url(self):
18+
"""
19+
Gets the server-relative URL of the form.
20+
21+
:rtype: str or None
22+
"""
23+
return self.properties.get("ServerRelativeUrl", None)
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
12
from office365.sharepoint.base_entity_collection import BaseEntityCollection
23
from office365.sharepoint.forms.form import Form
34

@@ -7,5 +8,9 @@ class FormCollection(BaseEntityCollection):
78
def __init__(self, context, resource_path=None):
89
super(FormCollection, self).__init__(context, Form, resource_path)
910

10-
def get_by_page_type(self):
11-
pass
11+
def get_by_id(self, _id):
12+
"""Gets the form with the specified ID."""
13+
return Form(self.context, ResourcePathServiceOperation("GetById", [_id], self.resource_path))
14+
15+
def get_by_page_type(self, form_type):
16+
return Form(self.context, ResourcePathServiceOperation("GetByPageType", [form_type], self.resource_path))

office365/sharepoint/lists/list.py

+17-18
Original file line numberDiff line numberDiff line change
@@ -176,33 +176,33 @@ def get_list_item_changes_since_token(self, query):
176176
self.context.add_query(qry)
177177
return result
178178

179-
def save_as_template(self, fileName, name, description, saveData):
179+
def save_as_template(self, file_name, name, description, save_data):
180180
"""
181181
Saves the list as a template in the list template gallery and includes the option of saving with or
182182
without the data that is contained in the current list.
183183
184-
:param bool saveData: true to save the data of the original list along with the list template; otherwise, false.
184+
:param bool save_data: true to save the data of the original list along with the list template; otherwise, false.
185185
:param str description: A string that contains the description for the list template.
186186
:param str name: A string that contains the title for the list template.
187-
:param str fileName: A string that contains the file name for the list template with an .stp extension.
187+
:param str file_name: A string that contains the file name for the list template with an .stp extension.
188188
:return:
189189
"""
190190
payload = {
191-
"strFileName": fileName,
191+
"strFileName": file_name,
192192
"strName": name,
193193
"strDescription": description,
194-
"bSaveData": saveData
194+
"bSaveData": save_data
195195
}
196196
qry = ServiceOperationQuery(self, "saveAsTemplate", None, payload, None, None)
197197
self.context.add_query(qry)
198198
return self
199199

200-
def get_item_by_unique_id(self, uniqueId):
200+
def get_item_by_unique_id(self, unique_id):
201201
"""
202202
Returns the list item with the specified ID.
203203
204-
:param str uniqueId:"""
205-
item = ListItem(self.context, ResourcePathServiceOperation("getItemByUniqueId", [uniqueId], self.resource_path))
204+
:param str unique_id:"""
205+
item = ListItem(self.context, ResourcePathServiceOperation("getItemByUniqueId", [unique_id], self.resource_path))
206206
return item
207207

208208
def get_web_dav_url(self, source_url):
@@ -478,16 +478,15 @@ def parent_web_path(self):
478478
return self.properties.get('ParentWebPath', None)
479479

480480
def get_property(self, name, default_value=None):
481-
if name == "UserCustomActions":
482-
default_value = self.user_custom_actions
483-
elif name == "ParentWeb":
484-
default_value = self.parent_web
485-
elif name == "RootFolder":
486-
default_value = self.root_folder
487-
elif name == "ContentTypes":
488-
default_value = self.content_types
489-
elif name == "DefaultView":
490-
default_value = self.default_view
481+
if default_value is None:
482+
property_mapping = {
483+
"ContentTypes": self.content_types,
484+
"DefaultView": self.default_view,
485+
"ParentWeb": self.parent_web,
486+
"RootFolder": self.root_folder,
487+
"UserCustomActions": self.user_custom_actions
488+
}
489+
default_value = property_mapping.get(name, None)
491490
return super(List, self).get_property(name, default_value)
492491

493492
def set_property(self, name, value, persist_changes=True):

office365/sharepoint/permissions/base_permissions.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def set(self, perm):
3535

3636
def has(self, perm):
3737
"""Determines whether the current instance has the specified permission.
38-
3938
"""
4039
if perm == PermissionKind.EmptyMask:
4140
return True
@@ -56,7 +55,7 @@ def clear_all(self):
5655
self.Low = 0
5756
self.High = 0
5857

59-
def to_json(self):
58+
def to_json(self, json_format=None):
6059
return {'Low': str(self.High), 'High': str(self.Low)}
6160

6261
@property

office365/sharepoint/permissions/information_rights_management_settings.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
class InformationRightsManagementSettings(BaseEntity):
5+
"""Represents the Information Rights Management (IRM) settings of a list in Microsoft SharePoint Foundation."""
56

67
@property
78
def policy_title(self):

0 commit comments

Comments
 (0)