activesp 0.0.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,28 @@
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
+
1
26
  module ActiveSP
2
27
 
3
28
  class PermissionSet
@@ -6,22 +31,32 @@ module ActiveSP
6
31
 
7
32
  attr_reader :scope
8
33
 
34
+ # @private
9
35
  def initialize(scope)
10
36
  @scope = scope
11
37
  end
12
38
 
13
- def permissions
14
- @scope.send(:permissions)
15
- end
16
-
39
+ # See {Base#key}
40
+ # @return [String]
17
41
  def key
18
42
  encode_key("P", [@scope.key])
19
43
  end
20
44
 
45
+ # Returns the permissions in this permission set as an array of hashes with :accessor mapping to a user,
46
+ # group or role and :mask mapping to the permission as an integer
47
+ # @return [Array<Hash{:accessor, :permission => User, Group, Role, Integer}>]
48
+ # @example
49
+ # set.permissions #=> [{:accessor=>#<ActiveSP::User login_name=SHAREPOINT\system>, :mask=>134287360}]
50
+ def permissions
51
+ @scope.send(:permissions)
52
+ end
53
+
54
+ # @private
21
55
  def to_s
22
56
  "#<ActiveSP::PermissionSet scope=#{@scope}>"
23
57
  end
24
58
 
59
+ # @private
25
60
  alias inspect to_s
26
61
 
27
62
  end
@@ -1,7 +1,35 @@
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
+
1
26
  module ActiveSP
2
27
 
28
+ # @private
3
29
  module PersistentCaching
4
30
 
31
+ private
32
+
5
33
  def persistent(&blk)
6
34
  class << self ; self ; end.instance_eval do
7
35
  alias_method :old_new, :new
@@ -18,6 +46,7 @@ module ActiveSP
18
46
 
19
47
  end
20
48
 
49
+ # @private
21
50
  class PersistentCache
22
51
 
23
52
  def initialize
@@ -38,11 +67,42 @@ module ActiveSP
38
67
 
39
68
  module PersistentCachingConfig
40
69
 
70
+ # Configures the scope of the persistent cache. The default scope of the cache
71
+ # is the {Connection} object, i.e., each connection has its own cache. For example
72
+ # you can use this to make thread local caches. Note that the cache is not actually
73
+ # thread safe at this point so this may not be such a bad idea.
74
+ #
75
+ # Caching in ActiveSP at the moment is very aggressive. What this means that everything
76
+ # you ever accessed will be cached. You can override the cache for a particular object
77
+ # by calling {Base#reload} on it. One advantage of this caching strategy is that every time
78
+ # you access an object in SharePoint, it is guaranteed to be the same object in Ruby as
79
+ # well, irrespective of how you obtained a reference to that object. This eliminates a
80
+ # whole slew of issues, but you need to be aware of this.
81
+ #
82
+ # This method expects a block to which a new cache object is passed. The idea is that
83
+ # you store this cache object in a place that reflects the scope of your cache. If you
84
+ # already had a cache object stored for you current scope, you do not do anything with
85
+ # the cache object. The cache object that the block returns is the cache that will be used.
86
+ # Note that the block is called everytime ActiveSP needs the cache, so make it as
87
+ # efficient as possible. The example below illustrates how you can use the ||= operator
88
+ # for this to get a thread local cache.
89
+ #
90
+ # You can use this block to return a cache of your own. A cache is only expected to have
91
+ # a lookup method to which the cache key is passed (do not assume it is an integer or a
92
+ # string because it is not) as parameter and is expected to return the value for that
93
+ # key. In case of a cache miss, the method should yield to retrieve the value, store it
94
+ # with the given key and return the value. You can use this to plug in a cache that has
95
+ # a limited size, or that uses weak references to clean up the cache. The latter suggestion
96
+ # is a lot safer than the former!
97
+ #
98
+ # @example How to configure caching strategy
99
+ # c = ActiveSP::Connection.new(:login => l, :password => p, :root => r)
100
+ # c.configure_persistent_cache { |cache| Thread.current[:sp_cache] ||= cache }
41
101
  def configure_persistent_cache(&blk)
42
102
  @last_persistent_cache_object = PersistentCache.new
