Skip to content

Commit 23a8b90

Browse files
SharePoint API: sharing & permissions namespaces changes
1 parent 48658b0 commit 23a8b90

18 files changed

+408
-93
lines changed

generator/metadata/SharePoint.xml

+1-1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from office365.runtime.clientValue import ClientValue
2+
3+
4+
class ObjectIdentity(ClientValue):
5+
pass
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from office365.runtime.clientValue import ClientValue
2+
3+
4+
class UserIdentity(ClientValue):
5+
6+
def __init__(self):
7+
super().__init__()
8+
self.displayName = None
9+
self.ipAddress = None
10+
self.userPrincipalName = None

office365/runtime/client_object.py

+28-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class ClientObject(object):
66

77
def __init__(self, context, resource_path=None, properties=None, parent_collection=None):
88
"""
9-
Base client object
9+
Base client object which define named properties and relationships of an entity
1010
1111
:type parent_collection: office365.runtime.client_object_collection.ClientObjectCollection or None
1212
:type properties: dict or None
@@ -25,7 +25,10 @@ def __init__(self, context, resource_path=None, properties=None, parent_collecti
2525
self.set_property(k, v, True)
2626

2727
def is_property_available(self, name):
28-
"""Returns a Boolean value that indicates whether the specified property has been retrieved or set."""
28+
"""Returns a Boolean value that indicates whether the specified property has been retrieved or set.
29+
30+
:param str name: A Property name
31+
"""
2932
if name in self.properties:
3033
return True
3134
return False
@@ -43,8 +46,18 @@ def remove_from_parent_collection(self):
4346
return
4447
self._parent_collection.remove_child(self)
4548

49+
def get_property(self, name):
50+
getter_name = name[0].lower() + name[1:]
51+
if hasattr(self, getter_name):
52+
return getattr(self, getter_name)
53+
return self._properties.get(name, None)
54+
4655
def set_property(self, name, value, persist_changes=True):
47-
"""Set resource property value"""
56+
"""Sets Property value
57+
:param str name: Property name
58+
:param any value: Property value
59+
:param bool persist_changes: Persist changes
60+
"""
4861
if persist_changes:
4962
self._changes.append(name)
5063

@@ -62,15 +75,22 @@ def set_property(self, name, value, persist_changes=True):
6275
def to_json(self):
6376
return dict((k, v) for k, v in self.properties.items() if k in self._changes)
6477

65-
def ensure_property(self, name, action):
78+
def ensure_property(self, name_or_names, action):
6679
"""
67-
Ensures property is loaded
80+
Ensures if property is loaded or list of properties are loaded
6881
6982
:type action: any
70-
:type name: str
83+
:type name_or_names: str or list[str]
7184
"""
72-
if not self.is_property_available(name):
73-
self.context.load(self, [name])
85+
names_to_include = []
86+
if isinstance(name_or_names, list):
87+
names_to_include = [n for n in name_or_names if not self.is_property_available(n)]
88+
else:
89+
if not self.is_property_available(name_or_names):
90+
names_to_include.append(name_or_names)
91+
92+
if len(names_to_include) > 0:
93+
self.context.load(self, names_to_include)
7494
self.context.afterExecuteOnce += action
7595
else:
7696
action(self)
@@ -81,10 +101,6 @@ def entity_type_name(self):
81101
self._entity_type_name = "SP." + type(self).__name__
82102
return self._entity_type_name
83103

84-
@entity_type_name.setter
85-
def entity_type_name(self, value):
86-
self._entity_type_name = value
87-
88104
@property
89105
def resource_url(self):
90106
"""Generate resource Url"""

office365/runtime/client_query.py

+24-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import copy
2+
from office365.runtime.client_object import ClientObject
3+
4+
15
class ClientQuery(object):
26
"""Client query"""
37

48
def __init__(self, binding_type, parameter_type=None, parameter_name=None, return_type=None):
59
"""
10+
Base query
611
7-
:type binding_type: ClientObject
12+
:type binding_type: office365.runtime.client_object.ClientObject
813
:type parameter_type: ClientObject or ClientValue or dict or bytes or None
914
:type parameter_name: str or None
1015
:type return_type: ClientObject or ClientResult or ClientValueObject or None
@@ -38,9 +43,9 @@ def return_type(self):
3843
class CreateEntityQuery(ClientQuery):
3944
def __init__(self, parent_entity, create_info, entity_to_create):
4045
"""
41-
Create query
46+
Create entity query
4247
43-
:type entity_to_create: ClientObject
48+
:type entity_to_create: office365.runtime.client_object.ClientObject
4449
:type create_info: ClientObject or ClientValue or dict
4550
:type parent_entity: ClientObject
4651
"""
@@ -50,31 +55,39 @@ def __init__(self, parent_entity, create_info, entity_to_create):
5055
class ReadEntityQuery(ClientQuery):
5156
def __init__(self, entity_to_read, properties_to_include=None):
5257
"""
53-
Read query
58+
Read entity query
5459
5560
:type properties_to_include: list[str] or None
56-
:type entity_to_read: ClientObject
61+
:type entity_to_read: office365.runtime.client_object.ClientObject
5762
"""
58-
super(ReadEntityQuery, self).__init__(entity_to_read, None, None, entity_to_read)
63+
binding_type = copy.deepcopy(entity_to_read)
64+
super(ReadEntityQuery, self).__init__(binding_type, None, None, entity_to_read)
5965
if properties_to_include:
60-
entity_to_read.query_options.expand = properties_to_include
66+
self._include_properties(properties_to_include)
67+
68+
def _include_properties(self, properties_to_include):
69+
for n in properties_to_include:
70+
prop_val = self._binding_type.get_property(n)
71+
if isinstance(prop_val, ClientObject):
72+
self._binding_type.query_options.expand.append(n)
73+
self._binding_type.query_options.select.append(n)
6174

