activesp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ ACTIVESP_VERSION = File.readlines("VERSION")[0][/[\d.]*/]
5
+
6
+ desc "Build the gem"
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = "activesp"
9
+ s.version = ACTIVESP_VERSION
10
+ s.author = "Peter Vanbroekhoven"
11
+ s.email = "peter@xaop.com"
12
+ #s.homepage = "http://www.xaop.com/pages/dctmruby"
13
+ s.summary = "Interface to SharePoint"
14
+ s.description = s.summary
15
+ #s.rubyforge_project = s.name
16
+ s.files += %w(VERSION Rakefile)
17
+ s.files += Dir['lib/**/*.rb']
18
+ # s.bindir = "bin"
19
+ # s.executables.push(*(Dir['bin/*.rb'] - ["bin/encrypt-dmcl.rb"]).map { |f| File.basename(f) })
20
+ s.add_dependency('savon-xaop')
21
+ s.add_dependency('nokogiri')
22
+ # s.rdoc_options << '--exclude' << 'ext' << '--main' << 'README'
23
+ # s.extra_rdoc_files = ["README", "docs/README.html"]
24
+ s.has_rdoc = false
25
+ s.require_paths << 'lib'
26
+ # s.autorequire = 'mysql'
27
+ s.required_ruby_version = '>= 1.8.1'
28
+ s.platform = Gem::Platform::RUBY
29
+ end
30
+
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.need_zip = true
33
+ pkg.need_tar = true
34
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/activesp.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'nokogiri'
2
+ require 'time'
3
+
4
+ module ActiveSP
5
+ end
6
+
7
+ require 'activesp/util'
8
+ require 'activesp/caching'
9
+ require 'activesp/persistent_caching'
10
+
11
+ require 'activesp/base'
12
+
13
+ require 'activesp/connection'
14
+ require 'activesp/root'
15
+ require 'activesp/site'
16
+ require 'activesp/list'
17
+ require 'activesp/item'
18
+ require 'activesp/folder'
19
+ require 'activesp/url'
20
+ require 'activesp/content_type'
21
+ require 'activesp/field'
22
+ require 'activesp/ghost_field'
23
+ require 'activesp/user'
24
+ require 'activesp/group'
25
+ require 'activesp/role'
26
+ require 'activesp/permission_set'
@@ -0,0 +1,53 @@
1
+ module ActiveSP
2
+
3
+ class Base
4
+
5
+ extend Caching
6
+
7
+ def attributes
8
+ current_attributes
9
+ end
10
+ cache :attributes, :dup => true
11
+
12
+ def attribute_types
13
+ internal_attribute_types
14
+ end
15
+ cache :attribute_types, :dup => true
16
+
17
+ def has_attribute?(name)
18
+ attributes.has_key?(name)
19
+ end
20
+
21
+ def has_writable_attribute?(name)
22
+ has_attribute?(name) && !attribute_types[name].ReadOnly
23
+ end
24
+
25
+ def attribute(name)
26
+ attributes[name]
27
+ end
28
+
29
+ def set_attribute(name, value)
30
+ current_attributes[name] = value
31
+ end
32
+
33
+ def method_missing(m, *a, &b)
34
+ ms = m.to_s
35
+ if a.length == 0 && has_attribute?(ms)
36
+ attribute(ms)
37
+ elsif a.length == 1 && ms[-1] == ?= && has_writable_attribute?(ms[0..-2])
38
+ set_attribute(ms[0..-2], *a)
39
+ else
40
+ super
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def current_attributes
47
+ original_attributes
48
+ end
49
+ cache :current_attributes
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveSP
2
+
3
+ module Caching
4
+
5
+ def cache(name, options = {})
6
+ duplicate = options.delete(:dup)
7
+ options.empty? or raise ArgumentError, "unsupported options #{options.keys.map { |k| k.inspect }.join(", ")}"
8
+ (@cached_methods ||= []) << name
9
+ alias_method("#{name}__uncached", name)
10
+ access = private_instance_methods.include?(name) ? "private" : protected_instance_methods.include?(name) ? "protected" : "public"
11
+ private("#{name}__uncached")
12
+ module_eval <<-RUBY
13
+ def #{name}(*a, &b)
14
+ if defined? @#{name}
15
+ @#{name}
16
+ else
17
+ @#{name} = #{name}__uncached(*a, &b)
18
+ end#{".dup" if duplicate}
19
+ end
20
+ #{access} :#{name}
21
+ undef reload if instance_methods.include?("reload")
22
+ def reload
23
+ #{@cached_methods.map { |m| "remove_instance_variable(:@#{m}) if defined?(@#{m})" }.join(';')}
24
+ super if defined? super
25
+ end
26
+ RUBY
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,89 @@
1
+ require 'savon'
2
+ require 'net/ntlm_http'
3
+
4
+ Savon::Request.logger.level = Logger::ERROR
5
+
6
+ Savon::Response.error_handler do |soap_fault|
7
+ soap_fault[:detail][:errorstring]
8
+ end
9
+
10
+ module ActiveSP
11
+
12
+ class Connection
13
+
14
+ include Util
15
+ include PersistentCachingConfig
16
+
17
+ # TODO: create profile
18
+ attr_reader :login, :password, :root_url, :trace
19
+
20
+ def initialize(options = {})
21
+ @root_url = options.delete(:root) or raise ArgumentError, "missing :root option"
22
+ @login = options.delete(:login)
23
+ @password = options.delete(:password)
24
+ @trace = options.delete(:trace)
25
+ options.empty? or raise ArgumentError, "unknown options #{options.keys.map { |k| k.inspect }.join(", ")}"
26
+ cache = nil
27
+ configure_persistent_cache { |c| cache = c }
28
+ end
29
+
30
+ def find_by_key(key)
31
+ type, trail = decode_key(key)
32
+ case type[0]
33
+ when ?S
34
+ ActiveSP::Site.new(self, trail[0] == "" ? @root_url : File.join(@root_url, trail[0]), trail[1].to_i)
35
+ when ?L
36
+ ActiveSP::List.new(find_by_key(trail[0]), trail[1])
37
+ when ?U
38
+ ActiveSP::User.new(root, trail[0])
39
+ when ?G
40
+ ActiveSP::Group.new(root, trail[0])
41
+ when ?R
42
+ ActiveSP::Role.new(root, trail[0])
43
+ when ?A
44
+ find_by_key(trail[0]).field(trail[1])
45
+ when ?P
46
+ ActiveSP::PermissionSet.new(find_by_key(trail[0]))
47
+ when ?F
48
+ parent = find_by_key(trail[0])
49
+ if ActiveSP::Folder === parent
50
+ ActiveSP::Folder.new(parent.list, trail[1], parent)
51
+ else
52
+ ActiveSP::Folder.new(parent, trail[1], nil)
53
+ end
54
+ when ?I
55
+ parent = find_by_key(trail[0])
56
+ if ActiveSP::Folder === parent
57
+ ActiveSP::Item.new(parent.list, trail[1], parent)
58
+ else
59
+ ActiveSP::Item.new(parent, trail[1], nil)
60
+ end
61
+ when ?T
62
+ parent = find_by_key(trail[0])
63
+ if ActiveSP::List === parent
64
+ ActiveSP::ContentType.new(parent.site, parent, trail[1])
65
+ else
66
+ ActiveSP::ContentType.new(parent, nil, trail[1])
67
+ end
68
+ else
69
+ raise "not yet #{key.inspect}"
70
+ end
71
+ end
72
+
73
+ def fetch(url)
74
+ # TODO: support HTTPS too
75
+ @open_params ||= begin
76
+ u = URL(@root_url)
77
+ [u.host, u.port]
78
+ end
79
+ Net::HTTP.start(*@open_params) do |http|
80
+ request = Net::HTTP::Get.new(URL(url).full_path.gsub(/ /, "%20"))
81
+ request.ntlm_auth(@login, @password)
82
+ response = http.request(request)
83
+ response
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,103 @@
1
+ module ActiveSP
2
+
3
+ class ContentType < Base
4
+
5
+ include InSite
6
+ extend Caching
7
+ extend PersistentCaching
8
+ include Util
9
+
10
+ attr_reader :id
11
+
12
+ persistent { |site, list, id, *a| [site.connection, [:content_type, id]] }
13
+ def initialize(site, list, id, name = nil, description = nil, version = nil, group = nil)
14
+ @site, @list, @id = site, list, id
15
+ @Name = name if name
16
+ @Description = description if description
17
+ @Version = version if version
18
+ @Group = group if group
19
+ end
20
+
21
+ def scope
22
+ @list || @site
23
+ end
24
+
25
+ def supertype
26
+ superkey = split_id[0..-2].join("")
27
+ (@list ? @list.content_type(superkey) : nil) || @site.content_type(superkey)
28
+ end
29
+ cache :supertype
30
+
31
+ def key
32
+ encode_key("T", [scope.key, @id])
33
+ end
34
+
35
+ def Name
36
+ data["Name"].to_s
37
+ end
38
+ cache :Name
39
+
40
+ def Description
41
+ data["Description"].to_s
42
+ end
43
+ cache :Description
44
+
45
+ def Version
46
+ data["Version"].to_s
47
+ end
48
+ cache :Version
49
+
50
+ def Group
51
+ data["Group"].to_s
52
+ end
53
+ cache :Group
54
+
55
+ def fields
56
+ data.xpath("//sp:Field", NS).map { |field| scope.field(field["ID"]) }.compact
57
+ end
58
+ cache :fields, :dup => true
59
+
60
+ def to_s
61
+ "#<ActiveSP::ContentType Name=#{self.Name}>"
62
+ end
63
+
64
+ alias inspect to_s
65
+
66
+ private
67
+
68
+ def data
69
+ if @list
70
+ call("Lists", "get_list_content_type", "listName" => @list.id, "contentTypeId" => @id).xpath("//sp:ContentType", NS).first
71
+ else
72
+ call("Webs", "get_content_type", "contentTypeId" => @id).xpath("//sp:ContentType", NS).first
73
+ end
74
+ end
75
+ cache :data
76
+
77
+ def original_attributes
78
+ type_cast_attributes(@site, nil, internal_attribute_types, clean_item_attributes(data.attributes))
79
+ end
80
+ cache :original_attributes
81
+
82
+ def internal_attribute_types
83
+ @@internal_attribute_types ||= {
84
+ "Description" => GhostField.new("ColName", "Text", false, true),
85
+ "FeatureId" => GhostField.new("ColName", "Text", false, true),
86
+ "Group" => GhostField.new("ColName", "Text", false, true),
87
+ "Hidden" => GhostField.new("Hidden", "Bool", false, true),
88
+ "ID" => GhostField.new("ColName", "Text", false, true),
89
+ "Name" => GhostField.new("ColName", "Text", false, true),
90
+ "ReadOnly" => GhostField.new("ReadOnly", "Bool", false, true),
91
+ "Sealed" => GhostField.new("Sealed", "Bool", false, true),
92
+ "V2ListTemplateName" => GhostField.new("V2ListTemplateName", "Text", false, true),
93
+ "Version" => GhostField.new("Version", "Integer", false, true)
94
+ }
95
+ end
96
+
97
+ def split_id
98
+ ["0x"] + @id[2..-1].scan(/[0-9A-F][1-9A-F]|[1-9A-F][0-9A-F]|00[0-9A-F]{32}/)
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,147 @@
1
+ module ActiveSP
2
+
3
+ class Field < Base
4
+
5
+ include InSite
6
+ extend Caching
7
+ include Util
8
+
9
+ attr_reader :scope, :ID, :Name, :internal_type, :parent
10
+
11
+ # There is no call to get to the field info directly, so these should always
12
+ # be accessed through the site or list they belong to. Hence, we do not use
13
+ # caching here as it is useless.
14
+ def initialize(scope, id, name, type, parent, attributes_before_type_cast)
15
+ @scope, @ID, @Name, @internal_type, @parent, @attributes_before_type_cast = scope, id, name, type, parent, attributes_before_type_cast
16
+ @site = Site === @scope ? @scope : @scope.site
17
+ end
18
+
19
+ def key
20
+ encode_key("A", [@scope.key, @ID])
21
+ end
22
+
23
+ def List
24
+ list_for_lookup
25
+ end
26
+
27
+ def Type
28
+ translate_internal_type(self)
29
+ end
30
+
31
+ # This is only defined when it is true; joy to the world!
32
+ def Mult
33
+ !!attributes["Mult"]
34
+ end
35
+
36
+ def ReadOnly
37
+ !!attributes["ReadOnly"]
38
+ end
39
+
40
+ def to_s
41
+ "#<ActiveSP::Field name=#{self.Name}>"
42
+ end
43
+
44
+ alias inspect to_s
45
+
46
+ private
47
+
48
+ def list_for_lookup
49
+ # I think List may be undefined for attributes defined at the site level, and that they need to be specified
50
+ # when used at the list level.
51
+ if %w[Lookup LookupMulti].include?(@internal_type) and list = @attributes_before_type_cast["List"]
52
+ if list[0] == ?{ && list[-1] == ?} # We have a GUID of a list
53
+ ActiveSP::List.new(@site, list)
54
+ elsif list == "Self"
55
+ List === @scope ? @scope : nil
56
+ end
57
+ end
58
+ end
59
+
60
+ def original_attributes
61
+ @original_attributes ||= type_cast_attributes(@site, nil, internal_attribute_types, @attributes_before_type_cast.merge("List" => list_for_lookup, "Type" => self.Type, "internal_type" => internal_type))
62
+ end
63
+
64
+ def internal_attribute_types
65
+ @@internal_attribute_types ||= {
66
+ "AllowDeletion" => GhostField.new("AllowDeletion", "Bool", false, true),
67
+ "AppendOnly" => GhostField.new("AppendOnly", "Bool", false, true),
68
+ "AuthoringInfo" => GhostField.new("AuthoringInfo", "Text", false, true),
69
+ "CalType" => GhostField.new("CalType", "Integer", false, true),
70
+ "CanToggleHidden" => GhostField.new("CanToggleHidden", "Bool", false, true),
71
+ "ClassInfo" => GhostField.new("ClassInfo", "Text", false, true),
72
+ "ColName" => GhostField.new("ColName", "Text", false, true),
73
+ "Description" => GhostField.new("Description", "Text", false, true),
74
+ "Dir" => GhostField.new("Dir", "Text", false, true),
75
+ "DisplaceOnUpgrade" => GhostField.new("DisplaceOnUpgrade", "Bool", false, true),
76
+ "DisplayImage" => GhostField.new("DisplayImage", "Text", false, true),
77
+ "DisplayName" => GhostField.new("DisplayName", "Text", false, true),
78
+ "DisplayNameSrcField" => GhostField.new("DisplayNameSrcField", "Text", false, true),
79
+ "DisplaySize" => GhostField.new("DisplaySize", "Integer", false, true),
80
+ "ExceptionImage" => GhostField.new("ExceptionImage", "Text", false, true),
81
+ "FieldRef" => GhostField.new("FieldRef", "Text", false, true),
82
+ "FillInChoice" => GhostField.new("FillInChoice", "Bool", false, true),
83
+ "Filterable" => GhostField.new("Filterable", "Bool", false, true),
84
+ "Format" => GhostField.new("Format", "Bool", false, true),
85
+ "FromBaseType" => GhostField.new("FromBaseType", "Bool", false, true),
86
+ "Group" => GhostField.new("Group", "Text", false, true),
87
+ "HeaderImage" => GhostField.new("HeaderImage", "Text", false, true),
88
+ "Height" => GhostField.new("Height", "Integer", false, true),
89
+ "Hidden" => GhostField.new("Hidden", "Bool", false, true),
90
+ "ID" => GhostField.new("ID", "Text", false, true),
91
+ "IMEMode" => GhostField.new("IMEMode", "Text", false, true),
92
+ "internal_type" => GhostField.new("internal_type", "Text", false, true),
93
+ "IsolateStyles" => GhostField.new("IsolateStyles", "Bool", false, true),
94
+ "JoinColName" => GhostField.new("JoinColName", "Text", false, true),
95
+ "JoinRowOrdinal" => GhostField.new("JoinRowOrdinal", "Integer", false, true),
96
+ "JoinType" => GhostField.new("JoinType", "Text", false, true),
97
+ "List" => GhostField.new("List", "ListReference", false, true),
98
+ "Max" => GhostField.new("Max", "Integer", false, true),
99
+ "MaxLength" => GhostField.new("MaxLength", "Integer", false, true),
100
+ "Min" => GhostField.new("Min", "Integer", false, true),
101
+ "Mult" => GhostField.new("Mult", "Bool", false, true),
102
+ "Name" => GhostField.new("Name", "Text", false, true),
103
+ "Node" => GhostField.new("Node", "Text", false, true),
104
+ "NoEditFormBreak" => GhostField.new("NoEditFormBreak", "Bool", false, true),
105
+ "NumLines" => GhostField.new("NumLines", "Text", false, true),
106
+ "Percentage" => GhostField.new("Percentage", "Bool", false, true),
107
+ "PIAttribute" => GhostField.new("PIAttribute", "Text", false, true),
108
+ "PITarget" => GhostField.new("PITarget", "Text", false, true),
109
+ "PrependId" => GhostField.new("PrependId", "Bool", false, true),
110
+ "PrimaryKey" => GhostField.new("PrimaryKey", "Bool", false, true),
111
+ "PrimaryPIAttribute" => GhostField.new("PrimaryPIAttribute", "Text", false, true),
112
+ "PrimaryPITarget" => GhostField.new("PrimaryPITarget", "Text", false, true),
113
+ "ReadOnly" => GhostField.new("ReadOnly", "Bool", false, true),
114
+ "ReadOnlyEnforced" => GhostField.new("ReadOnlyEnforced", "Bool", false, true),
115
+ "RenderXMLUsingPattern" => GhostField.new("ReadOnly", "Bool", false, true),
116
+ "Required" => GhostField.new("Required", "Bool", false, true),
117
+ "RestrictedMode" => GhostField.new("RestrictedMode", "Bool", false, true),
118
+ "RichText" => GhostField.new("RichText", "Bool", false, true),
119
+ "RichTextMode" => GhostField.new("RichTextMode", "Text", false, true),
120
+ "RowOrdinal" => GhostField.new("RowOrdinal", "Integer", false, true),
121
+ "Sealed" => GhostField.new("Sealed", "Bool", false, true),
122
+ "ShowInDisplayForm" => GhostField.new("ShowInDisplayForm", "Bool", false, true),
123
+ "ShowInListSettings" => GhostField.new("ShowInListSettings", "Bool", false, true),
124
+ "ShowInFileDlg" => GhostField.new("ShowInFileDlg", "Bool", false, true),
125
+ "ShowInVersionHistory" => GhostField.new("ShowInVersionHistory", "Bool", false, true),
126
+ "Sortable" => GhostField.new("Sortable", "Bool", false, true),
127
+ "SourceID" => GhostField.new("SourceID", "Text", false, true),
128
+ "StaticName" => GhostField.new("StaticName", "Text", false, true),
129
+ "StorageTZ" => GhostField.new("StorageTZ", "Bool", false, true),
130
+ "TextOnly" => GhostField.new("TextOnly", "Bool", false, true),
131
+ "Title" => GhostField.new("Title", "Text", false, true),
132
+ "Type" => GhostField.new("Type", "Text", false, true),
133
+ "SetAs" => GhostField.new("SetAs", "Text", false, true),
134
+ "ShowField" => GhostField.new("ShowField", "Text", false, true),
135
+ "ShowInEditForm" => GhostField.new("ShowInEditForm", "Bool", false, true),
136
+ "ShowInNewForm" => GhostField.new("ShowInNewForm", "Bool", false, true),
137
+ "UnlimitedLengthInDocumentLibrary" => GhostField.new("UnlimitedLengthInDocumentLibrary", "Bool", false, true),
138
+ "Version" => GhostField.new("Version", "Integer", false, true),
139
+ "Width" => GhostField.new("Width", "Integer", false, true),
140
+ "WikiLinking" => GhostField.new("WikiLinking", "Bool", false, true),
141
+ "XName" => GhostField.new("XName", "Text", false, true)
142
+ }
143
+ end
144
+
145
+ end
146
+
147
+ end