Try our conversational search powered by Generative AI!

Loading...
Area: Optimizely CMS
ARCHIVED This content is retired and no longer maintained. See the latest version here.

Recommended reading 

Table of contents

Introduction

This document describes how drag-and-drop works in the EPiServer user interface. Drag-and-drop in EPiServer uses the "dnd" system in Dojo with some additions. A good tutorial about the dnd system in Dojo can be found here http://www.sitepen.com/blog/2011/12/05/dojo-drag-n-drop-redux/. Note: To use the additional functionality in EPiServer your drag source or target class should implement the EPiServer shell classes instead of the Dojo base classes ("epi/shell/dnd/Source" or "epi/shell/dnd/Target").

Creating sources and targets

The following example is a fully functioning component that both implements a drag source and a drag target.

JavaScript
define("epi/samples/dnd/DndSamples", [
"dojo",
"dijit",
"dijit/_Widget",
"dijit/_TemplatedMixin",
//EPiServer dnd classes
"epi/shell/dnd/Source",
"epi/shell/dnd/Target"
],

function (dojo, dijit, _Widget, _TemplatedMixin, Source, Target) {

    return dojo.declare("epi.samples.dnd.DndSamples", [_Widget, _TemplatedMixin], {

        templateString: '\
            <div>\
                <h2>Source</h2>\
                <ul dojoAttachPoint="source"></ul>\
                <h2>Target</h2>\
                <ul dojoAttachPoint="target"><li>Test</li></ul>\
            </div>',
            //Seems to be a bug when we have an empty list so we just create a dummy list item for the example.


        postCreate: function () {
            this.inherited(arguments);

            this._setupSource();
            this._setupTarget();
        },

        _setupSource: function () {
            var source = new Source(this.source, {
                creator: dojo.hitch(this, this._createDragItem),
                copyOnly: true,
                selfAccept: false,
                selfCopy: false
            });

            source.insertNodes(false,
            [{ url: "http://www.fakecompany.com", text: "Company web site", type: ["link"] },
            { url: "http://www.episerver.com", text: "EPiServer", type: ["link"] },
            { url: "http://www.dn.se", text: "Daily news", type: ["link"]}]);
        },

        _setupTarget: function () {
            var target = new Target(this.target, {
                accept: ["link", "someothertype"],
                //Set createItemOnDrop if you're only interested to receive the data, and not create a new node.
                createItemOnDrop: true
            });

            this.connect(target, "onDropData", "_onDropData");
        },

        _createDragItem: function (item, hint) {

            var node;

            if (hint == "avatar") {
                //This node is used when dragging the item.
                node = dojo.create("div", { innerHTML: item.text });
            } else {
                node = dojo.create("li", { innerHTML: item.text });
            }

            return {
                "node": node,
                "type": item.type,
                "data": item
            };
        },

        _onDropData: function (dndData, source, nodes, copy) {
            //Drop item is an array with dragged items. This example just handles the first item.
            var dropItem = dndData ? (dndData.length ? dndData[0] : dndData) : null;

            if (dropItem) {

                //The data property might be a deffered so we need to call dojo.when just in case.
                dojo.when(dropItem.data, dojo.hitch(this, function (value) {
                    //Do something with the data, here we just log it to the console.
                    console.log(value);
                }));
            }
        }
    });
});

Data types

Since JavaScript is a loosely typed language data types should be seen as a contract or interface. EPiServer uses a number of known types for drag and drop which are described here:
Type name Data format
"epi.cms.pagereference"
"epi.cms.blockreference"
"epi.cms.folderreference"
"epi.cms.contentreference"
A string representation of a content reference, for instance "3_122". There are converters that converts specific types (ie page reference) to content references. This means that a drag target that accepts content references also accepts page references but not vice versa.
"fileurl" A URL to any file, for instance "/myvpp/myfolder/mydocument.pdf"
"imageurl" A URL to an image, for instance "/myvpp/myfolder/myimage.jpg"
"link" An object with two properties:
  • url: the URL to the resource
  • text: the inner text for the link
"epi.cms.content.lighturi" (there are specific versions as well like "epi.cms.page.lighturi") An object with two properties:
  • name: The name of the page
  • uri: The string representation of the content uri. This might or might not include version information. For instance "epi.cms.contentdata:///8".
"epi.cms.content.light" (there are specific versions as well like "epi.cms.page.light") An object with two properties:
  • name: The name of the page
  • contentLink: The string representation of the content reference without any version information. For instance "123".

Type converters

