cmis 0.1.0-java → 0.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -42,6 +42,8 @@ server is http://cmis.alfresco.com/cmisatom.
42
42
 
43
43
  Nuxeo also provides a demo server. More information can be found here: http://doc.nuxeo.com/display/NXDOC/CMIS+for+Nuxeo#CMISforNuxeo-Onlinedemo .
44
44
 
45
+ The documentation below is heavily based on the [OpenCMIS Client API Developer's Guide](http://chemistry.apache.org/java/developing/guide.html)
46
+
45
47
  ## Connecting to a CMIS repository
46
48
 
47
49
  To be able to do anything useful on a CMIS repository, you must first find a repository, and create a session with it:
@@ -123,6 +125,8 @@ doc.download(file)
123
125
 
124
126
  ### Updating a document
125
127
 
128
+ Updating metadata:
129
+
126
130
  ```ruby
127
131
  id = root.create_cmis_document("cmis_logo_original.png", "/Users/ricn/cmis_logo.png")
128
132
  doc = @session.get_object(id)
@@ -133,6 +137,27 @@ doc = @session.get_object(id)
133
137
  puts "New name: #{doc.name}"
134
138
  ```
135
139
 
140
+ Updating the actual content of a document (using check out / check in):
141
+ ```ruby
142
+ root = @session.root_folder
143
+ doc = root.create_text_doc("my_file.txt", "content")
144
+ puts "Orginal version: " + doc.version_label
145
+ id = doc.check_out
146
+ working_copy = @session.get_object(id)
147
+
148
+ content_stream = CMIS::create_content_stream("/Users/ricn/updated_file.txt", @session)
149
+
150
+ # check_in parameters: boolean major, properties, contentStream, checkinComment
151
+ id = working_copy.check_in(false, nil, content_stream, "minor version")
152
+ doc = @session.get_object(id)
153
+
154
+ puts "New version: " + doc.version_label
155
+ ```
156
+
157
+ Update the content of a document directly:
158
+
159
+ TODO
160
+
136
161
  ### Deleting a document
137
162
 
138
163
  ```ruby
@@ -147,8 +172,9 @@ doc.delete # Yay, that was easy!
147
172
  folder1 = root.create_cmis_folder("folder1")
148
173
  folder11 = folder1.create_cmis_folder("Folder11")
149
174
  folder12 = folder1.create_cmis_folder("Folder12")
150
- # Delete it
151
- folder1.delete_tree(true, CMIS::UnfileObject::DELETE, true) # parameter explanation: boolean allversions, UnfileObject unfile, boolean continueOnFailure
175
+
176
+ # Parameter: boolean allversions, UnfileObject unfile, boolean continueOnFailure
177
+ folder1.delete_tree(true, CMIS::UnfileObject::DELETE, true)
152
178
  ```
153
179
 
154
180
  ## Working with CMIS Properties
@@ -183,25 +209,234 @@ puts "Content stream file name: " + doc.content_stream_file_name
183
209
  puts "Content stream mime type: " + doc.content_stream_mime_type
184
210
  ```
185
211
 
212
+ Get allowed actions for a document or folder:
213
+
214
+ ```ruby
215
+ root = @session.root_folder
216
+ allowed_actions = root.allowed_actions
217
+
218
+ allowed_actions.each do |a|
219
+ puts a.to_s + " is an allowed action on " + root.name
220
+ end
221
+ ```
222
+
223
+ A complete list of actions can be found here: http://chemistry.apache.org/java/0.8.0/maven/apidocs/org/apache/chemistry/opencmis/commons/enums/Action.html
224
+
186
225
  ## Working with CMIS Queries
187
226
 
188
227
  ```ruby
189
- query = "SELECT * FROM cmis:document WHERE cmis:name LIKE 'cmis%'"
190
- q = @session.query(query, false) # true means search all versions
228
+ query = "SELECT * FROM cmis:folder"
229
+ q = @session.query(query, false) # false means only latest versions
191
230
 
192
231
  q.each do |result|
193
- puts result.property_value_by_query_name("cmis:name")
232
+ props = result.properties
233
+ props.each do |p|
234
+ disp_name = p.display_name
235
+ puts "Name: " + p.first_value if disp_name == "Name"
236
+ end
194
237
  end
195
238
  ```
196
239
 
197
- ## DOCUMENTION TODO:
198
- * Add Multi-filing and Unfiling examples
199
- * Add Relationships examples
200
- * Add Access control examples
201
- * Add OperationContext examples
202
- * Add Advanced types usage examples
203
- * Add Performance notes
204
- * Add Troubleshooting notes
240
+ ## Capabilities
241
+ CMIS repositories has different capabilities. Some are designed for a specific application domain and do not provide capabilities that are not needed for that domain. This means a repository implementation may not be able to support all the capabilities that the CMIS specification provides. To allow this, some capabilities can be optionally supported by a CMIS repository.
242
+
243
+ This is how you check the capabilites of the repository:
244
+
245
+ ```ruby
246
+ rep_info = @session.repository_info
247
+ cap = rep_info.capabilities
248
+
249
+ puts "Navigation Capabilities"
250
+ puts "Get descendants supported: " + cap.is_get_descendants_supported.to_s
251
+ puts "Get folder tree supported: " + cap.is_get_folder_tree_supported.to_s
252
+ puts "=============================="
253
+ puts "Object Capabilities"
254
+ puts "Content Stream: " + cap.content_stream_updates_capability.value
255
+ puts "Changes: " + cap.changes_capability.value
256
+ puts "Renditions: " + cap.renditions_capability.value
257
+ puts "=============================="
258
+ puts "Filing Capabilities"
259
+ puts "Multifiling supported: " + cap.is_multifiling_supported.to_s
260
+ puts "Unfiling supported: " + cap.is_unfiling_supported.to_s
261
+ puts "Version specific filing supported: " + cap.is_version_specific_filing_supported.to_s
262
+ puts "=============================="
263
+ puts "Versioning Capabilities"
264
+ puts "PWC searchable: " + cap.is_pwc_searchable_supported.to_s
265
+ puts "PWC updatable: " + cap.is_pwc_updatable_supported.to_s
266
+ puts "All versions searchable: " + cap.is_all_versions_searchable_supported.to_s
267
+ puts "=============================="
268
+ puts "Query Capabilities"
269
+ puts "Query: " + cap.query_capability.value
270
+ puts "Join: " + cap.join_capability.value
271
+ puts "=============================="
272
+ puts "ACL Capabilities"
273
+ puts "ACL: " + cap.acl_capability.value
274
+ ```
275
+
276
+ ## Paging
277
+ When you retrieve the children of a CMIS object, the result set returned is of an arbitrary size. Retrieving a large result set synchronously could increase response times. To improve performance, you can use OpenCMIS's paging support to control the size of the result set retrieved from the repository. To use paging,
278
+ you must specify an [OperationContext](http://chemistry.apache.org/java/0.8.0/maven/apidocs/org/apache/chemistry/opencmis/client/api/OperationContext.html) when invoking children method call on the parent object. The OperationContext specifies the maximum number of items to retrieve in a page.
279
+
280
+ ```ruby
281
+ root = @session.root_folder
282
+ oc = CMIS::OperationContextImpl.new
283
+ oc.max_items_per_page = 3
284
+
285
+ # List all object in the root folder using paging
286
+ page1 = root.children(oc).skip_to(0).page.map(&:name)
287
+ page2 = root.children(oc).skip_to(1).page.map(&:name)
288
+
289
+ puts "Page 1:"
290
+ page1.each do |o|
291
+ puts o
292
+ end
293
+
294
+ puts "Page 2:"
295
+ page2.each do |o|
296
+ puts o
297
+ end
298
+ ```
299
+
300
+ ## Renditions
301
+
302
+ Some repositories provide a facility to retrieve alternative representations, or renditions of a document. An example is a preview thumbnail image of the content of a document, which could be presented to the user without needing to download the full document content. Another example is a PDF version of a word document.
303
+
304
+ A CMIS repository may have zero or more renditions for a document or folder in addition to the document's content stream.
305
+ At present the CMIS specification only allows renditions to be read. There are no facilities to create, update or delete renditions. Renditions are of a specific version of the document and may differ between document versions. Each rendition consists of a set of rendition attributes and a rendition stream. Rendition attributes are not object properties, and are not queryable. Renditions can be retrieved using the getRenditions service.
306
+
307
+ ```ruby
308
+ puts "Rendition support: " + @session.repository_info.capabilities.renditions_capability.to_s
309
+
310
+ id = @session.root_folder.create_text_doc("simple file.txt", "My content")
311
+ context = @session.create_operation_context
312
+ context.rendition_filter_string = "cmis:thumbnail"
313
+ doc = @session.get_object(id, context)
314
+
315
+ renditions = doc.renditions
316
+
317
+ puts "Renditions"
318
+ renditions.each do |r|
319
+ puts "Kind" + r.kind
320
+ puts "Mimetype: " + r.mime_type
321
+ end
322
+ ```
323
+
324
+ Note: If you run the code above you might not get the renditions directly. Many repositories renders them asynchronously so it will take some time before you see them.
325
+
326
+ ## Multi-filing
327
+ Multi-filing allows you to file a document object in more than one folder. This capability are optional, and your repository may not support them.
328
+
329
+ ```ruby
330
+ doc = @session.root_folder.create_text_doc("Multi-filing.txt", "Content")
331
+ folder = @session.root_folder.create_cmis_folder("multi-filing")
332
+ puts "Document parent count: " + doc.parents.size.to_s
333
+ doc.add_to_folder(folder, true) # true means all versions
334
+ puts "Document parent count: " + doc.parents.size.to_s
335
+ ```
336
+
337
+ ## Access control
338
+
339
+ Document or folder objects can have an access control list (ACL), which controls access to the object. An ACL is a list of Access Control Entries (ACEs). An ACE grants one or more permissions to a principal. A principal is a user, group, role, or something similar.
340
+
341
+ An ACE contains:
342
+ * One String with the principalid
343
+ * One or more Strings with the names of the permissions.
344
+ * A boolean flag direct, which is true if the ACE is directly assigned to the object, or false if the ACE is somehow derived
345
+
346
+ There are three basic permissions predefined by CMIS:
347
+ * cmis:read: permission for reading properties and reading content
348
+ * cmis:write: permission to write properties and the content of an object. A respository can defin the property to include cmis:read
349
+ * cmis:all: all the permissions of a repository. It includes all other basic CMIS permissions.
350
+
351
+ How these basic permissions are mapped to allowable actions is repository specific. You can discover the repository semantics for basic permissions with regard to allowable actions by examining the mappings parameter returned by session method repository_info. A repository can extend the basic permissions with its own repository-specific permissions. The folowing code snippet prints out the permissions available for a repository,
352
+ and the mappings of allowable actions to repository permissions:
353
+
354
+ ```ruby
355
+ acl_caps = @session.repository_info.acl_capabilities
356
+
357
+ puts "Propogation for this repository is " + acl_caps.acl_propagation.to_s
358
+
359
+ puts "Permissions for this repository are: "
360
+ acl_caps.permissions.each do |p|
361
+ puts "ID: " + p.id + " description: " + p.description
362
+ end
363
+
364
+ puts "Permission mappings for this repository are:"
365
+ repo_mapping = acl_caps.permission_mapping
366
+
367
+ repo_mapping.each do |key, value|
368
+ puts key + " maps to " + repo_mapping.get(key).permissions.to_s
369
+ end
370
+ ```
371
+
372
+ You can specify how a repository should handle non-direct ACEs when you create an ACL, by specifying one of the following acl propogation values:
373
+
374
+ * OBJECTONLY: apply ACEs to a document or folder, without changing the ACLs of other objects
375
+ * PROPAGATE: apply ACEs to the given object and all inheriting objects
376
+ * REPOSITORYDETERMINED: allow the repository to use its own method of computing how changing an ACL for an object influences the non-direct ACEs of other objects.
377
+
378
+ The following example creates a folder object, and prints out the ACEs in the created folder's ACL. It then creates a new ACL with one ACE, adds it to the folder, retrieves the folder again, and prints out the ACEs now present in the folder's ACL:
379
+
380
+ ```ruby
381
+ folder = @session.root_folder.create_cmis_folder("ACL test")
382
+ oc = CMIS::OperationContextImpl.new
383
+ oc.include_acls = true
384
+ folder = @session.get_object(folder, oc)
385
+
386
+ aces = folder.acl.aces
387
+ puts "Permissions before we add the guest user:"
388
+ aces.each do |a|
389
+ puts "Principal: " + a.principal.id
390
+ a.permissions.each do |p|
391
+ puts "Permission ID: " + p
392
+ end
393
+ end
394
+
395
+ permissions = ["cmis:read"]
396
+ principal = "guest" # Built in user in Alfresco
397
+ ace_in = @session.object_factory.create_ace(principal, permissions)
398
+ folder.add_acl([ace_in], CMIS::AclPropagation::REPOSITORYDETERMINED)
399
+ folder = @session.get_object(folder, oc)
400
+
401
+ aces = aces = folder.acl.aces
402
+ puts "Permissions after we added the guest user:"
403
+ aces.each do |a|
404
+ puts "Principal: " + a.principal.id
405
+ a.permissions.each do |p|
406
+ puts "Permission ID: " + p
407
+ end
408
+ end
409
+ ```
410
+
411
+ ## Relationships
412
+
413
+ A Relationship object is a relationship between a source object and a target object. The relationship has direction, from source to target. It is non-invasive, in that a relationship does not modify either the source or the target object. A relationship object has a type, like any other CMIS object. The source and target objects must be independent objects, such as a document, folder, or policy objects. A relationship object does not have a content-stream, and is not versionable, queryable, or fileable.
414
+
415
+ A repository does not have to support relationships. If it doesn't the relationship base object-type will not be returned by a "get types" call.
416
+
417
+ The following example creates a relationship between 2 objects. Alfresco supports relationships, but the base type cmis:relationship is not defined as creatable, so the example uses an existing type R:cmiscustom:assoc which is a creatable sub-type of cmis:relationship in Alfresco:
418
+
419
+ ```ruby
420
+ content_stream = CMIS::create_content_stream("/Users/ricn/source.txt", @session)
421
+ source_props = { CMIS::PropertyIds::OBJECT_TYPE_ID => "D:cmiscustom:document", CMIS::PropertyIds::NAME => "source.txt" }
422
+ source_doc = @session.root_folder.create_document(source_props, content_stream, CMIS::VersioningState::MAJOR)
423
+
424
+ content_stream = CMIS::create_content_stream("/Users/ricn/target.txt", @session)
425
+ target_props = { CMIS::PropertyIds::OBJECT_TYPE_ID => "D:cmiscustom:document", CMIS::PropertyIds::NAME => "target.txt" }
426
+ target_doc = @session.root_folder.create_document(target_props, content_stream, CMIS::VersioningState::MAJOR)
427
+
428
+ rel_props = {
429
+ "cmis:sourceId" => source_doc.id,
430
+ "cmis:targetId" => target_doc.id,
431
+ "cmis:objectTypeId" => "R:cmiscustom:assoc"
432
+ }
433
+
434
+ rel = @session.create_relationship(rel_props)
435
+ rel = @session.get_object(rel)
436
+
437
+ puts rel.source.id
438
+ puts rel.target.id
439
+ ```
205
440
 
206
441
  ## Contributing
207
442
 
data/lib/cmis.rb CHANGED
@@ -17,8 +17,10 @@ module CMIS
17
17
  import org.apache.chemistry.opencmis.commons.SessionParameter
18
18
  import org.apache.chemistry.opencmis.commons.PropertyIds
19
19
  import org.apache.chemistry.opencmis.commons.impl.MimeTypes
20
+ import org.apache.chemistry.opencmis.commons.data.AclCapabilities
20
21
 
21
22
  import org.apache.chemistry.opencmis.commons.enums.Action
23
+ import org.apache.chemistry.opencmis.commons.enums.AclPropagation
22
24
  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId
23
25
  import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl
24
26
  import org.apache.chemistry.opencmis.commons.enums.VersioningState
@@ -49,6 +51,10 @@ module CMIS
49
51
  def download(destination_path)
50
52
  FileUtils.download(self, destination_path)
51
53
  end
54
+
55
+ def allowed_actions
56
+ self.allowable_actions.allowable_actions.to_a
57
+ end
52
58
  end
53
59
 
54
60
  class FolderImpl
@@ -72,6 +78,10 @@ module CMIS
72
78
  def create_text_doc(name, content)
73
79
  FileUtils.create_text_document(self.id, name, content, "cmis:document", CMIS::VersioningState::MAJOR, session)
74
80
  end
81
+
82
+ def allowed_actions
83
+ self.allowable_actions.allowable_actions.to_a
84
+ end
75
85
  end
76
86
 
77
87
  def self.create_session(url, user, password, repo_id = nil)
data/lib/cmis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Cmis
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/spec/cmis_spec.rb CHANGED
@@ -79,6 +79,16 @@ describe "CMIS" do
79
79
  allowed_actions.to_a.should include(CMIS::Action::CAN_GET_PROPERTIES)
80
80
  end
81
81
 
82
+ it "should retrieve allowed actions for an object using convenient method" do
83
+ # for a folder
84
+ allowed_actions = @session.root_folder.allowed_actions
85
+ allowed_actions.should include(CMIS::Action::CAN_GET_PROPERTIES)
86
+
87
+ doc = create_random_doc(@test_folder)
88
+ allowed_actions = doc.allowed_actions
89
+ allowed_actions.should include(CMIS::Action::CAN_GET_PROPERTIES)
90
+ end
91
+
82
92
  it "should be possible to check if a document is versionable" do
83
93
  doc = create_random_doc(@test_folder)
84
94
  doc.type.is_versionable.should == true
@@ -158,6 +168,13 @@ describe "CMIS" do
158
168
  p.value.should == content.size if p.definition.id == "cmis:contentStreamLength"
159
169
  end
160
170
  end
171
+
172
+ it "should be possible to use multi-filing" do
173
+ doc = create_random_doc(@test_folder)
174
+ folder = @test_folder.create_cmis_folder("multi-filing")
175
+ doc.add_to_folder(folder, true) # true means all versions
176
+ doc.parents.size.should == 2
177
+ end
161
178
  end
162
179
 
163
180
  describe "Updating objects" do
@@ -246,6 +263,46 @@ describe "CMIS" do
246
263
  @sub_folder.children.map(&:name).size.should == 5
247
264
  end
248
265
  end
266
+
267
+ describe "Access control" do
268
+ it "should be possible to add acls to an object" do
269
+ folder = @test_folder.create_cmis_folder("ACL test")
270
+ oc = CMIS::OperationContextImpl.new
271
+ oc.include_acls = true
272
+ folder = @session.get_object(folder, oc)
273
+ original_acl_size = folder.acl.aces.size
274
+
275
+ permissions = ["cmis:read"]
276
+ principal = "guest"
277
+ ace_in = @session.object_factory.create_ace(principal, permissions)
278
+ folder.add_acl([ace_in], CMIS::AclPropagation::REPOSITORYDETERMINED)
279
+ folder = @session.get_object(folder, oc)
280
+ folder.acl.aces.size.should == original_acl_size + 2
281
+ end
282
+ end
283
+
284
+ describe "Relationships" do
285
+
286
+ it "should be possible to create relationships" do
287
+ content_stream = CMIS::create_content_stream(file_path("text_file.txt"), @session)
288
+ source_props = { CMIS::PropertyIds::OBJECT_TYPE_ID => "D:cmiscustom:document", CMIS::PropertyIds::NAME => "source.txt" }
289
+ source_doc = @test_folder.create_document(source_props, content_stream, CMIS::VersioningState::MAJOR)
290
+
291
+ target_props = { CMIS::PropertyIds::OBJECT_TYPE_ID => "D:cmiscustom:document", CMIS::PropertyIds::NAME => "target.txt" }
292
+ target_doc = @test_folder.create_document(target_props, content_stream, CMIS::VersioningState::MAJOR)
293
+
294
+ rel_props = {
295
+ "cmis:sourceId" => source_doc.id,
296
+ "cmis:targetId" => target_doc.id,
297
+ "cmis:objectTypeId" => "R:cmiscustom:assoc"
298
+ }
299
+
300
+ rel = @session.create_relationship(rel_props)
301
+ rel = @session.get_object(rel)
302
+ rel.source.id.should == source_doc.id
303
+ rel.target.id.should == target_doc.id
304
+ end
305
+ end
249
306
  end
250
307
 
251
308
  describe "Local Alfresco" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: java
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-28 00:00:00.000000000 Z
12
+ date: 2013-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec