cmis_active 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +9 -0
- data/LICENSE +26 -0
- data/README.md +55 -0
- data/Rakefile +34 -0
- data/TODO +7 -0
- data/VERSION.yml +5 -0
- data/active_cmis.gemspec +79 -0
- data/lib/active_cmis.rb +30 -0
- data/lib/active_cmis/acl.rb +181 -0
- data/lib/active_cmis/acl_entry.rb +26 -0
- data/lib/active_cmis/active_cmis.rb +87 -0
- data/lib/active_cmis/atomic_types.rb +245 -0
- data/lib/active_cmis/attribute_prefix.rb +35 -0
- data/lib/active_cmis/collection.rb +206 -0
- data/lib/active_cmis/document.rb +356 -0
- data/lib/active_cmis/exceptions.rb +82 -0
- data/lib/active_cmis/folder.rb +36 -0
- data/lib/active_cmis/internal/caching.rb +86 -0
- data/lib/active_cmis/internal/connection.rb +241 -0
- data/lib/active_cmis/internal/utils.rb +82 -0
- data/lib/active_cmis/ns.rb +18 -0
- data/lib/active_cmis/object.rb +563 -0
- data/lib/active_cmis/policy.rb +13 -0
- data/lib/active_cmis/property_definition.rb +179 -0
- data/lib/active_cmis/query_result.rb +40 -0
- data/lib/active_cmis/rel.rb +17 -0
- data/lib/active_cmis/relationship.rb +49 -0
- data/lib/active_cmis/rendition.rb +86 -0
- data/lib/active_cmis/repository.rb +327 -0
- data/lib/active_cmis/server.rb +121 -0
- data/lib/active_cmis/type.rb +200 -0
- data/lib/active_cmis/version.rb +10 -0
- metadata +132 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
module ActiveCMIS
|
2
|
+
# This class is used to manage different CMIS servers.
|
3
|
+
class Server
|
4
|
+
include Internal::Caching
|
5
|
+
# @return [URI] The location of the server
|
6
|
+
attr_reader :endpoint
|
7
|
+
# @return [Logger] A default logger for derived repositories
|
8
|
+
attr_reader :logger
|
9
|
+
# @return [Hash] Options to be used by the HTTP objects
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
# @return [Server] Cached by endpoint and logger
|
13
|
+
def self.new(endpoint, logger = nil, authentication_info = nil, options = nil)
|
14
|
+
endpoint = case endpoint
|
15
|
+
when URI; endpoint
|
16
|
+
else URI(endpoint.to_s)
|
17
|
+
end
|
18
|
+
server = super(endpoint, logger || ActiveCMIS.default_logger, authentication_info, options)
|
19
|
+
endpoints[endpoint.to_s][authentication_info][logger] ||= server
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [{(URI, Logger) => Server}] The cache of known Servers
|
23
|
+
def self.endpoints
|
24
|
+
@endpoints ||= Hash.new {|h, k| h[k] = Hash.new {|h2, k2| h2[k2] = {}}}
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def inspect
|
29
|
+
"Server #{@endpoint}"
|
30
|
+
end
|
31
|
+
# @return [String]
|
32
|
+
def to_s
|
33
|
+
"Server " + @endpoint.to_s + " : " + repositories.map {|h| h[:name] + "(#{h[:id]})"}.join(", ")
|
34
|
+
end
|
35
|
+
|
36
|
+
# A connection needs the URL to a CMIS REST endpoint.
|
37
|
+
#
|
38
|
+
# It's used to manage all communication with the CMIS Server
|
39
|
+
# @param endpoint [URI] The URL where the CMIS AtomPub REST endpoint can be found
|
40
|
+
# @param logger [Logger] The logger that will be used to log debug/info messages
|
41
|
+
# @param authentication_info [Array?] Optional authentication info to be used when retrieving the data from the AtomPub endpoint
|
42
|
+
def initialize(endpoint, logger, authentication_info = nil, options = nil)
|
43
|
+
@endpoint = endpoint
|
44
|
+
@logger = logger
|
45
|
+
@options = options || {}
|
46
|
+
|
47
|
+
method, *params = authentication_info
|
48
|
+
@authentication_info = authentication_info
|
49
|
+
if method
|
50
|
+
conn.authenticate(method, *params)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# This returns a new Server object using the specified authentication info
|
55
|
+
#
|
56
|
+
# @param (see ActiveCMIS::Internal::Connection#authenticate)
|
57
|
+
# @see Internal::Connection#authenticate
|
58
|
+
# @return [void]
|
59
|
+
def authenticate(*authentication_info)
|
60
|
+
self.class.new(endpoint, logger, authentication_info)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the _Repository identified by the ID
|
64
|
+
# Authentication will take place with the optional second paramater, if it
|
65
|
+
# is absent and there is server authentcation then the server authentication
|
66
|
+
# will be used
|
67
|
+
#
|
68
|
+
# Cached by the repository_id and, authentcation info. The cache can be reset
|
69
|
+
# by calling clear_repositories.
|
70
|
+
#
|
71
|
+
# @param [String] repository_id
|
72
|
+
# @param [Array] authentication_info
|
73
|
+
# @return [Repository]
|
74
|
+
def repository(repository_id, authentication_info = @authentication_info)
|
75
|
+
key = [repository_id, authentication_info]
|
76
|
+
cached_repositories[key] ||= uncached_repository(*key)
|
77
|
+
end
|
78
|
+
|
79
|
+
def uncached_repository(repository_id, authentication_info)
|
80
|
+
path = "/app:service/app:workspace[cra:repositoryInfo/c:repositoryId[child::text() = '#{repository_id}']]"
|
81
|
+
repository_data = repository_info.xpath(path, NS::COMBINED)
|
82
|
+
if repository_data.empty?
|
83
|
+
raise Error::ObjectNotFound.new("The repository #{repository_id} doesn't exist")
|
84
|
+
else
|
85
|
+
Repository.new(self, conn.dup, logger.dup, repository_data, authentication_info)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
private :uncached_repository
|
89
|
+
|
90
|
+
# Reset cache of Repository objects
|
91
|
+
#
|
92
|
+
# @return [void]
|
93
|
+
def clear_repositories
|
94
|
+
@cached_repositories = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Lists all the available repositories
|
99
|
+
#
|
100
|
+
# @return [<{:id, :name} => String>]
|
101
|
+
def repositories
|
102
|
+
repositories = repository_info.xpath("/app:service/app:workspace/cra:repositoryInfo", NS::COMBINED)
|
103
|
+
repositories.map {|ri| next {:id => ri.xpath("ns:repositoryId", "ns" => NS::CMIS_CORE).text,
|
104
|
+
:name => ri.xpath("ns:repositoryName", "ns" => NS::CMIS_CORE).text }}
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def repository_info
|
109
|
+
@repository_info ||= conn.get_xml(endpoint)
|
110
|
+
end
|
111
|
+
cache :repository_info
|
112
|
+
|
113
|
+
def cached_repositories
|
114
|
+
@cached_repositories ||= {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def conn
|
118
|
+
@conn ||= Internal::Connection.new(logger.dup, @options)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
module ActiveCMIS
|
2
|
+
module Type
|
3
|
+
# @private
|
4
|
+
def self.create(param_conn, repository, klass_data)
|
5
|
+
parent_id = klass_data.xpath("cra:type/c:parentId/text()", NS::COMBINED)
|
6
|
+
superclass = if parent = parent_id.first
|
7
|
+
repository.type_by_id(parent.to_s)
|
8
|
+
else
|
9
|
+
base_type_id = klass_data.xpath("cra:type/c:baseId", NS::COMBINED).text
|
10
|
+
case base_type_id
|
11
|
+
when "cmis:document"
|
12
|
+
Document
|
13
|
+
when "cmis:folder"
|
14
|
+
Folder
|
15
|
+
when "cmis:relationship"
|
16
|
+
Relationship
|
17
|
+
when "cmis:policy"
|
18
|
+
Policy
|
19
|
+
else
|
20
|
+
raise ActiveCMIS::Error.new("Type #{klass_data.xpath("cra:type/c:id", NS::COMBINED).text} without supertype, and not actually a valid base_type (#{base_type_id.inspect})\n" + klass_data.to_s)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
klass = ::Class.new(superclass) do
|
25
|
+
extend ActiveCMIS::Type::ClassMethods
|
26
|
+
include ActiveCMIS::Type::InstanceMethods
|
27
|
+
|
28
|
+
@repository = repository
|
29
|
+
@conn = param_conn
|
30
|
+
@data = klass_data
|
31
|
+
@self_link = klass_data.xpath("at:link[@rel = 'self']/@href", NS::COMBINED).text
|
32
|
+
|
33
|
+
end
|
34
|
+
klass
|
35
|
+
end
|
36
|
+
|
37
|
+
# Instance Methods on CMIS Types
|
38
|
+
module InstanceMethods
|
39
|
+
# Creates a new CMIS Object
|
40
|
+
# @overload initialize(attributes)
|
41
|
+
# @param [Hash] attributes Attributes for a newly created object of the current type
|
42
|
+
# @return [Object]
|
43
|
+
def initialize(rep = nil, data = nil, parameters = {})
|
44
|
+
case rep
|
45
|
+
when Hash
|
46
|
+
attributes = rep
|
47
|
+
rep = nil
|
48
|
+
data = nil # Force data and parameters to be empty in that case (NOTE: assert may be better?)
|
49
|
+
parameters = {}
|
50
|
+
when NilClass
|
51
|
+
else
|
52
|
+
if rep != self.class.repository
|
53
|
+
raise "Trying to create element in different repository than type"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
super(rep || self.class.repository, data, parameters)
|
57
|
+
unless attributes.nil?
|
58
|
+
update(attributes)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Class Methods on CMIS Types
|
64
|
+
#
|
65
|
+
# The following info is also available:
|
66
|
+
# id, local_name, local_namespace, display_name, query_name, description, base_id, parent_id, creatable, fileable,
|
67
|
+
# queryable, fulltext_indexed, controllable_policy, controllable_acl, versionable, content_stream_allowed
|
68
|
+
module ClassMethods
|
69
|
+
include Internal::Caching
|
70
|
+
|
71
|
+
cached_reader :id, :local_name, :local_namespace, :display_name, :query_name, :description, :base_id,
|
72
|
+
:parent_id, :creatable, :fileable, :queryable, :fulltext_indexed, :controllable_policy, :controllable_acl,
|
73
|
+
:versionable, :content_stream_allowed
|
74
|
+
|
75
|
+
# @param [Boolean] inherited (false) *Ignored*
|
76
|
+
# @return [{String => PropertyDefinition}] A list of propery definitions for all properties on the type
|
77
|
+
def attributes(inherited = false)
|
78
|
+
load_from_data unless defined?(@attributes)
|
79
|
+
if inherited && superclass.respond_to?(:attributes)
|
80
|
+
super.merge(@attributes)
|
81
|
+
else
|
82
|
+
@attributes
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return [{String => PropertyDefinition}] A list of all required definitions (includes inherited attributes)
|
87
|
+
def required_attributes
|
88
|
+
attributes(true).reject {|key, value| !value.required}
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [<String>] A list of prefixes that can be used for adressing attributes (like cmis:)
|
92
|
+
def attribute_prefixes
|
93
|
+
@prefixes ||= attributes(true).keys.map do |key|
|
94
|
+
if key =~ /^([^:]+):/
|
95
|
+
$1
|
96
|
+
end
|
97
|
+
end.compact.uniq
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [void]
|
101
|
+
def reload
|
102
|
+
remove_instance_variable(:@attributes) if defined? @attributes
|
103
|
+
[:attributes] + __reload # Could also do if defined?(super) then super else __reload end, but we don't do anything but remove_instance_variable @attributes in superclasses anyway
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [String]
|
107
|
+
def inspect
|
108
|
+
"#<#{repository.key}::Class #{key}>"
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return [String] The CMIS ID of the type
|
112
|
+
def key
|
113
|
+
@key ||= data.xpath("cra:type/c:id", NS::COMBINED).text
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [Collection<Class>] All direct subtypes (1 level deep)
|
117
|
+
def subtypes
|
118
|
+
types_feed = Internal::Utils.extract_links(data, 'down', 'application/atom+xml', 'type' => 'feed')
|
119
|
+
raise "No subtypes link for #{id}" if types_feed.empty?
|
120
|
+
|
121
|
+
Collection.new(repository, types_feed.first) do |entry|
|
122
|
+
id = entry.xpath("cra:type/c:id", NS::COMBINED).text
|
123
|
+
repository.type_by_id id
|
124
|
+
end
|
125
|
+
end
|
126
|
+
cache :subtypes
|
127
|
+
|
128
|
+
# @return [Array<Class>] All subtypes
|
129
|
+
def all_subtypes
|
130
|
+
subtypes.map do |t|
|
131
|
+
t.all_subtypes
|
132
|
+
end.flatten << self
|
133
|
+
end
|
134
|
+
cache :all_subtypes
|
135
|
+
|
136
|
+
private
|
137
|
+
# @private
|
138
|
+
attr_reader :self_link, :conn
|
139
|
+
def data
|
140
|
+
@data ||= conn.get_atom_entry(self_link)
|
141
|
+
end
|
142
|
+
cache :data
|
143
|
+
|
144
|
+
def load_from_data
|
145
|
+
@attributes = {}
|
146
|
+
data.xpath('cra:type', NS::COMBINED).children.each do |node|
|
147
|
+
next unless node.namespace
|
148
|
+
next unless node.namespace.href == NS::CMIS_CORE
|
149
|
+
|
150
|
+
case node.node_name
|
151
|
+
when "id"
|
152
|
+
@id = node.text
|
153
|
+
when "localName"
|
154
|
+
@local_name = node.text
|
155
|
+
when "localNamespace"
|
156
|
+
@local_namespace = node.text
|
157
|
+
when "displayName"
|
158
|
+
@display_name = node.text
|
159
|
+
when "queryName"
|
160
|
+
@query_name = node.text
|
161
|
+
when "description"
|
162
|
+
@description = node.text
|
163
|
+
when "baseId"
|
164
|
+
@base_id = node.text
|
165
|
+
when "parentId"
|
166
|
+
@parent_id = node.text
|
167
|
+
when "creatable"
|
168
|
+
@creatable = AtomicType::Boolean.xml_to_bool(node.text)
|
169
|
+
when "fileable"
|
170
|
+
@fileable = AtomicType::Boolean.xml_to_bool(node.text)
|
171
|
+
when "queryable"
|
172
|
+
@queryable = AtomicType::Boolean.xml_to_bool(node.text)
|
173
|
+
when "fulltextIndexed"
|
174
|
+
@fulltext_indexed = AtomicType::Boolean.xml_to_bool(node.text)
|
175
|
+
when "controllablePolicy"
|
176
|
+
@controllable_policy = AtomicType::Boolean.xml_to_bool(node.text)
|
177
|
+
when "controllableACL"
|
178
|
+
@controllable_acl = AtomicType::Boolean.xml_to_bool(node.text)
|
179
|
+
when "versionable"
|
180
|
+
@versionable = AtomicType::Boolean.xml_to_bool(node.text)
|
181
|
+
when "contentStreamAllowed"
|
182
|
+
# FIXME? this is an enumeration, should perhaps wrap
|
183
|
+
@content_stream_allowed = node.text
|
184
|
+
when /^property(?:DateTime|String|Html|Id|Boolean|Integer|Decimal)Definition$/
|
185
|
+
attr = PropertyDefinition.new(self, node.children)
|
186
|
+
@attributes[attr.id] = attr
|
187
|
+
end
|
188
|
+
end
|
189
|
+
if %w(cmis:folder cmis:document).include? @base_id and not @fileable
|
190
|
+
repository.logger.warn "The server behaved strange: #{@id}, with basetype #{@base_id} MUST be fileable"
|
191
|
+
@fileable = true
|
192
|
+
elsif @base_id == "cmis:relationship" and @fileable
|
193
|
+
repository.logger.warn "The server behaved strange: #{@id}, with basetype #{@base_id} MUST NOT be fileable"
|
194
|
+
@fileable = false
|
195
|
+
end
|
196
|
+
@attributes.freeze
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cmis_active
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joeri Samson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ntlm-http
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.1
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0.1'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.1.1
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.1'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: require_relative
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.0.2
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1.0'
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.0.2
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '1.0'
|
67
|
+
description: A CMIS library implementing both reading and updating capabilities through
|
68
|
+
the AtomPub/REST binding to CMIS.
|
69
|
+
email: joeri@xaop.com
|
70
|
+
executables: []
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files:
|
73
|
+
- LICENSE
|
74
|
+
- README.md
|
75
|
+
- TODO
|
76
|
+
files:
|
77
|
+
- AUTHORS
|
78
|
+
- LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- TODO
|
82
|
+
- VERSION.yml
|
83
|
+
- active_cmis.gemspec
|
84
|
+
- lib/active_cmis.rb
|
85
|
+
- lib/active_cmis/acl.rb
|
86
|
+
- lib/active_cmis/acl_entry.rb
|
87
|
+
- lib/active_cmis/active_cmis.rb
|
88
|
+
- lib/active_cmis/atomic_types.rb
|
89
|
+
- lib/active_cmis/attribute_prefix.rb
|
90
|
+
- lib/active_cmis/collection.rb
|
91
|
+
- lib/active_cmis/document.rb
|
92
|
+
- lib/active_cmis/exceptions.rb
|
93
|
+
- lib/active_cmis/folder.rb
|
94
|
+
- lib/active_cmis/internal/caching.rb
|
95
|
+
- lib/active_cmis/internal/connection.rb
|
96
|
+
- lib/active_cmis/internal/utils.rb
|
97
|
+
- lib/active_cmis/ns.rb
|
98
|
+
- lib/active_cmis/object.rb
|
99
|
+
- lib/active_cmis/policy.rb
|
100
|
+
- lib/active_cmis/property_definition.rb
|
101
|
+
- lib/active_cmis/query_result.rb
|
102
|
+
- lib/active_cmis/rel.rb
|
103
|
+
- lib/active_cmis/relationship.rb
|
104
|
+
- lib/active_cmis/rendition.rb
|
105
|
+
- lib/active_cmis/repository.rb
|
106
|
+
- lib/active_cmis/server.rb
|
107
|
+
- lib/active_cmis/type.rb
|
108
|
+
- lib/active_cmis/version.rb
|
109
|
+
homepage: http://xaop.com/labs/activecmis/
|
110
|
+
licenses: []
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 1.8.6
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 2.7.6
|
129
|
+
signing_key:
|
130
|
+
specification_version: 4
|
131
|
+
summary: A library to interact with CMIS repositories through the AtomPub/REST binding
|
132
|
+
test_files: []
|