viewpoint-spws 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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