/*
* Copyright 2015 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This tool displays the sitemap of a site
* @private
*/
Ext.define('Ametys.plugins.web.sitemap.SitemapTool', {
extend: 'Ametys.tool.Tool',
/**
* @private
* @property {Ext.tree.Panel} _tree The sites tree panel
*/
constructor: function(config)
{
this.callParent(arguments);
this._indicators = config.indicators ? JSON.parse(config.indicators) : [];
Ametys.message.MessageBus.on(Ametys.message.Message.SELECTION_CHANGED, this._onSelectionChanged, this);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._onCreated, this);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDeleted, this);
Ametys.message.MessageBus.on(Ametys.message.Message.WORKFLOW_CHANGED, this._onWorkflowChanged, this);
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
createPanel: function ()
{
var langCombo = Ametys.cms.language.LanguageDAO.createComboBox({
itemId: 'sitemap-tool-languages',
fieldLabel: "{{i18n PLUGINS_WEB_TOOL_SITEMAP_SITEMAPTOOL_LANGUAGE}}",
flex: 1,
synchronize: true,
listeners: {
'change': Ext.bind(this._onChangeLang, this)
}
});
this._tree = this._createSitemapTreePanel(langCombo.getValue());
return Ext.create ('Ext.Panel', {
cls: 'sitemap-tool',
border: false,
layout: 'fit',
// Languages combo box
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
layout: {
type: 'hbox',
align: 'stretch'
},
border: false,
defaults : {
cls: 'ametys',
labelWidth: 55,
labelSeparator: ''
},
items: langCombo
}],
items: this._tree
});
},
/**
* Listens when a language is changed.
* @param {Ext.form.field.Field} field The field
* @param {Object} newValue The new language value.
* @param {Object} oldValue The original value.
* @private
*/
_onChangeLang: function(field, newValue, oldValue)
{
this.showRefreshing();
// Reload sitemap
this._tree.setSitemapName (newValue, Ext.bind(this._refreshCb, this));
},
sendCurrentSelection: function()
{
var selection = this._tree.getSelectionModel().getSelection();
var parameters = {};
var targets = [];
if (selection.length > 0)
{
var node = selection[0];
targets.push(this.getMessageTargetConfiguration(node));
parameters.creation = targets[0].id;
}
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
parameters: parameters,
targets: targets
});
},
setParams: function (params)
{
this.callParent(arguments);
this.refresh();
},
refresh: function ()
{
this.showRefreshing();
this._tree.initialize (Ext.bind(this._refreshCb, this));
},
/**
* @private
* Callback function after (re)loading the tree
*/
_refreshCb: function ()
{
this.showRefreshed();
// Select root node
this._tree.getSelectionModel().select(0);
},
/**
* Get the tree of the tool
* @return {Ext.tree.Panel} The main tree component of the tool
*/
getTree: function()
{
return this._tree;
},
/**
* Expand the tree and select a node by its path
* @param {String} path The path of the node
*/
selectNodeByPath: function (path)
{
this.getTree().selectByPath(path);
},
/**
* @private
* Draw the tree panel for the sites
* @param {String} defaultLang the default sitemap name
*/
_createSitemapTreePanel: function (defaultLang)
{
return Ext.create('Ametys.web.sitemap.SitemapTree', {
// Language selection is handled by the tool itself
sitemapContext: defaultLang,
defaultSitemapName: defaultLang,
indicators: this._indicators,
stateful: true,
stateId: this.getId() + '$sitemap-tree',
// handle drag & drop
viewConfig: {
plugins: {
ptype: 'ametystreeviewdragdrop',
containerScroll: true,
appendOnly: false,
sortOnDrop: false,
expandDelay: 500,
allowContainerDrops: false,
setAmetysDragInfos: Ext.bind(this.getDragInfo, this),
setAmetysDropZoneInfos: Ext.bind(this.getDropInfo, this)
}
},
listeners: {
'selectionchange': Ext.bind(this.sendCurrentSelection, this),
'itemdblclick': Ext.bind(this._openPage, this)
}
});
},
/**
* @private
* This event is thrown by the getDragData to add the 'source' of the drag.
* @param {Object} item The default drag data that will be transmitted. You have to add a 'source' item in it:
* @param {Ametys.relation.RelationPoint} item.source The source (in the relation way) of the drag operation.
*/
getDragInfo: function (item)
{
var targets = [];
for (var i = 0; i < item.records.length; i++)
{
var node = item.records[i];
if (!node.isRoot() && node.getData().moveable)
{
targets.push(this.getMessageTargetConfiguration(node));
}
}
if (targets.length > 0)
{
item.source = {
relationTypes: [Ametys.relation.Relation.MOVE, Ametys.relation.Relation.REFERENCE],
targets: targets
};
}
},
/**
* @private
* This event is thrown before the beforeDrop event and create the target of the drop operation relation.
* @param {Ext.data.Model[]} targetRecords The target records of the drop operation.
* @param {Object} item The default drag data that will be transmitted. You have to add a 'target' item in it:
* @param {Object} item.target The target (in the relation way) of the drop operation. A Ametys.relation.RelationPoint config.
* @param {"append"/"before"/"after"} dropPosition The drop mode
*/
getDropInfo: function(targetRecords, item, dropPosition)
{
var targets = [];
var positionInTargets = -1
for (var i = 0; i < targetRecords.length; i++)
{
var node = targetRecords[i];
if (node.isRoot() || dropPosition == 'append')
{
targets.push(this.getMessageTargetConfiguration(node));
}
else if (node.parentNode != null) // dropPosition == 'before' or 'after'
{
// Get node position
for (var i=0; i < node.parentNode.childNodes.length; i++)
{
if (node.parentNode.childNodes[i].getId() == node.getId())
{
positionInTargets = i + (dropPosition == 'after' ? + 1 : 0);
break;
}
}
targets.push(this.getMessageTargetConfiguration(node.parentNode));
}
}
if (targets.length > 0)
{
item.target = {
relationTypes: [Ametys.relation.Relation.MOVE, Ametys.relation.Relation.REFERENCE],
targets: targets,
positionInTargets: positionInTargets
};
}
},
/**
* @private
* Get the target configuration object for given record
* @param {Ext.data.Model} record The tree record to convert to its Ametys.message.MessageTarget configuration
* @return {Object} The configuration to create a Ametys.message.MessageTarget. Can be null, if the record is null or not relevant to be a messagetarget.
*/
getMessageTargetConfiguration: function (record)
{
if (record == null)
{
// Empty selection
return null;
}
else
{
if (record.isRoot())
{
return {
id: Ametys.message.MessageTarget.SITEMAP,
parameters: {id: record.getId()}
};
}
else
{
return {
id: Ametys.message.MessageTarget.PAGE,
parameters: {ids: [record.getId()]}
};
}
}
},
/**
* @private
* Open page on double-click event
* @param {Ext.view.View} view the tree view
* @param {Ext.data.Model} node The record that belongs to the double-clicked item
*/
_openPage: function (view, node)
{
Ametys.tool.ToolsManager.openTool (node.isRoot() ? "uitool-sitemappage" : "uitool-page", {id: node.getId()});
},
/**
* @private
* Listener on the current selection
* @param {Ametys.message.Message} message the selection message
*/
_onSelectionChanged: function(message)
{
var target = message.getTarget(Ametys.message.MessageTarget.PAGE) || message.getTarget(Ametys.message.MessageTarget.SITEMAP);
if (target != null)
{
var selection = this._tree.getSelectionModel().getSelection();
var node = this._tree.getStore().getNodeById(target.getParameters().id);
if (node != null && selection.length > 0 && node == selection[0])
{
// same selection
return;
}
// Pages from 2 different sitemaps can have the same path
if (this._tree.getCurrentSitemapName() == target.getParameters().lang)
{
// Page
this.selectNodeByPath(target.getParameters().path);
}
else if (this._tree.getCurrentSitemapName() == target.getParameters().name)
{
// Sitemap
this.selectNodeByPath(target.getParameters().name);
}
}
},
/**
* @private
* Listener on site edition
* @param {Ametys.message.Message} message The edition message
*/
_onModified: function(message)
{
var major = message.getParameters().major == true;
var target = message.getTarget(Ametys.message.MessageTarget.SITEMAP);
if (target != null)
{
this._tree.getStore().load({node: this._tree.getRootNode()});
}
var targets = message.getTargets(Ametys.message.MessageTarget.PAGE);
if (targets.length > 0)
{
var store = this._tree.getStore();
Ext.Array.forEach(targets, function(target) {
var node = this._tree.getStore().getNodeById(target.getParameters().id);
if (node != null)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.SitemapDAO",
methodName: "getPageProperties",
parameters: [target.getParameters().id],
callback: {
scope: this,
handler: function (data) {
if (major)
{
// only refresh the edited node
this._tree.refreshNode(target.getParameters().id, Ext.bind(this._tree.updateNodeData, this._tree, [target.getParameters().id, data]));
}
else
{
// Update page node only
this._tree.updateNodeData (target.getParameters().id, data);
}
}
},
errorMessage: false
});
}
}, this);
}
},
/**
* @private
* Listener invoked on workflow changed message
* @param {Ametys.message.Message} message The message
*/
_onWorkflowChanged: function (message)
{
var targets = message.getTargets(Ametys.message.MessageTarget.CONTENT);
if (targets.length > 0)
{
for (var i=0; i < targets.length; i++)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.SitemapDAO",
methodName: "getReferencingPages",
parameters: [targets[i].getParameters().id],
callback: {
scope: this,
handler: this._getReferencingPagesCb
},
errorMessage: "{{i18n PLUGINS_WEB_DAOS_CONTENT_GET_REFERENCING_PAGES_ERROR}}"
});
}
}
},
/**
* @private
* Callback function invoked after getting referencing pages of a content.
* Update pages UI if visible in the tree.
* @param {Object[]} pages the referencing pages
*/
_getReferencingPagesCb: function (pages)
{
var me = this;
Ext.Array.forEach (pages, function (page, index) {
if (me._tree.getCurrentSiteName() == page.siteName && me._tree.getCurrentSitemapName() == page.lang)
{
me._tree.updateNodeData (page.id, page);
me._tree.refreshNode(page.id);
}
})
},
/**
* @private
* Listener on page creation
* @param {Ametys.message.Message} message The creation message
*/
_onCreated: function (message)
{
var target = message.getTarget(Ametys.message.MessageTarget.PAGE);
if (target != null)
{
var store = this._tree.getStore();
var node = store.getNodeById(target.getParameters().parentId);
if (node == null)
{
node = this._tree.getRootNode();
}
node.set('expanded', true);
store.load({node: node});
}
},
/**
* @private
* Listener on site deletion
* @param {Ametys.message.Message} message The deletion message
*/
_onDeleted: function (message)
{
var targets = message.getTargets(Ametys.message.MessageTarget.PAGE);
if (targets.length > 0)
{
var store = this._tree.getStore();
var me = this;
Ext.Array.forEach(targets, function(target) {
var node = store.getNodeById(target.getParameters().id);
if (node != null)
{
var parentNode = node.parentNode;
var parentNode = node.parentNode;
me._tree.getSelectionModel().select([parentNode]);
node.remove();
}
});
}
}
});