activesp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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