/*
* 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.
*/
/**
* {@link Ext.tree.Panel} for sitemap
*
* var tree = Ext.create('Ametys.web.sitemap.SitemapTree', {
*
* siteContext: Ametys.web.helper.ContextToolbar.SITE_CONTEXT_CURRENT,
* sitemapContext: Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL,
* defaultSitemapName: null,
* defaultSiteName: Ametys.getAppParameter('siteName'),
*
* allowCreation: true,
* values: ["page://xxxx", "page://yyyy"],
*
* checkMode: true
* });
*
* tree.initialize (Ext.bind(this._initializeCb, this));
*/
Ext.define('Ametys.web.sitemap.SitemapTree', {
extend: 'Ext.tree.Panel',
/**
* @cfg {String} defaultSiteName The default site name. Defaults to the current site name
*/
/**
* @cfg {String} [defaultSitemapName=null] The default language.
*/
/**
* @cfg {Boolean} [showSelectDecorators=true] When true a button will be displayed to select which decorator to display
*/
/**
* @property {Boolean} _showSelectDecorators The value of #cfg-showSelectDecorators.
* @private
*/
/**
* @cfg {String} [siteContext=Ametys.web.helper.ContextToolbar.SITE_CONTEXT_CURRENT] The site context.
* Valid contexts are:
* <ul>
* <li>Ametys.web.helper.ContextToolbar.SITE_CONTEXT_CURRENT for current site only</li>
* <li>Ametys.web.helper.ContextToolbar.SITE_CONTEXT_ALL for all sites</li>
* </ul>
*/
/**
* @cfg {String} [sitemapContext=Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL] The sitemap context.
* Valid contexts are:
* <ul>
* <li>Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL for all languages</li>
* <li>a language code such as 'fr', 'en', .. for using a specifying language.</li>
* </ul>
*/
/**
* @cfg {Boolean} [checkMode=false] Set to true to select pages by check boxes.
*/
/**
* @cfg {String[]} [rights] Array of rights to check (AND condition) on pages
*/
/**
* @private
* @property {String[]} _rights See #cfg-rights
*/
/**
* @private
* @property {Object[]} _decorators Array of decorators
*/
_decorators: [],
/**
* @private
* @property {Boolean} _isRefreshingRootNode To prevent multiple requests to refresh the root node.
*/
_isRefreshingRootNode: false,
constructor: function(config)
{
this._decorators = config.indicators || [];
this._activeDecorators = config.activeDecorators || [];
Ext.applyIf (config, {
rootVisible: true,
siteContext: Ametys.web.helper.ContextToolbar.SITE_CONTEXT_CURRENT,
sitemapContext: Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL,
defaultSiteName: Ametys.getAppParameter("siteName"),
defaultSitemapName: null,
cls: 'sitemap-tree',
border: false,
scrollable: true,
showSelectDecorators: true,
store: Ext.create('Ext.data.TreeStore', {
model: config.checkMode ? 'Ametys.web.sitemap.SitemapTree.CheckablePage' : 'Ametys.web.sitemap.SitemapTree.Page',
checkMode: config.checkMode || false,
autoLoad : false,
root: {
expanded: false,
text: "{{i18n PLUGINS_WEB_SITEMAP_TREE_ROOT_LABEL}}",
title: "{{i18n PLUGINS_WEB_SITEMAP_TREE_ROOT_LABEL}}",
iconCls: "a-tree-glyph ametysicon-world91",
path: '',
leaf: true
},
proxy: {
type: 'ametys',
plugin: 'web',
url: 'repository/sitemap.json',
reader: {
type: 'json',
rootProperty: 'pages'
},
extraParams: {
rights: config.rights || []
}
},
listeners: {
'beforeload': Ext.bind(this._onBeforeLoad, this)
}
})
});
this._rights = config.rights || [];
this._counter = {};
var dockedItems = [];
if (config.siteContext == Ametys.web.helper.ContextToolbar.SITE_CONTEXT_ALL || config.sitemapContext == Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL)
{
var toolbarCfg = {
onSelectSiteFn: Ext.bind(this._onSelectSite, this),
onSelectSitemapFn: Ext.bind(this._onSelectSitemap, this),
siteContext: config.siteContext,
sitemapContext: config.sitemapContext,
defaultSiteName: config.defaultSiteName
}
// Add a toolbar to select site and/or language in combo boxes
dockedItems.push(Ametys.web.helper.ContextToolbar.getToolbar(toolbarCfg));
}
this._showSelectDecorators = config.showSelectDecorators;
// Add toolbar for tools
dockedItems.push(this._getToolBarConfig());
dockedItems.push(this._getNoResultPanel());
config.dockedItems = dockedItems;
this.callParent(arguments);
this.on('beforeselect', this._onBeforeSelect, this);
this.on('beforeitemdblclick', this._onBeforeSelect, this);
var me = this;
var view = this.view.lockedView || this.view;
var tpl = new Ext.XTemplate(view.cellTpl.html.replace('</div></td>',
'<tpl for="(values.column.getItemId().startsWith(\'tree\') && record.data.decorators) ? record.data.decorators.split(\',\') : []">' // startsWith(\'tree\') to apply to the main column only
+ '<tpl if="this.isActive(arguments[1])">'
+ '<tpl if="this.getDecorator(arguments[1]).iconGlyph">'
+ '<span class="sitemap-decorator-glyph {% out.push(this.getDecorator(arguments[1]).iconGlyph) %}" title="{% out.push(this.getDecorator(arguments[1]).label) %}"></span>'
+ '<tpl else>'
+ '<img class="sitemap-decorator-icon" src="{% out.push(Ametys.CONTEXT_PATH +this.getDecorator(arguments[1]).icon) %}" title="{% out.push(this.getDecorator(arguments[1]).label) %}" alt="[{% out.push(this.getDecorator(arguments[1]).label) %}]"/>'
+ '</tpl>'
+ '</tpl>'
+ '</tpl>'
+ '</div></td>'),
{
priority: view.cellTpl.priority,
/**
* Get the decorator data if available
*/
getDecorator: function(id)
{
return Ext.Array.findBy(me._decorators, function(v) { return v.id == id;});
},
/**
* Return true if decorator is active
*/
isActive: function(id)
{
return me._activeDecorators.includes(id);
}
});
if (this.view.lockedView)
{
this.view.lockedView.cellTpl = tpl;
}
else
{
this.view.cellTpl = tpl;
}
},
applyState: function (state)
{
this.callParent(arguments);
var me = this;
this._activeDecorators = state.activeDecorators || [];
var indicatorMenu = this.down('toolbar > button[itemId="indicators-menu"]');
if (indicatorMenu)
{
indicatorMenu.getMenu().items.each(function(item) {
item.setChecked(Ext.Array.contains(me._activeDecorators, item.name), true);
});
}
},
getState: function ()
{
var state = this.callParent(arguments) || {};
// save indicators
state.activeDecorators = this._activeDecorators
return state;
},
/**
* @private
* Get configuration for toolbar
* @return {Object} the config object
*/
_getToolBarConfig: function ()
{
var me = this;
var menuItems = [];
Ext.Array.forEach(this._decorators, function(indicator) {
menuItems.push({
xtype: 'menucheckitem',
iconCls: indicator.iconGlyph,
text: indicator.label,
name: indicator.id,
checkHandler: me.selectDecorators,
checked: Ext.Array.contains(me._activeDecorators, indicator.id),
scope: me
})
});
return {
dock: 'top',
xtype: 'toolbar',
layout: {
type: 'hbox',
align: 'stretch'
},
defaultType: 'button',
items: [{
// Filter input
xtype: 'textfield',
cls: 'ametys',
flex: 1,
maxWidth: 300,
itemId: 'search-filter-input',
emptyText: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_FILTER}}",
minLength: 3,
minLengthText: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_FILTER_INVALID}}",
msgTarget: 'qtip',
listeners: {change: Ext.Function.createBuffered(this._searchFilter, 500, this)},
style: {
marginRight: '0px'
}
},
{
// Clear filter
tooltip: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_CLEAR_FILTER}}",
handler: Ext.bind (this._clearSearchFilter, this),
iconCls: 'a-btn-glyph ametysicon-eraser11 size-16',
cls: 'a-btn-light'
},
{
xtype: 'tbspacer',
flex: 0.0001
},
{
// Collapse all
tooltip: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_COLLAPSE_ALL}}",
handler: Ext.bind (this.collapseNode, this, [], false),
iconCls: 'a-btn-glyph ametysicon-minus-sign4 size-16',
cls: 'a-btn-light'
},
{
// Refresh node
tooltip: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_REFRESH_PAGE}}",
handler: Ext.bind (this.refreshNode, this, [], false),
iconCls: 'a-btn-glyph ametysicon-arrow123 size-16',
cls: 'a-btn-light'
},
{
// Decorators
hidden: !this._showSelectDecorators || this._decorators.length == 0,
tooltip: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_ICONS}}",
iconCls: 'a-btn-glyph ametysicon-puzzle33 size-16',
cls: 'a-btn-light',
itemId: 'indicators-menu',
menu: {
xtype: 'menu',
items: menuItems
}
}
]
};
},
/**
* @private
* Get the 'no result' button configuration. This button is shown when filter matches no result.
* @return {Object} the button configuration
*/
_getNoResultPanel: function ()
{
return {
dock: 'top',
xtype: 'button',
hidden: true,
itemId: 'no-result',
ui: 'tool-hintmessage',
text: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_FILTER_NO_MATCH}}" + "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_FILTER_NO_MATCH_ACTION}}",
scope: this,
handler: this._clearSearchFilter
};
},
/**
* @private
* Show or hide the 'no result' button.
* @param {Boolean} show true to show the button, false to hide it.
*/
_showHideNoResultPanel: function (show)
{
this.down("button[itemId='no-result']").setVisible(show);
},
/**
* This listener is called on 'keyup' event on filter input field.
* Filters the tree by text input.
* @param {Ext.form.Field} field The field
* @private
*/
_searchFilter: function (field)
{
var value = new String(field.getValue()).trim();
this._filterField = field;
if (this._filterValue == value)
{
// Do nothing
return;
}
this._filterValue = value;
if (value.length > 2)
{
var rootNode = this.getRootNode();
this._getFilteredPages (value, rootNode);
}
else
{
this._showHideNoResultPanel(false);
this.clearFilter();
}
},
/**
* Get the tags the name matches the given value
* @param {String} value The value to match
* @param {Ext.data.Model} node The node where starting search
* @param {Boolean} [childNodesOnly] set to 'true' to filter the child nodes only.
* @private
*/
_getFilteredPages: function (value, node, childNodesOnly)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.SitemapDAO",
methodName: 'filterPagesByRegExp',
parameters: [node.getId(), value],
errorMessage: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_SEARCH_ERROR}}",
callback: {
handler: this._filterPagesCb,
scope: this,
arguments: {
node: node,
childNodesOnly: childNodesOnly
}
}
});
},
/**
* @private
* Callback function after searching pages
* @param {Object} paths The paths of matching pages
* @param {Object[]} args The callback arguments
*/
_filterPagesCb: function(paths, args)
{
var hasResult = false;
var node = args.node || this.getRootNode();
if (!paths)
{
return;
}
if (paths.length == 0)
{
if (args.childNodesOnly)
{
// There is no child nodes matching but some other nodes are matching.
hasResult = true;
for (var i=0; i < node.childNodes.length; i++)
{
this.filterBy (function () {return false}, node.childNodes[i]);
}
}
else
{
this.filterBy (function () {return false}, node);
}
}
else
{
hasResult = true;
var childs = node.childNodes;
this._expandAndFilter (paths, this.getRootNode(), node);
}
if (!hasResult)
{
this._showHideNoResultPanel(true);
if (this._filterField)
{
Ext.defer (this._filterField.markInvalid, 100, this._filterField, ["{{i18n PLUGINS_WEB_UITOOL_SITEMAP_TREE_FILTER_NO_MATCH}}"]);
}
}
else
{
this._showHideNoResultPanel(false)
if (this._filterField)
{
this._filterField.clearInvalid();
}
}
},
/**
* Expand the tree to the given paths.
* @param {Object[]} paths The paths to expand
* @param {Ext.data.Model} rootNode The concerned root node
* @param {Ext.data.Model} node The node from which apply filter
* @private
*/
_expandAndFilter: function(paths, rootNode, node)
{
node = node || rootNode;
this._counter[rootNode.getId()] = paths.length;
for (var i=0; i < paths.length; i++)
{
var path = paths[i].substr(0, paths[i].lastIndexOf("/"));
this.expandPath (rootNode.getPath('name') + '/' + path, 'name', null, Ext.bind (this._filterPaths, this, [paths, rootNode, node], false));
}
},
/**
* Filter nodes by path once the last expand has been processed
* @param {String[]} paths The path to filter by
* @param {Ext.data.Model} rootNode The concerned root node
* @param {Ext.data.Model} node The node from which apply filter
* @private
*/
_filterPaths: function (paths, rootNode, node)
{
// only execute the filterBy after the last expandPath()
if (--this._counter[rootNode.getId()] == 0)
{
var filterFn = Ext.bind (this._filterByPath, this, [paths, rootNode], true);
// FIXME Ensure that expand is complete by deferring the filterBy function ...
Ext.defer(this.filterBy, 50, this, [filterFn, node]);
}
},
/**
* Returns true if the node path is a part of given paths
* @param {Ext.data.Model} node The node to test
* @param {String[]} paths The paths
* @param {Ext.data.Model} rootNode The root node to build the complete paths
* @private
*/
_filterByPath: function (node, paths, rootNode)
{
var currentPath = node.getPath('name');
for (var i=0; i < paths.length; i++)
{
var path = rootNode.getPath('name') + '/' + paths[i] + '/';
if (path.indexOf(currentPath + '/') == 0)
{
return true;
}
}
return false;
},
/**
* Filters by a function. The specified function will be called for each Record in this Store.
* If the function returns true the Record is included, otherwise it is filtered out.
* @param {Function} filterFn A function to be called.
*/
filterBy: function (filterFn)
{
this.clearFilter();
this.getStore().filterBy(filterFn);
},
/**
* Clear the filter search
* @param {Ext.Button} btn The button
* @private
*/
_clearSearchFilter: function(btn)
{
this.clearFilter();
if (this._filterField)
{
this._filterField.reset();
}
this._filterValue = null;
this._showHideNoResultPanel(false);
var selection = this.getSelectionModel().getSelection()[0];
if (selection)
{
this.ensureVisible(selection.getPath('name'), {field: 'name'});
}
},
/**
* Clear all filters
*/
clearFilter: function (node)
{
this.getStore().clearFilter();
// TODO checked mode?
var selection = this.getSelectionModel().getSelection()[0];
if (selection)
{
this.ensureVisible(selection.getPath());
}
},
/**
* Expand the tree and select a node by its complete path
* @param {String} path The complete path from the root node
*/
selectByPath: function (path)
{
this.selectPath(path, 'name');
},
/**
* Collapse recursively the children of the node, then select the collapsed node.
* @param {Ext.data.NodeInterface} [node] The node the collapse. Can be null to collapse the whole tree.
*/
collapseNode: function (node)
{
node = node || this.getRootNode();
node.collapseChildren(true);
this.getSelectionModel().select(node);
},
/**
* This function reload the given node
* @param {String} id The id of the node to reload
* @param {Function} callback The callback function to call after reload. Can be null. Has the following parameters:
* @param {Ext.data.Model} callback.node The refreshed node
*/
refreshNode: function (id, callback)
{
callback = Ext.isFunction(callback) ? callback : Ext.EmptyFn;
var node;
if (id == null)
{
var selection = this.getSelectionModel().getSelection();
node = selection.length > 0 ? selection[0] : null;
// Workaround - Refresh selection in case node is not existing anymore (deleted by another user for example).
this.getSelectionModel().deselect(node, true);
this.getSelectionModel().select(node);
}
else
{
node = this.getStore().getNodeById(id);
}
if (node != null)
{
// Set leaf to false, to allow children to be added during the load. Leaf will be set to true again if needed after the load.
node.set('leaf', false);
var me = this;
this.getStore().load({
node: node,
callback: function () {
if (me._filterValue && me._filterValue.length > 2)
{
// Filter child nodes only
me._getFilteredPages (this._filterValue, node, true);
}
else
{
Ext.defer(this._expandNode, 200, this, [node, callback]);
}
},
scope: this
});
}
},
/**
* Expand the given node
* @param {String} node The node to expand.
* @param {Function} callback The callback function to call after reload. Can be null. Has the following parameters:
* @param {Ext.data.Model} callback.node The expanded node
* @private
*/
_expandNode: function (node, callback)
{
if (node == null)
{
var selection = this.getSelectionModel().getSelection();
node = selection.length > 0 ? selection[0] : null;
}
if (node != null)
{
callback = callback ? Ext.bind(callback, null, [node]) : null;
this.expandNode (node, false, callback);
}
},
/**
* @private
* Listener when an indicator is checked/unchecked
* @param {Ext.menu.CheckItem} item the item
* @param {Boolean} checked the checked status
*/
selectDecorators: function (item, checked)
{
var hasChanges = false;
if (checked && !Ext.Array.contains(this._activeDecorators, item.name))
{
this._activeDecorators.push(item.name);
hasChanges = true;
}
else if (!checked && Ext.Array.contains(this._activeDecorators, item.name))
{
Ext.Array.remove(this._activeDecorators, item.name);
hasChanges = true;
}
if (hasChanges)
{
this.saveState();
this.view.refresh();
}
},
/**
* Update the node data. This will updates the node UI (icon, decorators)
* @param id The node id
* @param data The data to update
*/
updateNodeData: function (id, data)
{
var node = this.getStore().getNodeById(id);
if (node)
{
node.beginEdit();
for (var i in data)
{
node.set(i, data[i]);
}
node.endEdit();
node.commit();
this.view.refreshNode(node);
}
},
/**
* @private
* Listener on site selection
* @param {Ext.form.field.ComboBox} combo The sites' ComboBox
* @param {Ext.data.Model} record the selected record
*/
_onSelectSite: function (combo, record)
{
var value = combo.getValue();
if (this._siteName == value)
{
// No change
return;
}
this._siteName = value;
if (this.down("combobox[itemId='sitemaps-combo']"))
{
// Load available languages
this.down("combobox[itemId='sitemaps-combo']").getStore().load();
}
// Reload tree with current sitemap
this.refreshRootNode();
},
/**
* @private
* Listener on sitemap selection
* @param {Ext.form.field.ComboBox} combo The sitemaps' ComboBox
* @param {Ext.data.Model} record the selected record
*/
_onSelectSitemap: function (combo, record)
{
var value = combo.getValue();
if (this._sitemapName == value)
{
// No change
return;
}
this._sitemapName = value;
// Reload tree
this.refreshRootNode();
},
/**
* Returns the current sitemap name
* @return the current sitemap name
*/
getCurrentSitemapName: function ()
{
return this._sitemapName || this.defaultSitemapName;
},
/**
* Returns the current site name
* @return the current site name
*/
getCurrentSiteName: function ()
{
return this._siteName || this.defaultSiteName;
},
/**
* Expand the tree to the given path.
* @param {String[]} path The path to expand
* @param {Function} callback The callback function to call after expand. Can be null.
*/
expandNodeByPath: function(path, callback)
{
var rootNode = this.getRootNode();
this.expandPath (rootNode.getPath('name') + '/' + path, 'name', null, callback);
},
/**
* @private
* Listener before node selection.
* Cancel the selection if the property 'isAllowed' is equals to false.
* @param {Ext.selection.Model} sm the selectioon model
* @param {Ext.data.Model} record The selected record
* @param {Number} index The row index selected
*/
_onBeforeSelect: function (sm, record, index)
{
if (record.get('isAllowed') === false)
{
return false;
}
},
/**
* @private
* Listener before loading tree node
* @param {Ext.data.TreeStore} store The tree store
* @param {Ext.data.operation.Operation} operation The operation object that will be passed to the Proxy to load the Store
*/
_onBeforeLoad: function(store, operation)
{
var node = operation.node;
if (node != null)
{
operation.setParams(Ext.apply(operation.getParams() || {}, {
id: node.getId()
}));
}
},
/**
* @private
* Callback function called after loading available sites
* @param {Function} [initCb] function to call after initialization process
*/
_loadSitesCb: function (initCb)
{
// Set default value
this.down("combobox[itemId='sites-combo']").setValue(this.defaultSiteName);
// Then refresh sitemaps or page tree
if (this.down("combobox[itemId='sitemaps-combo']"))
{
this.down("combobox[itemId='sitemaps-combo']").getStore().load({callback: Ext.bind(this._loadSitemapsCb, this, [initCb])});
}
else
{
this.refreshRootNode(initCb);
}
},
/**
* @private
* Callback function called after loading available sitemaps
* @param {Function} [initCb] function to call after initialization process
*/
_loadSitemapsCb: function (initCb)
{
var store = this.down("combobox[itemId='sitemaps-combo']").getStore();
if (this.defaultSitemapName == null)
{
var userLocale = Ametys.getAppParameter('user').locale;
if (store.find('name', userLocale) != -1)
{
this.defaultSitemapName = userLocale;
}
else if (store.find('name', 'en') != -1)
{
this.defaultSitemapName = 'en';
}
else
{
this.defaultSitemapName = store.getAt(0).get('name');
}
}
// Set default value
this.down("combobox[itemId='sitemaps-combo']").setValue(this.defaultSitemapName);
this._sitemapName = this.defaultSitemapName;
// Then refresh node
this.refreshRootNode(initCb);
},
/**
* Initialize the tree
* @param {Function} [initCb] function to call after initialization process
*/
initialize: function (initCb)
{
if (this.siteContext == Ametys.web.helper.ContextToolbar.SITE_CONTEXT_ALL)
{
// Load available sites
this.down("combobox[itemId='sites-combo']").getStore().load({callback: Ext.bind(this._loadSitesCb, this, [initCb])});
}
else if (this.sitemapContext == Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL)
{
// Load available sitemaps
this.down("combobox[itemId='sitemaps-combo']").getStore().load({callback: Ext.bind(this._loadSitemapsCb, this, [initCb])});
}
else
{
this.refreshRootNode(initCb);
}
},
/**
* Set the language name and reload the sitemap tree if language was changed.<br/>
* This should ONLY be used when languages and sites combobox are not displayed.
* @param lang The language to set
* @param {Function} [callback] function to call after reloading the tree
*/
setSitemapName: function (lang, callback)
{
if (this._sitemapName != lang)
{
this._sitemapName = lang;
}
this.refreshRootNode (callback);
},
/**
* Refresh the whole tree
* @param {Function} [callback] function to call after refreshing
*/
refreshRootNode: function (callback)
{
if (this._isRefreshingRootNode)
{
return;
}
var siteName = this._siteName || this.defaultSiteName;
var lang = this._sitemapName || this.defaultSitemapName;
this._isRefreshingRootNode = true;
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.SitemapDAO",
methodName: "getSitemapProperties",
parameters: [siteName, lang, this._rights],
callback: {
scope: this,
handler: this._getRootNodeCb,
arguments: {
callback: callback
}
},
errorMessage: {
category: this.self.getName(),
msg: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_ERROR}}"
},
waitMessage: true // FIXME target: this ?
});
},
/**
* @private
* Callback function after retrieving root node
* @param {Object} response The server response
* @param {Object} args the callback arguments
*/
_getRootNodeCb: function (response, args)
{
this._isRefreshingRootNode = false;
this.setRootNode({
id: response.id,
name: response.lang,
expanded: true,
text: "{{i18n PLUGINS_WEB_SITEMAP_TREE_ROOT_LABEL}}",
title: "{{i18n PLUGINS_WEB_SITEMAP_TREE_ROOT_LABEL}}",
iconCls: "a-tree-glyph ametysicon-world91",
path: '',
isAllowed: response.isAllowed,
editable : false,
allowDrag : false,
allowDrop : true
});
if (Ext.isFunction (args.callback))
{
this.store.getRoot().on('expand', function() { args.callback (response.id); }, this, { single: true });
}
}
});