43
103
  class << self ; self ; end.send(:define_method, :persistent_cache) do
44
104
  cache = blk.call(@last_persistent_cache_object)
45
- @last_persistent_cache_object = PersistentCache.new unless cache == @last_persistent_cache_object
105
+ @last_persistent_cache_object = PersistentCache.new if cache == @last_persistent_cache_object
46
106
  cache
47
107
  end
48
108
  end
data/lib/activesp/role.rb CHANGED
@@ -1,3 +1,28 @@
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
+
1
26
  module ActiveSP
2
27
 
3
28
  class Role < Base
@@ -7,43 +32,59 @@ module ActiveSP
7
32
  include Util
8
33
  include InSite
9
34
 
10
- attr_reader :name
11
-
12
35
  persistent { |site, name, *a| [site.connection, [:role, name]] }
36
+ # @private
13
37
  def initialize(site, name)
14
38
  @site, @name = site, name
15
39
  end
16
40
 
41
+ # See {Base#key}
42
+ # @return [String]
17
43
  def key
18
44
  encode_key("R", [@name])
19
45
  end
20
46
 
47
+ # Returns the list of users in this role
48
+ # @return [User]
21
49
  def users
22
50
  call("UserGroup", "get_user_collection_from_role", "roleName" => @name).xpath("//spdir:User", NS).map do |row|
23
51
  attributes = clean_attributes(row.attributes)
24
52
  User.new(@site, attributes["LoginName"])
25
53
  end
26
54
  end
27
- cache :users, :dup => true
55
+ cache :users, :dup => :always
28
56
 
57
+ # Returns the list of groups in this role
58
+ # @return [Group]
29
59
  def groups
30
60
  call("UserGroup", "get_group_collection_from_role", "roleName" => @name).xpath("//spdir:Group", NS).map do |row|
31
61
  attributes = clean_attributes(row.attributes)
32
62
  Group.new(@site, attributes["Name"])
33
63
  end
34
64
  end
35
- cache :groups, :dup => true
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
36
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
37
81
  def to_s
38
82
  "#<ActiveSP::Role name=#{@name}>"
39
83
  end
40
84
 
85
+ # @private
41
86
  alias inspect to_s
42
87
 
43
- def is_role?
44
- true
45
- end
46
-
47
88
  private
48
89
 
49
90
  def data
data/lib/activesp/root.rb CHANGED
@@ -1,5 +1,31 @@
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
+
1
26
  module ActiveSP
2
27
 
28
+ # @private
3
29
  NS = {
4
30
  "sp" => "http://schemas.microsoft.com/sharepoint/soap/",
5
31
  "z" => "#RowsetSchema",
@@ -10,34 +36,71 @@ module ActiveSP
10
36
 
11
37
  extend Caching
12
38
 
39
+ # Returns the root site as an object of class {Site}
40
+ # @return [Site]
13
41
  def root
14
42
  Site.new(self, @root_url)
15
43
  end
16
44
  cache :root
17
45
 
46
+ # Returns the list of users in the system
47
+ # @return [Array<User>]
18
48
  def users
19
49
  root.send(:call, "UserGroup", "get_user_collection_from_site").xpath("//spdir:User", NS).map do |row|
20
50
  attributes = clean_attributes(row.attributes)
21
51
  User.new(root, attributes["LoginName"])
22
52
  end
23
53
  end
24
- cache :users, :dup => true
54
+ cache :users, :dup => :always
25
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>]
26
69
  def groups
27
70
  root.send(:call, "UserGroup", "get_group_collection_from_site").xpath("//spdir:Group", NS).map do |row|
28
71
  attributes = clean_attributes(row.attributes)
29
72
  Group.new(root, attributes["Name"])
30
73
  end
31
74
  end
32
- cache :groups, :dup => true
75
+ cache :groups, :dup => :always
76
+
77
+ def group(name)
78
+ if group = groups_by_name[name]
79
+ group
80
+ end
81
+ end
33
82
 
83
+ # Returns the list of roles in the system
84
+ # @return [Array<Role>]
34
85
  def roles
35
86
  root.send(:call, "UserGroup", "get_role_collection_from_web").xpath("//spdir:Role", NS).map do |row|
