File upload on create content

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-28-2010 07:33 PM
I have a problem regarding file uploads. Today, I have a form which creates content (via "create content") based on my custom content type. This custom content type has some mandatory properties. Now, I want to do the same when uploading a file. I know how to set the correct document type when uploading a file (via flash-upload.get.js), but the problem is that the content cannot be created due to my mandatory properties. Now I have two options:
1. Modify the "Upload" action to somehow include my mandatory properties.
2. Somehow configure the form to understand file upload.
Can the form service understand file upload today? Is there any way to modify the flash-upload to render the correct mandatory fields?
Thanks,
Niklas
- Labels:
-
Archive

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-09-2010 07:55 AM
//Calle
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-06-2010 10:25 AM
Then I configured in share-config-custom.xml a config section with "model-type" evaluator that includes the "cm:content" field:
<config evaluator="model-type" condition="custom:myType"> <forms> <form submission-url="/api/custom/upload"> <field-visibility> <show id="cm:content" force="true" /> <show id="cm:title" force="true" /> <show id="cm:description" force="true" /> <show id="cm:author" force="true" /> <show id="size" for-mode="view" /> <show id="cm:creator" for-mode="view" /> <show id="cm:created" for-mode="view" /> <show id="cm:modifier" for-mode="view" /> <show id="cm:modified" for-mode="view" /> <!– Custom –> <show id="custom:myField" /> <!– tags and categories –> <show id="cm:taggable" for-mode="edit" force="true" /> <show id="cm:categories" /> <!– cm:dublincore aspect –> <show id="cm:publisher" /> <show id="cm:contributor" /> <show id="cm:type" /> <show id="cm:identifier" /> <show id="cm:dcsource" /> <show id="cm:coverage" /> <show id="cm:rights" /> <show id="cm:subject" /> <!– cm:complianceable aspect –> <show id="cm:removeAfter" /> <!– cm:effectivity aspect –> <show id="cm:from" /> <show id="cm:to" /> <!– cm:summarizable aspect –> <show id="cm:summary" /> <!– cm:translatable aspect –> <show id="cm:translations" /> <!– cm:localizable aspect –> <show id="cm:locale" /> <!– cm:ownable aspect –> <show id="cm:owner" /> <!– cm:attachable aspect –> <show id="cm:attachments" /> <!– cm:emailed aspect –> <show id="cm:originator" /> <show id="cm:addressee" /> <show id="cm:addressees" /> <show id="cm:sentdate" /> <show id="cm:subjectline" /> </field-visibility> <appearance> <field id="cm:content"> <control template="/org/alfresco/components/form/controls/fileUpload.ftl" /> </field> <set id="" appearance="panel" label-id="set.label.documentoCondicionesComerciales" /> <field id="cm:title"> <control template="/org/alfresco/components/form/controls/textfield.ftl" /> </field>(…) </config>
The control I include for the content "fileUpload.ftl" is just a control that has a standard "file" typed input field:
<div class="form-field"> <label for="${fieldHtmlId}">${field.label?html}:<#if field.mandatory><span class="mandatory-indicator">${msg("form.required.fields.marker")}</span></#if></label> <input type="file" id="${fieldHtmlId}" name="${field.name}" /></div>
With all this in place I can go to the form console, request a form of kind "type",id "custom:myType", mode "create" and "multipart" and use it to upload one file with my custom model and fields applied and set. I suppose in a future I would be able to get rid of the custom API, but for now it does the trick.
Now the problem is that I don't manage to make the "create-content" toolbar to show my type. I suppose I will have to make my own page in Share or something like this, but I'm a bit stuck. Any clue ?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-14-2010 11:07 AM
First of all, have you manage to hook your create for with Alfresco share so that you can start creating new custom type?
Is your custom type also deriving from cm:content?
Regards.
Olivier
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-14-2010 03:02 PM
Yes I managed to hook my create with Share. I copied the standard create-content component in share and made some changes so that it would invoke the form UI component with itemKind "type" and the corresponding custom type.
Yes, my custom type derives from cm:content, but the same approach should be reasonable for other types. But keep in mind that deriving from cm:content for all custom contents and deriving from cm:folder for all custom folder types seems to be considered a best practice in Alfresco reference books.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-15-2010 08:52 AM
Thank you for your reply.
Your pointer is useful, but I am sorry. I don't have your expertise and I need more information to go on. Would you mind detailing how you proceeded and the files you modified?
Regards.
Olivier
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-15-2010 12:13 PM
I created '<tomcat>/shared/classes/alfresco/web-extension/site-data/pages/custom-create-content.xml', with this content:
<?xml version='1.0' encoding='UTF-8'?><page> <title>Create Custom Content</title> <title-id>page.customCreateContent.title</title-id> <description>Create text-based content</description> <description-id>page.eunCreateContent.description</description-id> <template-instance>custom-create-content</template-instance> <authentication>user</authentication></page>
I created '<tomcat>/shared/classes/alfresco/web-extension/site-data/template-instances/custom-create-content.xml', with this content:
<?xml version='1.0' encoding='UTF-8'?><template-instance> <template-type>org/alfresco/create-content</template-type> <properties> <pageFamily>documentlibrary</pageFamily> <container>documentLibrary</container> </properties> <components> <!– title: normal, portlet, repository & portlet+repository –> <component> <region-id>title</region-id> <url>/components/title/collaboration-title</url> </component> <component> <region-id>portlet-title</region-id> <url>/components/title/portlet-collaboration-title</url> </component> <component> <region-id>repo-title</region-id> <url>/components/title/simple-title</url> <properties> <title>title.repository</title> <subtitle>title.browser</subtitle> </properties> </component> <component> <region-id>portlet-repo-title</region-id> <url>/components/title/simple-title</url> <properties> <title>title.repository</title> <subtitle>title.browser</subtitle> </properties> </component> <component> <region-id>navigation</region-id> <url>/components/navigation/collaboration-navigation</url> </component> <component> <region-id>create-content-mgr</region-id> <url>/components/create-content/create-content-mgr</url> </component> <component> <region-id>create-content</region-id> <url>/components/form?type={type}&destination={destination}</url> <properties> <itemKind>type</itemKind> <itemId>{type}</itemId> <mode>create</mode> <submitType>multipart</submitType> <showCaption>true</showCaption> <showCancelButton>true</showCancelButton> </properties> </component> </components></template-instance>
'<tomcat>/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/toolbar.get.config.xml' :
<toolbar> <createContent> <content mimetype="text/plain" icon="plain-text" label="menu.create-content.text" /> <content mimetype="text/html" icon="html" label="menu.create-content.html" /> <content mimetype="text/xml" icon="xml" label="menu.create-content.xml" /> </createContent> <uploadCustomContent> <content type="custom:customType1" icon="plain-text" label="menu.upload-custom-content.customType1" /> <content type="custom:customType2" icon="plain-text" label="menu.upload-custom-content.customType2" /> </uploadCustomContent> <actionSets> <actionSet id="default"> <action type="action-link" id="onActionCopyTo" label="menu.selected-items.copy" /> <action type="action-link" id="onActionMoveTo" permission="delete" label="menu.selected-items.move" /> <action type="action-link" id="onActionDelete" permission="delete" label="menu.selected-items.delete" /> <action type="action-link" id="onActionAssignWorkflow" asset="document" label="menu.selected-items.assign-workflow" /> <action type="action-link" id="onActionManagePermissions" permission="permissions" label="menu.selected-items.manage-permissions" /> </actionSet> </actionSets></toolbar>
'<tomcat>/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/toolbar.get.js' :
<import resource="classpath:alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/include/toolbar.lib.js">
Similar for repo-toolbar.get.config.xml and repo-toolbar.get.js
'<tomcat>/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/include/toolbar.lib.js' :
const PREFERENCES_ROOT = "org.alfresco.share.documentList";function getPreferences(){ var preferences = {}; // Request the current user's preferences var result = remote.call("/api/people/" + stringUtils.urlEncode(user.name) + "/preferences?pf=" + PREFERENCES_ROOT); if (result.status == 200 && result != "{}") { var prefs = eval('(' + result + ')'); try { // Populate the preferences object literal for easy look-up later preferences = eval('(prefs.' + PREFERENCES_ROOT + ')'); if (typeof preferences != "object") { preferences = {}; } } catch (e) { } } model.preferences = preferences;}function getActionSet(myConfig){ // Actions var xmlActionSet = myConfig..actionSet.(@id == "default"), actionSet = []; // Found match? if (xmlActionSet.@id == "default") { for each(var xmlAction in xmlActionSet.action) { actionSet.push( { id: xmlAction.@id.toString(), type: xmlAction.@type.toString(), permission: xmlAction.@permission.toString(), asset: xmlAction.@asset.toString(), href: xmlAction.@href.toString(), label: xmlAction.@label.toString() }); } } model.actionSet = actionSet;}function getCreateContent(myConfig){ // New Content var xmlCreateContent = myConfig.createContent, xmlUploadCustomContent = myConfig.uploadCustomContent, createContent = [], uploadCustomContent = []; if (xmlCreateContent != null) { for each (var xmlContent in xmlCreateContent.content) { createContent.push( { mimetype: xmlContent.@mimetype.toString(), icon: xmlContent.@icon.toString(), permission: xmlContent.@permission.toString(), formid: xmlContent.@formid.toString(), label: xmlContent.@label.toString() }); } } if (xmlUploadCustomContent != null) { for each (var xmlContent in xmlUploadCustomContent.content) { uploadCustomContent.push( { type: xmlContent.@type.toString(), icon: xmlContent.@icon.toString(), permission: xmlContent.@permission.toString(), formid: xmlContent.@formid.toString(), label: xmlContent.@label.toString() }); } } // Google Docs enabled? var googleDocsEnabled = false, googleDocsConfig = config.scoped["DocumentLibrary"]["google-docs"]; if (googleDocsConfig !== null) { googleDocsEnabled = (googleDocsConfig.getChildValue("enabled").toString() == "true"); var configs = googleDocsConfig.getChildren("creatable-types"), creatableConfig, configItem, creatableType, mimetype; if (configs) { for (var i = 0; i < configs.size(); i++) { creatableConfig = configs.get(i).childrenMap["creatable"]; if (creatableConfig) { for (var j = 0; j < creatableConfig.size(); j++) { configItem = creatableConfig.get(j); // Get type and mimetype from each config item creatableType = configItem.attributes["type"].toString(); mimetype = configItem.value.toString(); if (creatableType && mimetype) { createContent.push( { mimetype: mimetype, icon: creatableType, permission: "create-google-doc", formid: "doclib-create-googledoc", label: "google-docs." + creatableType }); } } } } } } model.googleDocsEnabled = googleDocsEnabled; model.createContent = createContent; model.uploadCustomContent = uploadCustomContent;}/** * Main entrypoint for component webscript logic * * @method main */function main(){ var myConfig = new XML(config.script); getPreferences(); getActionSet(myConfig); getCreateContent(myConfig);}main();
'<tomcat>/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/include/toolbar.lib.ftl':
<#include "../../../include/alfresco-macros.lib.ftl" /><#macro toolbarTemplate><#nested><#assign el=args.htmlid?html><div id="${el}-body" class="toolbar"> <div id="${el}-headerBar" class="header-bar flat-button theme-bg-2"> <div class="left"> <div class="hideable toolbar-hidden DocListTree"> <div class="create-content"> <button id="${el}-createContent-button" name="createContent">${msg("button.create-content")}</button> <div id="${el}-createContent-menu" class="yuimenu"> <div class="bd"> <ul> <#list createContent as content> <#assign href>create-content?mimeType=${content.mimetype?html}&destination={nodeRef}<#if (content.formid!"") != "">&formId=${content.formid?html}</#if></#assign> <li><a href="${siteURL(href)}" rel="${content.permission!""}"><span class="${content.icon}-file">${msg(content.label)}</span></a></li> </#list> <#list uploadCustomContent as content> <#assign href>custom-create-content?type=${content.type?html}&destination={nodeRef}<#if (content.formid!"") != "">&formId=${content.formid?html}</#if></#assign> <li><a href="${siteURL(href)}" rel="${content.permission!""}"><span class="${content.icon}-file">${msg(content.label)}</span></a></li> </#list> </ul> </div> </div> </div> <div class="separator"> </div> </div> <div class="hideable toolbar-hidden DocListTree"> <div class="new-folder"><button id="${el}-newFolder-button" name="newFolder">${msg("button.new-folder")}</button></div> <div class="separator"> </div> </div> <div class="hideable toolbar-hidden DocListTree"> <div class="file-upload"><button id="${el}-fileUpload-button" name="fileUpload">${msg("button.upload")}</button></div> <div class="separator"> </div> </div> <div class="selected-items hideable toolbar-hidden DocListTree DocListFilter TagFilter DocListCategories"> <button class="no-access-check" id="${el}-selectedItems-button" name="doclist-selectedItems-button">${msg("menu.selected-items")}</button> <div id="${el}-selectedItems-menu" class="yuimenu"> <div class="bd"> <ul> <#list actionSet as action> <li><a type="${action.asset!""}" rel="${action.permission!""}" href="${action.href}"><span class="${action.id}">${msg(action.label)}</span></a></li> </#list> <li><a href="#"><hr /></a></li> <li><a href="#"><span class="onActionDeselectAll">${msg("menu.selected-items.deselect-all")}</span></a></li> </ul> </div> </div> </div> </div> <div class="right"> <div class="customize" style="display: none;"><button id="${el}-customize-button" name="customize">${msg("button.customize")}</button></div> <div class="hide-navbar"><button id="${el}-hideNavBar-button" name="hideNavBar">${msg("button.navbar.hide")}</button></div> <div class="rss-feed"><button id="${el}-rssFeed-button" name="rssFeed">${msg("link.rss-feed")}</button></div> </div> </div> <div id="${el}-navBar" class="nav-bar flat-button theme-bg-4"> <div class="hideable toolbar-hidden DocListTree DocListCategories"> <div class="folder-up"><button class="no-access-check" id="${el}-folderUp-button" name="folderUp">${msg("button.up")}</button></div> <div class="separator"> </div> </div> <div id="${el}-breadcrumb" class="breadcrumb hideable toolbar-hidden DocListTree DocListCategories"></div> <div id="${el}-description" class="description hideable toolbar-hidden DocListFilter TagFilter"></div> </div></div></#macro>
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-15-2010 03:28 PM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-30-2011 09:54 AM

/Erik
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-01-2011 03:03 AM
Description XML:
<webscript> <shortname>CUSTOM type documents</shortname> <description>Webscript to crete custom documents</description> <url>/custom/documentos/{type}</url> <format default="html"/> <authentication>user</authentication></webscript>
Javascript:
function _DOCUMENTOS_addFile(files, field, properties){ var name = _DOCUMENTOS_prop2Name(field.name); files[name] = field; return true;}function _DOCUMENTOS_addProperty(properties, field){ var separator = ","; var name = _DOCUMENTOS_prop2Name(field.name); if (!_DOCUMENTOS_isMultiValued(name)) { properties[name] = field.value; } else { properties[name] = field.value.split(separator); } }function _DOCUMENTOS_constructDetailsUrl(callerUrl, nodeRef){ var baseUrl = callerUrl.substring(0,callerUrl.lastIndexOf("/custom-create-content")); var url = baseUrl + "/document-details?nodeRef=" + nodeRef; return url;}function _DOCUMENTOS_isMultiValued(name){ var qName = dictionaryService.getQName(name); var propDef = dictionaryService.getProperty(qName); return propDef.isMultiValued();}function main() { var destination = null; var files = []; var i = null; var node = null; var properties = []; var redirect = null; var type = null; for each (field in formdata.fields) { var name = String(field.name).toLowerCase(); if (field.isFile) { _DOCUMENTOS_addFile(files, field, properties); } else if ("alf_destination" == name) { destination = field.value; } else if ("alf_redirect" == name) { redirect = field.value; } else if (-1 != name.search("^prop_")){ _DOCUMENTOS_addProperty(properties, field); } } parent = search.findNode(destination); if (undefined == properties['cm:name'] && undefined != files["cm:content"]) { filename = files["cm:content"].filename; } else { filename = properties["cm:name"]; } var counter = 1, tmpFilename = filename, dotIndex; while (null !== parent.childByNamePath(tmpFilename)) { dotIndex = filename.lastIndexOf("."); if (dotIndex == 0) { // File didn't have a proper 'name' instead it had just a suffix and started with a ".", create "1.txt" tmpFilename = counter + filename; } else if (dotIndex > 0) { // Filename contained ".", create "filename-1.txt" tmpFilename = filename.substring(0, dotIndex) + "-" + counter + filename.substring(dotIndex); } else { // Filename didn't contain a dot at all, create "filename-1" tmpFilename = filename + "-" + counter; } counter++; } filename = tmpFilename; properties["cm:name"] = filename; type = "custom:" + url.templateArgs.type; node = parent.createNode(filename, type, properties); for (i in files) { node.properties[i].write(files[i]["content"]); node.properties[i].mimetype = files[i]["mimetype"]; node.properties[i].guessEncoding(); } model.node = node; model.type = url.templateArgs.type; model.detailsUrl = _DOCUMENTOS_constructDetailsUrl(headers.referer, node.nodeRef);}function _DOCUMENTOS_prop2Name(name){ name = name.substring(5); name = name.replace("_", ":"); return name;}main();
JSON template (.json.ftl), not sure why I needed this:
{ "persistedObject": "${node.nodeRef}", "message": "Successfully persisted form for item [type]eun_${type}"}
HTML template, this is really the nastiest thing I could to manage to go back to the details view page after uploading, but couldn't find a better way:
<script>parent.location.href="${detailsUrl}"</script>
Bye.