Sometimes you might want to support drag and drop to or from something that you do not control the source code for. You might also have the need to convert the dragged data to another format. Since you might specify types as an array both for the source and the target you can sometimes handle this by specifying all types that the source can deliver and that the target accepts. For a target you might have to convert the data on acceptance. For the source this works fine as long as the object can be created and still support several types. An example: A dnd source delivers image URL:s. It may specify the type as ["imageurl", "fileurl"] as both these types expects the object to the a string with the URL to the actual content. This content can then be dragged to targets that accepts either an "imageurl" or a "fileurl". But what if you want to drag this content to a target that accepts a link? The source cannot create an object that can be both an "imageurl" or "fileurl" and a link at the same time since the link expects a complex object with two properties: url and text.

To handle these kind of scenarios EPiServer has added something called object type converters. These are simple classes that implements a convert method with sourceDataType, targetDataType and data as parameters. There is a converter registry, which is a singleton, that can be used to register a converter for different combinations of conversions. These can be registered in the initialization method of your module class:

JavaScript
var converterRegistry = this.resolveDependency("epi.objectconverterregistry");
var converter = new ContentLightUriConverter();
converter.registerConverter(converterRegistry);

The following example shows how to convert between different content dnd formats. In a standard installation, this is used to be able to drag pages from for instance the page tree to a page selector to select a page reference or a Tiny MCE editor to create a link to the page.

JavaScript
define(
["dojo",
"epi/dependency",
"epi/UriParser",
"epi/cms/core/ContentReference"
],
    function (dojo, dependency, UriParser, ContentReference) {

        return dojo.declare("epi.cms.conversion.ContentLightUriConverter", null, {
            // summary:
            //    Converts data between light weight content types containing an URI as a specifyer and other types.
            // 
            // tags:
            //    public
            _pageDataStore: null,

            constructor: function(params) {
                // summary:
                //    The constructor function
                // 
                // params: Object
                //    The parameters that define the object.
                // tags:
                //    public

                dojo.mixin(this, params);

                this._pageDataStore = this._pageDataStore || dependency.resolve("epi.storeregistry").get("epi.cms.contentdata");
            },

            registerConverter: function(/*Object*/registry) {
                // summary:
                //    Registers the type conversions that this class supports to the given registry.
                // 
                // registry: Object
                //    The converter registry to add mappings to.
                registry.registerConverter("epi.cms.page.lighturi", "link", this);
                registry.registerConverter("epi.cms.page.lighturi", "epi.cms.pagereference", this);
                registry.registerConverter("epi.cms.block.lighturi", "epi.cms.blockreference", this);
                registry.registerConverter("epi.cms.folder.lighturi", "epi.cms.folderreference", this);
                registry.registerConverter("epi.cms.content.lighturi", "epi.cms.contentreference", this);
                registry.registerConverter("epi.cms.content.lighturi", "epi.cms.content.light", this);
                registry.registerConverter("epi.cms.page.lighturi", "epi.cms.content.light", this);
                registry.registerConverter("epi.cms.block.lighturi", "epi.cms.content.light", this);
                registry.registerConverter("epi.cms.folder.lighturi", "epi.cms.content.light", this);
            },

            convert: function(/*String*/sourceDataType, /*String*/targetDataType, /*Object*/data) {
                // summary:
                //    Converts data between two types.
                // 
                // sourceDataType: String
                //    The source data type.
                // 
                // targetDataType: String
                //    The target data type.
                // 
                // converter: Object
                //    The class that performs the actual conversion.
                // tags:
                //    public
                if (!data) {
                    return null;
                } else if (targetDataType === "epi.cms.content.lighturi") {
                    return data;
                }
                var uri = new UriParser(data.uri);
                var link = uri.getId();
                var versionAgnosticId = new ContentReference(link).createVersionUnspecificReference().toString();

                if (targetDataType === "epi.cms.content.light"
                    || targetDataType === "epi.cms.page.light"
                    || targetDataType === "epi.cms.block.light"
                    || targetDataType === "epi.cms.folder.light") {
                    return { contentLink: versionAgnosticId, name: data.name };
                } else if (targetDataType === "epi.cms.contentreference"
                    || targetDataType === "epi.cms.pagereference"
                    || targetDataType === "epi.cms.blockreference"
                    || targetDataType === "epi.cms.folderreference") {
                    return versionAgnosticId;
                }
                if (targetDataType === "link") {
                    //should only be available for pages right now but allow anything for future types as files etc.
                    return dojo.when(this._pageDataStore.get(versionAgnosticId), function(page) {
                        return { url: page.properties.pageLinkURL, text: page.name };
                    });
                }
            }
        });
    });
Do you find this information helpful? Please log in to provide feedback.

Last updated: Jul 09, 2014

Recommended reading