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.
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