I created a project alfresco amp.
To add a document, I run this Test class:
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
Map<String, String> sessionParameters = new HashMap<String, String>();
sessionParameters.put(SessionParameter.USER, "admin");
sessionParameters.put(SessionParameter.PASSWORD, "admin");
sessionParameters.put(SessionParameter.ATOMPUB_URL, "http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom");
sessionParameters.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
SessionFactory sessionFactory = SessionFactoryImpl.newInstance();
Session lSession = sessionFactory.getRepositories(sessionParameters).get(0).createSession();
Folder root = lSession.getRootFolder();
Map<String, Object> folderProperties = new HashMap<String, Object>();
folderProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
folderProperties.put(PropertyIds.NAME, "oo");
Folder newFolder = root.createFolder(folderProperties);
Map<String, Object> lProperties = new HashMap<String, Object>();
String name = "lol.txt";
lProperties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
lProperties.put(PropertyIds.NAME, name);
byte[] content = "CMIS Testdata One".getBytes();
InputStream stream = new ByteArrayInputStream(content);
ContentStream contentStream = new ContentStreamImpl(name, new BigInteger(content), "text/plain", stream);
Document newContent1 = newFolder.createDocument(lProperties, contentStream, null);
System.out.println("Document created: " + newContent1.getId());
}
}
The document is created with success; I got: Document created: e3184105-e59e-4b8a-88e7-9442942433a4;1.0
My problem is how can I access to this document (With which url can I access to that document).
Please help?.
It looks like you've created a document and you now want to know what URL to use to get to it. You have many options, some of which include...
Use the Alfresco web app's download URL:
http://localhost:8080/alfresco/s/api/node/workspace/SpacesStore/dac36aab-dd49-4abc-a4bc-0e0d5729c9ad/content;cm%3Acontent
Use the Share web app's download URL:
http://localhost:8080/share/proxy/alfresco/slingshot/node/content/workspace/SpacesStore/dac36aab-dd49-4abc-a4bc-0e0d5729c9ad/test.txt
Use the CMIS URL (AtomPub binding):
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/atom/content/test.txt?id=dac36aab-dd49-4abc-a4bc-0e0d5729c9ad%3B1.0
Use the CMIS URL (Browser binding):
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root?objectId=dac36aab-dd49-4abc-a4bc-0e0d5729c9ad%3B1.0&cmisselector=content
Write your own URL handler that fetches the input stream via CMIS and returns that stream to the requester. Assuming you are using something like Spring MVC, the code for that might look like:
public InputStream download(String objectId) {
Session session = getSession();
CmisObject obj = session.getObject(objectId);
Document doc = null;
if (obj.getBaseTypeId().equals(BaseTypeId.CMIS_DOCUMENT)) {
doc = (Document) obj;
}
return doc.getContentStream().getStream();
}
Each of the above options assumes a test file in a test folder named "test.txt" with an Alfresco Node Reference of:
workspace://SpacesStore/dac36aab-dd49-4abc-a4bc-0e0d5729c9ad
And a CMIS Object ID of:
dac36aab-dd49-4abc-a4bc-0e0d5729c9ad;1.0
Related
We have a problem with a Folder object and custom aspects:
...
properties.put(PropertyIds.OBJECT_TYPE_ID, "F:sd:folderDocument,P:sd:info");
properties.put("sd:cause", "asdfg");
Folder stDocument = folder.createFolder(properties);
...
Conten of sd:cause is "nothing" in CMIS 1.1 but in CMIS 1.0 work fine.
NOT WORK!
params.put(SessionParameter.ATOMPUB_URL, "http://localhost:8084/alfresco/api/-default-/public/cmis/versions/1.1/atom");
WORK!
params.put(SessionParameter.ATOMPUB_URL, "http://localhost:8084/alfresco/api/-default-/public/cmis/versions/1.0/atom");
We need work in version 1.1
In CMIS 1.1 you add an aspect by adding the aspect type ID to the cmis:secondaryObjectTypeIds property. Here is an example: https://gist.github.com/jpotts/7242070
Make sure you are NOT using the alfresco object factory from the CMIS extensions project when using CMIS 1.1.
The unit test with cmis:secondaryObjectTypeIds is:
#Test
public void createStDocumentWithCMIS11() {
String folderId = "workspace://SpacesStore/03de40f1-e80d-4e0d-8b67-67e93f6e30a1";
// Connection and session to CMIS 1.1
HashMap<String, String> params = new HashMap<>();
params.put(SessionParameter.ATOMPUB_URL, "http://localhost:8084/alfresco/api/-default-/cmis/versions/1.1/atom");
params.put(SessionParameter.USER, "admin");
params.put(SessionParameter.PASSWORD, "admin");
params.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
params.put(SessionParameter.OBJECT_FACTORY_CLASS, "org.alfresco.cmis.client.impl.AlfrescoObjectFactoryImpl");
SessionFactory factory = SessionFactoryImpl.newInstance();
Session session = factory.getRepositories(params).get(0).createSession();
// Find root folder
Folder folder = (Folder) session.getObject(folderId);
assertNotNull(folder);
// Properties for type
Map<String, Object> properties = new HashMap<>();
properties.put(PropertyIds.NAME, "Test CMIS folder type stDocument");
properties.put(PropertyIds.OBJECT_TYPE_ID, "F:sd:structDocument");
properties.put("sd:situation", "situation");
// Create folder
Folder stDocument = folder.createFolder(properties);
assertNotNull(stDocument);
// Add secondary objects (Aspects)
List<Object> aspects = stDocument.getProperty("cmis:secondaryObjectTypeIds").getValues();
aspects.add("P:sd:additionalInfo");
HashMap<String, Object> props = new HashMap<>();
props.put("cmis:secondaryObjectTypeIds", aspects);
stDocument.updateProperties(props);
// Add aspect's property
HashMap<String, Object> propsAspects = new HashMap<>();
propsAspects.put("sd:cause", "test");
stDocument.updateProperties(propsAspects);
assertEquals("test", stDocument.getProperty("sd:cause").getValueAsString());
}
But not work... :(
I am using CameraX API to take pictures in my android app, save them and then display them from their path. With the previous version alpha-09 I was able to do so with onImageSaved(File file). However with the alpha-10 I have to use onImageSaved(OutputFileResults outputFileResults) and then get the path from the uri retrieved by the outputFileResults. But the Uri I get is always wrong. For instance when my image is saved at: "/external/images/media/1581680878237.jpg" I get the uri's path: "/external/images/media/113758".
Here is my code:
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_IMAGE");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(
activity.getContentResolver(),
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues).build();
imageCapture.takePicture(outputFileOptions, Runnable::run, new ImageCapture.OnImageSavedCallback() {
#Override
public void onImageSaved(#NonNull ImageCapture.OutputFileResults outputFileResults) {
Uri uri = outputFileResults.getSavedUri();
if(uri != null){
System.out.println("URI PATH" + uri.getPath());
System.out.println("URI PATH" + uri.toString());
activity.runOnUiThread(cameraProvider::unbindAll);
galleryAddPic(uri);
Bundle params = new Bundle();
params.putString("FILE_PATH", uri.getPath());
Navigation.findNavController(root).navigate(R.id.navigation_edit_image, params);
}
}
#Override
public void onError(#NonNull ImageCaptureException exception) {
exception.printStackTrace();
}
});
So I finally managed to save the image taken by ImageCapture by using an other method (especially an other ImageCapture.OutputFileOptions.Builde). I didn't use an Uri object to save the image but a File object.
File mImageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "YOUR_DIRECTORY");
boolean isDirectoryCreated = mImageDir.exists() || mImageDir.mkdirs();
if(isDirectoryCreated){
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/YOUR_DIRECTORY", "YOUR_IMAGE.jpg");
ImageCapture.OutputFileOptions.Builder outputFileOptionsBuilder =
new ImageCapture.OutputFileOptions.Builder(file);
imageCapture.takePicture(outputFileOptionsBuilder.build(), Runnable::run, new ImageCapture.OnImageSavedCallback() {
#Override
public void onImageSaved(#NonNull ImageCapture.OutputFileResults outputFileResults) {
Bundle params = new Bundle();
params.putString("FILE_PATH", file.getPath());
Navigation.findNavController(root).navigate(R.id.navigation_edit_image, params);
}
#Override
public void onError(#NonNull ImageCaptureException exception) {
exception.printStackTrace();
}
});
}
Be aware that if you use outputFileResults.getSavedUri() with this method you will always have a null uri.
As of CameraX alpha 10, ImageCapture supports 3 types of save location: File, MediaStore URI and OutputStream, depending on which OutputFileOptions.Builder() is used.
The Uri field in OutputFileResults is only populated if the OutputFileOptions is MediaStore URI type. For File type, the caller should have the save location already, there is no need to return the info; for OutputStream type, the save location is unknown to CameraX. See the JavaDoc:
public Uri getSavedUri ()
Returns the Uri of the saved file.
This field is only returned if the ImageCapture.OutputFileOptions is
backed by MediaStore constructed with #Builder(ContentResolver, Uri,
ContentValues).
For more info, please checkout the developer doc.
I am trying to create a zip file with SharpZipLib from files stored on Azure Storage. Unfortunately I am not able to return them because the Api always returns a Json:
{"Version":{"Major":1,"Minor":1,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1},"Content":{"Headers":[{"Key":"Content-Disposition","Value":["attachment; filename=Documents.zip"]},{"Key":"Content-Type","Value":["application/octet-stream"]},{"Key":"Content-Length","Value":["498"]}]},"StatusCode":200,"ReasonPhrase":"OK","Headers":[],"RequestMessage":null,"IsSuccessStatusCode":true}
The zipping should work which I use, however I am not sure if everything is correct since I was never able to see the file.
This is the code for zipping the files and returning the zip file:
[HttpGet("DownloadFiles")]
public async Task<HttpResponseMessage> DownloadFiles(string invoiceNr, List<string> fileNames)
{
List<CloudBlockBlob> blobs = _documentService.GetBlobs(invoiceNr, fileNames);
MemoryStream outputMemStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(outputMemStream);
zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
foreach (CloudBlockBlob blob in blobs)
{
using (MemoryStream blobStream = new MemoryStream())
{
await blob.DownloadToStreamAsync(blobStream);
ZipEntry newEntry = new ZipEntry(blob.Name);
newEntry.DateTime = DateTime.Now;
zipStream.PutNextEntry(newEntry);
StreamUtils.Copy(blobStream, zipStream, new byte[4096]);
zipStream.CloseEntry();
}
}
zipStream.IsStreamOwner = false; // False stops the Close also Closing the underlying stream.
zipStream.Close(); // Must finish the ZipOutputStream before using outputMemStream.
outputMemStream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(outputMemStream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "Documents.zip";
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = outputMemStream.Length;
return result;
}
Is it the wrong way of returning a file from WebAPi? Am I doing something wrong in general?
Thanks in advance for the help.
As you declare your web method as
public async Task<HttpResponseMessage> DownloadFiles(...)
ASP.NET Core treats HttpResponseMessage as model and your methods returns instance of this file serialized as JSON.
The correct version of this method is
public async Task<IActionResult> DownloadFiles()
{
...
return File(outputMemStream, "application/octet-stream", "Documents.zip");
}
Hi and thanks in advance for the help
I have a problem with insertion and update documents in alfresco, So when I set a property like "cmis:creationDate or cmis:lastModificationDate", the document is created successfully but the properties that has Updatability=ReadOnly doesn't set to the new value given it's set automatically by alfresco.
Is there any solution to set Updatibility of these properties to "ReadWrite"?
I'm using Aalfresco 5.0 and openCmis 0.13 this is my code :
public void createDocument(Folder folder) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date d = sdf.parse("21/12/2012");
String name = "myNewDocument.txt";
Map<String, Object> properties = new HashMap<String, Object>();
Calendar cal = new GregorianCalendar();
cal.setTime(d);
properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document,P:cm:titled,P:cm:author");
properties.put(PropertyIds.NAME, name);
properties.put(PropertyIds.CREATION_DATE, cal);
properties.put(PropertyIds.LAST_MODIFICATION_DATE, cal);
properties.put("cm:title", "Title");
properties.put("cm:description", "Description");
properties.put("cm:author", "author");
properties.put("cmis:creationDate ", cal);
byte[] content = "Hello World!".getBytes();
InputStream stream = new ByteArrayInputStream(content);
ContentStream contentStream = new ContentStreamImpl(name, BigInteger.valueOf(content.length), "text/plain", stream);
Document newDoc = folder.createDocument(properties, contentStream, VersioningState.MAJOR);
}
Updating the read only fields requires work on the Alfresco side. There are policies in place that prevent properties of the aspect cm:auditable from being changed.
You can update the fields in Alfresco using the NodeService API after you've disabled that policy behavior. Here's an example:
policyBehaviourFilter.disableBehaviour(node, ContentModel.ASPECT_AUDITABLE);
// Update CreatedDate
nodeService.setProperty(node, ContentModel.PROP_CREATED, dateTime);
//Enable policy
policyBehaviourFilter.enableBehaviour(node, ContentModel.ASPECT_AUDITABLE);
You could package this into a custom webscript to allow the properties to be changed remotely.
I'm looking to use NVelocity in my ASP.NET MVC application, not as a view engine, just for rendering some email templates.
However, I cannot for the life of me get it to work. I have downloaded it from the castle project and followed the example at http://www.castleproject.org/others/nvelocity/usingit.html#step1
No matter what I try I don't seem to be able to load a template located in my site. The example suggests using the absolute path, which I have tried to no avail:
Template t = engine.GetTemplate("/Templates/TestEmail.vm");
So please can someone give me two examples. One of loading a template located in the web site directory and secondly one parsing a string variable (as it is likely that my templates will be stored in a database).
Many thanks
Ben
I've used this class in one of my past projects:
public interface ITemplateRepository
{
string RenderTemplate(string templateName, IDictionary<string, object> data);
string RenderTemplate(string masterPage, string templateName, IDictionary<string, object> data);
}
public class NVelocityTemplateRepository : ITemplateRepository
{
private readonly string _templatesPath;
public NVelocityTemplateRepository(string templatesPath)
{
_templatesPath = templatesPath;
}
public string RenderTemplate(string templateName, IDictionary<string, object> data)
{
return RenderTemplate(null, templateName, data);
}
public string RenderTemplate(string masterPage, string templateName, IDictionary<string, object> data)
{
if (string.IsNullOrEmpty(templateName))
{
throw new ArgumentException("The \"templateName\" parameter must be specified", "templateName");
}
var name = !string.IsNullOrEmpty(masterPage)
? masterPage : templateName;
var engine = new VelocityEngine();
var props = new ExtendedProperties();
props.AddProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, _templatesPath);
engine.Init(props);
var template = engine.GetTemplate(name);
template.Encoding = Encoding.UTF8.BodyName;
var context = new VelocityContext();
var templateData = data ?? new Dictionary<string, object>();
foreach (var key in templateData.Keys)
{
context.Put(key, templateData[key]);
}
if (!string.IsNullOrEmpty(masterPage))
{
context.Put("childContent", templateName);
}
using (var writer = new StringWriter())
{
engine.MergeTemplate(name, context, writer);
return writer.GetStringBuilder().ToString();
}
}
}
In order to instantiate the NVelocityTemplateRepository class you need to provide an absolute path where your templates root is. Then you use relative paths to reference your vm files.
I also added the following method to process a string instead of a template file (say if retrieving the template content from a database):
public string RenderTemplateContent(string templateContent, IDictionary<string, object> data)
{
if (string.IsNullOrEmpty(templateContent))
throw new ArgumentException("Template content cannot be null", "templateContent");
var engine = new VelocityEngine();
engine.Init();
var context = GetContext(data);
using (var writer = new StringWriter()) {
engine.Evaluate(context, writer, "", templateContent);
return writer.GetStringBuilder().ToString();
}
}
And used StructureMap to initialize the service:
ForRequestedType<ITemplateService>()
.TheDefault.Is.ConstructedBy(()=>
new NVelocityTemplateService(HttpContext.Current.Server.MapPath("~/Content/Templates/")));
You might find the TemplateEngine component useful.
It's an abstraction over template engines with a NVelocity implementation, similar to Darin's answer, but it should perform marginally better since it uses a single instance of the VelocityEngine (as opposed to initializing one instance per render) and has optional caching. It also has a couple other features, like logging, NVelocity property overriding and loading templates from assembly resources.