AEM 63 - Touch UI Assets Console add Metadata while Uploading Files

Goal


Show simple Metadata Form in the Upload Dialog of Touch UI Assets Console; With this extension users can add metadata while uploading files

For AEM 62 check this post

Demo | Package Install


Metadata Form in Upload Dialog





Metadata Validation



Metadata in CRX



Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de), create folder /apps/eaem-assets-file-upload-with-metadata

2) Metadata form shown in Upload Dialog is an Authoring Dialog; Create the dialog /apps/eaem-assets-file-upload-with-metadata/dialog, add metadata form nodes




      Dialog XML

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/foundation/container">
    <layout
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
        margin="{Boolean}false"/>
    <items jcr:primaryType="nt:unstructured">
        <column
            jcr:primaryType="nt:unstructured"
            sling:resourceType="granite/ui/components/foundation/container">
            <items jcr:primaryType="nt:unstructured">
                <title
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/foundation/form/textfield"
                    fieldDescription="Enter Title"
                    fieldLabel="Title"
                    name="./eaemTitle"
                    required="{Boolean}true"/>
                <useBy
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/foundation/form/datepicker"
                    class="field"
                    displayedFormat="YYYY-MM-DD HH:mm"
                    fieldLabel="Use By"
                    name="./eaemUseBy"
                    type="datetime"/>
                <onlyForWeb
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/foundation/form/checkbox"
                    name="./eaemOnlyForWeb"
                    text="Only for web?"/>
                <type
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/foundation/form/select"
                    fieldDescription="Select Size"
                    fieldLabel="Size"
                    name="./eaemSize">
                    <items jcr:primaryType="nt:unstructured">
                        <def
                            jcr:primaryType="nt:unstructured"
                            text="Select Size"
                            value=""/>
                        <small
                            jcr:primaryType="nt:unstructured"
                            text="Small"
                            value="small"/>
                        <medium
                            jcr:primaryType="nt:unstructured"
                            text="Medium"
                            value="medium"/>
                        <large
                            jcr:primaryType="nt:unstructured"
                            text="Large"
                            value="large"/>
                    </items>
                </type>
                <tags
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="cq/gui/components/common/tagspicker"
                    allowCreate="{Boolean}true"
                    fieldLabel="Tags"
                    name="./eaemTags"/>
            </items>
        </column>
    </items>
</jcr:root>


3) Create node /apps/eaem-assets-file-upload-with-metadata/clientlib of type cq:ClientLibraryFolder, add String[] property categories with value dam.gui.coral.fileupload String[] property dependencies with value underscore

4) Create file (nt:file) /apps/eaem-assets-file-upload-with-metadata/clientlib/js.txt, add

            fileupload-with-metadata.js

5) Create file (nt:file) /apps/eaem-assets-file-upload-with-metadata/clientlib/fileupload-with-metadata.js, add the following code

