pyu-activesp 0.0.4.1.2
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.
- data/LICENSE +25 -0
- data/README.rdoc +105 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/activesp/associations.rb +76 -0
- data/lib/activesp/base.rb +139 -0
- data/lib/activesp/caching.rb +60 -0
- data/lib/activesp/connection.rb +126 -0
- data/lib/activesp/content_type.rb +152 -0
- data/lib/activesp/field.rb +193 -0
- data/lib/activesp/file.rb +71 -0
- data/lib/activesp/folder.rb +103 -0
- data/lib/activesp/ghost_field.rb +100 -0
- data/lib/activesp/group.rb +107 -0
- data/lib/activesp/item.rb +338 -0
- data/lib/activesp/list.rb +509 -0
- data/lib/activesp/permission_set.rb +64 -0
- data/lib/activesp/persistent_caching.rb +112 -0
- data/lib/activesp/role.rb +116 -0
- data/lib/activesp/root.rb +128 -0
- data/lib/activesp/site.rb +305 -0
- data/lib/activesp/url.rb +146 -0
- data/lib/activesp/user.rb +97 -0
- data/lib/activesp/util.rb +326 -0
- data/lib/activesp.rb +53 -0
- metadata +118 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
# Copyright (c) 2010 XAOP bvba
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation
|
5
|
+
# files (the "Software"), to deal in the Software without
|
6
|
+
# restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following
|
10
|
+
# conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
#
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
#
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module ActiveSP
|
27
|
+
|
28
|
+
class Group < Base
|
29
|
+
|
30
|
+
extend Caching
|
31
|
+
extend PersistentCaching
|
32
|
+
include Util
|
33
|
+
include InSite
|
34
|
+
|
35
|
+
persistent { |site, name, *a| [site.connection, [:group, name]] }
|
36
|
+
# @private
|
37
|
+
def initialize(site, name)
|
38
|
+
@site, @name = site, name
|
39
|
+
end
|
40
|
+
|
41
|
+
# See {Base#key}
|
42
|
+
# @return [String]
|
43
|
+
def key
|
44
|
+
encode_key("G", [@name])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the list of users in this group
|
48
|
+
# @return [User]
|
49
|
+
def users
|
50
|
+
call("UserGroup", "get_user_collection_from_group", "groupName" => @name).xpath("//spdir:User", NS).map do |row|
|
51
|
+
attributes = clean_attributes(row.attributes)
|
52
|
+
User.new(@site, attributes["LoginName"])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
cache :users, :dup => :always
|
56
|
+
|
57
|
+
# Returns false. The same method is present on {Role} where it returns true. Roles and groups can generally be
|
58
|
+
# duck-typed, and this method is there for the rare case where you do need to make the distinction
|
59
|
+
# @return [Boolean]
|
60
|
+
def is_role?
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# See {Base#save}
|
65
|
+
# @return [void]
|
66
|
+
def save
|
67
|
+
p untype_cast_attributes(@site, nil, internal_attribute_types, changed_attributes)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @private
|
71
|
+
def to_s
|
72
|
+
"#<ActiveSP::Group name=#{@name}>"
|
73
|
+
end
|
74
|
+
|
75
|
+
# @private
|
76
|
+
alias inspect to_s
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def data
|
81
|
+
call("UserGroup", "get_group_info", "groupName" => @name).xpath("//spdir:Group", NS).first
|
82
|
+
end
|
83
|
+
cache :data
|
84
|
+
|
85
|
+
def attributes_before_type_cast
|
86
|
+
clean_attributes(data.attributes)
|
87
|
+
end
|
88
|
+
cache :attributes_before_type_cast
|
89
|
+
|
90
|
+
def original_attributes
|
91
|
+
type_cast_attributes(@site, nil, internal_attribute_types, attributes_before_type_cast)
|
92
|
+
end
|
93
|
+
cache :original_attributes
|
94
|
+
|
95
|
+
def internal_attribute_types
|
96
|
+
@@internal_attribute_types ||= {
|
97
|
+
"Description" => GhostField.new("Description", "Text", false, true),
|
98
|
+
"ID" => GhostField.new("ID", "Text", false, true),
|
99
|
+
"Name" => GhostField.new("Name", "Text", false, true),
|
100
|
+
"OwnerID" => GhostField.new("OsnerID", "Integer", false, true),
|
101
|
+
"OwnerIsUser" => GhostField.new("OwnerIsUser", "Bool", false, true)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,338 @@
|
|
1
|
+
# Copyright (c) 2010 XAOP bvba
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation
|
5
|
+
# files (the "Software"), to deal in the Software without
|
6
|
+
# restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following
|
10
|
+
# conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
#
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
#
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module ActiveSP
|
27
|
+
|
28
|
+
class Item < Base
|
29
|
+
|
30
|
+
include InSite
|
31
|
+
extend Caching
|
32
|
+
include Util
|
33
|
+
|
34
|
+
# Returns the list in which the item is located
|
35
|
+
# @return [List]
|
36
|
+
attr_reader :list
|
37
|
+
|
38
|
+
# @private
|
39
|
+
def initialize(list, id, folder = :unset, uid = nil, url = nil, attributes_before_type_cast = nil)
|
40
|
+
@list, @id = list, id
|
41
|
+
@folder = folder if folder != :unset # We have to allow for nil
|
42
|
+
@uid = uid if uid
|
43
|
+
@site = list.site
|
44
|
+
@url = url if url
|
45
|
+
@attributes_before_type_cast = attributes_before_type_cast if attributes_before_type_cast
|
46
|
+
end
|
47
|
+
|
48
|
+
def ID
|
49
|
+
@id
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_folder?
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the folder, if any, that this item is located in.
|
57
|
+
# @return [Folder, nil]
|
58
|
+
def folder
|
59
|
+
query = Builder::XmlMarkup.new.Query do |xml|
|
60
|
+
xml.Where do |xml|
|
61
|
+
xml.Eq do |xml|
|
62
|
+
xml.FieldRef(:Name => "FileRef")
|
63
|
+
xml.Value(::File.dirname(url).sub(/\A\//, ""), :Type => "Text")
|
64
|
+
end
|
65
|
+
xml.Eq do |xml|
|
66
|
+
xml.FieldRef(:Name => "FSObjType")
|
67
|
+
xml.Value(1, :Type => "Text")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@list.items(:folder => :all, :query => query).first
|
72
|
+
end
|
73
|
+
cache :folder
|
74
|
+
|
75
|
+
# Returns the parent of this item.
|
76
|
+
# @return [Folder, List]
|
77
|
+
def parent
|
78
|
+
folder || @list
|
79
|
+
end
|
80
|
+
|
81
|
+
# @private
|
82
|
+
def id
|
83
|
+
uid
|
84
|
+
end
|
85
|
+
|
86
|
+
# @private
|
87
|
+
def uid
|
88
|
+
attributes["UniqueID"]
|
89
|
+
end
|
90
|
+
cache :uid
|
91
|
+
|
92
|
+
# The URL of this item
|
93
|
+
# @return [String]
|
94
|
+
def url
|
95
|
+
# URL(@list.url).join(attributes["ServerUrl"]).to_s
|
96
|
+
attributes["ServerUrl"]
|
97
|
+
end
|
98
|
+
cache :url
|
99
|
+
|
100
|
+
def absolute_url
|
101
|
+
URL(@list.url).join(attributes["ServerUrl"]).to_s
|
102
|
+
end
|
103
|
+
cache :absolute_url
|
104
|
+
|
105
|
+
# See {Base#key}
|
106
|
+
# @return [String]
|
107
|
+
def key
|
108
|
+
encode_key("I", [@list.key, @id])
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a list of the URLs of the attachments of this item. Note that for items in a document
|
112
|
+
# library, this returns an empty list
|
113
|
+
# @return [Array<String>]
|
114
|
+
def attachment_urls
|
115
|
+
@list.when_list do
|
116
|
+
result = call("Lists", "get_attachment_collection", "listName" => @list.id, "listItemID" => @id)
|
117
|
+
return result.xpath("//sp:Attachment", NS).map { |att| att.text }
|
118
|
+
end
|
119
|
+
@list.when_document_library { raise TypeError, "a document library does not support attachments" }
|
120
|
+
@list.raise_on_unknown_type
|
121
|
+
end
|
122
|
+
cache :attachment_urls, :dup => :always
|
123
|
+
|
124
|
+
# Yields each attachment as a ActiveSP::File object.
|
125
|
+
#
|
126
|
+
def each_attachment
|
127
|
+
attachment_urls.each { |url| yield ActiveSP::File.new(self, url, true) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def add_attachment(parameters = {})
|
131
|
+
@list.when_list do
|
132
|
+
parameters = parameters.dup
|
133
|
+
content = parameters.delete(:content) or raise ArgumentError, "Specify the content in the :content parameter"
|
134
|
+
file_name = parameters.delete(:file_name) or raise ArgumentError, "Specify the file name in the :file_name parameter"
|
135
|
+
result = call("Lists", "add_attachment", "listName" => @list.ID, "listItemID" => self.ID, "fileName" => file_name, "attachment" => Base64.encode64(content.to_s))
|
136
|
+
add_result = result.xpath("//sp:AddAttachmentResult", NS).first
|
137
|
+
if add_result
|
138
|
+
clear_cache_for(:attachment_urls)
|
139
|
+
return ActiveSP::File.new(self, add_result.text, true)
|
140
|
+
else
|
141
|
+
raise "cannot add attachment"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
@list.when_document_library { raise TypeError, "a document library does not support attachments" }
|
145
|
+
@list.raise_on_unknown_type
|
146
|
+
end
|
147
|
+
|
148
|
+
association :attachments do
|
149
|
+
def create(parameters = {})
|
150
|
+
@object.add_attachment(parameters)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def content
|
155
|
+
@list.when_list { raise TypeError, "a list has attachments" }
|
156
|
+
@list.when_document_library { return ActiveSP::File.new(self, url, false) }
|
157
|
+
@list.raise_on_unknown_type
|
158
|
+
end
|
159
|
+
|
160
|
+
def content=(data)
|
161
|
+
@list.when_list { raise TypeError, "a list has attachments" }
|
162
|
+
@list.when_document_library { @list.create_document(:overwrite => true, :content => data, "FileLeafRef" => original_attributes["FileLeafRef"]) }
|
163
|
+
@list.raise_on_unknown_type
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns a list of the content URLs for this item. For items in document libraries, this
|
167
|
+
# returns the url, for other items this returns the attachments. These URLs can be used
|
168
|
+
# to download all contents. See {Connection#fetch}
|
169
|
+
# @return [Array<String>]
|
170
|
+
def content_urls
|
171
|
+
@list.when_list { return attachment_urls }
|
172
|
+
@list.when_document_library { return is_folder? ? [] : [url] }
|
173
|
+
@list.raise_on_unknown_type
|
174
|
+
end
|
175
|
+
cache :content_urls, :dup => :always
|
176
|
+
|
177
|
+
# Returns the content type of this item
|
178
|
+
# @return [ContentType]
|
179
|
+
def content_type
|
180
|
+
ContentType.new(@site, @list, attributes["ContentTypeId"])
|
181
|
+
end
|
182
|
+
cache :content_type
|
183
|
+
|
184
|
+
# def versions
|
185
|
+
# call("Versions", "get_versions", "fileName" => attributes["ServerUrl"])
|
186
|
+
# end
|
187
|
+
|
188
|
+
# See {Base#save}
|
189
|
+
# @return [self]
|
190
|
+
def save
|
191
|
+
update_attributes_internal(untype_cast_attributes(@site, nil, internal_attribute_types, changed_attributes))
|
192
|
+
self
|
193
|
+
end
|
194
|
+
|
195
|
+
def check_out
|
196
|
+
@list.when_list { raise TypeError, "cannot check out list items; they would disappear" }
|
197
|
+
@list.raise_on_unknown_type
|
198
|
+
result = call("Lists", "check_out_file", "pageUrl" => absolute_url, "checkoutToLocal" => false)
|
199
|
+
checkout_result = result.xpath("//sp:CheckOutFileResult", NS).first.text
|
200
|
+
if checkout_result == "true"
|
201
|
+
self
|
202
|
+
else
|
203
|
+
raise "cannot check out this item"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def check_in(options = {})
|
208
|
+
options = options.dup
|
209
|
+
type = options.delete(:type) or raise ArgumentError, ":type parameter not specified"
|
210
|
+
comment = options.delete(:comment)
|
211
|
+
options.empty? or raise ArgumentError, "unsupported options #{options.keys.map { |k| k.inspect }.join(", ")}"
|
212
|
+
@list.when_list { raise TypeError, "cannot check in list items because you can't check them out" }
|
213
|
+
@list.raise_on_unknown_type
|
214
|
+
if checkin_type = { :minor => 0, :major => 1, :overwrite => 2 }[type]
|
215
|
+
if type == :minor && !@list.attribute("EnableMinorVersion")
|
216
|
+
raise TypeError, "this list does not support minor versions"
|
217
|
+
end
|
218
|
+
result = call("Lists", "check_in_file", "pageUrl" => absolute_url, "comment" => comment, "CheckinType" => checkin_type)
|
219
|
+
checkin_result = result.xpath("//sp:CheckInFileResult", NS).first.text
|
220
|
+
if checkin_result == "true"
|
221
|
+
self
|
222
|
+
else
|
223
|
+
raise "cannot check in this item"
|
224
|
+
end
|
225
|
+
else
|
226
|
+
raise ArgumentError, "invalid checkin type #{type.inspect}, valid values are :minor, :major and :overwrite"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def cancel_checkout
|
231
|
+
@list.when_list { raise TypeError, "cannot undo check-out for list items because you can't check them out" }
|
232
|
+
@list.raise_on_unknown_type
|
233
|
+
result = call("Lists", "undo_check_out", "pageUrl" => absolute_url)
|
234
|
+
cancel_result = result.xpath("//sp:UndoCheckOutResult", NS).first.text
|
235
|
+
if cancel_result == "true"
|
236
|
+
self
|
237
|
+
else
|
238
|
+
raise "cannot cancel check-out for this item"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def update_attributes(attributes)
|
243
|
+
attributes.each do |k, v|
|
244
|
+
set_attribute(k, v)
|
245
|
+
end
|
246
|
+
save
|
247
|
+
end
|
248
|
+
|
249
|
+
def destroy
|
250
|
+
updates = Builder::XmlMarkup.new.Batch("OnError" => "Continue", "ListVersion" => 1) do |xml|
|
251
|
+
xml.Method("ID" => 1, "Cmd" => "Delete") do
|
252
|
+
xml.Field(self.ID, "Name" => "ID")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
result = call("Lists", "update_list_items", "listName" => @list.id, "updates" => updates)
|
256
|
+
create_result = result.xpath("//sp:Result", NS).first
|
257
|
+
error_code = create_result.xpath("./sp:ErrorCode", NS).first.text.to_i(0)
|
258
|
+
if error_code == 0
|
259
|
+
@ID = nil
|
260
|
+
else
|
261
|
+
raise "cannot create item, error code = #{error_code}"
|
262
|
+
end
|
263
|
+
self
|
264
|
+
end
|
265
|
+
|
266
|
+
# @private
|
267
|
+
def to_s
|
268
|
+
"#<ActiveSP::Item url=#{url}>"
|
269
|
+
end
|
270
|
+
|
271
|
+
# @private
|
272
|
+
alias inspect to_s
|
273
|
+
|
274
|
+
def ==(object)
|
275
|
+
::ActiveSP::List === object && self.ID == object.ID
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
|
280
|
+
def raw_attributes
|
281
|
+
query_options = Builder::XmlMarkup.new.QueryOptions do |xml|
|
282
|
+
xml.Folder
|
283
|
+
end
|
284
|
+
query = Builder::XmlMarkup.new.Query do |xml|
|
285
|
+
xml.Where do |xml|
|
286
|
+
xml.Eq do |xml|
|
287
|
+
xml.FieldRef(:Name => "ID")
|
288
|
+
xml.Value(@id, :Type => "Counter")
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
@list.__each_item(query_options, "query" => query) do |attributes|
|
293
|
+
return attributes
|
294
|
+
end
|
295
|
+
end
|
296
|
+
cache :raw_attributes
|
297
|
+
|
298
|
+
def attributes_before_type_cast
|
299
|
+
clean_item_attributes(raw_attributes)
|
300
|
+
end
|
301
|
+
cache :attributes_before_type_cast
|
302
|
+
|
303
|
+
def original_attributes
|
304
|
+
type_cast_attributes(@site, @list, @list.fields_by_name, attributes_before_type_cast)
|
305
|
+
end
|
306
|
+
cache :original_attributes
|
307
|
+
|
308
|
+
def internal_attribute_types
|
309
|
+
list.fields_by_name
|
310
|
+
end
|
311
|
+
|
312
|
+
def update_attributes_internal(attributes)
|
313
|
+
attributes = attributes.dup
|
314
|
+
if file_leaf_ref = attributes.delete("FileLeafRef")
|
315
|
+
base_name = ::File.basename(file_leaf_ref, ".*")
|
316
|
+
end
|
317
|
+
updates = Builder::XmlMarkup.new.Batch("OnError" => "Continue", "ListVersion" => 1) do |xml|
|
318
|
+
xml.Method("ID" => 1, "Cmd" => "Update") do
|
319
|
+
xml.Field(self.ID, "Name" => "ID")
|
320
|
+
construct_xml_for_update_list_items(xml, @list.fields_by_name, attributes)
|
321
|
+
xml.Field(base_name, "Name" => "BaseName") if base_name
|
322
|
+
end
|
323
|
+
end
|
324
|
+
result = call("Lists", "update_list_items", "listName" => @list.id, "updates" => updates)
|
325
|
+
create_result = result.xpath("//sp:Result", NS).first
|
326
|
+
error_code = create_result.xpath("./sp:ErrorCode", NS).first.text.to_i(0)
|
327
|
+
if error_code == 0
|
328
|
+
row = result.xpath("//z:row", NS).first
|
329
|
+
@attributes_before_type_cast = clean_item_attributes(row.attributes)
|
330
|
+
reload
|
331
|
+
else
|
332
|
+
raise "cannot create item, error code = #{error_code}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|