/**
* Copyright (C) 2005-2016 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* <p>This extends the [abstract document list view]{@link module:alfresco/lists/views/AlfListView}
* to define a widget for rendering items that have been selected from a [picker]{@link module:alfresco/pickers/DocumentListPicker}.
* The key difference is that this widget does not listen for "bulk" data deliveries but rather renders individual
* items as they are published. It also provides the ability to remove items from the current selection. The ultimate
* purpose it to be able to provide a value that is an array of selected items (e.g. for use in a form control).</p>
*
* @module alfresco/pickers/PickedItems
* @extends module:alfresco/lists/views/AlfListView
* @author Dave Draper
* @author David Webster
*/
define(["dojo/_base/declare",
"alfresco/lists/views/AlfListView",
"dojo/_base/lang",
"dojo/_base/array",
"alfresco/core/ObjectTypeUtils"],
function (declare, AlfListView, lang, array, ObjectTypeUtils) {
return declare([AlfListView], {
/**
* Set the i18n scope so that it's possible to pick up the overridden "no items" message.
*
* @instance
* @type {string}
* @default
*/
i18nScope: "alfresco.pickers.PickedItems",
/**
* An array of the i18n files to use with this widget.
*
* @instance
* @type {object[]}
* @default [{i18nFile: "./i18n/PickedItems.properties"}]
*/
i18nRequirements: [{i18nFile: "./i18n/PickedItems.properties"}],
/**
* If true, forces user to choose a single item only.
*
* @instance
* @type {Boolean}
* @default
*/
singleItemMode: false,
/**
* If true, allow zero items to be selected
*
* @instance
* @type {Boolean}
* @default
*/
allowNone: false,
/**
* Topic published when picker is valid
*
* @instance
* @type {string}
* @default
*/
validTopic: "ALF_PICKER_VALID",
/**
* Topic published when picker is invalid
*
* @instance
* @type {string}
* @default
*/
invalidTopic: "ALF_PICKER_INVALID",
/**
* Should picker validity be published globally?
*/
publishValidityGlobally: true,
/**
* Implements the widget life-cycle method to add drag-and-drop upload capabilities to the root DOM node.
* This allows files to be dragged and dropped from the operating system directly into the browser
* and uploaded to the location represented by the document list.
*
* @instance
*/
postCreate: function alfresco_pickers_PickedItems__postCreate() {
// TODO: Change doc load subscription...
// DOESN'T INHERIT BY DESIGN
this.removeUploadDragAndDrop(this.domNode);
// this.setupKeyboardNavigation();
// Initialise the data...
this.currentData = {
items: []
};
if (ObjectTypeUtils.isArray(this.value))
{
this.setPickedItems(this.value);
}
this.alfSubscribe("ALF_ITEM_SELECTED", lang.hitch(this, this.addPickedItem));
this.alfSubscribe("ALF_ITEM_REMOVED", lang.hitch(this, this.removePickedItem));
this.alfSubscribe("ALF_SET_PICKED_ITEMS", lang.hitch(this, this.onSetPickedItems));
this.alfSubscribe("ALF_ITEM_MOVED_DOWN", lang.hitch(this, this.onReorder, 1));
this.alfSubscribe("ALF_ITEM_MOVED_UP", lang.hitch(this, this.onReorder, -1));
this.renderView(false);
this.isValid();
},
/**
* Reset the current Data object.
*/
clearData: function alfresco_pickers_PickedItems__clearData() {
// Intentionally overridden to do nothing.
},
/**
* This is the dot-notation path to the attribute in the item that is the unique key. It is used
* to ensure that duplicate items are not added and also so that items can be removed.
*
* @instance
* @type {string}
* @default
*/
itemKey: "nodeRef",
/**
* Handles published information about picked items and renders the item in the view.
*
* @instance
* @param {object} payload The details of the item that has been picked
*/
addPickedItem: function alfresco_pickers_PickedItems__addPickedItem(payload) {
var keyToAdd = lang.getObject(this.itemKey, false, payload);
if (keyToAdd)
{
var existingKey = this.findPickedItem(keyToAdd);
if (existingKey)
{
this.alfLog("log", "Item is already picked - it will not be added a second time", payload, this);
}
else
{
// Should the item to add be the only item selected?
if (this.singleItemMode)
{
this.alfLog("info", "Removing all other selected items", payload, this);
array.forEach(this.currentData.items, function(item) {
this.alfPublish("ALF_ITEM_REMOVED", item);
}, this);
this.currentData.items = [payload];
this.renderView(false);
}
else {
this.currentData.items.push(payload);
array.forEach(this.currentData.items, function(item, index) {
item.index = index;
});
this.renderView(false);
}
// Publish the data about the items currently selected...
this.alfPublish("ALF_ITEMS_SELECTED", {
pickedItems: this.currentData.items
});
}
this.isValid();
}
else
{
this.alfLog("warn", "The supplied item does not have a key attribute as expected", payload, this);
}
},
/**
* Handles published information about previously picked items that have been removed.
* It finds the item, removes it from the current data set and then re-renders the data
*
* @instance
* @param {object} payload The details of the item that has been picked
*/
removePickedItem: function alfresco_pickers_PickedItems__removePickedItem(payload) {
var keyToRemove = lang.getObject(this.itemKey, false, payload);
if (keyToRemove)
{
// Filter out the target item...
this.currentData.items = array.filter(this.currentData.items, function(item) {
var key = lang.getObject(this.itemKey, false, item);
return key !== keyToRemove;
}, this);
array.forEach(this.currentData.items, function(item, index) {
item.index = index;
});
this.renderView(false);
this.alfPublish("ALF_ITEMS_SELECTED", {
pickedItems: this.currentData.items
});
this.isValid();
}
else
{
this.alfLog("warn", "The supplied item does not have a key attribute as expected", payload, this);
}
},
/**
* This module searches the current data set to try to find an item with the supplied
* key. The key is matched against the dot-notation path to an attribute located in
* each object. By default, the key looks for the nodeRef attribute in an item structure
* that matches data returned for a standard document list.
*
* @instance
* @param {string} targetKey The value to match against a key
* @returns {object} A matching item or null if one could not be found
*/
findPickedItem: function alfresco_pickers_PickedItems__findPickedItem(targetKey) {
var target = null;
var targetItems = array.filter(this.currentData.items, function(item) {
var key = lang.getObject(this.itemKey, false, item);
return key === targetKey;
}, this);
if (targetItems.length > 0)
{
target = targetItems[0];
}
return target;
},
/**
* Handles requests to set the picked items via a publication.
*
* @instance
* @param {object} payload
*/
onSetPickedItems: function alfresco_pickers_PickedItems_setPickedItems(payload) {
var items = lang.getObject("pickedItems", false, payload);
if (ObjectTypeUtils.isArray(items))
{
this.setPickedItems(items);
}
},
/**
* Sets and renders the supplied items
*
* @instance
* @pararm {object[]} items
*/
setPickedItems: function alfresco_pickers_PickedItems_setPickedItems(items) {
if (ObjectTypeUtils.isArray(items))
{
array.forEach(items, function(item) {
this.addPickedItem(item);
}, this);
}
else
{
this.alfLog("warn", "No items supplied to 'setPickedItems' function", items, this);
}
this.renderView(false);
this.isValid();
},
/**
* Re-orders the item in the current data.
*
* @instance
* @param {number} adjustment How to adjust the position, e.g. -1 to move up, 1 to move down
* @param {object} item The item to move.
*/
onReorder: function alfresco_pickers_PickedItems__onReorder(adjustment, item) {
var targetKey = lang.getObject(this.itemKey, false, item);
if (targetKey)
{
var targetIndex = null;
// Use "some" rather than "forEach" to exit loop immediately on match...
var found = array.some(this.currentData.items, function(currItem, index) {
var currKey = lang.getObject(this.itemKey, false, currItem);
if (currKey === targetKey)
{
targetIndex = index;
}
return currKey === targetKey;
}, this);
if (found === true)
{
var updatedIndex = targetIndex + adjustment;
if (updatedIndex >= 0 && updatedIndex <= this.currentData.items.length)
{
this.currentData.items[updatedIndex].index = targetIndex;
this.currentData.items[targetIndex].index = updatedIndex;
var tmp1 = this.currentData.items[updatedIndex];
var tmp2 = this.currentData.items[targetIndex];
this.currentData.items[targetIndex] = tmp1;
this.currentData.items[updatedIndex] = tmp2;
this.renderView(false);
this.alfPublish("ALF_ITEMS_SELECTED", {
pickedItems: this.currentData.items
});
}
}
}
},
/**
* Called to determine if this control is valid or not & send the appropriate flags if it isn't.
*
* @instance
*/
isValid: function alfresco_pickers_PickedItems_isValid() {
// This control is valid if there are one or more items selected, or allowNone is true.
if (this.allowNone || this.currentData.items.length > 0)
{
this.alfPublish(this.validTopic, {name: this.id}, this.publishValidityGlobally);
}
else
{
this.alfPublish(this.invalidTopic, {name: this.id}, this.publishValidityGlobally);
}
},
/**
* The widgets to be processed to generate each item in the rendered view.
*
* @instance
* @type {object[]}
*/
widgets: [
{
name: "alfresco/lists/views/layouts/Row",
config: {
widgets: [
{
name: "alfresco/lists/views/layouts/Cell",
config: {
width: "20px",
widgets: [
{
name: "alfresco/renderers/FileType",
config: {
size: "small",
renderAsLink: false
}
}
]
}
},
{
name: "alfresco/lists/views/layouts/Cell",
config: {
widgets: [
{
name: "alfresco/renderers/Property",
config: {
propertyToRender: "node.properties.cm:name",
renderAsLink: false
}
}
]
}
},
{
name: "alfresco/lists/views/layouts/Cell",
config: {
width: "20px",
widgets: [
{
name: "alfresco/renderers/PublishAction",
config: {
iconClass: "delete-16",
publishTopic: "ALF_ITEM_REMOVED",
publishPayloadType: "CURRENT_ITEM"
}
}
]
}
}
]
}
}
]
});
});