36
87
  attributes = clean_attributes(row.attributes)
37
88
  Role.new(root, attributes["Name"])
38
89
  end
39
90
  end
40
- cache :roles, :dup => true
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
41
104
 
42
105
  end
43
106
 
@@ -47,6 +110,7 @@ module ActiveSP
47
110
 
48
111
  end
49
112
 
113
+ # @private
50
114
  module InSite
51
115
 
52
116
  private
data/lib/activesp/site.rb CHANGED
@@ -1,3 +1,28 @@
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
+
1
26
  module ActiveSP
2
27
 
3
28
  class Site < Base
@@ -6,51 +31,73 @@ module ActiveSP
6
31
  extend PersistentCaching
7
32
  include Util
8
33
 
9
- attr_reader :url, :connection
34
+ # The URL of this site
35
+ # @return [String]
36
+ attr_reader :url
37
+ # @private
38
+ attr_reader :connection
10
39
 
11
40
  persistent { |connection, url, *a| [connection, [:site, url]] }
41
+ # @private
12
42
  def initialize(connection, url, depth = 0)
13
43
  @connection, @url, @depth = connection, url, depth
14
44
  @services = {}
15
45
  end
16
46
 
47
+ # @private
17
48
  def relative_url(url = @url)
18
49
  url[@connection.root_url.rindex("/") + 1..-1]
19
50
  end
20
51
 
52
+ # Returns the containing site, or nil if this is the root site
53
+ # @return [Site]
21
54
  def supersite
22
- if @depth > 0
23
- Site.new(@connection, File.dirname(@url), @depth - 1)
55
+ unless is_root_site?
56
+ Site.new(@connection, ::File.dirname(@url), @depth - 1)
24
57
  end
25
58
  end
26
59
  cache :supersite
27
60
 
61
+ # Returns the root site, or this site if it is the root site
62
+ # @return [Site]
28
63
  def rootsite
29
- @depth > 0 ? supersite.rootsite : self
64
+ is_root_site? ? self : supersite.rootsite
30
65
  end
31
66
  cache :rootsite
32
67
 
68
+ # Returns true if this site is the root site
69
+ # @return [Boolean]
33
70
  def is_root_site?
34
71
  @depth == 0
35
72
  end
36
73
 
37
- def key
74
+ # See {Base#key}
75
+ # @return [String]
76
+ def key # This documentation is not ideal. The ideal doesn't work out of the box
38
77
  encode_key("S", [@url[@connection.root_url.length + 1..-1], @depth])
39
78
  end
40
79
 
80
+ # Returns the list of sites below this site. Does not recurse
81
+ # @return [Array<List>]
41
82
  def sites
42
83
  result = call("Webs", "get_web_collection")
43
84
  result.xpath("//sp:Web", NS).map { |web| Site.new(connection, web["Url"].to_s, @depth + 1) }
44
85
  end
45
- cache :sites, :dup => true
86
+ cache :sites, :dup => :always
46
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]
47
92
  def site(name)
48
- result = call("Webs", "get_web", "webUrl" => File.join(@url, name))
93
+ result = call("Webs", "get_web", "webUrl" => ::File.join(@url, name))
49
94
  Site.new(connection, result.xpath("//sp:Web", NS).first["Url"].to_s, @depth + 1)
50
95
  rescue Savon::SOAPFault
51
96
  nil
52
97
  end
53
98
 
99
+ # Returns the list if lists in this sute. Does not recurse
100
+ # @return [Array<List>]
54
101
  def lists
55
102
  result1 = call("Lists", "get_list_collection")
56
103
  result2 = call("SiteData", "get_list_collection")
@@ -66,28 +113,42 @@ module ActiveSP
66
113
  List.new(self, list["ID"].to_s, list["Title"].to_s, clean_attributes(list.attributes), result2_by_id[list["ID"].to_s])
67
114
  end
68
115
  end
69
- cache :lists, :dup => true
116
+ cache :lists, :dup => :always
70
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]
71
122
  def list(name)
72
- lists.find { |list| File.basename(list.attributes["RootFolder"]) == name }
123
+ lists.find { |list| ::File.basename(list.url) == name }
73
124
  end
