viewpoint-spws 0.5.0

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.
@@ -0,0 +1,48 @@
1
+ =begin
2
+ This file is part of ViewpointSPWS; the Ruby library for Microsoft Sharepoint Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ # This class represents a Sharepoint List returned from the Lists Web Service with a
20
+ # ServerTemplate id of 107 (Tasks).
21
+ # @see http://msdn.microsoft.com/en-us/library/ms774810(v=office.12).aspx
22
+ class Viewpoint::SPWS::Types::TasksList < Viewpoint::SPWS::Types::List
23
+ include Viewpoint::SPWS::Types
24
+
25
+ # Add a Task to this List
26
+ # @param [Hash] opts parameters for this Task
27
+ # @option opts [String] :title The title of this Task
28
+ # @option opts [DateTime] :due_date The date in which this Task is due
29
+ # @option opts [Symbol] :priority The priority of the Task
30
+ # :high, :normal, :low
31
+ # @option opts [Symbol] :status The status of the Task
32
+ # :not_started, :in_progress, :completed, :deferred, :waiting
33
+ # @option opts [Fixnum] :percent_complete The percentage of completion.
34
+ # Must be a number from 0 to 100.
35
+ # @return [Viewpoint::SPWS::Types::ListItem] The newly added Task
36
+ def add_item!(opts)
37
+ raise "Title argument required" unless opts[:title]
38
+ topts = opts.clone
39
+ topts[:priority] = :normal unless topts[:priority]
40
+ topts[:status] = :not_started unless topts[:status]
41
+ if(topts[:percent_complete] && !(0..100).include?(topts[:percent_complete]))
42
+ raise "Invalid :percent_complete #{topts[:percent_complete]}"
43
+ end
44
+
45
+ super(topts)
46
+ end
47
+
48
+ end
@@ -0,0 +1,80 @@
1
+ =begin
2
+ This file is part of ViewpointSPWS; the Ruby library for Microsoft Sharepoint Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ # This class represents a Sharepoint User Item returned from the UserGroup Web Service
20
+ class Viewpoint::SPWS::Types::User
21
+ include Viewpoint::SPWS::Types
22
+
23
+ attr_reader :id, :sid, :name, :login_name, :email, :notes, :flags, :site_user
24
+
25
+ # @param [Viewpoint::SPWS::Websvc::UserGroup] ws The webservice instance this user spawned from
26
+ # @param [Nokogiri::XML::Element] xml the List element we are building from
27
+ def initialize(ws, xml)
28
+ @ws = ws
29
+ parse_xml_fields(xml)
30
+ end
31
+
32
+ def is_site_admin?
33
+ @is_site_admin
34
+ end
35
+
36
+ def is_domain_group?
37
+ @is_domain_group
38
+ end
39
+
40
+ private
41
+
42
+ # Parse the fields out of the passed XML document.
43
+ # @param[Nokogiri::XML::Element] xml
44
+ def parse_xml_fields(xml)
45
+ @xmldoc = xml
46
+ set_field :@id, 'ID'
47
+ set_field :@sid, 'Sid'
48
+ set_field :@name, 'Name'
49
+ set_field :@login_name, 'LoginName'
50
+ set_field :@email, 'Email'
51
+ set_field :@notes, 'Notes'
52
+ set_field :@is_site_admin, 'IsSiteAdmin', 'Boolean'
53
+ set_field :@is_domain_group, 'IsDomainGroup', 'Boolean'
54
+ set_field :@flags, 'Flags'
55
+ set_field :@site_user, 'SiteUser'
56
+ @xmldoc = nil
57
+ end
58
+
59
+ # Parse a Sharepoint managed field
60
+ # @param [Symbol] vname The instance variable we will set the value to if it exists
61
+ # @param [String] fname The field name to check for
62
+ def set_mfield(vname, fname)
63
+ instance_variable_set vname, @xmldoc[fname].split(';#').last if @xmldoc[fname]
64
+ end
65
+
66
+ # @param [Symbol] vname The instance variable we will set the value to if it exists
67
+ # @param [String] fname The field name to check for
68
+ # @param [String] type ('String') optional type for additional processing
69
+ def set_field(vname, fname, type = 'String')
70
+ case type
71
+ when 'Boolean'
72
+ val = (@xmldoc[fname] =~ /True/i) ? true : false
73
+ instance_variable_set vname, val
74
+ else
75
+ instance_variable_set vname, @xmldoc[fname] if @xmldoc[fname]
76
+ end
77
+ end
78
+
79
+ end
80
+
@@ -0,0 +1,5 @@
1
+ module Viewpoint
2
+ module SPWS
3
+ VERSION = "0.5.0"
4
+ end
5
+ end
@@ -0,0 +1,108 @@
1
+ =begin
2
+ This file is part of ViewpointSPWS; the Ruby library for Microsoft Sharepoint Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ # This class represents the Sharepoint Copy Web Service.
20
+ # @see http://msdn.microsoft.com/en-us/library/copy(v=office.12).aspx
21
+ class Viewpoint::SPWS::Websvc::Copy
22
+ include Viewpoint::SPWS::Websvc::WebServiceBase
23
+
24
+ def initialize(spcon)
25
+ @default_ns = 'http://schemas.microsoft.com/sharepoint/soap/'
26
+ @ws_endpoint = '_vti_bin/Copy.asmx'
27
+ super
28
+ end
29
+
30
+ # Copies a document represented by a Byte array to one or more locations on a server.
31
+ # @see http://msdn.microsoft.com/en-us/library/copy.copy.copyintoitems(v=office.12).aspx
32
+ # @param [String] srcfile Either a relative or absolute path to the input file
33
+ # @param [Array<String>] tgturls An array of absolute URLs to copy the source to.
34
+ # @todo parse the return and check for errors
35
+ def copy_into_items(srcfile, tgturls)
36
+ soapmsg = build_soap_envelope do |type, builder|
37
+ if(type == :header)
38
+ else
39
+ builder.CopyIntoItems {
40
+ builder.parent.default_namespace = @default_ns
41
+ builder.SourceUrl(srcfile)
42
+ builder.DestinationUrls {
43
+ tgturls.each {|tgt| builder.string(tgt) }
44
+ }
45
+ builder.Fields
46
+ builder.Stream(Base64.encode64(File.read(srcfile)))
47
+ }
48
+ end
49
+ end
50
+
51
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
52
+ end
53
+
54
+ # Copies a document from one location on a server running Windows SharePoint Services
55
+ # to another location on the same server.
56
+ # @see http://msdn.microsoft.com/en-us/library/copy.copy.copyintoitemslocal(v=office.12).aspx
57
+ # @param [String] srcurl An absolute URL to the document you want to copy from.
58
+ # @param [Array<String>] tgturls An array of absolute URLs to copy the source to.
59
+ # @todo parse the return and check for errors
60
+ def copy_into_items_local(srcurl, tgturls)
61
+ soapmsg = build_soap_envelope do |type, builder|
62
+ if(type == :header)
63
+ else
64
+ builder.CopyIntoItemsLocal {
65
+ builder.parent.default_namespace = @default_ns
66
+ builder.SourceUrl(srcurl)
67
+ builder.DestinationUrls {
68
+ tgturls.each {|tgt| builder.string(tgt) }
69
+ }
70
+ }
71
+ end
72
+ end
73
+
74
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
75
+ end
76
+
77
+ # Generates a Byte array representation of a document
78
+ # @see http://msdn.microsoft.com/en-us/library/copy.copy.getitem(v=office.12).aspx
79
+ # @param [String] tgturl An absolute URL to the document you want to retrieve the
80
+ # byte stream for.
81
+ # @return [Hash] it returns a Hash with the keys :stream that has the Base64 encoded
82
+ # file data and a key :fields which is an Array of Hash data that contains document
83
+ # metadata.
84
+ def get_item(tgturl)
85
+ soapmsg = build_soap_envelope do |type, builder|
86
+ if(type == :header)
87
+ else
88
+ builder.GetItem {
89
+ builder.parent.default_namespace = @default_ns
90
+ builder.Url(tgturl)
91
+ }
92
+ end
93
+ end
94
+
95
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
96
+ ns = {"xmlns"=> @default_ns}
97
+ data = {}
98
+ data[:stream] = soaprsp.xpath('//xmlns:GetItemResponse/xmlns:Stream',ns).first.content
99
+ fields = []
100
+ soaprsp.xpath('//xmlns:GetItemResponse/xmlns:Fields/xmlns:FieldInformation',ns).each do |f|
101
+ fields << {:type => f['Type'], :display_name => f['DisplayName'],
102
+ :id => f['Id'], :value => f['Value']}
103
+ end
104
+ data[:fields] = fields
105
+ data
106
+ end
107
+
108
+ end
@@ -0,0 +1,484 @@
1
+ =begin
2
+ This file is part of ViewpointSPWS; the Ruby library for Microsoft Sharepoint Web Services.
3
+
4
+ Copyright © 2011 Dan Wanek <dan.wanek@gmail.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ =end
18
+
19
+ # This class represents the Sharepoint Lists Web Service.
20
+ # @see http://msdn.microsoft.com/en-us/library/ms774654(v=office.12).aspx
21
+ class Viewpoint::SPWS::Websvc::Lists
22
+ include Viewpoint::SPWS::Websvc::WebServiceBase
23
+
24
+ def initialize(spcon)
25
+ @default_ns = 'http://schemas.microsoft.com/sharepoint/soap/'
26
+ @ws_endpoint = '_vti_bin/Lists.asmx'
27
+ super
28
+ end
29
+
30
+ # Add a List to this site.
31
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.addlist(v=office.12).aspx
32
+ # @param [String] name A name for the List
33
+ # @param [String] desc A description of the List
34
+ # @param [Integer] templ_id The template type for this List. See the Microsoft docs for
35
+ # additional information.
36
+ # @return [Viewpoint::SPWS::List] The new List object
37
+ def add_list(name, desc, templ_id)
38
+ soapmsg = build_soap_envelope do |type, builder|
39
+ if(type == :header)
40
+ else
41
+ builder.AddList {
42
+ builder.parent.default_namespace = @default_ns
43
+ builder.listName(name)
44
+ builder.description(desc)
45
+ builder.templateID(templ_id)
46
+ }
47
+ end
48
+ end
49
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
50
+ return soaprsp
51
+ ns = {"xmlns"=> @default_ns}
52
+ new_list(soaprsp.xpath('//xmlns:AddListResult/xmlns:List', ns).first)
53
+ end
54
+
55
+ # Add a List to this site from a feature ID.
56
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.addlistfromfeature(v=office.12).aspx
57
+ # @param [String] name A name for the List
58
+ # @param [String] desc A description of the List
59
+ # @param [String] feature_id The GUID of the feature ID.
60
+ # @param [Integer] templ_id The template type for this List. See the Microsoft docs for
61
+ # additional information.
62
+ # @return [Viewpoint::SPWS::List] The new List object
63
+ def add_list_from_feature(name, desc, feature_id, templ_id)
64
+ soapmsg = build_soap_envelope do |type, builder|
65
+ if(type == :header)
66
+ else
67
+ builder.AddListFromFeature {
68
+ builder.parent.default_namespace = @default_ns
69
+ builder.listName(name)
70
+ builder.description(desc)
71
+ builder.featureID(feature_id)
72
+ builder.templateID(templ_id)
73
+ }
74
+ end
75
+ end
76
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
77
+ ns = {"xmlns"=> @default_ns}
78
+ new_list(soaprsp.xpath('//xmlns:AddListResult/xmlns:List', ns).first)
79
+ end
80
+
81
+ # Delete a list from this site.
82
+ # @param [String] name Either the name or the GUID of the list
83
+ # @todo Work out the return value
84
+ def delete_list(name)
85
+ soapmsg = build_soap_envelope do |type, builder|
86
+ if(type == :header)
87
+ else
88
+ builder.DeleteList {
89
+ builder.parent.default_namespace = @default_ns
90
+ builder.listName(name)
91
+ }
92
+ end
93
+ end
94
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
95
+ #ns = {"xmlns"=> @default_ns}
96
+ end
97
+
98
+ # Returns a collection of content type definition schemas
99
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.getlistcontenttypes(v=office.12).aspx
100
+ # @param [String] name Either the name or the GUID of the list
101
+ # @param [String] content_type_id (nil) The content type ID. I'm still not clear on what specifying
102
+ # this value does. In my limitted testing it does not seem to have an effect on the output.
103
+ # @todo Work out the return value
104
+ def get_list_content_types(name, content_type_id = nil)
105
+ soapmsg = build_soap_envelope do |type, builder|
106
+ if(type == :header)
107
+ else
108
+ builder.GetListContentTypes {
109
+ builder.parent.default_namespace = @default_ns
110
+ builder.listName(name)
111
+ builder.contentTypeId(content_type_id)
112
+ }
113
+ end
114
+ end
115
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
116
+ end
117
+
118
+ # Returns all the lists for a Sharepoint site.
119
+ # @param [Boolean] show_hidden Whether or not to show hidden lists. Default = false
120
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.getlistcollection(v=office.12).aspx
121
+ def get_list_collection(show_hidden = false)
122
+ soapmsg = build_soap_envelope do |type, builder|
123
+ if(type == :header)
124
+ else
125
+ builder.GetListCollection {
126
+ builder.parent.default_namespace = @default_ns
127
+ }
128
+ end
129
+ end
130
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
131
+ ns = {"xmlns"=> @default_ns}
132
+ lists = []
133
+ soaprsp.xpath('//xmlns:Lists/xmlns:List', ns).each do |l|
134
+ lists << new_list(l)
135
+ end
136
+ if(!show_hidden)
137
+ lists.reject! do |i|
138
+ i.hidden?
139
+ end
140
+ end
141
+ lists
142
+ end
143
+
144
+ # Retrieve a specific Sharepoint List
145
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.getlist(v=office.12).aspx
146
+ # @param [String] list title or the GUID for the list
147
+ # @return [Viewpoint::SPWS::List]
148
+ def get_list(list)
149
+ soapmsg = build_soap_envelope do |type, builder|
150
+ if(type == :header)
151
+ else
152
+ builder.GetList {
153
+ builder.parent.default_namespace = @default_ns
154
+ builder.listName(list)
155
+ }
156
+ end
157
+ end
158
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
159
+ ns = {"xmlns"=> @default_ns}
160
+ new_list(soaprsp.xpath('//xmlns:GetListResult/xmlns:List', ns).first)
161
+ end
162
+
163
+ # Get List Items based on certain parameters
164
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.getlistitems(v=office.12).aspx
165
+ # @param [String] list title or the GUID for the list
166
+ # @param [Hash] opts
167
+ # @option opts [String] :view_name ('') GUID for the view surrounded by curly braces
168
+ # If nothing is passed it used the default of the View
169
+ # @option opts [String] :row_limit ('') A String representing the number of rows to return.
170
+ # @option opts [Boolean] :recursive (true) If true look in subfolders as well as root
171
+ # @option opts [Boolean] :date_in_utc (true) If true return dates in UTC
172
+ # @option opts [String] :folder ('')
173
+ # Filter document library items for items in the specified folder
174
+ # @yield [builder] Yields a Builder object that can be used to build a CAML Query. See the
175
+ # example on how to use it.
176
+ # @yieldparam [Nokogiro::XML::Builder] builder The builder object used to create the Query
177
+ # @option opts [Array<String,Symbol>] :view_fields and array of String or Symbols that
178
+ # represent which fields to return with the query.
179
+ # @example The following example shows how to prepare a CAML Query with a block. It filters for all objects of ObjectType '0' = Files
180
+ # items = listws.get_list_items('Shared Documents',:recursive => true) do |b|
181
+ # b.Query {
182
+ # b.Where {
183
+ # b.Eq {
184
+ # b.FieldRef(:Name => 'FSObjType')
185
+ # b.Value(0, :Type => 'Integer')
186
+ # }
187
+ # }
188
+ # }
189
+ # end
190
+ def get_list_items(list, opts = {})
191
+ # Set Default values
192
+ opts[:recursive] = true unless opts.has_key?(:recursive)
193
+ opts[:view_name] = '' unless opts.has_key?(:view_name)
194
+ opts[:row_limit] = '' unless opts.has_key?(:row_limit)
195
+ opts[:date_in_utc] = true unless opts.has_key?(:date_in_utc)
196
+ opts[:folder] = '' unless opts.has_key?(:folder)
197
+
198
+ soapmsg = build_soap_envelope do |type, builder|
199
+ if(type == :header)
200
+ else
201
+ builder.GetListItems {
202
+ builder.parent.default_namespace = @default_ns
203
+ builder.listName(list)
204
+ builder.viewName(opts[:view_name])
205
+ builder.rowLimit(opts[:row_limit])
206
+
207
+ if block_given?
208
+ builder.query {
209
+ builder.parent.default_namespace = ''
210
+ yield builder
211
+ }
212
+ end
213
+
214
+ if(opts[:view_fields])
215
+ builder.viewFields {
216
+ builder.ViewFields {
217
+ builder.parent.default_namespace = ''
218
+ opts[:view_fields].each do |f|
219
+ builder.FieldRef(:Name => f.to_s.camel_case)
220
+ end
221
+ }
222
+ }
223
+ end
224
+
225
+ builder.queryOptions {
226
+ builder.QueryOptions {
227
+ builder.parent.default_namespace = ''
228
+ builder.Folder(opts[:folder])
229
+ builder.ViewAttributes(:Scope => 'Recursive') if opts[:recursive]
230
+ builder.DateInUtc('True') if opts[:date_in_utc]
231
+ builder.IncludeAttachmentUrls('True')
232
+ }
233
+ }
234
+ # @todo Is this worth supporting???
235
+ #builder.webID(parms[:web_id])
236
+ }
237
+ end
238
+ end
239
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
240
+ ns = {'xmlns:z' => "#RowsetSchema"}
241
+ items = []
242
+ soaprsp.xpath('//z:row', ns).each do |li|
243
+ items << Types::ListItem.new(self, list, li)
244
+ end
245
+ items
246
+ end
247
+
248
+ # Get List Items changes from a given change token
249
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.getlistitemchangessincetoken(v=office.12).aspx
250
+ # @param [String] list title or the GUID for the list
251
+ # @param [String] token (nil) The change token or nil to get all
252
+ # @param [Hash] opts
253
+ # @option opts [String] :view_name ('') GUID for the view surrounded by curly braces
254
+ # If nothing is passed it used the default of the View
255
+ # @option opts [String] :row_limit ('') A String representing the number of rows to return.
256
+ # @option opts [Boolean] :recursive (true) If true look in subfolders as well as root
257
+ # @option opts [Boolean] :date_in_utc (true) If true return dates in UTC
258
+ # @option opts [String] :folder ('')
259
+ # Filter document library items for items in the specified folder
260
+ # @yield [builder] Yields a Builder object that can be used to build a CAML Query. See the
261
+ # example on how to use it.
262
+ # @yieldparam [Nokogiro::XML::Builder] builder The builder object used to create the Query
263
+ # @example The following example shows how to prepare a CAML Query with a block. It filters for all objects of ObjectType '0' = Files
264
+ # items = listws.get_list_items('Shared Documents',:recursive => true) do |b|
265
+ # b.Query {
266
+ # b.Where {
267
+ # b.Eq {
268
+ # b.FieldRef(:Name => 'FSObjType')
269
+ # b.Value(0, :Type => 'Integer')
270
+ # }
271
+ # }
272
+ # }
273
+ # end
274
+ # @return [Hash] returns a Hash with the keys :last_change_token, which contains a String
275
+ # that holds a change token that may be used in subsequent calls, and :items which holds
276
+ # an Array of ListItem objects.
277
+ def get_list_item_changes_since_token(list, token=nil, opts = {})
278
+ # Set Default values
279
+ opts[:recursive] = true unless opts.has_key?(:recursive)
280
+ opts[:view_name] = '' unless opts.has_key?(:view_name)
281
+ opts[:row_limit] = '' unless opts.has_key?(:row_limit)
282
+ opts[:date_in_utc] = true unless opts.has_key?(:date_in_utc)
283
+ opts[:folder] = '' unless opts.has_key?(:folder)
284
+
285
+ soapmsg = build_soap_envelope do |type, builder|
286
+ if(type == :header)
287
+ else
288
+ builder.GetListItemChangesSinceToken {
289
+ builder.parent.default_namespace = @default_ns
290
+ builder.listName(list)
291
+ builder.viewName(opts[:view_name])
292
+ builder.rowLimit(opts[:row_limit])
293
+
294
+ if block_given?
295
+ builder.query {
296
+ builder.parent.default_namespace = ''
297
+ yield builder
298
+ }
299
+ end
300
+
301
+ builder.queryOptions {
302
+ builder.QueryOptions {
303
+ builder.parent.default_namespace = ''
304
+ builder.Folder(opts[:folder])
305
+ builder.ViewAttributes(:Scope => 'Recursive') if opts[:recursive]
306
+ builder.DateInUtc('True') if opts[:date_in_utc]
307
+ builder.IncludeAttachmentUrls('True')
308
+ }
309
+ }
310
+
311
+ builder.changeToken(token)
312
+ # @todo Is this worth supporting???
313
+ #builder.contains()
314
+ }
315
+ end
316
+ end
317
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
318
+ ns = {'xmlns:z' => "#RowsetSchema",
319
+ 'xmlns:rs' => "urn:schemas-microsoft-com:rowset",
320
+ 'xmlns' => @default_ns,
321
+ }
322
+ items = []
323
+ soaprsp.xpath('//rs:data/z:row', ns).each do |li|
324
+ items << Types::ListItem.new(self, list, li)
325
+ end
326
+ changes = soaprsp.xpath('//xmlns:GetListItemChangesSinceTokenResult/xmlns:listitems/xmlns:Changes',ns).first
327
+ {:last_change_token => changes['LastChangeToken'], :items => items}
328
+ end
329
+
330
+ # Adds, deletes, or updates the specified items in a list
331
+ # @see http://msdn.microsoft.com/en-us/library/lists.lists.updatelistitems(v=office.12).aspx
332
+ # @see BATCH Operation http://msdn.microsoft.com/en-us/library/ms437562(v=office.12).aspx
333
+ # @param [String] list title or the GUID for the list
334
+ # @param [Hash] updates
335
+ # @option updates [String] :view_name ('') GUID for the view without curly braces
336
+ # If nothing is passed it used the default of the View
337
+ # @option updates [String] :on_error ('Continue') What to do if an error ocurrs. It must
338
+ # be either 'Return' or 'Contiue'
339
+ # @option updates [String] :list_version ('') The version of the list we wish to modify
340
+ # @option updates [String] :version ('') The version of Sharepoint we are acting on
341
+ # @option updates [Array<Hash>] :item_updates An array of Hashes that specify what to update.
342
+ # Each hash needs an :id key to specify the item ID and a :command key that specifies
343
+ # "New", "Update" or "Delete". All other keys are field names in either CamelCase or
344
+ # ruby_case with values to set them to.
345
+ # {:item_updates =>
346
+ # [{:id => 95, :command => 'Update', :status => 'Completed'},
347
+ # {:id => 107, :command => 'Update', :title => "Test"}]}
348
+ # @yield [builder] Yields a Builder object that can be used to build a Batch request. See the
349
+ # example on how to use it. For more information on Batch requests:
350
+ # @see http://msdn.microsoft.com/en-us/library/dd585784(v=office.11).aspx
351
+ # @yieldparam [Nokogiro::XML::Builder] builder The builder object used to create the Batch
352
+ # @example The following example shows how to prepare a Batch request with a block. It updates a Task item to Status 'Completed'.
353
+ # item_id = 95
354
+ # listws.update_list_items('Task List') do |b|
355
+ # b.Method(:ID => 1, :Cmd => 'Update') do
356
+ # b.Field(item_id,:Name => 'ID')
357
+ # b.Field("Completed", :Name => 'Status')
358
+ # end
359
+ # end
360
+ # @example How to Add and Delete via passed parameters
361
+ # updates = [
362
+ # {:id => "New", :command => "New", :title => "Test Task"},
363
+ # {:id => 'New',:command => 'New', :title => 'Test Task 2'},
364
+ # {:id => 98,:command => 'Delete'},
365
+ # ]
366
+ # resp = listws.update_list_items('Task List',:item_updates => updates)
367
+ def update_list_items(list, updates = {})
368
+ # Set Default values
369
+ updates[:view_name] = '' unless updates.has_key?(:view_name)
370
+ updates[:on_error] = 'Continue' unless updates.has_key?(:on_error)
371
+ updates[:list_version] = '' unless updates.has_key?(:list_version)
372
+ updates[:version] = '' unless updates.has_key?(:version)
373
+
374
+ soapmsg = build_soap_envelope do |type, builder|
375
+ if(type == :header)
376
+ else
377
+ builder.UpdateListItems {
378
+ builder.parent.default_namespace = @default_ns
379
+ builder.listName(list)
380
+
381
+ builder.updates {
382
+ builder.Batch(:ViewName => updates[:view_name],
383
+ :OnError => updates[:on_error],
384
+ :ListVersion => updates[:list_version],
385
+ :Version => updates[:version]) {
386
+ builder.parent.default_namespace = ''
387
+
388
+ # First format passed item_updates
389
+ updates[:item_updates] && updates[:item_updates].each_with_index do |iu,idx|
390
+ iu = iu.clone
391
+ builder.Method(:ID => "VP_IDX#{idx}", :Cmd => iu.delete(:command)) do
392
+ builder.Field(iu.delete(:id), :Name => 'ID')
393
+ iu.each_pair do |k,v|
394
+ builder.Field(v, :Name => k.to_s.camel_case)
395
+ end
396
+ end
397
+ end
398
+
399
+ # Now include block-passed updates
400
+ if block_given?
401
+ yield builder
402
+ end
403
+ }
404
+ }
405
+ }
406
+ end
407
+ end
408
+
409
+ soaprsp = Nokogiri::XML(send_soap_request(soapmsg.doc.to_xml))
410
+ parse_update_list_items(list, soaprsp)
411
+ end
412
+
413
+
414
+ # ------------------------- Helper Methods ------------------------- #
415
+
416
+ # Retrieve a file from Sharepoint. This is not a standard Web Service method, buth
417
+ # rather a convenience method that is part of ViewpointSPWS.
418
+ # @param [String] file_ref The fileref property from a ListItem object
419
+ # @return [String] A String representing the bytestream of a file. You should be able
420
+ # to write it out to a file with something like this:
421
+ # @example
422
+ # resp = listws.get_file(listitem.file_ref)
423
+ # File.open(listitem.file_name,'w+') do |f|
424
+ # f.write(resp)
425
+ # end
426
+ def get_file(file_ref)
427
+ p1 = Pathname.new @spcon.site_base.request_uri
428
+ p2 = Pathname.new "/#{file_ref}"
429
+ target = p2.relative_path_from p1
430
+ @spcon.get(target.to_s)
431
+ end
432
+
433
+
434
+ private
435
+
436
+ # Parse the SOAP Response and return an appropriate List type
437
+ def new_list(xmllist)
438
+ case xmllist['ServerTemplate']
439
+ when "101"
440
+ Types::DocumentLibrary.new(self, xmllist)
441
+ when "107"
442
+ Types::TasksList.new(self, xmllist)
443
+ else
444
+ Types::List.new(self, xmllist)
445
+ end
446
+ end
447
+
448
+ def parse_update_list_items(list, soaprsp)
449
+ updates = {}
450
+ ns = {'xmlns' => @default_ns, 'xmlns:z' => '#RowsetSchema'}
451
+ soaprsp.xpath('//xmlns:Results/xmlns:Result',ns).each do |rs|
452
+ estat = check_update_error(rs.xpath('./xmlns:ErrorCode',ns).first.text)
453
+ id, status = rs['ID'].split(/,/)
454
+ case status
455
+ when 'New'
456
+ updates[:new] = [] unless updates.has_key?(:new)
457
+ li = rs.xpath('./z:row',ns).first
458
+ updates[:new] << Types::ListItem.new(self, list, li)
459
+ when 'Update'
460
+ updates[:update] = [] unless updates.has_key?(:update)
461
+ lu = rs.xpath('./z:row',ns).first
462
+ attrs = {}
463
+ lu.attributes.each_pair {|k,v| attrs[k] = v.value}
464
+ updates[:update] << attrs
465
+ when 'Delete'
466
+ updates[:delete] = {} unless updates.has_key?(:delete)
467
+ updates[:delete][id] = estat
468
+ else
469
+ raise "Unknown update return value, #{rs['ID']}"
470
+ end
471
+ end
472
+ updates
473
+ end
474
+
475
+ def check_update_error(code)
476
+ case code
477
+ when '0x00000000'
478
+ true
479
+ else
480
+ raise "Error code returned in update_list_items: #{code}"
481
+ end
482
+ end
483
+
484
+ end