6275

6376
class UpdateEntityQuery(ClientQuery):
6477
def __init__(self, entity_to_update):
6578
"""
66-
Update query
79+
Update entity query
6780
68-
:type entity_to_update: ClientObject
81+
:type entity_to_update: office365.runtime.client_object.ClientObject
6982
"""
7083
super(UpdateEntityQuery, self).__init__(entity_to_update, entity_to_update, None, None)
7184

7285

7386
class DeleteEntityQuery(ClientQuery):
7487
def __init__(self, entity_to_delete):
7588
"""
76-
Delete query
89+
Delete entity query
7790
78-
:type entity_to_delete: ClientObject
91+
:type entity_to_delete: office365.runtime.client_object.ClientObject
7992
"""
8093
super(DeleteEntityQuery, self).__init__(entity_to_delete, None, None, None)

office365/runtime/odata/odata_query_options.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,44 @@ def _normalize(key, value):
55

66

77
class QueryOptions(object):
8-
"""OData query options"""
98

10-
def __init__(self):
11-
self.select = None # type: list or None
12-
self.expand = None # type: list or None
13-
self.filter = None # type: str or None
14-
self.orderBy = None # type: str or None
15-
self.skip = None # type: int or None
16-
self.top = None # type: int or None
9+
def __init__(self, select=None, expand=None, filter_expr=None, orderBy=None, top=None, skip=None):
10+
"""
11+
A query option is a set of query string parameters applied to a resource that can help control the amount
12+
of data being returned for the resource in the URL
13+
14+
:param list[str] select: The $select system query option allows the clients to requests a limited set of
15+
properties for each entity or complex type.
16+
:param list[str] expand: The $expand system query option specifies the related resources to be included in
17+
line with retrieved resources.
18+
:param str filter_expr: The $filter system query option allows clients to filter a collection of resources
19+
that are addressed by a request URL.
20+
:param str orderBy: The $orderby system query option allows clients to request resources in either ascending
21+
order using asc or descending order using desc
22+
:param int top: The $top system query option requests the number of items in the queried collection to
23+
be included in the result.
24+
:param int skip: The $skip query option requests the number of items in the queried collection that
25+
are to be skipped and not included in the result.
26+
"""
27+
if expand is None:
28+
expand = []
29+
if select is None:
30+
select = []
31+
self.select = select
32+
self.expand = expand
33+
self.filter = filter_expr
34+
self.orderBy = orderBy
35+
self.skip = skip
36+
self.top = top
1737

1838
@property
1939
def is_empty(self):
20-
result = {k: v for (k, v) in self.__dict__.items() if v is not None}
40+
result = {k: v for (k, v) in self.__dict__.items() if v is not None and v}
2141
return not result
2242

2343
def to_url(self):
2444
"""Convert query options to url
2545
:return: str
2646
"""
2747
return '&'.join(['$%s=%s' % (key, _normalize(key, value))
28-
for (key, value) in self.__dict__.items() if value is not None])
48+
for (key, value) in self.__dict__.items() if value is not None and value])

office365/runtime/resource_path_service_operation.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class ResourcePathServiceOperation(ResourcePath):
88

