cmis-ruby 0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +6 -0
- data/LICENSE +176 -0
- data/Rakefile +7 -0
- data/cmis-ruby.gemspec +27 -0
- data/lib/cmis-ruby.rb +20 -0
- data/lib/cmis/children.rb +95 -0
- data/lib/cmis/connection.rb +147 -0
- data/lib/cmis/document.rb +62 -0
- data/lib/cmis/folder.rb +76 -0
- data/lib/cmis/helpers.rb +58 -0
- data/lib/cmis/item.rb +11 -0
- data/lib/cmis/object.rb +120 -0
- data/lib/cmis/object_factory.rb +27 -0
- data/lib/cmis/policy.rb +26 -0
- data/lib/cmis/property_definition.rb +32 -0
- data/lib/cmis/query.rb +100 -0
- data/lib/cmis/query_result.rb +15 -0
- data/lib/cmis/relationship.rb +18 -0
- data/lib/cmis/repository.rb +112 -0
- data/lib/cmis/server.rb +43 -0
- data/lib/cmis/type.rb +106 -0
- data/lib/cmis/version.rb +3 -0
- data/readme.md +23 -0
- data/spec/document_spec.rb +45 -0
- data/spec/folder_spec.rb +75 -0
- data/spec/helper.rb +27 -0
- data/spec/object_spec.rb +100 -0
- data/spec/relationship_spec.rb +19 -0
- data/spec/repository_spec.rb +150 -0
- data/spec/server_spec.rb +22 -0
- data/spec/type_spec.rb +69 -0
- metadata +141 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module CMIS
|
2
|
+
class Document < Object
|
3
|
+
|
4
|
+
def initialize(raw, repository)
|
5
|
+
super
|
6
|
+
cmis_properties %w( cmis:isImmutable cmis:isLatestVersion
|
7
|
+
cmis:isMajorVersion cmis:isLatestMajorVersion
|
8
|
+
cmis:isPrivateWorkingCopy cmis:versionLabel
|
9
|
+
cmis:versionSeriesId cmis:isVersionSeriesCheckedOut
|
10
|
+
cmis:versionSeriesCheckedOutBy
|
11
|
+
cmis:versionSeriesCheckedOutId cmis:checkinComment
|
12
|
+
cmis:contentStreamLength cmis:contentStreamMimeType
|
13
|
+
cmis:contentStreamFileName cmis:contentStreamId )
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_in_folder(folder)
|
17
|
+
r = connection.execute!({ cmisaction: 'createDocument',
|
18
|
+
repositoryId: repository.id,
|
19
|
+
properties: properties,
|
20
|
+
objectId: folder.cmis_object_id,
|
21
|
+
folderId: folder.cmis_object_id,
|
22
|
+
content: @local_content })
|
23
|
+
|
24
|
+
ObjectFactory.create(r, repository)
|
25
|
+
end
|
26
|
+
|
27
|
+
def copy_in_folder(folder)
|
28
|
+
id = connection.execute!({ cmisaction: 'createDocument',
|
29
|
+
repositoryId: repository.id,
|
30
|
+
sourceId: cmis_object_id,
|
31
|
+
objectId: folder.cmis_object_id })
|
32
|
+
|
33
|
+
repository.object(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def content
|
37
|
+
connection.execute!({ cmisselector: 'content',
|
38
|
+
repositoryId: repository.id,
|
39
|
+
objectId: cmis_object_id })
|
40
|
+
|
41
|
+
rescue CMIS::CMISRequestError
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_content(stream, mime_type, filename)
|
46
|
+
content = { stream: stream,
|
47
|
+
mime_type: mime_type,
|
48
|
+
filename: filename }
|
49
|
+
|
50
|
+
if detached?
|
51
|
+
@local_content = content
|
52
|
+
else
|
53
|
+
update_change_token connection.execute!({ cmisaction: 'setContent',
|
54
|
+
repositoryId: repository.id,
|
55
|
+
objectId: cmis_object_id,
|
56
|
+
content: content,
|
57
|
+
changeToken: change_token })
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
data/lib/cmis/folder.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module CMIS
|
2
|
+
class Folder < Object
|
3
|
+
|
4
|
+
def initialize(raw, repository)
|
5
|
+
super
|
6
|
+
cmis_properties %w( cmis:parentId cmis:path
|
7
|
+
cmis:allowedChildObjectTypeIds )
|
8
|
+
end
|
9
|
+
|
10
|
+
def parent
|
11
|
+
repository.object(parent_id) if parent_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def allowed_child_object_types
|
15
|
+
return nil unless allowed_child_object_type_ids
|
16
|
+
allowed_child_object_type_ids.map { |type_id| repository.type(type_id) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def children(options = {})
|
20
|
+
Children.new(self, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def create(object)
|
24
|
+
case object
|
25
|
+
when Relationship
|
26
|
+
raise "'cmis:relationship' is not fileable. Use Repository#create_relationship"
|
27
|
+
|
28
|
+
when Document
|
29
|
+
return object.create_in_folder(self)
|
30
|
+
|
31
|
+
when Folder
|
32
|
+
o = connection.execute!({ cmisaction: 'createFolder',
|
33
|
+
repositoryId: repository.id,
|
34
|
+
properties: object.properties,
|
35
|
+
objectId: cmis_object_id })
|
36
|
+
|
37
|
+
when Policy
|
38
|
+
o = connection.execute!({ cmisaction: 'createPolicy',
|
39
|
+
repositoryId: repository.id,
|
40
|
+
properties: object.properties,
|
41
|
+
objectId: cmis_object_id })
|
42
|
+
when Item
|
43
|
+
o = connection.execute!({ cmisaction: 'createItem',
|
44
|
+
repositoryId: repository.id,
|
45
|
+
properties: object.properties,
|
46
|
+
objectId: cmis_object_id })
|
47
|
+
|
48
|
+
else
|
49
|
+
raise "Unexpected base_type_id: #{object.base_type_id}"
|
50
|
+
end
|
51
|
+
|
52
|
+
ObjectFactory.create(o, repository)
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_tree
|
56
|
+
connection.execute!({ cmisaction: 'deleteTree',
|
57
|
+
repositoryId: repository.id,
|
58
|
+
objectId: cmis_object_id })
|
59
|
+
end
|
60
|
+
|
61
|
+
def add(object)
|
62
|
+
connection.execute!({ cmisaction: 'addObjectToFolder',
|
63
|
+
repositoryId: repository.id,
|
64
|
+
objectId: object.cmis_object_id,
|
65
|
+
folderId: cmis_object_id })
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove(object)
|
69
|
+
connection.execute!({ cmisaction: 'removeObjectFromFolder',
|
70
|
+
repositoryId: repository.id,
|
71
|
+
objectId: object.cmis_object_id,
|
72
|
+
folderId: cmis_object_id })
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/cmis/helpers.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module CMIS
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
def initialize_properties(raw)
|
7
|
+
@properties = get_properties_map(raw)
|
8
|
+
end
|
9
|
+
|
10
|
+
def cmis_properties(properties)
|
11
|
+
properties.each do |property_name|
|
12
|
+
method_name = method_name(property_name)
|
13
|
+
class_eval "def #{method_name};@properties['#{property_name}'];end"
|
14
|
+
class_eval "def #{method_name}=(value);@properties['#{property_name}']=value;end"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def update_change_token(r)
|
19
|
+
if r['properties']
|
20
|
+
@change_token = r['properties']['cmis:changeToken']['value']
|
21
|
+
elsif r['succinctProperties']
|
22
|
+
@change_token = r['succinctProperties']['cmis:changeToken']
|
23
|
+
else
|
24
|
+
raise "Unexpected hash: #{r}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def method_name(property_name)
|
31
|
+
if property_name == 'cmis:objectId'
|
32
|
+
'cmis_object_id'
|
33
|
+
else
|
34
|
+
property_name.gsub('cmis:', '').underscore
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_properties_map(raw)
|
39
|
+
raw = raw.with_indifferent_access
|
40
|
+
|
41
|
+
if raw['succinctProperties']
|
42
|
+
result = raw['succinctProperties']
|
43
|
+
elsif raw['properties']
|
44
|
+
result = raw['properties'].reduce({}) do |h, (k, v)|
|
45
|
+
val = v['value']
|
46
|
+
val = v['value'].first if v['value'].is_a?(Array) and v['cardinality'] == 'single'
|
47
|
+
val = Time.at(val / 1000) if val and v['type'] == 'datetime'
|
48
|
+
h.merge(k => val)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
result = {}
|
52
|
+
end
|
53
|
+
|
54
|
+
result.with_indifferent_access
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/cmis/item.rb
ADDED
data/lib/cmis/object.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
module CMIS
|
2
|
+
class Object
|
3
|
+
|
4
|
+
include Helpers
|
5
|
+
|
6
|
+
attr_reader :connection
|
7
|
+
attr_reader :repository
|
8
|
+
attr_accessor :properties
|
9
|
+
|
10
|
+
def initialize(raw, repository)
|
11
|
+
initialize_properties(raw)
|
12
|
+
cmis_properties %w( cmis:objectId cmis:baseTypeId cmis:objectTypeId
|
13
|
+
cmis:secondaryObjectTypeIds cmis:name cmis:description
|
14
|
+
cmis:createdBy cmis:creationDate cmis:lastModifiedBy
|
15
|
+
cmis:lastModificationDate cmis:changeToken )
|
16
|
+
|
17
|
+
@repository = repository
|
18
|
+
@connection = repository.connection
|
19
|
+
end
|
20
|
+
|
21
|
+
def object_type
|
22
|
+
repository.type(object_type_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete
|
26
|
+
connection.execute!({ cmisaction: 'delete',
|
27
|
+
repositoryId: repository.id,
|
28
|
+
objectId: cmis_object_id,
|
29
|
+
allVersions: true })
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_properties(properties)
|
33
|
+
update_change_token connection.execute!({ cmisaction: 'update',
|
34
|
+
repositoryId: repository.id,
|
35
|
+
objectId: cmis_object_id,
|
36
|
+
properties: properties,
|
37
|
+
changeToken: change_token })
|
38
|
+
end
|
39
|
+
|
40
|
+
def parents
|
41
|
+
result = connection.execute!({ cmisselector: 'parents',
|
42
|
+
repositoryId: repository.id,
|
43
|
+
objectId: cmis_object_id })
|
44
|
+
|
45
|
+
result.map { |o| ObjectFactory.create(o['object'], repository) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def allowable_actions
|
49
|
+
connection.execute!({ cmisselector: 'allowableActions',
|
50
|
+
repositoryId: repository.id,
|
51
|
+
objectId: cmis_object_id })
|
52
|
+
end
|
53
|
+
|
54
|
+
def relationships(direction = :either)
|
55
|
+
result = connection.execute!({ cmisselector: 'relationships',
|
56
|
+
repositoryId: repository.id,
|
57
|
+
objectId: cmis_object_id,
|
58
|
+
relationshipDirection: direction })
|
59
|
+
|
60
|
+
result['objects'].map { |r| Relationship.new(r, repository) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def policies
|
64
|
+
result = connection.execute!({ cmisselector: 'policies',
|
65
|
+
repositoryId: repository.id,
|
66
|
+
objectId: cmis_object_id })
|
67
|
+
|
68
|
+
result.map { |r| Policy.new(r, repository) }
|
69
|
+
end
|
70
|
+
|
71
|
+
# By default removes from all folders
|
72
|
+
def unfile(folder = nil)
|
73
|
+
options = { repositoryId: repository.id,
|
74
|
+
cmisaction: 'removeObjectFromFolder',
|
75
|
+
objectId: cmis_object_id }
|
76
|
+
options[:folderId] = folder.cmis_object_id if folder
|
77
|
+
|
78
|
+
connection.execute!(options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def move(target_folder)
|
82
|
+
object_parents = parents
|
83
|
+
|
84
|
+
unless object_parents.size == 1
|
85
|
+
raise 'Cannot move object because it is not in excatly one folder'
|
86
|
+
end
|
87
|
+
|
88
|
+
connection.execute!({ cmisaction: 'move',
|
89
|
+
repositoryId: repository.id,
|
90
|
+
objectId: cmis_object_id,
|
91
|
+
targetFolderId: target_folder.cmis_object_id,
|
92
|
+
sourceFolderId: object_parents.first.cmis_object_id })
|
93
|
+
end
|
94
|
+
|
95
|
+
def acls
|
96
|
+
connection.execute!({ cmisselector: 'acl',
|
97
|
+
repositoryId: repository.id,
|
98
|
+
objectId: cmis_object_id })
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_aces(aces)
|
102
|
+
connection.execute!({ cmisaction: 'applyACL',
|
103
|
+
repositoryId: repository.id,
|
104
|
+
objectId: cmis_object_id,
|
105
|
+
addACEs: aces })
|
106
|
+
end
|
107
|
+
|
108
|
+
def remove_aces(aces)
|
109
|
+
connection.execute!({ cmisaction: 'applyACL',
|
110
|
+
repositoryId: repository.id,
|
111
|
+
objectId: cmis_object_id,
|
112
|
+
removeACEs: aces })
|
113
|
+
end
|
114
|
+
|
115
|
+
def detached?
|
116
|
+
cmis_object_id.nil?
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CMIS
|
2
|
+
class ObjectFactory
|
3
|
+
|
4
|
+
def self.create(raw, repository)
|
5
|
+
case base_type_id(raw)
|
6
|
+
when 'cmis:folder' then Folder.new(raw, repository)
|
7
|
+
when 'cmis:document' then Document.new(raw, repository)
|
8
|
+
when 'cmis:relationship' then Relationship.new(raw, repository)
|
9
|
+
when 'cmis:policy' then Policy.new(raw, repository)
|
10
|
+
when 'cmis:item' then Item.new(raw, repository)
|
11
|
+
else raise "Unexpected baseTypeId: #{base_type_id}."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.base_type_id(raw)
|
18
|
+
if raw['properties']
|
19
|
+
raw['properties']['cmis:baseTypeId']['value']
|
20
|
+
elsif raw['succinctProperties']
|
21
|
+
raw['succinctProperties']['cmis:baseTypeId']
|
22
|
+
else
|
23
|
+
raise "Unexpected json: #{raw}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/cmis/policy.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module CMIS
|
2
|
+
class Policy < Object
|
3
|
+
|
4
|
+
attr_reader :policy_text
|
5
|
+
|
6
|
+
def initialize(raw, repository)
|
7
|
+
super
|
8
|
+
cmis_properties %w( cmis:policyText )
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply_to(object)
|
12
|
+
connection.execute!({ cmisaction: 'applyPolicy',
|
13
|
+
repositoryId: repository_id,
|
14
|
+
policyId: cmis_object_id,
|
15
|
+
objectId: object.cmis_object_id })
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_from(object)
|
19
|
+
connection.execute!({ cmisaction: 'removePolicy',
|
20
|
+
repositoryId: repository_id,
|
21
|
+
policyId: cmis_object_id,
|
22
|
+
objectId: object.cmis_object_id })
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module CMIS
|
4
|
+
class PropertyDefinition
|
5
|
+
|
6
|
+
def initialize(hash = {})
|
7
|
+
@hash = hash.with_indifferent_access
|
8
|
+
|
9
|
+
@hash.each_key do |key|
|
10
|
+
class_eval "def #{key.underscore};@hash['#{key}'];end"
|
11
|
+
class_eval "def #{key.underscore}=(value);@hash['#{key}']=value;end"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def readonly?
|
16
|
+
updatability == 'readonly'
|
17
|
+
end
|
18
|
+
|
19
|
+
def oncreate?
|
20
|
+
updatability == 'oncreate'
|
21
|
+
end
|
22
|
+
|
23
|
+
def readwrite?
|
24
|
+
updatability == 'readwrite'
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_hash
|
28
|
+
@hash
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/cmis/query.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module CMIS
|
2
|
+
class Query
|
3
|
+
|
4
|
+
# Options: from, page_size
|
5
|
+
def initialize(repository, statement, options = {})
|
6
|
+
@repository = repository
|
7
|
+
@statement = statement
|
8
|
+
@options = options.stringify_keys
|
9
|
+
|
10
|
+
init_options
|
11
|
+
end
|
12
|
+
|
13
|
+
# Options: limit
|
14
|
+
def each_result(options = {}, &block)
|
15
|
+
return enum_for(:each_result, options) unless block_given?
|
16
|
+
|
17
|
+
init_options
|
18
|
+
limit = parse_limit(options)
|
19
|
+
counter = 0
|
20
|
+
|
21
|
+
while has_next?
|
22
|
+
results.each do |object|
|
23
|
+
break unless counter < limit
|
24
|
+
yield object
|
25
|
+
counter = counter.next
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Options: limit
|
31
|
+
def each_page(options = {}, &block)
|
32
|
+
return enum_for(:each_page, options) unless block_given?
|
33
|
+
|
34
|
+
init_options
|
35
|
+
limit = parse_limit(options)
|
36
|
+
counter = 0
|
37
|
+
|
38
|
+
while has_next?
|
39
|
+
break unless counter < limit
|
40
|
+
yield r = results
|
41
|
+
counter += r.size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def results
|
46
|
+
result = do_query
|
47
|
+
|
48
|
+
@skip_count += result.results.size
|
49
|
+
@has_next = result.has_more_items
|
50
|
+
@total = result.num_items
|
51
|
+
|
52
|
+
result.results
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_next?
|
56
|
+
@has_next
|
57
|
+
end
|
58
|
+
|
59
|
+
def total
|
60
|
+
@total ||= do_query.num_items
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def init_options
|
66
|
+
@method = (@options['method'] || 'get').to_s.downcase
|
67
|
+
@max_items = @options['page_size'] || 10
|
68
|
+
@skip_count = @options['from'] || 0
|
69
|
+
@has_next = true
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_limit(options)
|
73
|
+
options.stringify_keys!
|
74
|
+
limit = options['limit'] || 10
|
75
|
+
limit = BigDecimal::INFINITY if limit == :all
|
76
|
+
raise 'Not a valid limit' unless limit.is_a? Numeric
|
77
|
+
limit
|
78
|
+
end
|
79
|
+
|
80
|
+
def do_query
|
81
|
+
params = { repositoryId: @repository.id,
|
82
|
+
maxItems: @max_items,
|
83
|
+
skipCount: @skip_count }
|
84
|
+
if @method == 'post'
|
85
|
+
params.merge!(cmisselector: 'query', q: @statement)
|
86
|
+
else
|
87
|
+
params.merge!(cmisaction: 'query', statement: @statement)
|
88
|
+
end
|
89
|
+
|
90
|
+
result = @repository.connection.execute!(params)
|
91
|
+
|
92
|
+
results = result['results'].map do |r|
|
93
|
+
ObjectFactory.create(r, @repository)
|
94
|
+
end
|
95
|
+
|
96
|
+
QueryResult.new(results, result['numItems'], result['hasMoreItems'])
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|