pyu-activesp 0.0.4.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +25 -0
- data/README.rdoc +105 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/activesp/associations.rb +76 -0
- data/lib/activesp/base.rb +139 -0
- data/lib/activesp/caching.rb +60 -0
- data/lib/activesp/connection.rb +126 -0
- data/lib/activesp/content_type.rb +152 -0
- data/lib/activesp/field.rb +193 -0
- data/lib/activesp/file.rb +71 -0
- data/lib/activesp/folder.rb +103 -0
- data/lib/activesp/ghost_field.rb +100 -0
- data/lib/activesp/group.rb +107 -0
- data/lib/activesp/item.rb +338 -0
- data/lib/activesp/list.rb +509 -0
- data/lib/activesp/permission_set.rb +64 -0
- data/lib/activesp/persistent_caching.rb +112 -0
- data/lib/activesp/role.rb +116 -0
- data/lib/activesp/root.rb +128 -0
- data/lib/activesp/site.rb +305 -0
- data/lib/activesp/url.rb +146 -0
- data/lib/activesp/user.rb +97 -0
- data/lib/activesp/util.rb +326 -0
- data/lib/activesp.rb +53 -0
- metadata +118 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
# Copyright (c) 2010 XAOP bvba
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation
|
5
|
+
# files (the "Software"), to deal in the Software without
|
6
|
+
# restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following
|
10
|
+
# conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
#
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
#
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module ActiveSP
|
27
|
+
|
28
|
+
class Role < Base
|
29
|
+
|
30
|
+
extend Caching
|
31
|
+
extend PersistentCaching
|
32
|
+
include Util
|
33
|
+
include InSite
|
34
|
+
|
35
|
+
persistent { |site, name, *a| [site.connection, [:role, name]] }
|
36
|
+
# @private
|
37
|
+
def initialize(site, name)
|
38
|
+
@site, @name = site, name
|
39
|
+
end
|
40
|
+
|
41
|
+
# See {Base#key}
|
42
|
+
# @return [String]
|
43
|
+
def key
|
44
|
+
encode_key("R", [@name])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the list of users in this role
|
48
|
+
# @return [User]
|
49
|
+
def users
|
50
|
+
call("UserGroup", "get_user_collection_from_role", "roleName" => @name).xpath("//spdir:User", NS).map do |row|
|
51
|
+
attributes = clean_attributes(row.attributes)
|
52
|
+
User.new(@site, attributes["LoginName"])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
cache :users, :dup => :always
|
56
|
+
|
57
|
+
# Returns the list of groups in this role
|
58
|
+
# @return [Group]
|
59
|
+
def groups
|
60
|
+
call("UserGroup", "get_group_collection_from_role", "roleName" => @name).xpath("//spdir:Group", NS).map do |row|
|
61
|
+
attributes = clean_attributes(row.attributes)
|
62
|
+
Group.new(@site, attributes["Name"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
cache :groups, :dup => :always
|
66
|
+
|
67
|
+
# Returns true. The same method is present on {Group} where it returns false. Roles and groups can generally be
|
68
|
+
# duck-typed, and this method is there for the rare case where you do need to make the distinction
|
69
|
+
# @return [Boolean]
|
70
|
+
def is_role?
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
# See {Base#save}
|
75
|
+
# @return [void]
|
76
|
+
def save
|
77
|
+
p untype_cast_attributes(@site, nil, internal_attribute_types, changed_attributes)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @private
|
81
|
+
def to_s
|
82
|
+
"#<ActiveSP::Role name=#{@name}>"
|
83
|
+
end
|
84
|
+
|
85
|
+
# @private
|
86
|
+
alias inspect to_s
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def data
|
91
|
+
call("UserGroup", "get_role_info", "roleName" => @name).xpath("//spdir:Role", NS).first
|
92
|
+
end
|
93
|
+
cache :data
|
94
|
+
|
95
|
+
def attributes_before_type_cast
|
96
|
+
clean_attributes(data.attributes)
|
97
|
+
end
|
98
|
+
cache :attributes_before_type_cast
|
99
|
+
|
100
|
+
def original_attributes
|
101
|
+
type_cast_attributes(@site, nil, internal_attribute_types, attributes_before_type_cast)
|
102
|
+
end
|
103
|
+
cache :original_attributes
|
104
|
+
|
105
|
+
def internal_attribute_types
|
106
|
+
@@internal_attribute_types ||= {
|
107
|
+
"Description" => GhostField.new("Description", "Text", false, true),
|
108
|
+
"ID" => GhostField.new("ID", "Text", false, true),
|
109
|
+
"Name" => GhostField.new("Name", "Text", false, true),
|
110
|
+
"Type" => GhostField.new("Type", "Integer", false, true)
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# Copyright (c) 2010 XAOP bvba
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation
|
5
|
+
# files (the "Software"), to deal in the Software without
|
6
|
+
# restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following
|
10
|
+
# conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
#
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
#
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module ActiveSP
|
27
|
+
|
28
|
+
# @private
|
29
|
+
NS = {
|
30
|
+
"sp" => "http://schemas.microsoft.com/sharepoint/soap/",
|
31
|
+
"z" => "#RowsetSchema",
|
32
|
+
"spdir" => "http://schemas.microsoft.com/sharepoint/soap/directory/"
|
33
|
+
}
|
34
|
+
|
35
|
+
module Root
|
36
|
+
|
37
|
+
extend Caching
|
38
|
+
|
39
|
+
# Returns the root site as an object of class {Site}
|
40
|
+
# @return [Site]
|
41
|
+
def root
|
42
|
+
Site.new(self, @root_url)
|
43
|
+
end
|
44
|
+
cache :root
|
45
|
+
|
46
|
+
# Returns the list of users in the system
|
47
|
+
# @return [Array<User>]
|
48
|
+
def users
|
49
|
+
root.send(:call, "UserGroup", "get_user_collection_from_site").xpath("//spdir:User", NS).map do |row|
|
50
|
+
attributes = clean_attributes(row.attributes)
|
51
|
+
User.new(root, attributes["LoginName"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
cache :users, :dup => :always
|
55
|
+
|
56
|
+
# Returns the user with the given login, or nil when the user does not exist
|
57
|
+
# @param [String] login The login of the user
|
58
|
+
# @return [User, nil]
|
59
|
+
def user(login)
|
60
|
+
if user = users_by_login[login]
|
61
|
+
user
|
62
|
+
elsif data = root.send(:call, "UserGroup", "get_user_info", "userLoginName" => login).xpath("//spdir:User", NS).first
|
63
|
+
users_by_login[login] = User.new(root, login, clean_attributes(data))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the list of groups in the system
|
68
|
+
# @return [Array<Group>]
|
69
|
+
def groups
|
70
|
+
root.send(:call, "UserGroup", "get_group_collection_from_site").xpath("//spdir:Group", NS).map do |row|
|
71
|
+
attributes = clean_attributes(row.attributes)
|
72
|
+
Group.new(root, attributes["Name"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
cache :groups, :dup => :always
|
76
|
+
|
77
|
+
def group(name)
|
78
|
+
if group = groups_by_name[name]
|
79
|
+
group
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the list of roles in the system
|
84
|
+
# @return [Array<Role>]
|
85
|
+
def roles
|
86
|
+
root.send(:call, "UserGroup", "get_role_collection_from_web").xpath("//spdir:Role", NS).map do |row|
|
87
|
+
attributes = clean_attributes(row.attributes)
|
88
|
+
Role.new(root, attributes["Name"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
cache :roles, :dup => :always
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def users_by_login
|
96
|
+
users.inject({}) { |h, u| h[u.login_name] = u ; h }
|
97
|
+
end
|
98
|
+
cache :users_by_login
|
99
|
+
|
100
|
+
def groups_by_name
|
101
|
+
groups.inject({}) { |h, g| h[g.Name] = g ; h }
|
102
|
+
end
|
103
|
+
cache :groups_by_name
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class Connection
|
108
|
+
|
109
|
+
include Root
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# @private
|
114
|
+
module InSite
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def call(*a, &b)
|
119
|
+
@site.send(:call, *a, &b)
|
120
|
+
end
|
121
|
+
|
122
|
+
def fetch(url)
|
123
|
+
@site.send(:fetch, url)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
# Copyright (c) 2010 XAOP bvba
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation
|
5
|
+
# files (the "Software"), to deal in the Software without
|
6
|
+
# restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following
|
10
|
+
# conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
#
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
#
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module ActiveSP
|
27
|
+
|
28
|
+
class Site < Base
|
29
|
+
|
30
|
+
extend Caching
|
31
|
+
extend PersistentCaching
|
32
|
+
include Util
|
33
|
+
|
34
|
+
# The URL of this site
|
35
|
+
# @return [String]
|
36
|
+
attr_reader :url
|
37
|
+
# @private
|
38
|
+
attr_reader :connection
|
39
|
+
|
40
|
+
persistent { |connection, url, *a| [connection, [:site, url]] }
|
41
|
+
# @private
|
42
|
+
def initialize(connection, url, depth = 0)
|
43
|
+
@connection, @url, @depth = connection, url, depth
|
44
|
+
@services = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
# @private
|
48
|
+
def relative_url(url = @url)
|
49
|
+
url[@connection.root_url.rindex("/") + 1..-1]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the containing site, or nil if this is the root site
|
53
|
+
# @return [Site]
|
54
|
+
def supersite
|
55
|
+
unless is_root_site?
|
56
|
+
Site.new(@connection, ::File.dirname(@url), @depth - 1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
cache :supersite
|
60
|
+
|
61
|
+
# Returns the root site, or this site if it is the root site
|
62
|
+
# @return [Site]
|
63
|
+
def rootsite
|
64
|
+
is_root_site? ? self : supersite.rootsite
|
65
|
+
end
|
66
|
+
cache :rootsite
|
67
|
+
|
68
|
+
# Returns true if this site is the root site
|
69
|
+
# @return [Boolean]
|
70
|
+
def is_root_site?
|
71
|
+
@depth == 0
|
72
|
+
end
|
73
|
+
|
74
|
+
# See {Base#key}
|
75
|
+
# @return [String]
|
76
|
+
def key # This documentation is not ideal. The ideal doesn't work out of the box
|
77
|
+
encode_key("S", [@url[@connection.root_url.length + 1..-1], @depth])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the list of sites below this site. Does not recurse
|
81
|
+
# @return [Array<List>]
|
82
|
+
def sites
|
83
|
+
result = call("Webs", "get_web_collection")
|
84
|
+
result.xpath("//sp:Web", NS).map { |web| Site.new(connection, web["Url"].to_s, @depth + 1) }
|
85
|
+
end
|
86
|
+
cache :sites, :dup => :always
|
87
|
+
|
88
|
+
# Returns the site with the given name. This name is what appears in the URL as name and is immutable. Return nil
|
89
|
+
# if such a site does not exist
|
90
|
+
# @param [String] name The name if the site
|
91
|
+
# @return [Site]
|
92
|
+
def site(name)
|
93
|
+
result = call("Webs", "get_web", "webUrl" => ::File.join(@url, name))
|
94
|
+
Site.new(connection, result.xpath("//sp:Web", NS).first["Url"].to_s, @depth + 1)
|
95
|
+
rescue Savon::SOAPFault
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the list if lists in this sute. Does not recurse
|
100
|
+
# @return [Array<List>]
|
101
|
+
def lists
|
102
|
+
result1 = call("Lists", "get_list_collection")
|
103
|
+
result2 = call("SiteData", "get_list_collection")
|
104
|
+
result2_by_id = {}
|
105
|
+
result2.xpath("//sp:_sList", NS).each do |element|
|
106
|
+
data = {}
|
107
|
+
element.children.each do |ch|
|
108
|
+
data[ch.name] = ch.inner_text
|
109
|
+
end
|
110
|
+
result2_by_id[data["InternalName"]] = data
|
111
|
+
end
|
112
|
+
result1.xpath("//sp:List", NS).select { |list| list["Title"] != "User Information List" }.map do |list|
|
113
|
+
List.new(self, list["ID"].to_s, list["Title"].to_s, clean_attributes(list.attributes), result2_by_id[list["ID"].to_s])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
cache :lists, :dup => :always
|
117
|
+
|
118
|
+
# Returns the list with the given name. The name is what appears in the URL as name and is immutable. Returns nil
|
119
|
+
# if such a list does not exist
|
120
|
+
# @param [String] name The name of the list
|
121
|
+
# @return [List]
|
122
|
+
def list(name)
|
123
|
+
lists.find { |list| ::File.basename(list.url) == name }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the site or list with the given name, or nil if it does not exist
|
127
|
+
# @param [String] name The name of the site or list
|
128
|
+
# @return [Site, List]
|
129
|
+
def /(name)
|
130
|
+
list(name) || site(name)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns the list of content types defined for this site. These include the content types defined on
|
134
|
+
# containing sites as they are automatically inherited
|
135
|
+
# @return [Array<ContentType>]
|
136
|
+
def content_types
|
137
|
+
result = call("Webs", "get_content_types", "listName" => @id)
|
138
|
+
result.xpath("//sp:ContentType", NS).map do |content_type|
|
139
|
+
supersite && supersite.content_type(content_type["ID"]) || ContentType.new(self, nil, content_type["ID"], content_type["Name"], content_type["Description"], content_type["Version"], content_type["Group"])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
cache :content_types, :dup => :always
|
143
|
+
|
144
|
+
# @private
|
145
|
+
def content_type(id)
|
146
|
+
content_types.find { |t| t.id == id }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns the permission set associated with this site. This returns the permission set of
|
150
|
+
# the containing site if it does not have a permission set of its own
|
151
|
+
# @return [PermissionSet]
|
152
|
+
def permission_set
|
153
|
+
if attributes["InheritedSecurity"]
|
154
|
+
supersite.permission_set
|
155
|
+
else
|
156
|
+
PermissionSet.new(self)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
cache :permission_set
|
160
|
+
|
161
|
+
# Returns the list of fields for this site. This includes fields inherited from containing sites
|
162
|
+
# @return [Array<Field>]
|
163
|
+
def fields
|
164
|
+
call("Webs", "get_columns").xpath("//sp:Field", NS).map do |field|
|
165
|
+
attributes = clean_attributes(field.attributes)
|
166
|
+
supersite && supersite.field(attributes["ID"].downcase) || Field.new(self, attributes["ID"].downcase, attributes["StaticName"], attributes["Type"], nil, attributes) if attributes["ID"] && attributes["StaticName"]
|
167
|
+
end.compact
|
168
|
+
end
|
169
|
+
cache :fields, :dup => :always
|
170
|
+
|
171
|
+
# Returns the result of {Site#fields} hashed by name
|
172
|
+
# @return [Hash{String => Field}]
|
173
|
+
def fields_by_name
|
174
|
+
fields.inject({}) { |h, f| h[f.attributes["StaticName"]] = f ; h }
|
175
|
+
end
|
176
|
+
cache :fields_by_name, :dup => :always
|
177
|
+
|
178
|
+
# @private
|
179
|
+
def field(id)
|
180
|
+
fields.find { |f| f.ID == id }
|
181
|
+
end
|
182
|
+
|
183
|
+
# See {Base#save}
|
184
|
+
# @return [void]
|
185
|
+
def save
|
186
|
+
p untype_cast_attributes(self, nil, internal_attribute_types, changed_attributes)
|
187
|
+
end
|
188
|
+
|
189
|
+
def accessible?
|
190
|
+
data
|
191
|
+
true
|
192
|
+
rescue Savon::HTTPError
|
193
|
+
false
|
194
|
+
end
|
195
|
+
|
196
|
+
# @private
|
197
|
+
def to_s
|
198
|
+
"#<ActiveSP::Site url=#{@url}>"
|
199
|
+
end
|
200
|
+
|
201
|
+
# @private
|
202
|
+
alias inspect to_s
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
def call(service, m, *args, &blk)
|
207
|
+
result = service(service).call(m, *args, &blk)
|
208
|
+
Nokogiri::XML.parse(result.http.body)
|
209
|
+
end
|
210
|
+
|
211
|
+
def fetch(url)
|
212
|
+
@connection.fetch(url)
|
213
|
+
end
|
214
|
+
|
215
|
+
def service(name)
|
216
|
+
@services[name] ||= Service.new(self, name)
|
217
|
+
end
|
218
|
+
|
219
|
+
def data
|
220
|
+
# Looks like you can't call this as a non-admin. To investigate further
|
221
|
+
call("SiteData", "get_web")
|
222
|
+
rescue Savon::HTTPError
|
223
|
+
# This can fail when you don't have access to this site
|
224
|
+
call("Webs", "get_web", "webUrl" => ".")
|
225
|
+
end
|
226
|
+
cache :data
|
227
|
+
|
228
|
+
def attributes_before_type_cast
|
229
|
+
if element = data.xpath("//sp:sWebMetadata", NS).first
|
230
|
+
result = {}
|
231
|
+
element.children.each do |ch|
|
232
|
+
result[ch.name] = ch.inner_text
|
233
|
+
end
|
234
|
+
result
|
235
|
+
else
|
236
|
+
element = data.xpath("//sp:Web", NS).first
|
237
|
+
clean_attributes(element.attributes)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
cache :attributes_before_type_cast
|
241
|
+
|
242
|
+
def original_attributes
|
243
|
+
type_cast_attributes(self, nil, internal_attribute_types, attributes_before_type_cast)
|
244
|
+
end
|
245
|
+
cache :original_attributes
|
246
|
+
|
247
|
+
def internal_attribute_types
|
248
|
+
@@internal_attribute_types ||= {
|
249
|
+
"AllowAnonymousAccess" => GhostField.new("AllowAnonymousAccess", "Bool", false, true),
|
250
|
+
"AnonymousViewListItems" => GhostField.new("AnonymousViewListItems", "Bool", false, true),
|
251
|
+
"Author" => GhostField.new("Author", "InternalUser", false, true),
|
252
|
+
"Description" => GhostField.new("Description", "Text", false, true),
|
253
|
+
"ExternalSecurity" => GhostField.new("ExternalSecurity", "Bool", false, true),
|
254
|
+
"InheritedSecurity" => GhostField.new("InheritedSecurity", "Bool", false, true),
|
255
|
+
"IsBucketWeb" => GhostField.new("IsBucketWeb", "Bool", false, true),
|
256
|
+
"Language" => GhostField.new("Language", "Integer", false, true),
|
257
|
+
"LastModified" => GhostField.new("LastModified", "XMLDateTime", false, true),
|
258
|
+
"LastModifiedForceRecrawl" => GhostField.new("LastModifiedForceRecrawl", "XMLDateTime", false, true),
|
259
|
+
"Permissions" => GhostField.new("Permissions", "Text", false, true),
|
260
|
+
"Title" => GhostField.new("Title", "Text", false, true),
|
261
|
+
"UsedInAutocat" => GhostField.new("UsedInAutocat", "Bool", false, true),
|
262
|
+
"ValidSecurityInfo" => GhostField.new("ValidSecurityInfo", "Bool", false, true),
|
263
|
+
"WebID" => GhostField.new("WebID", "Text", false, true)
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
def permissions
|
268
|
+
result = call("Permissions", "get_permission_collection", "objectName" => ::File.basename(@url), "objectType" => "Web")
|
269
|
+
result.xpath("//spdir:Permission", NS).map do |row|
|
270
|
+
accessor = row["MemberIsUser"][/true/i] ? User.new(rootsite, row["UserLogin"]) : Group.new(rootsite, row["GroupName"])
|
271
|
+
{ :mask => Integer(row["Mask"]), :accessor => accessor }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
cache :permissions, :dup => :always
|
275
|
+
|
276
|
+
# @private
|
277
|
+
class Service
|
278
|
+
|
279
|
+
def initialize(site, name)
|
280
|
+
@site, @name = site, name
|
281
|
+
@client = Savon::Client.new(::File.join(URI.escape(site.url), "_vti_bin", name + ".asmx?WSDL"))
|
282
|
+
@client.request.ntlm_auth(site.connection.login, site.connection.password) if site.connection.login
|
283
|
+
end
|
284
|
+
|
285
|
+
def call(m, *args)
|
286
|
+
t1 = Time.now
|
287
|
+
if Hash === args[-1]
|
288
|
+
body = args.pop
|
289
|
+
end
|
290
|
+
@client.send(m, *args) do |soap|
|
291
|
+
if body
|
292
|
+
soap.body = body.inject({}) { |h, (k, v)| h["wsdl:#{k}"] = v ; h }
|
293
|
+
end
|
294
|
+
yield soap if block_given?
|
295
|
+
end
|
296
|
+
ensure
|
297
|
+
t2 = Time.now
|
298
|
+
puts "SP - time: %.3fs, site: %s, service: %s, method: %s, body: %s" % [t2 - t1, @site.url, @name, m, body.inspect] if @site.connection.trace
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
data/lib/activesp/url.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# Copyright (c) 2010 XAOP bvba
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation
|
5
|
+
# files (the "Software"), to deal in the Software without
|
6
|
+
# restriction, including without limitation the rights to use,
|
7
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following
|
10
|
+
# conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
#
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
#
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
# @private
|
27
|
+
def URL(*args)
|
28
|
+
case args.length
|
29
|
+
when 1
|
30
|
+
url = args[0]
|
31
|
+
if URL === url
|
32
|
+
url
|
33
|
+
else
|
34
|
+
URL.parse(url)
|
35
|
+
end
|
36
|
+
when 2..6
|
37
|
+
URL.new(*args)
|
38
|
+
else
|
39
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1..6)"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @private
|
44
|
+
class URL < Struct.new(:protocol, :host, :port, :path, :query, :fragment)
|
45
|
+
|
46
|
+
def self.parse(url)
|
47
|
+
if /^(?:([^:\/?#]+):)?(?:\/\/([^\/?#:]*)(?::(\d+))?)?([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/ === url.strip
|
48
|
+
new($1 ? $1.downcase : nil, $2 ? $2.downcase : nil, $3 ? $3.to_i : nil, $4, $5, $6)
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
"%s://%s%s" % [protocol, authority, full_path]
|
56
|
+
end
|
57
|
+
|
58
|
+
def authority
|
59
|
+
"%s%s" % [host, (!port || port == (protocol == "http" ? 80 : 443)) ? "" : ":#{port}"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def full_path
|
63
|
+
result = path.dup
|
64
|
+
result << "?" << query if query
|
65
|
+
result << "#" << fragment if fragment
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def join(url)
|
70
|
+
url = URL(url)
|
71
|
+
if url
|
72
|
+
if url.protocol == protocol
|
73
|
+
url.protocol = nil
|
74
|
+
end
|
75
|
+
unless url.protocol
|
76
|
+
url.protocol = protocol
|
77
|
+
unless url.host
|
78
|
+
url.host = host
|
79
|
+
url.port = port
|
80
|
+
if url.path.empty?
|
81
|
+
url.path = path
|
82
|
+
unless url.query
|
83
|
+
url.query = query
|
84
|
+
end
|
85
|
+
else
|
86
|
+
url.path = join_url_paths(url.path, path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
url.complete
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def complete
|
97
|
+
self.protocol ||= "http"
|
98
|
+
self.port ||= self.protocol == "http" ? 80 : 443
|
99
|
+
self.path = "/" if self.path.empty?
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.unescape(s)
|
104
|
+
s.gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
|
105
|
+
[$1.delete('%')].pack('H*')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.escape(s)
|
110
|
+
s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) do
|
111
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.parse_query(qs, d = '&;')
|
116
|
+
params = {}
|
117
|
+
(qs || '').split(/[&;] */n).inject(params) do |h, p|
|
118
|
+
k, v = unescape(p).split('=', 2)
|
119
|
+
if cur = params[k]
|
120
|
+
if Array === cur
|
121
|
+
params[k] << v
|
122
|
+
else
|
123
|
+
params[k] = [cur, v]
|
124
|
+
end
|
125
|
+
else
|
126
|
+
params[k] = v
|
127
|
+
end
|
128
|
+
end
|
129
|
+
params
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.construct_query(hash)
|
133
|
+
hash.map { |k, v| "%s=%s" % [k, escape(v)] }.join('&')
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def join_url_paths(url, base)
|
139
|
+
if url[0] == ?/
|
140
|
+
url
|
141
|
+
else
|
142
|
+
base[0..base.rindex("/")] + url
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|