activesp 0.0.1

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.
@@ -0,0 +1,29 @@
1
+ module ActiveSP
2
+
3
+ class PermissionSet
4
+
5
+ include Util
6
+
7
+ attr_reader :scope
8
+
9
+ def initialize(scope)
10
+ @scope = scope
11
+ end
12
+
13
+ def permissions
14
+ @scope.send(:permissions)
15
+ end
16
+
17
+ def key
18
+ encode_key("P", [@scope.key])
19
+ end
20
+
21
+ def to_s
22
+ "#<ActiveSP::PermissionSet scope=#{@scope}>"
23
+ end
24
+
25
+ alias inspect to_s
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveSP
2
+
3
+ module PersistentCaching
4
+
5
+ def persistent(&blk)
6
+ class << self ; self ; end.instance_eval do
7
+ alias_method :old_new, :new
8
+ define_method(:new) do |*a|
9
+ cache_scope, indices = *blk.call(a)
10
+ if cache_scope.respond_to?(:persistent_cache)
11
+ cache_scope.persistent_cache.lookup(indices) { old_new(*a) }
12
+ else
13
+ old_new(*a)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ class PersistentCache
22
+
23
+ def initialize
24
+ @cache = {}
25
+ end
26
+
27
+ def lookup(indices)
28
+ if o = @cache[indices]
29
+ # puts " Cache hit for #{indices.inspect}"
30
+ else
31
+ o = @cache[indices] ||= yield
32
+ # puts " Cache miss for #{indices.inspect}"
33
+ end
34
+ o
35
+ end
36
+
37
+ end
38
+
39
+ module PersistentCachingConfig
40
+
41
+ def configure_persistent_cache(&blk)
42
+ @last_persistent_cache_object = PersistentCache.new
43
+ class << self ; self ; end.send(:define_method, :persistent_cache) do
44
+ cache = blk.call(@last_persistent_cache_object)
45
+ @last_persistent_cache_object = PersistentCache.new unless cache == @last_persistent_cache_object
46
+ cache
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,75 @@
1
+ module ActiveSP
2
+
3
+ class Role < Base
4
+
5
+ extend Caching
6
+ extend PersistentCaching
7
+ include Util
8
+ include InSite
9
+
10
+ attr_reader :name
11
+
12
+ persistent { |site, name, *a| [site.connection, [:role, name]] }
13
+ def initialize(site, name)
14
+ @site, @name = site, name
15
+ end
16
+
17
+ def key
18
+ encode_key("R", [@name])
19
+ end
20
+
21
+ def users
22
+ call("UserGroup", "get_user_collection_from_role", "roleName" => @name).xpath("//spdir:User", NS).map do |row|
23
+ attributes = clean_attributes(row.attributes)
24
+ User.new(@site, attributes["LoginName"])
25
+ end
26
+ end
27
+ cache :users, :dup => true
28
+
29
+ def groups
30
+ call("UserGroup", "get_group_collection_from_role", "roleName" => @name).xpath("//spdir:Group", NS).map do |row|
31
+ attributes = clean_attributes(row.attributes)
32
+ Group.new(@site, attributes["Name"])
33
+ end
34
+ end
35
+ cache :groups, :dup => true
36
+
37
+ def to_s
38
+ "#<ActiveSP::Role name=#{@name}>"
39
+ end
40
+
41
+ alias inspect to_s
42
+
43
+ def is_role?
44
+ true
45
+ end
46
+
47
+ private
48
+
49
+ def data
50
+ call("UserGroup", "get_role_info", "roleName" => @name).xpath("//spdir:Role", NS).first
51
+ end
52
+ cache :data
53
+
54
+ def attributes_before_type_cast
55
+ clean_attributes(data.attributes)
56
+ end
57
+ cache :attributes_before_type_cast
58
+
59
+ def original_attributes
60
+ type_cast_attributes(@site, nil, internal_attribute_types, attributes_before_type_cast)
61
+ end
62
+ cache :original_attributes
63
+
64
+ def internal_attribute_types
65
+ @@internal_attribute_types ||= {
66
+ "Description" => GhostField.new("Description", "Text", false, true),
67
+ "ID" => GhostField.new("ID", "Text", false, true),
68
+ "Name" => GhostField.new("Name", "Text", false, true),
69
+ "Type" => GhostField.new("Type", "Integer", false, true)
70
+ }
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveSP
2
+
3
+ NS = {
4
+ "sp" => "http://schemas.microsoft.com/sharepoint/soap/",
5
+ "z" => "#RowsetSchema",
6
+ "spdir" => "http://schemas.microsoft.com/sharepoint/soap/directory/"
7
+ }
8
+
9
+ module Root
10
+
11
+ extend Caching
12
+
13
+ def root
14
+ Site.new(self, @root_url)
15
+ end
16
+ cache :root
17
+
18
+ def users
19
+ root.send(:call, "UserGroup", "get_user_collection_from_site").xpath("//spdir:User", NS).map do |row|
20
+ attributes = clean_attributes(row.attributes)
21
+ User.new(root, attributes["LoginName"])
22
+ end
23
+ end
24
+ cache :users, :dup => true
25
+
26
+ def groups
27
+ root.send(:call, "UserGroup", "get_group_collection_from_site").xpath("//spdir:Group", NS).map do |row|
28
+ attributes = clean_attributes(row.attributes)
29
+ Group.new(root, attributes["Name"])
30
+ end
31
+ end
32
+ cache :groups, :dup => true
33
+
34
+ def roles
35
+ root.send(:call, "UserGroup", "get_role_collection_from_web").xpath("//spdir:Role", NS).map do |row|
36
+ attributes = clean_attributes(row.attributes)
37
+ Role.new(root, attributes["Name"])
38
+ end
39
+ end
40
+ cache :roles, :dup => true
41
+
42
+ end
43
+
44
+ class Connection
45
+
46
+ include Root
47
+
48
+ end
49
+
50
+ module InSite
51
+
52
+ private
53
+
54
+ def call(*a, &b)
55
+ @site.send(:call, *a, &b)
56
+ end
57
+
58
+ def fetch(url)
59
+ @site.send(:fetch, url)
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,215 @@
1
+ module ActiveSP
2
+
3
+ class Site < Base
4
+
5
+ extend Caching
6
+ extend PersistentCaching
7
+ include Util
8
+
9
+ attr_reader :url, :connection
10
+
11
+ persistent { |connection, url, *a| [connection, [:site, url]] }
12
+ def initialize(connection, url, depth = 0)
13
+ @connection, @url, @depth = connection, url, depth
14
+ @services = {}
15
+ end
16
+
17
+ def relative_url(url = @url)
18
+ url[@connection.root_url.rindex("/") + 1..-1]
19
+ end
20
+
21
+ def supersite
22
+ if @depth > 0
23
+ Site.new(@connection, File.dirname(@url), @depth - 1)
24
+ end
25
+ end
26
+ cache :supersite
27
+
28
+ def rootsite
29
+ @depth > 0 ? supersite.rootsite : self
30
+ end
31
+ cache :rootsite
32
+
33
+ def is_root_site?
34
+ @depth == 0
35
+ end
36
+
37
+ def key
38
+ encode_key("S", [@url[@connection.root_url.length + 1..-1], @depth])
39
+ end
40
+
41
+ def sites
42
+ result = call("Webs", "get_web_collection")
43
+ result.xpath("//sp:Web", NS).map { |web| Site.new(connection, web["Url"].to_s, @depth + 1) }
44
+ end
45
+ cache :sites, :dup => true
46
+
47
+ def site(name)
48
+ result = call("Webs", "get_web", "webUrl" => File.join(@url, name))
49
+ Site.new(connection, result.xpath("//sp:Web", NS).first["Url"].to_s, @depth + 1)
50
+ rescue Savon::SOAPFault
51
+ nil
52
+ end
53
+
54
+ def lists
55
+ result1 = call("Lists", "get_list_collection")
56
+ result2 = call("SiteData", "get_list_collection")
57
+ result2_by_id = {}
58
+ result2.xpath("//sp:_sList", NS).each do |element|
59
+ data = {}
60
+ element.children.each do |ch|
61
+ data[ch.name] = ch.inner_text
62
+ end
63
+ result2_by_id[data["InternalName"]] = data
64
+ end
65
+ result1.xpath("//sp:List", NS).select { |list| list["Title"] != "User Information List" }.map do |list|
66
+ List.new(self, list["ID"].to_s, list["Title"].to_s, clean_attributes(list.attributes), result2_by_id[list["ID"].to_s])
67
+ end
68
+ end
69
+ cache :lists, :dup => true
70
+
71
+ def list(name)
72
+ lists.find { |list| File.basename(list.attributes["RootFolder"]) == name }
73
+ end
74
+
75
+ def /(name)
76
+ list(name) || site(name)
77
+ end
78
+
79
+ def content_types
80
+ result = call("Webs", "get_content_types", "listName" => @id)
81
+ result.xpath("//sp:ContentType", NS).map do |content_type|
82
+ 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
+ end
84
+ end
85
+ cache :content_types, :dup => true
86
+
87
+ def content_type(id)
88
+ content_types.find { |t| t.id == id }
89
+ end
90
+
91
+ def permission_set
92
+ if attributes["InheritedSecurity"]
93
+ supersite.permission_set
94
+ else
95
+ PermissionSet.new(self)
96
+ end
97
+ end
98
+ cache :permission_set
99
+
100
+ def fields
101
+ call("Webs", "get_columns").xpath("//sp:Field", NS).map do |field|
102
+ attributes = clean_attributes(field.attributes)
103
+ supersite && supersite.field(attributes["ID"].downcase) || Field.new(self, attributes["ID"].downcase, attributes["StaticName"], attributes["Type"], nil, attributes) if attributes["ID"] && attributes["StaticName"]
104
+ end.compact
105
+ end
106
+ cache :fields, :dup => true
107
+
108
+ def fields_by_name
109
+ fields.inject({}) { |h, f| h[f.attributes["StaticName"]] = f ; h }
110
+ end
111
+ cache :fields_by_name, :dup => true
112
+
113
+ def field(id)
114
+ fields.find { |f| f.ID == id }
115
+ end
116
+
117
+ def to_s
118
+ "#<ActiveSP::Site url=#{@url}>"
119
+ end
120
+
121
+ alias inspect to_s
122
+
123
+ private
124
+
125
+ def call(service, m, *args, &blk)
126
+ result = service(service).call(m, *args, &blk)
127
+ Nokogiri::XML.parse(result.http.body)
128
+ end
129
+
130
+ def fetch(url)
131
+ @connection.fetch(url)
132
+ end
133
+
134
+ def service(name)
135
+ @services[name] ||= Service.new(self, name)
136
+ end
137
+
138
+ def data
139
+ call("SiteData", "get_web")
140
+ end
141
+ cache :data
142
+
143
+ 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
148
+ end
149
+ result
150
+ end
151
+ cache :attributes_before_type_cast
152
+
153
+ def original_attributes
154
+ type_cast_attributes(self, nil, internal_attribute_types, attributes_before_type_cast)
155
+ end
156
+ cache :original_attributes
157
+
158
+ def internal_attribute_types
159
+ @@internal_attribute_types ||= {
160
+ "AllowAnonymousAccess" => GhostField.new("AllowAnonymousAccess", "Bool", false, true),
161
+ "AnonymousViewListItems" => GhostField.new("AnonymousViewListItems", "Bool", false, true),
162
+ "Author" => GhostField.new("Author", "InternalUser", false, true),
163
+ "Description" => GhostField.new("Description", "Text", false, true),
164
+ "ExternalSecurity" => GhostField.new("ExternalSecurity", "Bool", false, true),
165
+ "InheritedSecurity" => GhostField.new("InheritedSecurity", "Bool", false, true),
166
+ "IsBucketWeb" => GhostField.new("IsBucketWeb", "Bool", false, true),
167
+ "Language" => GhostField.new("Language", "Integer", false, true),
168
+ "LastModified" => GhostField.new("LastModified", "XMLDateTime", false, true),
169
+ "LastModifiedForceRecrawl" => GhostField.new("LastModifiedForceRecrawl", "XMLDateTime", false, true),
170
+ "Permissions" => GhostField.new("Permissions", "Text", false, true),
171
+ "Title" => GhostField.new("Title", "Text", false, true),
172
+ "UsedInAutocat" => GhostField.new("UsedInAutocat", "Bool", false, true),
173
+ "ValidSecurityInfo" => GhostField.new("ValidSecurityInfo", "Bool", false, true),
174
+ "WebID" => GhostField.new("WebID", "Text", false, true)
175
+ }
176
+ end
177
+
178
+ def permissions
179
+ result = call("Permissions", "get_permission_collection", "objectName" => File.basename(@url), "objectType" => "Web")
180
+ result.xpath("//spdir:Permission", NS).map do |row|
181
+ accessor = row["MemberIsUser"][/true/i] ? User.new(rootsite, row["UserLogin"]) : Group.new(rootsite, row["GroupName"])
182
+ { :mask => Integer(row["Mask"]), :accessor => accessor }
183
+ end
184
+ end
185
+ cache :permissions, :dup => true
186
+
187
+ class Service
188
+
189
+ def initialize(site, name)
190
+ @site, @name = site, name
191
+ @client = Savon::Client.new(File.join(site.url, "_vti_bin", name + ".asmx?WSDL"))
192
+ @client.request.ntlm_auth(site.connection.login, site.connection.password) if site.connection.login
193
+ end
194
+
195
+ def call(m, *args)
196
+ t1 = Time.now
197
+ if Hash === args[-1]
198
+ body = args.pop
199
+ end
200
+ @client.send(m, *args) do |soap|
201
+ if body
202
+ soap.body = body.inject({}) { |h, (k, v)| h["wsdl:#{k}"] = v ; h }
203
+ end
204
+ yield soap if block_given?
205
+ end
206
+ ensure
207
+ t2 = Time.now
208
+ puts "SP - time: %.3fs, site: %s, service: %s, method: %s, body: %s" % [t2 - t1, @site.url, @name, m, body.inspect] if @site.connection.trace
209
+ end
210
+
211
+ end
212
+
213
+ end
214
+
215
+ end
@@ -0,0 +1,119 @@
1
+ def URL(*args)
2
+ case args.length
3
+ when 1
4
+ url = args[0]
5
+ if URL === url
6
+ url
7
+ else
8
+ URL.parse(url)
9
+ end
10
+ when 2..6
11
+ URL.new(*args)
12
+ else
13
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1..6)"
14
+ end
15
+ end
16
+
17
+ class URL < Struct.new(:protocol, :host, :port, :path, :query, :fragment)
18
+
19
+ def self.parse(url)
20
+ if /^(?:([^:\/?#]+):)?(?:\/\/([^\/?#:]*)(?::(\d+))?)?([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/ === url.strip
21
+ new($1 ? $1.downcase : nil, $2 ? $2.downcase : nil, $3 ? $3.to_i : nil, $4, $5, $6)
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
27
+ def to_s
28
+ "%s://%s%s" % [protocol, authority, full_path]
29
+ end
30
+
31
+ def authority
32
+ "%s%s" % [host, (!port || port == (protocol == "http" ? 80 : 443)) ? "" : ":#{port}"]
33
+ end
34
+
35
+ def full_path
36
+ result = path.dup
37
+ result << "?" << query if query
38
+ result << "#" << fragment if fragment
39
+ result
40
+ end
41
+
42
+ def join(url)
43
+ url = URL(url)
44
+ if url
45
+ if url.protocol == protocol
46
+ url.protocol = nil
47
+ end
48
+ unless url.protocol
49
+ url.protocol = protocol
50
+ unless url.host
51
+ url.host = host
52
+ url.port = port
53
+ if url.path.empty?
54
+ url.path = path
55
+ unless url.query
56
+ url.query = query
57
+ end
58
+ else
59
+ url.path = join_url_paths(url.path, path)
60
+ end
61
+ end
62
+ end
63
+ url.complete
64
+ else
65
+ nil
66
+ end
67
+ end
68
+
69
+ def complete
70
+ self.protocol ||= "http"
71
+ self.port ||= self.protocol == "http" ? 80 : 443
72
+ self.path = "/" if self.path.empty?
73
+ self
74
+ end
75
+
76
+ def self.unescape(s)
77
+ s.gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
78
+ [$1.delete('%')].pack('H*')
79
+ end
80
+ end
81
+
82
+ def self.escape(s)
83
+ s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) do
84
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
85
+ end
86
+ end
87
+
88
+ def self.parse_query(qs, d = '&;')
89
+ params = {}
90
+ (qs || '').split(/[&;] */n).inject(params) do |h, p|
91
+ k, v = unescape(p).split('=', 2)
92
+ if cur = params[k]
93
+ if Array === cur
94
+ params[k] << v
95
+ else
96
+ params[k] = [cur, v]
97
+ end
98
+ else
99
+ params[k] = v
100
+ end
101
+ end
102
+ params
103
+ end
104
+
105
+ def self.construct_query(hash)
106
+ hash.map { |k, v| "%s=%s" % [k, escape(v)] }.join('&')
107
+ end
108
+
109
+ private
110
+
111
+ def join_url_paths(url, base)
112
+ if url[0] == ?/
113
+ url
114
+ else
115
+ base[0..base.rindex("/")] + url
116
+ end
117
+ end
118
+
119
+ end