(function($, $document) {
    var METADATA_DIALOG = "/apps/eaem-assets-file-upload-with-metadata/dialog.html",
        METADATA_PREFIX = "eaem",
        UPLOAD_LIST_DIALOG = "#uploadListDialog",
        ACTION_CHECK_DATA_VALIDITY = "ACTION_CHECK_DATA_VALIDITY",
        ACTION_POST_METADATA = "ACTION_POST_METADATA",
        url = document.location.pathname;

    if( url.indexOf("/assets.html") == 0 ){
        handleAssetsConsole();
    }else if(url.indexOf(METADATA_DIALOG) == 0){
        handleMetadataDialog();
    }

    function handleAssetsConsole(){
        $document.on("foundation-contentloaded", handleFileAdditions);
    }

    function handleMetadataDialog(){
        $(function(){
            _.defer(styleMetadataIframe);
        });
    }

    function registerReceiveDataListener(handler) {
        if (window.addEventListener) {
            window.addEventListener("message", handler, false);
        } else if (window.attachEvent) {
            window.attachEvent("onmessage", handler);
        }
    }

    function styleMetadataIframe(){
        var $dialog = $("coral-dialog");

        if(_.isEmpty($dialog)){
            return;
        }

        $dialog.css("overflow", "hidden");

        $dialog[0].open = true;

        var $header = $dialog.css("background-color", "#fff").find(".coral-Dialog-header");

        $header.find(".cq-dialog-actions").remove();

        $dialog.find(".coral-Dialog-wrapper").css("margin","0").find(".coral-Dialog-content").css("padding","0");

        registerReceiveDataListener(postMetadata);

        function postMetadata(event){
            var message = JSON.parse(event.data);

            if( message.action !== ACTION_CHECK_DATA_VALIDITY ){
                return;
            }

            var $dialog = $(".cq-dialog"),
                $fields = $dialog.find("[name^='./']"),
                data = {}, $field, $fValidation, name, value, values,
                isDataInValid = false;

            $fields.each(function(index, field){
                $field = $(field);
                name = $field.attr("name");
                value = $field.val();

                $fValidation = $field.adaptTo("foundation-validation");

                if($fValidation && !$fValidation.checkValidity()){
                    isDataInValid = true;
                }

                $field.updateErrorUI();

                if(_.isEmpty(value)){
                    return;
                }

                name = name.substr(2);

                if(!name.indexOf(METADATA_PREFIX) === 0){
                    return
                }

                if(!_.isEmpty(data[name])){
                    if(_.isArray(data[name])){
                        data[name].push(value);
                    }else{
                        values =  [];
                        values.push(data[name]);
                        values.push(value);

                        data[name] = values;
                        data[name + "@TypeHint"] = "String[]";
                    }
                }else{
                    data[name] = value;
                }
            });

            sendValidityMessage(isDataInValid, data);
        }

        function sendValidityMessage(isDataInValid, data){
            var message = {
                action: ACTION_CHECK_DATA_VALIDITY,
                data: data,
                isDataInValid: isDataInValid
            };

            parent.postMessage(JSON.stringify(message), "*");
        }
    }

    function handleFileAdditions(){
        var $fileUpload = $("coral-chunkfileupload"),
            $metadataIFrame, $uploadButton, validateUploadButton,
            metadata;

        $fileUpload.on('coral-fileupload:fileadded', addMetadataDialog);

        $fileUpload.on('coral-fileupload:loadend', postMetadata);

        function sendDataMessage(message){
            $metadataIFrame[0].contentWindow.postMessage(JSON.stringify(message), "*");
        }

        function addMetadataDialog(){
            _.debounce(addDialog, 500)();
        }

        function addDialog(){
            var $dialog = $(UPLOAD_LIST_DIALOG);

            if(!_.isEmpty($dialog.find("iframe"))){
                return;
            }

            var iFrame = '<iframe width="550px" height="450px" frameborder="0" src="' + METADATA_DIALOG + '"/>',
            $dialogContent = $dialog.find("coral-dialog-content");

            $metadataIFrame = $(iFrame).appendTo($dialogContent.css("max-height", "600px"));
            $dialogContent.find("input").css("width", "30rem");
            $dialogContent.closest(".coral-Dialog-wrapper").css("top", "30%").css("left", "50%");

            addValidateUploadButton($dialog);
        }

        function addValidateUploadButton($dialog){
            var $footer = $dialog.find("coral-dialog-footer");

            $uploadButton = $footer.find("coral-button-label:contains('Upload')").closest("button");

            validateUploadButton = new Coral.Button().set({
                variant: 'primary'
            });

            validateUploadButton.label.textContent = Granite.I18n.get('Upload');

            validateUploadButton.classList.add('dam-asset-upload-button');

            $footer[0].appendChild(validateUploadButton);

            $uploadButton.hide();

            validateUploadButton.hide();

            validateUploadButton.on('click', function() {
                checkDataValidity();
            });

            $metadataIFrame.on('load', function(){
                validateUploadButton.show();
            });

            registerReceiveDataListener(isMetadataValid);
        }

        function isMetadataValid(event){
            var message = JSON.parse(event.data);

            if( (message.action !== ACTION_CHECK_DATA_VALIDITY)){
                return;
            }

            if(message.isDataInValid){
                return;
            }

            metadata = message.data;

            validateUploadButton.hide();

            $uploadButton.click();
        }

        function checkDataValidity(){
            var message = {
                action: ACTION_CHECK_DATA_VALIDITY
            };

            sendDataMessage(message);
        }

        function postMetadata(event){
            var detail = event.originalEvent.detail,
                folderPath = detail.action.replace(".createasset.html", ""),
                assetMetadataPath = folderPath + "/" + detail.item.name + "/jcr:content/metadata";

            //jcr:content/metadata created by the DAM Update asset workflow may not be available when the below post
            //call executes; ideally, post the parameters to aem, a scheduler runs and adds the metadata when metadata
            //node becomes available
            $.ajax({
                type : 'POST',
                url : assetMetadataPath,
                data  : metadata
            })
        }
    }

})(jQuery, jQuery(document));

6) #212 is for demo purposes only; in real world implementations, the metadata node created by DAM Update Asset workflow may not exist yet, when the ajax post metadata call executes. Replace this direct call with a servlet temporarily storing the form metadata and update the metadata node (when it becomes available) in a scheduler or listener

3 comments:

  1. Hi Sreekanth, if part of the metadata is a pathbrowser field which allows user to select which folder the image should be saved, how do you change the action path dynamically?

    ReplyDelete
  2. In point 6) you mention that the node may not exist. I think this is the case as your sample code never adds the metadata to an asset. Can you point me in the right direction for how to modify this code to actually work and update the asset. Any help is much appreciated. thanks

    ReplyDelete