sharepoint-ruby 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ae2fa825b6ae80d590550ff62b460e1dadcf48eb
4
+ data.tar.gz: 929e567c7977f3da76dfb0111578b422f195f5a1
5
+ SHA512:
6
+ metadata.gz: 738befa9bedaf7032b6d3e7f00bc01fcd3145d16d4e44c35f18f38b86ab15267405b14ac1bd7f19573a481a66ec96ca21b1b62266c81ea47355422af6a5da343
7
+ data.tar.gz: f869e68e7d377126d05651a06022d23c02660cfd132d192306472f65e6fa21c63050e1993250018daa8a75f5a180e5c302820778b51cc8c61ff5ca9b94ced807
@@ -0,0 +1,126 @@
1
+ module Sharepoint
2
+ class Field < Sharepoint::Object
3
+ include Sharepoint::Type
4
+ sharepoint_resource
5
+ belongs_to :list
6
+
7
+ field 'DefaultValue'
8
+ field 'Description'
9
+ field 'Direction'
10
+ field 'EnforceUniqueValues'
11
+ field 'FieldTypeKind'
12
+ field 'Group'
13
+ field 'Hidden'
14
+ field 'Indexed'
15
+ field 'JSLink'
16
+ field 'ReadOnlyField'
17
+ field 'Required'
18
+ field 'SchemaXml'
19
+ field 'Scope'
20
+ field 'Sealed'
21
+ field 'Sortable'
22
+ field 'StaticName'
23
+ field 'Title'
24
+ field 'TypeAsString'
25
+ #field 'ValidationFormula'
26
+ #field 'ValidationMessage'
27
+ end
28
+
29
+ module Taxonomy
30
+ class TaxonomyField < Field
31
+ end
32
+ end
33
+
34
+ class FieldCalculated < Field
35
+ field 'DateFormat'
36
+ field 'Formula'
37
+ field 'OutputType'
38
+ end
39
+
40
+ class FieldComputed < Field
41
+ field 'EnableLookup'
42
+ end
43
+
44
+ class FieldDateTime < Field
45
+ field 'DateTimeCalendarType'
46
+ field 'DisplayFormat'
47
+ field 'FriendlyDisplayFormat'
48
+ end
49
+
50
+ class FieldGeolocation < Field
51
+ end
52
+
53
+ class FieldGuid < Field
54
+ end
55
+
56
+ class FieldLookup < Field
57
+ field 'AllowMultipleValues'
58
+ field 'IsRelationship'
59
+ field 'LookUpField'
60
+ field 'LookUpList'
61
+ field 'LookUpWebId'
62
+ field 'PrimaryFieldId'
63
+ field 'RelationshipDeleteBehavior'
64
+
65
+ def create_uri
66
+ unless parent.nil?
67
+ parent.__metadata['uri'] + '/fields/addfield'
68
+ else
69
+ 'fields/addfield'
70
+ end
71
+ end
72
+
73
+ def create
74
+ @site.query :post, create_uri, { parameters: @data }.to_json
75
+ end
76
+ end
77
+
78
+ class FieldUser < FieldLookup
79
+ field 'AllowDisplay'
80
+ field 'Presence'
81
+ field 'SelectionGroup'
82
+ field 'SelectionMode'
83
+ end
84
+
85
+ class FieldMultiChoice < Field
86
+ field 'Choices'
87
+ field 'FillInChoice'
88
+ end
89
+
90
+ class FieldChoice < Field
91
+ field 'EditFormat'
92
+ end
93
+
94
+ class FieldRatingScale < Field
95
+ field 'GridEndNumber'
96
+ field 'GridNAOptionText'
97
+ field 'GridStartNumber'
98
+ field 'GridTextRangeAverage'
99
+ field 'GridTextRangeHigh'
100
+ field 'GridTextRangeLow'
101
+ end
102
+
103
+ class FieldMultiLineText < Field
104
+ field 'AllowHyperlink'
105
+ field 'AppendOnly'
106
+ field 'NumberOfLines'
107
+ field 'RestrictedMode'
108
+ field 'RichText'
109
+ end
110
+
111
+ class FieldNumber < Field
112
+ field 'MaximumValue'
113
+ field 'MinimumValue'
114
+ end
115
+
116
+ class FieldCurrency < FieldNumber
117
+ field 'CurrencyLocaleId'
118
+ end
119
+
120
+ class FieldText < Field
121
+ field 'MaxLength'
122
+ end
123
+
124
+ class FieldUrl < Field
125
+ end
126
+ end
@@ -0,0 +1,69 @@
1
+ require 'open-uri'
2
+
3
+ module Sharepoint
4
+ class Folder < Sharepoint::Object
5
+ include Sharepoint::Type
6
+ sharepoint_resource get_from_name: 'GetFolderByServerRelativeUrl'
7
+
8
+ field 'WelcomePage'
9
+ field 'UniqueContentTypeOrder'
10
+
11
+ method :recycle
12
+
13
+ def file_from_name name
14
+ @site.query :get, "#{__metadata['uri']}/files/getbyurl('#{URI::encode(name.to_s)}')"
15
+ end
16
+
17
+ def add_file name, content
18
+ uri = "#{__metadata['uri']}/files/add(overwrite=true,url='#{URI::encode(name.to_s)}')"
19
+ @site.query :post, uri, content
20
+ end
21
+
22
+ def add_folder name
23
+ uri = "#{__metadata['uri']}/folders"
24
+ body = { '__metadata' => { 'type' => 'SP.Folder' }, 'ServerRelativeUrl' => name.to_s }
25
+ @site.query :post, uri, body.to_json
26
+ end
27
+ end
28
+
29
+ class File < Sharepoint::Object
30
+ include Sharepoint::Type
31
+ belongs_to :folder
32
+ sharepoint_resource getter: 'GetFileByServerRelativeUrl', no_root_collection: true
33
+
34
+ method :approve, default_params: ({ comment: '' })
35
+ method :deny, default_params: ({ comment: '' })
36
+ method :checkin, default_params: ({ comment: '', checkintype: 0 })
37
+ method :checkout
38
+ method :undo_checkout
39
+ method :copy_to, default_params: ({ overwrite: true })
40
+ method :move_to, default_params: ({ flags: 9 })
41
+ method :get_limited_webpart_manager, default_params: ({ scope: 0 }), http_method: :get
42
+ method :download, endpoint: '$value', http_method: :get, skip_json: true
43
+ method :upload, endpoint: '$value', http_method: :put
44
+ method :publish, default_params: ({ comment: '' })
45
+ method :unpublish, default_params: ({ comment: '' })
46
+ method :recycle
47
+
48
+ def upload_from_file filename
49
+ content = String.new
50
+ ::File.open filename, 'rb' do |file|
51
+ line = nil
52
+ content += line while line = file.gets
53
+ end
54
+ upload content
55
+ end
56
+
57
+ def download_to_file filename
58
+ content = download
59
+ ::File.open filename, "w:#{content.encoding.name}" do |file|
60
+ file.write content
61
+ end
62
+ end
63
+ end
64
+
65
+ class FileVersion < Sharepoint::Object
66
+ include Sharepoint::Type
67
+ belongs_to :file
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ require 'curb'
2
+ require 'cgi'
3
+
4
+ module Sharepoint
5
+ module HttpAuth
6
+ class Session
7
+ attr_accessor :site
8
+ attr_reader :user, :password
9
+
10
+ def initialize site
11
+ @site = site
12
+ end
13
+
14
+ def authenticate user, password
15
+ @user = user
16
+ @password = password
17
+ end
18
+
19
+ def cookie
20
+ String.new
21
+ end
22
+
23
+ def curl curb
24
+ curb.http_auth_types = :ntlm
25
+ curb.username = @user
26
+ curb.password = @password
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,235 @@
1
+ module Sharepoint
2
+ LIST_TEMPLATE_TYPE = {
3
+ InvalidType: -1,
4
+ NoListTemplate: 0,
5
+ GenericList: 100,
6
+ DocumentLibrary: 101,
7
+ Survey: 102,
8
+ Links: 103,
9
+ Announcements: 104,
10
+ Contacts: 105,
11
+ Events: 106,
12
+ Tasks: 107,
13
+ DiscussionBoard: 108,
14
+ PictureLibrary: 109,
15
+ DataSources: 110,
16
+ UserInformation: 112,
17
+ WebPartCatalog: 113,
18
+ ListTemplateCatalog: 114,
19
+ XMLForm: 115,
20
+ MasterPageCatalog: 116,
21
+ NoCodeWorkflows: 117,
22
+ WorkflowProcess: 118,
23
+ WebPageLibrary: 119,
24
+ CustomGrid: 120,
25
+ SolutionCatalog: 121,
26
+ NoCodePublic: 122,
27
+ ThemeCatalog: 123,
28
+ DesignCatalog: 124,
29
+ AppDataCatalog: 125,
30
+ DataConnectionLibrary: 130,
31
+ WorkflowHistory: 140,
32
+ GanttTask: 150,
33
+ HelpLibrary: 151,
34
+ AccessRequest: 160,
35
+ TasksWithTimelineAndHierarchy: 171,
36
+ MaintenanceLogs: 175,
37
+ Meetings: 200,
38
+ Agenda: 201,
39
+ MeetingUser: 202,
40
+ Decision: 204,
41
+ MeetingObjective: 207,
42
+ TextBox: 210,
43
+ ThingsToBring: 211,
44
+ HomePageLibrary: 212,
45
+ Posts: 301,
46
+ Comments: 302,
47
+ Categories: 303,
48
+ Facility: 402,
49
+ Whereabouts: 403,
50
+ CallTrack: 404,
51
+ Circulation: 405,
52
+ Timecard: 420,
53
+ Hollidays: 421,
54
+ IMEDic: 499,
55
+ ExternalList: 600,
56
+ MySiteDocumentLibrary: 700,
57
+ IssueTracking: 1100,
58
+ AdminTasks: 1200,
59
+ HealthRules: 1220,
60
+ HealthReports: 1221,
61
+ DeveloperSiteDraftApps: 1230,
62
+ MicroFeed: 544 # Is undefined, but used by Sharepoint Online
63
+ }
64
+
65
+ module VtiBin
66
+ def self.translate_field_names input
67
+ return input unless defined? VtiDisplayNameDictionary
68
+ hash = Hash.new
69
+ input.keys.each do |key|
70
+ if VtiDisplayNameDictionary.keys.include? key
71
+ hash[VtiDisplayNameDictionary[key]] = input[key]
72
+ else
73
+ hash[key] = input[key]
74
+ end
75
+ end
76
+ hash
77
+ end
78
+ end
79
+
80
+ class List < Sharepoint::Object
81
+ include Sharepoint::Type
82
+ sharepoint_resource get_from_name: 'lists/getbytitle'
83
+
84
+ def find_items options = {}
85
+ @site.query :get, (make_item_filter options)
86
+ end
87
+
88
+ def item_count
89
+ @site.query :get, "#{__metadata['id']}/ItemCount"
90
+ end
91
+
92
+ def add_item attributes
93
+ attributes['__metadata'] ||= Hash.new
94
+ attributes['__metadata']['type'] ||= list_item_entity_type_full_name
95
+ @site.query :post, item_uri, attributes.to_json
96
+ end
97
+
98
+ def add_folder path, attributes
99
+ path = path.gsub(/\/*$/, '') # remove slashes at the end of the path
100
+ site_url = "#{@site.protocole}://#{@site.server_url}/"
101
+ action = "#{site_url}_vti_bin/listdata.svc/#{self.title}"
102
+ path = root_folder.server_relative_url + '/' + path
103
+ attributes['ContentTypeID'] ||= '0x01200059042D1A09191046851FA83D5B89816A'
104
+ attributes['Path'] ||= path
105
+ payload = VtiBin.translate_field_names(attributes).to_json
106
+ # Create the item using _vti_bin api
107
+ response = @site.query :post, action, payload, true do |curl|
108
+ curl.headers['Slug'] = "#{path}/#{attributes['Title']}|0x0120"
109
+ end
110
+ response = JSON.parse response
111
+ unless response['d'].nil?
112
+ # Fetch the item we just created using the REST api
113
+ item_id = response['d']['ID']
114
+ @site.query :get, "#{site_url}_api/#{__metadata['id']}/items(#{item_id})"
115
+ else
116
+ raise Sharepoint::SPException.new response, action, payload
117
+ end
118
+ end
119
+
120
+ field 'BaseTemplate', access: [ :read, :initialize ], default: LIST_TEMPLATE_TYPE[:GenericList]
121
+ field 'ContentTypesEnabled', default: true
122
+ field 'DefaultContentApprovalWorkflowId'
123
+ field 'DefaultDisplayFormUrl'
124
+ field 'DefaultEditFormUrl'
125
+ field 'DefaultNewFormUrl'
126
+ field 'Description'
127
+ field 'Direction'
128
+ field 'DocumentTemplateUrl'
129
+ field 'DraftVersionVisibility', default: 1
130
+ field 'EnableAttachments', default: false
131
+ field 'EnableFolderCreation', default: true
132
+ field 'EnableMinorVersions', default: true
133
+ field 'EnableModeration', default: true
134
+ field 'EnableVersioning', default: true
135
+ field 'ForceCheckout', default: false
136
+ field 'Hidden', default: false
137
+ field 'IrmEnabled', default: false
138
+ field 'IrmExpire', default: false
139
+ field 'IrmReject', default: false
140
+ field 'IsApplicationList', default: false
141
+ field 'MultipleDataList', default: false
142
+ field 'NoCrawl', default: false
143
+ field 'OnQuickLaunch', default: false
144
+ field 'Title'
145
+ field 'ValidationFormula'
146
+ field 'ValidationMessage'
147
+
148
+ private
149
+ def item_uri
150
+ url = @data['Items']['__deferred']
151
+ url = url['uri'] if url.class != String
152
+ url
153
+ end
154
+
155
+ def make_item_filter options = {}
156
+ url = item_uri
157
+ has_options = false
158
+ options.each do |key,value|
159
+ url += if has_options then '&' else '?' end
160
+ url += "$#{key}=#{URI::encode value.to_s}"
161
+ has_options = true
162
+ end
163
+ url
164
+ end
165
+ end
166
+
167
+ class ListItem < Sharepoint::Object
168
+ include Sharepoint::Type
169
+ belongs_to :list
170
+
171
+ method :break_role_inheritance, default_params: ({ copyroleassignements: true })
172
+ method :recycle
173
+ method :reset_role_inheritance
174
+ method :validate_update_item_list
175
+ end
176
+
177
+ class View < Sharepoint::Object
178
+ include Sharepoint::Type
179
+ belongs_to :list
180
+
181
+ field 'Aggregations'
182
+ field 'AggregationsStatus'
183
+ field 'ContentTypeId'
184
+ field 'DefaultView'
185
+ field 'DefaultViewForContentType'
186
+ field 'EditorModified'
187
+ field 'Formats'
188
+ field 'Hidden'
189
+ field 'IncludeRootFolder'
190
+ #field 'JsLink'
191
+ field 'ListViewXml'
192
+ field 'Method'
193
+ field 'MobileDefaultView'
194
+ field 'MobileView'
195
+ field 'Paged'
196
+ field 'RowLimit'
197
+ field 'Scope'
198
+ field 'Title'
199
+ field 'Toolbar'
200
+ field 'ViewData'
201
+ field 'ViewJoins'
202
+ field 'ViewProjectedFields'
203
+ field 'ViewQuery'
204
+
205
+ method :break_role_inheritance, default_params: ({ copyroleassignements: true })
206
+ method :recycle
207
+ method :render_list_form_data
208
+ method :reserve_list_item_id
209
+ end
210
+
211
+ class ViewFieldCollection < Sharepoint::Object
212
+ include Sharepoint::Type
213
+ sharepoint_resource
214
+ belongs_to :view
215
+
216
+ def add_view_field name
217
+ @site.query :post, "#{__metadata['uri']}/addviewfield('#{URI.encode name}')"
218
+ end
219
+
220
+ def move_view_field_to name, index
221
+ @site.query :post, "#{__metadata['uri']}/moveviewfieldto", {
222
+ field: name,
223
+ index: index
224
+ }.to_json
225
+ end
226
+
227
+ def remove_all_view_fields
228
+ @site.query :post, "#{__metadata['uri']}/removeallviewfields"
229
+ end
230
+
231
+ def remove_view_field name
232
+ @site.query :post, "#{__metadata['uri']}/removeviewfield('#{URI.encode name}')"
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,179 @@
1
+ require 'sharepoint-properties'
2
+ require 'sharepoint-stringutils'
3
+
4
+ module Sharepoint
5
+ class Site
6
+ end
7
+
8
+ class Object < Sharepoint::ObjectProperties
9
+ class << self
10
+ attr_accessor :fields
11
+
12
+ def fields
13
+ @fields ||= []
14
+ if self.superclass != Sharepoint::Object
15
+ self.superclass.fields | @fields
16
+ else
17
+ @fields
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def field name, options = {}
24
+ options[:access] ||= [ :read, :write ]
25
+ @fields ||= []
26
+ @fields << { name: name, access: options[:access], default: options[:default] }
27
+ end
28
+
29
+ def method name, method_params = {}
30
+ method_params[:http_method] ||= :post
31
+ method_params[:endpoint] ||= name.to_s.camelize
32
+ method_params[:default_params] ||= Hash.new
33
+ define_method name, ->(params = Hash.new) do
34
+ action = "#{__metadata['uri']}/#{method_params[:endpoint]}"
35
+ body = nil
36
+ # Set default parameters
37
+ (method_params[:default_params].each do |key,value|
38
+ params[key] == value if params[key].nil?
39
+ end)
40
+ if (method_params[:http_method] == :get) and (params.class < Hash) and (params.keys.count > 0)
41
+ # if method is get, Fill action with parameters
42
+ action += '('
43
+ (params.each do |key,value|
44
+ action += ',' unless params.keys.first == key
45
+ action += key + '='
46
+ action += (if (value.class < String) or (value.class < Symbol)
47
+ "'#{(URI.encode value.gsub("'", %q(\\\')))}'"
48
+ else
49
+ value
50
+ end)
51
+ end)
52
+ action += ')'
53
+ else
54
+ # if method is post, send parameters in the body
55
+ body = (params.class < Hash ? params.to_json : params)
56
+ end
57
+ # Call action
58
+ @site.query method_params[:http_method], action, body, method_params[:skip_json]
59
+ end
60
+ end
61
+
62
+ def sharepoint_resource options = {}
63
+ options[:method_name] ||= (self.name).split('::').last.downcase + 's'
64
+ options[:getter] ||= options[:method_name]
65
+ options[:get_from_name] ||= options[:getter]
66
+ Sharepoint::Site.send :define_method, options[:method_name] do
67
+ self.query :get, options[:getter].to_s
68
+ end unless options[:no_root_collection] == true
69
+ Sharepoint::Site.send :define_method, (self.name).split('::').last.downcase do |id|
70
+ if id =~ /^[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}$/
71
+ self.query :get, "#{options[:getter]}(guid'#{id}')"
72
+ else
73
+ self.query :get, "#{options[:get_from_name]}('#{URI.encode id}')"
74
+ end
75
+ end
76
+ end
77
+
78
+ def belongs_to resource_name
79
+ resource_name = resource_name.to_s
80
+ class_name = (self.name).split('::').last.downcase
81
+ method_name = class_name.pluralize
82
+ define_singleton_method "all_from_#{resource_name}" do |resource|
83
+ resource.site.query :get, "#{resource.__metadata['uri']}/#{method_name}"
84
+ end
85
+ define_singleton_method "get_from_#{resource_name}" do |resource, name|
86
+ resource.site.query :get, "#{resource.__metadata['uri']}/#{method_name}('#{URI.encode name}')"
87
+ end
88
+ define_method "create_uri" do
89
+ unless self.parent.nil?
90
+ "#{self.parent.__metadata['uri']}/#{method_name}"
91
+ else
92
+ method_name
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ attr_accessor :parent
99
+
100
+ def initialize site, data
101
+ @parent = nil
102
+ super site, data
103
+ end
104
+
105
+ def guid
106
+ return @guid unless @guid.nil?
107
+ __metadata['id'].scan(/guid'([^']+)'/) do ||
108
+ @guid = $1
109
+ break
110
+ end
111
+ @guid
112
+ end
113
+
114
+ def reload
115
+ @site.query :get, __metadata['uri']
116
+ end
117
+
118
+ def save
119
+ if @data['__metadata'].nil? or @data['__metadata']['id'].nil?
120
+ create
121
+ elsif @updated_data.keys.count > 0
122
+ update
123
+ end
124
+ end
125
+
126
+ def destroy
127
+ @site.query :post, resource_uri do |curl|
128
+ curl.headers['X-HTTP-Method'] = 'DELETE'
129
+ curl.headers['If-Match'] = __metadata['etag']
130
+ end
131
+ end
132
+
133
+ def copy new_object = nil
134
+ updating = !new_object.nil?
135
+ new_object ||= self.class.new @site
136
+ self.class.fields.each do |field|
137
+ next unless @data.keys.include? field[:name].to_s
138
+ next if (field[:access] & [ :write, :initialize ]).count == 0
139
+ value = @data[field[:name].to_s]
140
+ if updating == false
141
+ new_object.data[field[:name].to_s] = value
142
+ elsif new_object.data[field[:name].to_s] != value
143
+ new_object.updated_data[field[:name].to_s] = value
144
+ end
145
+ end
146
+ new_object
147
+ end
148
+
149
+ private
150
+ def sharepoint_typename
151
+ if self.is_a?(Sharepoint::GenericSharepointObject)
152
+ @generic_type_name
153
+ else
154
+ self.class.name.split('::').last
155
+ end
156
+ end
157
+
158
+ def resource_uri
159
+ @data['__metadata']['uri'].gsub(/^https:\/\/[^\/]+\/_api\/web\//i, '')
160
+ end
161
+
162
+ def create_uri
163
+ sharepoint_typename.downcase.pluralize
164
+ end
165
+
166
+ def create
167
+ @site.query :post, create_uri, @data.to_json
168
+ end
169
+
170
+ def update
171
+ @updated_data['__metadata'] ||= @data['__metadata']
172
+ @site.query :post, resource_uri, @updated_data.to_json do |curl|
173
+ curl.headers['X-HTTP-Method'] = 'MERGE'
174
+ curl.headers['If-Match'] = __metadata['etag']
175
+ end
176
+ @updated_data = Hash.new
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,96 @@
1
+ require 'sharepoint-stringutils'
2
+
3
+ module Sharepoint
4
+ class ObjectProperties
5
+ attr_accessor :site, :data, :updated_data
6
+
7
+ def initialize site, data
8
+ @site = site
9
+ @data = data
10
+ @updated_data = Hash.new
11
+ @properties = Hash.new
12
+ @properties_names = Array.new
13
+ @properties_original_names = Array.new
14
+ initialize_properties
15
+ end
16
+
17
+ def add_property property, value = nil
18
+ editable = is_property_editable? property
19
+ property = property.to_s
20
+ @data[property] = nil if @data[property].nil?
21
+ @data[property] = value unless value.nil?
22
+ @updated_data[property] = value if (@initialize_properties == false) and (editable == true)
23
+ unless @properties_original_names.include? property
24
+ @properties_names << property.underscore.to_sym
25
+ @properties_original_names << property
26
+ define_singleton_method property.underscore do
27
+ get_property property
28
+ end
29
+ define_singleton_method property.underscore + '=' do |new_value|
30
+ @data[property] = new_value
31
+ @updated_data[property] = new_value
32
+ end if editable == true
33
+ end
34
+ end
35
+
36
+ def add_properties properties
37
+ properties.each do |property|
38
+ add_property property
39
+ end
40
+ end
41
+
42
+ def available_properties
43
+ @properties_names
44
+ end
45
+
46
+ private
47
+
48
+ def initialize_properties
49
+ @initialize_properties = true
50
+ # Create the properties defined for the Sharepoint type used
51
+ self.class.fields.each do |field|
52
+ add_property field[:name], field[:default] if field[:access].include? :read
53
+ end
54
+ # Set the values and create any missing properties from the OData object
55
+ @data.dup.each do |key,value|
56
+ add_property key, value
57
+ end
58
+ @initialize_properties = false
59
+ end
60
+
61
+ def get_property property_name
62
+ data = @data[property_name]
63
+ if not @properties[property_name].nil?
64
+ @properties[property_name]
65
+ elsif data.class == Hash
66
+ if not data['__deferred'].nil?
67
+ @properties[property_name] = get_deferred_property property_name
68
+ elsif not data['__metadata'].nil?
69
+ @properties[property_name] = @site.make_object_from_data data
70
+ else
71
+ @properties[property_name] = data
72
+ end
73
+ elsif not data.nil?
74
+ @properties[property_name] = data
75
+ else
76
+ @properties[property_name] = nil
77
+ end
78
+ end
79
+
80
+ def get_deferred_property property_name
81
+ deferred_data = @data[property_name]['__deferred']
82
+ uri = deferred_data['uri'].gsub /^http.*\/_api\/web\//i, ''
83
+ @site.query :get, uri
84
+ end
85
+
86
+ def is_property_editable? property_name
87
+ # We don't know a priori what the fields are for a generic object, so leave the validation work to the user
88
+ return true if self.is_a?(GenericSharepointObject)
89
+
90
+ self.class.fields.each do |field|
91
+ return field[:access].include? :write if field[:name] == property_name
92
+ end
93
+ false
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,131 @@
1
+ require 'curb'
2
+ require 'json'
3
+ require 'sharepoint-session'
4
+ require 'sharepoint-object'
5
+ require 'sharepoint-types'
6
+
7
+ module Sharepoint
8
+ class SPException < Exception
9
+ def initialize data, uri = nil, body = nil
10
+ @data = data['error']
11
+ @uri = uri
12
+ @body = body
13
+ end
14
+
15
+ def lang ; @data['message']['lang'] ; end
16
+ def message ; @data['message']['value'] ; end
17
+ def code ; @data['code'] ; end
18
+ def uri ; @uri ; end
19
+ def request_body ; @body ; end
20
+ end
21
+
22
+ class Site
23
+ attr_reader :server_url
24
+ attr_accessor :url, :protocole
25
+ attr_accessor :session
26
+ attr_accessor :name
27
+ attr_accessor :verbose
28
+
29
+ def initialize server_url, site_name
30
+ @server_url = server_url
31
+ @name = site_name
32
+ @url = "#{@server_url}/#{@name}"
33
+ @session = Session.new self
34
+ @web_context = nil
35
+ @protocole = 'https'
36
+ @verbose = false
37
+ end
38
+
39
+ def authentication_path
40
+ "#{@protocole}://#{@server_url}/_forms/default.aspx?wa=wsignin1.0"
41
+ end
42
+
43
+ def api_path uri
44
+ "#{@protocole}://#{@url}/_api/web/#{uri}"
45
+ end
46
+
47
+ def filter_path uri
48
+ uri
49
+ end
50
+
51
+ def context_info
52
+ query :get, ''
53
+ end
54
+
55
+ # Sharepoint uses 'X-RequestDigest' as a CSRF security-like.
56
+ # The form_digest method acquires a token or uses a previously acquired
57
+ # token if it is still supposed to be valid.
58
+ def form_digest
59
+ if @web_context.nil? or (not @web_context.is_up_to_date?)
60
+ @getting_form_digest = true
61
+ @web_context = query :post, "#{@protocole}://#{@server_url}/_api/contextinfo"
62
+ @getting_form_digest = false
63
+ end
64
+ @web_context.form_digest_value
65
+ end
66
+
67
+ def query method, uri, body = nil, skip_json=false, &block
68
+ uri = if uri =~ /^http/ then uri else api_path(uri) end
69
+ arguments = [ uri ]
70
+ arguments << body if method != :get
71
+ result = Curl::Easy.send "http_#{method}", *arguments do |curl|
72
+ curl.headers["Cookie"] = @session.cookie
73
+ curl.headers["Accept"] = "application/json;odata=verbose"
74
+ if method != :get
75
+ curl.headers["Content-Type"] = curl.headers["Accept"]
76
+ curl.headers["X-RequestDigest"] = form_digest unless @getting_form_digest == true
77
+ end
78
+ curl.verbose = @verbose
79
+ @session.send :curl, curl unless not @session.methods.include? :curl
80
+ block.call curl unless block.nil?
81
+ end
82
+
83
+ unless skip_json || (result.body_str.nil? || result.body_str.empty?)
84
+ begin
85
+ data = JSON.parse result.body_str
86
+ raise Sharepoint::SPException.new data, uri, body unless data['error'].nil?
87
+ make_object_from_response data
88
+ rescue JSON::ParserError => e
89
+ raise Exception.new("Exception with body=#{body}, e=#{e.inspect}, #{e.backtrace.inspect}, response=#{result.body_str}")
90
+ end
91
+ else
92
+ result.body_str
93
+ end
94
+ end
95
+
96
+ def make_object_from_response data
97
+ if data['d']['results'].nil?
98
+ data['d'] = data['d'][data['d'].keys.first] if data['d']['__metadata'].nil?
99
+ if not data['d'].nil?
100
+ make_object_from_data data['d']
101
+ else
102
+ nil
103
+ end
104
+ else
105
+ array = Array.new
106
+ data['d']['results'].each do |result|
107
+ array << (make_object_from_data result)
108
+ end
109
+ array
110
+ end
111
+ end
112
+
113
+ # Uses sharepoint's __metadata field to solve which Ruby class to instantiate,
114
+ # and return the corresponding Sharepoint::Object.
115
+ def make_object_from_data data
116
+ type_name = data['__metadata']['type'].gsub(/^SP\./, '')
117
+ type_parts = type_name.split '.'
118
+ type_name = type_parts.pop
119
+ constant = Sharepoint
120
+ type_parts.each do |part| constant = constant.const_get part end
121
+
122
+ klass = constant.const_get type_name rescue nil
123
+ if klass
124
+ klass.new self, data
125
+ else
126
+ Sharepoint::GenericSharepointObject.new type_name, self, data
127
+ end
128
+ end
129
+ end
130
+ end
131
+
@@ -0,0 +1,92 @@
1
+ require 'erb'
2
+ require 'curb'
3
+
4
+ module Sharepoint
5
+ MICROSOFT_STS_URL = "https://login.microsoftonline.com/extSTS.srf"
6
+
7
+ module Soap
8
+ class Authenticate
9
+ SOURCE = "soap/authenticate.xml.erb"
10
+
11
+ attr_accessor :username, :password, :login_url
12
+
13
+ def self.initialize
14
+ return if @initialized == true
15
+ @erb = ERB.new(::File.read ::File.dirname(__FILE__) + '/' + SOURCE)
16
+ @erb.filename = SOURCE
17
+ @erb.def_method self, 'render()'
18
+ @initialized = true
19
+ end
20
+
21
+ def initialize params = {}
22
+ Authenticate.initialize
23
+ @username = params[:username]
24
+ @password = params[:password]
25
+ @login_url = params[:url]
26
+ end
27
+ end
28
+ end
29
+
30
+ class Session
31
+ class ConnexionToStsFailed < Exception ; end
32
+ class ConnexionToSharepointFailed < Exception; end
33
+ class UnknownAuthenticationError < Exception; end
34
+ class AuthenticationFailed < Exception
35
+ def initialize message ; super message ; end
36
+ end
37
+
38
+ attr_accessor :site
39
+
40
+ def initialize site
41
+ @site = site
42
+ end
43
+
44
+ def authenticate user, password, sts_url = nil
45
+ sts_url ||= MICROSOFT_STS_URL
46
+ authenticate_to_sts user, password, sts_url
47
+ get_access_token
48
+ end
49
+
50
+ def cookie
51
+ "FedAuth=#{@fed_auth};rtFa=#{@rtFa}"
52
+ end
53
+
54
+ private
55
+ def authenticate_to_sts user, password, sts_url
56
+ query = Soap::Authenticate.new username: user, password: password, url: @site.authentication_path
57
+ response = Curl::Easy.http_post sts_url, query.render rescue raise ConnexionToStsFailed.new
58
+
59
+ response.body_str.scan(/<wsse:BinarySecurityToken[^>]*>([^<]+)</) do
60
+ offset = ($~.offset 1)
61
+ @security_token = response.body[offset[0]..offset[1] - 1]
62
+ end
63
+ authentication_failed response.body_str if @security_token.nil?
64
+ end
65
+
66
+ def get_cookie_from_header header, cookie_name
67
+ result = nil
68
+ header.scan(/#{cookie_name}=([^;]+);/) do
69
+ offset = $~.offset 1
70
+ result = header[offset[0]..offset[1] - 1]
71
+ end
72
+ result
73
+ end
74
+
75
+ def get_access_token
76
+ http = Curl::Easy.http_post @site.authentication_path, @security_token
77
+ @rtFa = get_cookie_from_header http.header_str, 'rtFa'
78
+ @fed_auth = get_cookie_from_header http.header_str, 'FedAuth'
79
+ raise UnknownAuthenticationError.new if @fed_auth.nil? or @rtFa.nil?
80
+ end
81
+
82
+ def authentication_failed xml
83
+ message = 'Unknown authentication error'
84
+ xml.scan(/<psf:text[^>]*>([^<]+)</) do
85
+ offset = ($~.offset 1)
86
+ message = xml[offset[0]..offset[1] - 1]
87
+ end
88
+ raise AuthenticationFailed.new message
89
+ end
90
+ end
91
+ end
92
+
@@ -0,0 +1,37 @@
1
+ # Defines underscore and pluralize methods
2
+ # unless they've already been defined by another script.
3
+
4
+ unless String.new.methods.include? :underscore
5
+ class String
6
+ def underscore
7
+ self.gsub(/::/, '/').
8
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
9
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
10
+ tr("-", "_").
11
+ downcase
12
+ end
13
+ end
14
+ end
15
+
16
+ unless String.new.methods.include? :camelize
17
+ class String
18
+ def camelize
19
+ self.split("_").each {|s| s.capitalize! }.join("")
20
+ end
21
+ end
22
+ end
23
+
24
+ unless String.new.methods.include? :pluralize
25
+ class String
26
+ def pluralize
27
+ if self.match /y$/
28
+ self.gsub /y$/, 'ies'
29
+ elsif self.match /us$/
30
+ self.gsub /us$/, 'i'
31
+ else
32
+ self + 's'
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,83 @@
1
+ require 'sharepoint-object'
2
+
3
+ module Sharepoint
4
+ class UnsupportedType < ::Exception
5
+ attr_accessor :type_name
6
+
7
+ def initialize type_name
8
+ @type_name = type_name
9
+ end
10
+
11
+ def message
12
+ "unsupported type '#{@type_name}'"
13
+ end
14
+ end
15
+
16
+ module Type
17
+ def initialize site, data = nil
18
+ data ||= Hash.new
19
+ data['__metadata'] ||= {
20
+ 'type' => "SP.#{self.class.name.split('::').last}"
21
+ }
22
+ super site, data
23
+ end
24
+ end
25
+ end
26
+
27
+ require 'sharepoint-users'
28
+ require 'sharepoint-lists'
29
+ require 'sharepoint-files'
30
+ require 'sharepoint-fields'
31
+ require 'date'
32
+
33
+ module Sharepoint
34
+ ##
35
+ ## Sharepoint Management
36
+ ##
37
+ class Web < Sharepoint::Object
38
+ include Sharepoint::Type
39
+
40
+ field 'CustomMasterUrl'
41
+ field 'Description'
42
+ field 'EnableMinimalDownload'
43
+ field 'MasterUrl'
44
+ field 'QuickLaunchEnabled'
45
+ field 'SaveSiteAsTemplateEnabled'
46
+ field 'SyndicationEnabled'
47
+ field 'Title'
48
+ field 'TreeViewEnabled'
49
+ field 'UiVersion'
50
+ field 'UiVersionConfigurationEnabled'
51
+
52
+ method :apply_theme
53
+ method :break_role_inheritance, default_params: ({ clearsubscopes: true })
54
+ end
55
+
56
+ class ContextWebInformation < Sharepoint::Object
57
+ include Sharepoint::Type
58
+
59
+ def issued_time
60
+ strtime = (form_digest_value.split ',').last
61
+ DateTime.strptime strtime, '%d %b %Y %H:%M:%S %z'
62
+ end
63
+
64
+ def timeout_time
65
+ issued_time + Rational(form_digest_timeout_seconds, 86400)
66
+ end
67
+
68
+ def is_up_to_date?
69
+ DateTime.now < timeout_time
70
+ end
71
+ end
72
+
73
+ class FeatureCollection < Sharepoint::Object
74
+ include Sharepoint::Type
75
+ end
76
+
77
+ ##
78
+ ## Other types
79
+ ##
80
+ class PropertyValues < Sharepoint::Object
81
+ include Sharepoint::Type
82
+ end
83
+ end
@@ -0,0 +1,36 @@
1
+ module Sharepoint
2
+ class Group < Sharepoint::Object
3
+ include Sharepoint::Type
4
+ sharepoint_resource getter: :sitegroups
5
+ end
6
+
7
+ class User < Sharepoint::Object
8
+ include Sharepoint::Type
9
+ sharepoint_resource getter: :siteusers
10
+ belongs_to :group
11
+ end
12
+
13
+ class UserCustomAction < Sharepoint::Object
14
+ include Sharepoint::Type
15
+ sharepoint_resource
16
+ end
17
+
18
+ class RoleAssignment < Sharepoint::Object
19
+ include Sharepoint::Type
20
+ sharepoint_resource
21
+ end
22
+
23
+ class RoleDefinition < Sharepoint::Object
24
+ include Sharepoint::Type
25
+ end
26
+
27
+ class GenericSharepointObject < Sharepoint::Object
28
+ include Sharepoint::Type
29
+ sharepoint_resource
30
+
31
+ def initialize type_name, site, data
32
+ super site, data
33
+ @generic_type_name = type_name
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
2
+ <s:Header>
3
+ <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
4
+ <a:ReplyTo>
5
+ <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
6
+ </a:ReplyTo>
7
+ <a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
8
+ <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
9
+ <o:UsernameToken>
10
+ <o:Username><%= @username %></o:Username>
11
+ <o:Password><%= @password %></o:Password>
12
+ </o:UsernameToken>
13
+ </o:Security>
14
+ </s:Header>
15
+ <s:Body>
16
+ <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
17
+ <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
18
+ <a:EndpointReference>'
19
+ <a:Address><%= @login_url %></a:Address>'
20
+ </a:EndpointReference>'
21
+ </wsp:AppliesTo>'
22
+ <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
23
+ <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
24
+ <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>'
25
+ </t:RequestSecurityToken>'
26
+ </s:Body>'
27
+ </s:Envelope>
28
+
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sharepoint-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Michael Martin Moro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: curb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.8.6
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.8'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.6
33
+ description: Client for Sharepoint's REST API
34
+ email: michael@unetresgrossebite.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - lib/sharepoint-fields.rb
40
+ - lib/sharepoint-files.rb
41
+ - lib/sharepoint-http-auth.rb
42
+ - lib/sharepoint-lists.rb
43
+ - lib/sharepoint-object.rb
44
+ - lib/sharepoint-properties.rb
45
+ - lib/sharepoint-ruby.rb
46
+ - lib/sharepoint-session.rb
47
+ - lib/sharepoint-stringutils.rb
48
+ - lib/sharepoint-types.rb
49
+ - lib/sharepoint-users.rb
50
+ - lib/soap/authenticate.xml.erb
51
+ homepage: https://github.com/Plaristote/sharepoint-ruby
52
+ licenses:
53
+ - BSD
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.5.1
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: sharepoint client
75
+ test_files: []