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.
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ module CMIS
2
+
3
+ class Item < Object
4
+
5
+ def initialize(raw, repository)
6
+ super
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -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
@@ -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