74
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]
75
129
  def /(name)
76
130
  list(name) || site(name)
77
131
  end
78
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>]
79
136
  def content_types
80
137
  result = call("Webs", "get_content_types", "listName" => @id)
81
138
  result.xpath("//sp:ContentType", NS).map do |content_type|
82
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"])
83
140
  end
84
141
  end
85
- cache :content_types, :dup => true
142
+ cache :content_types, :dup => :always
86
143
 
144
+ # @private
87
145
  def content_type(id)
88
146
  content_types.find { |t| t.id == id }
89
147
  end
90
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]
91
152
  def permission_set
92
153
  if attributes["InheritedSecurity"]
93
154
  supersite.permission_set
@@ -97,27 +158,47 @@ module ActiveSP
97
158
  end
98
159
  cache :permission_set
99
160
 
161
+ # Returns the list of fields for this site. This includes fields inherited from containing sites
162
+ # @return [Array<Field>]
100
163
  def fields
101
164
  call("Webs", "get_columns").xpath("//sp:Field", NS).map do |field|
102
165
  attributes = clean_attributes(field.attributes)
103
166
  supersite && supersite.field(attributes["ID"].downcase) || Field.new(self, attributes["ID"].downcase, attributes["StaticName"], attributes["Type"], nil, attributes) if attributes["ID"] && attributes["StaticName"]
104
167
  end.compact
105
168
  end
106
- cache :fields, :dup => true
169
+ cache :fields, :dup => :always
107
170
 
171
+ # Returns the result of {Site#fields} hashed by name
172
+ # @return [Hash{String => Field}]
108
173
  def fields_by_name
109
174
  fields.inject({}) { |h, f| h[f.attributes["StaticName"]] = f ; h }
110
175
  end
111
- cache :fields_by_name, :dup => true
176
+ cache :fields_by_name, :dup => :always
112
177
 
178
+ # @private
113
179
  def field(id)
114
180
  fields.find { |f| f.ID == id }
115
181
  end
116
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
117
197
  def to_s
118
198
  "#<ActiveSP::Site url=#{@url}>"
119
199
  end
120
200
 
201
+ # @private
121
202
  alias inspect to_s
122
203
 
123
204
  private
@@ -136,17 +217,25 @@ module ActiveSP
136
217
  end
137
218
 
138
219
  def data
220
+ # Looks like you can't call this as a non-admin. To investigate further
139
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" => ".")
140
225
  end
141
226
  cache :data
142
227
 
143
228
  def attributes_before_type_cast
144
- element = data.xpath("//sp:sWebMetadata", NS).first
145
- result = {}
146
- element.children.each do |ch|
147
- result[ch.name] = ch.inner_text
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)
148
238
  end
149
- result
150
239
  end
151
240
  cache :attributes_before_type_cast
152
241
 
@@ -176,19 +265,20 @@ module ActiveSP
176
265
  end
177
266
 
178
267
  def permissions
179
- result = call("Permissions", "get_permission_collection", "objectName" => File.basename(@url), "objectType" => "Web")
268
+ result = call("Permissions", "get_permission_collection", "objectName" => ::File.basename(@url), "objectType" => "Web")
180
269
  result.xpath("//spdir:Permission", NS).map do |row|
181
270
  accessor = row["MemberIsUser"][/true/i] ? User.new(rootsite, row["UserLogin"]) : Group.new(rootsite, row["GroupName"])
182
271
  { :mask => Integer(row["Mask"]), :accessor => accessor }
183
272
  end
184
273
  end
185
- cache :permissions, :dup => true
274
+ cache :permissions, :dup => :always
186
275
 
276
+ # @private
187
277
  class Service
188
278
 
189
279
  def initialize(site, name)
190
280
  @site, @name = site, name
191
- @client = Savon::Client.new(File.join(site.url, "_vti_bin", name + ".asmx?WSDL"))
281
+ @client = Savon::Client.new(::File.join(URI.escape(site.url), "_vti_bin", name + ".asmx?WSDL"))
192
282
  @client.request.ntlm_auth(site.connection.login, site.connection.password) if site.connection.login
193
283
  end
194
284