99
def __init__(self, method_name, method_parameters=None, parent=None):
1010
"""
11-
:type method_parameters: list or dict or office365.runtime.clientValue.ClientValue
11+
:type method_parameters: list or dict or office365.runtime.clientValue.ClientValue or None
1212
:type method_name: str
1313
1414

office365/sharepoint/folders/folder.py

+5
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ def files(self):
120120
from office365.sharepoint.files.file_collection import FileCollection
121121
return FileCollection(self.context, ResourcePath("Files", self.resource_path))
122122

123+
@property
124+
def serverRelativeUrl(self):
125+
"""Gets the server-relative URL of the list folder."""
126+
return self.properties.get("ServerRelativeUrl", None)
127+
123128
@property
124129
def folders(self):
125130
"""Get a folder collection"""

office365/sharepoint/listitems/listitem.py

+68-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,84 @@
11
from office365.runtime.clientValueCollection import ClientValueCollection
22
from office365.runtime.client_query import UpdateEntityQuery, DeleteEntityQuery
3+
from office365.runtime.client_result import ClientResult
34
from office365.runtime.resource_path import ResourcePath
45
from office365.runtime.resource_path_service_operation import ResourcePathServiceOperation
56
from office365.runtime.queries.serviceOperationQuery import ServiceOperationQuery
67
from office365.sharepoint.fields.fieldLookupValue import FieldLookupValue
78
from office365.sharepoint.fields.fieldMultiLookupValue import FieldMultiLookupValue
89
from office365.sharepoint.permissions.securable_object import SecurableObject
10+
from office365.sharepoint.sharing.externalSharingSiteOption import ExternalSharingSiteOption
11+
from office365.sharepoint.sharing.objectSharingInformation import ObjectSharingInformation
12+
from office365.sharepoint.sharing.sharingResult import SharingResult
13+
from office365.sharepoint.ui.applicationpages.clientPeoplePickerQueryParameters import ClientPeoplePickerQueryParameters
14+
from office365.sharepoint.ui.applicationpages.clientPeoplePickerWebServiceInterface import \
15+
ClientPeoplePickerWebServiceInterface
916

1017

1118
class ListItem(SecurableObject):
1219
"""ListItem resource"""
1320

21+
def share(self, user_principal_name,
22+
shareOption=ExternalSharingSiteOption.View,
23+
sendEmail=True, emailSubject=None, emailBody=None):
24+
"""
25+
Share a ListItem (file or folder facet)
26+
27+
:param str user_principal_name: User identifier
28+
:param ExternalSharingSiteOption shareOption: The sharing type of permission to grant on the object.
29+
:param bool sendEmail: A flag to determine if an email notification SHOULD be sent (if email is configured).
30+
:param str emailSubject: The email subject.
31+
:param str emailBody: The email subject.
32+
:return: SharingResult
33+
"""
34+
35+
result = ClientResult(SharingResult(self.context))
36+
file_result = ClientResult(str)
37+
38+
role_values = {
39+
ExternalSharingSiteOption.View: "role:1073741826",
40+
ExternalSharingSiteOption.Edit: "role:1073741827",
41+
}
42+
43+
def _property_resolved(target_item):
44+
file_result.value = target_item.get_property("EncodedAbsUrl")
45+
46+
def _picker_value_resolved(picker_value):
47+
from office365.sharepoint.webs.web import Web
48+
result.value = Web.share_object(self.context, file_result.value, picker_value, role_values[shareOption],
49+
0,
50+
False, sendEmail, False, emailSubject, emailBody)
51+
52+
self.ensure_property("EncodedAbsUrl", _property_resolved)
53+
params = ClientPeoplePickerQueryParameters(user_principal_name)
54+
ClientPeoplePickerWebServiceInterface.client_people_picker_resolve_user(self.context,
55+
params, _picker_value_resolved)
56+
return result.value
57+
58+
def unshare(self):
59+
"""
60+
Share a ListItem (file or folder facet)
61+
"""
62+
result = ClientResult(SharingResult(self.context))
63+
64+
def _property_resolved(target_item):
65+
abs_url = target_item.get_property("EncodedAbsUrl")
66+
from office365.sharepoint.webs.web import Web
67+
result.value = Web.unshare_object(self.context, abs_url)
68+
69+
self.ensure_property("EncodedAbsUrl", _property_resolved)
70+
return result.value
71+
72+
def get_sharing_information(self):
73+
result = ClientResult(ObjectSharingInformation(self.context))
74+
75+
def _item_resolved(return_type):
76+
result.value = ObjectSharingInformation.get_list_item_sharing_information(
77+
self.context, return_type.parentList.properties["Id"], return_type.properties["Id"])
78+
79+
self.ensure_property(["Id", "ParentList"], _item_resolved)
80+
return result.value
81+
1482
def update(self):
1583
"""Update the list item."""
1684
self.ensure_type_name(self.parentList)
@@ -89,8 +157,6 @@ def set_property(self, name, value, persist_changes=True):
89157
if name == "Id" and self._parent_collection:
90158
self._resource_path = ResourcePathServiceOperation(
91159
"getItemById", [value], self._parent_collection.resource_path.parent)
92-
elif name == "GUID":
93-
self._resource_path = ResourcePathServiceOperation("GetFileById", [value], self.context.web.resource_path)
94160

95161
def ensure_type_name(self, target_list):
96162
"""

0 commit comments

Comments
 (0)