pyu-activesp 0.0.4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +25 -0
- data/README.rdoc +105 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/activesp/associations.rb +76 -0
- data/lib/activesp/base.rb +139 -0
- data/lib/activesp/caching.rb +60 -0
- data/lib/activesp/connection.rb +126 -0
- data/lib/activesp/content_type.rb +152 -0
- data/lib/activesp/field.rb +193 -0
- data/lib/activesp/file.rb +71 -0
- data/lib/activesp/folder.rb +103 -0
- data/lib/activesp/ghost_field.rb +100 -0
- data/lib/activesp/group.rb +107 -0
- data/lib/activesp/item.rb +338 -0
- data/lib/activesp/list.rb +509 -0
- data/lib/activesp/permission_set.rb +64 -0
- data/lib/activesp/persistent_caching.rb +112 -0
- data/lib/activesp/role.rb +116 -0
- data/lib/activesp/root.rb +128 -0
- data/lib/activesp/site.rb +305 -0
- data/lib/activesp/url.rb +146 -0
- data/lib/activesp/user.rb +97 -0
- data/lib/activesp/util.rb +326 -0
- data/lib/activesp.rb +53 -0
- metadata +118 -0
@@ -0,0 +1,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
|