/*
* Copyright 2014 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 UI helper provides a dialog to select one or more pages on a sitemap tree.
* See {@link #open} method.
*
* Ametys.web.helper.ChoosePage.open({
* multiple: false,
siteContext: Ametys.web.sitemap.SitemapTree.SITE_CONTEXT_CURRENT,
sitemapContext: Ametys.web.sitemap.SitemapTree.SITEMAP_CONTEXT_CURRENT,
allowCreation: true,
callback: Ext.bind(this._choosePageCb, this)
* });
*/
Ext.define('Ametys.web.helper.ChoosePage', {
singleton: true,
/**
* Configure and open the dialog box
* @param {Object} config The configuration options :
* @param {String} [config.icon] The full path to icon (16x16 pixels) for the dialog box.
* @param {String} [config.iconCls=ametysicon-world91] The CSS class for icon of the dialog box
* @param {String} [config.title] The title of the dialog box.
* @param {String} [config.helpMessage] The help message to display on top of dialog box.
* @param {String} [config.values] the selected pages as an array of id.
* @param {String} [config.siteContext=Ametys.web.helper.ContextToolbar#SITE_CONTEXT_CURRENT] The site context. Set to Ametys.web.helper.ContextToolbar#SITE_CONTEXT_ALL to see all sites.
* @param {String} [config.sitemapContext=Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL] The sitemap context. Set to Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL to see all languages. Set to a language code such as 'fr', 'en', .. for using a specifying language.
* @param {String} [config.defaultSiteName] The default site name.
* @param {String} [config.defaultSitemapName] The default sitemap language.
* @param {Boolean} [config.multiple]=false `true` to allow selecting multiple pages.
* @param {Boolean} [config.allowCreation=false] `true` to allow the creation of new pages from the dialog box.
* @param {Boolean} [config.allowDeletion=false] `true` to allow to delete current selection
* @param {Boolean} [config.showSelectDecorators=true] `true` to display a button to allow to the user to select the decorators to display
* @param {Function} config.callback The callback function invoked when pages are selected. The callback function will received the following parameters:
* @param {String[]/String} config.callback.ids The identifiers of the selected pages. In simple mode, the value is a string, in multiple mode it is an array. If the selection had been deleted, null or [] is returned. If the dialog has been closed, undefined is returned.
* @param {String} config.callback.title In simple mode, the title of the page. In multiple mode, nothing is returned
* @param {String} config.callback.path In simple mode, the title of the path. In multiple mode, nothing is returned
*/
open: function (config)
{
config = config || {};
box = this._createDialogBox({
icon: config.icon,
iconCls: config.iconCls,
title: config.title,
helpMessage: config.helpMessage,
siteContext: config.siteContext || Ametys.web.helper.ContextToolbar.SITE_CONTEXT_CURRENT,
sitemapContext: config.sitemapContext || Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL,
defaultSitemapName: config.defaultSitemapName,
defaultSiteName: config.defaultSiteName || Ametys.getAppParameter('siteName'),
multiple: config.multiple || false,
callback: config.callback,
values: config.values || [],
allowCreation: config.allowCreation || false,
allowDeletion: config.allowDeletion || false,
showSelectDecorators: config.showSelectDecorators,
rightId: config.rightId || 'Web_Rights_Page_Create'
});
box.show();
},
/**
* Creates the dialog box
* @param {Object} config The configuration options :
* @param {String} [config.icon] The full path to icon (16x16 pixels) for the dialog box.
* @param {String} [config.iconCls] The CSS class for icon of the dialog box
* @param {String} [config.title] The title of the dialog box.
* @param {String} [config.helpMessage] The help message to display on top of dialog box.
* @param {String} [config.values] the selected pages as an array of id.
* @param {String} [config.siteContext=Ametys.web.helper.ContextToolbar#SITE_CONTEXT_CURRENT] The site context. Set to Ametys.web.helper.ContextToolbar#SITE_CONTEXT_ALL to see all sites.
* @param {String} [config.sitemapContext=Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL] The sitemap context. Set to Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL to see all languages. Set to a language code such as 'fr', 'en', .. for using a specifying language.
* @param {String} [config.defaultSiteName] The default site name.
* @param {String} [config.defaultSitemapName] The default sitemap language.
* @param {Boolean} [config.multiple]=false `true` to allow selecting multiple pages.
* @param {Boolean} [config.allowCreation=false] `true` to allow the creation of new pages from the dialog box.
* @param {Boolean} [config.allowDeletion=false] `true` to allow to delete current selection
* @param {Boolean} [config.showSelectDecorators=true] `true` to display a button to allow to the user to select the decorators to display
* @param {Function} config.callback The callback function invoked when pages are selected. The callback function will received the following parameters:
* @param {String[]/String} config.callback.ids The identifiers of the selected pages. In simple mode, the value is a string, in multiple mode it is an array
* @param {String} config.callback.title In simple mode, the title of the page. In multiple mode, nothing is returned
* @param {String} config.callback.path In simple mode, the title of the path. In multiple mode, nothing is returned
* @private
*/
_createDialogBox: function (config)
{
var self = this;
var tree = Ext.create('Ametys.web.sitemap.SitemapTree', {
border: true,
flex: 1,
siteContext: config.siteContext,
sitemapContext: config.sitemapContext,
defaultSitemapName: config.defaultSitemapName,
defaultSiteName: config.defaultSiteName,
allowCreation: config.allowCreation,
showSelectDecorators: config.showSelectDecorators,
checkMode: config.multiple
});
tree.getView().cellFocused = false;
var box = Ext.create('Ametys.window.DialogBox', {
title: config.title || "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_TITLE}}",
icon: config.icon,
iconCls: config.icon ? null : config.iconCls || 'ametysicon-website38',
width: 420,
height: 500,
scrollable: false,
bodyStyle: {
padding: 0
},
cls: 'ametys-dialogbox choose-page-dialog',
sitemapConfig : {
multiple: config.multiple,
allowCreation: config.allowCreation,
callback: config.callback,
values: config.values,
rightId: config.rightId
},
layout: {
type: 'vbox',
align : 'stretch',
pack : 'start'
},
items: [{
xtype: 'component',
cls: 'a-text',
padding: '5',
html: config.helpMessage || (config.multiple ? "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_SELECT_PAGES_HELP_MESSAGE}}" : "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_SELECT_PAGE_HELP_MESSAGE}}")
},
tree,
{
xtype: 'component',
cls: 'a-text link',
html: "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_NO_PAGE_SELECTED}}",
hidden: !config.allowCreation
}
],
_clickEventRegistered: false,
referenceHolder: true,
defaultButton: 'validate',
closeAction: 'destroy',
buttons : [{
reference: 'validate',
itemId: 'ok-btn',
disabled: true,
text :"{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_BUTTON_OK}}"
}, {
itemId: 'delete-btn',
hidden: !config.allowDeletion || config.values.length == 0,
text :"{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_BUTTON_DELETE}}"
}, {
itemId: 'cancel-btn',
text :"{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_BUTTON_CANCEL}}"
}
]
});
box.addListener("close", this._close, this, [box]);
box.down("button[itemId='ok-btn']").setHandler (Ext.bind(this._validate, this, [box]));
box.down("button[itemId='delete-btn']").setHandler (Ext.bind(this._delete, this, [box]));
box.down("button[itemId='cancel-btn']").setHandler (Ext.bind(this._cancel, this, [box]));
tree.on('selectionchange', Ext.bind(this._onSelectPage, this, [box], 2));
tree.initialize(Ext.bind (this._initValues, this, [box, config.values]));
return box;
},
/**
* @private
* Initialize selected pages
* @param {Ametys.window.DialogBox} box the dialog box
* @param {String[]} values the page ids
*/
_initValues: function (box, values)
{
var tree = box.items.get(1);
if (values && values.length > 0)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.PageDAO",
methodName: "getPagesInfos",
parameters: [values],
callback: {
scope: this,
handler: this._getPagesInfosCb,
arguments: {
box: box
}
},
errorMessage: {
category: this.self.getName(),
msg: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_ERROR}}"
}
});
}
if (values.length == 0 || this.multiple)
{
var rootNode = tree.getRootNode();
tree.getSelectionModel().select(rootNode);
tree.getView().focusNode(rootNode);
}
},
/**
* @private
* Callback after retrieving pages' properties. Select pages in tree.
* @param {Object} response The server response
* @param {Object} args The arguments transmitted
* @param {Ametys.window.DialogBox} args.box The dialog box
*/
_getPagesInfosCb: function (response, args)
{
var pages = response.pages;
var paths = [];
var box = args.box;
var tree = box.items.get(1);
Ext.Array.each (pages, function (page) {
if (page.siteName == tree.getCurrentSiteName() && page.lang == tree.getCurrentSitemapName())
{
tree.expandNodeByPath(page.path, function (successful, node) {
if (successful)
{
if (box.sitemapConfig.multiple)
{
node.set('checked', true);
}
else
{
tree.getSelectionModel().select(node);
tree.getView().focusNode(node);
}
}
})
}
});
},
/**
* This method update the dialog box depending on the current selection
* @private
*/
_onSelectPage: function(sm, nodes, box)
{
var isValidSelection = this._isSelectionValid(nodes);
box.down("button[itemId='ok-btn']").setDisabled(!isValidSelection);
if (box.sitemapConfig.allowCreation)
{
this._updateLinkCreation(box);
}
},
/**
* Check if selection is valid according to the configuration parameters
* @param {Ext.data.TreeModel} nodes The selected nodes.
* @return {boolean} true if the selection is valid
* @private
*/
_isSelectionValid : function(nodes)
{
if (nodes.length == 0)
{
return false;
}
for (var i=0; i <nodes.length; i++)
{
if (nodes[i].isRoot())
{
return false;
}
}
return true;
},
/**
* This method retrieves the current selection, and set the tag creation link accordingly.
* @private
*/
_updateLinkCreation: function (box, selection)
{
var tree = box.items.get(1);
var selection = tree.getSelectionModel().getSelection();
var createLink = box.child("component[cls~=link]");
var node = selection[0];
if (node != null)
{
this._checkUserRight(box, node);
}
else
{
// No page is selected
box._clickEventRegistered = false;
createLink.update("{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_NO_PAGE_SELECTED}}");
}
},
/**
* @private
* Check if user has the specified right on page node
* @param {Ametys.window.DialogBox} box The current dialog box
* @param node The page node
*/
_checkUserRight: function (box, node)
{
var me = this,
tree = box.items.get(1),
pageId = node.getId(),
rightId = box.sitemapConfig.rightId,
createLink = box.child("component[cls~=link]");
function callback (hasRight)
{
if (hasRight)
{
createLink.update("<a class='action'>{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_CREATE_PAGE_LINK}}</a>");
// add a click event listener on the <a class='action'> dom node to call the #_createPage method.
if (!box._clickEventRegistered)
{
createLink.mon(createLink.getEl(), 'click', Ext.bind(me._createPage, me, [box]), me, {delegate: 'a.action'});
box._clickEventRegistered = true;
}
}
else
{
// No right to create page
box._clickEventRegistered = false;
createLink.update("{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_CREATE_PAGE_NORIGHT}}");
}
}
Ametys.web.page.PageDAO.hasRight([pageId, rightId], callback, {scope: this});
},
/**
* Retrieve the current tree values, and call the callback function from the initial configuration sent to #open
* @param {Ametys.window.DialogBox} box The current dialog box
* @private
*/
_validate: function (box)
{
var selection, ids;
var tree = box.items.get(1);
if (box.sitemapConfig.multiple)
{
selection = tree.getChecked();
ids = Ext.Array.map(selection, function(record) {
return record.getId();
});
}
else
{
selection = tree.getSelectionModel().getSelection();
}
if (box.sitemapConfig.callback)
{
if (box.sitemapConfig.multiple)
{
box.sitemapConfig.callback.call(this, ids);
}
else
{
var record = selection[0];
box.sitemapConfig.callback.call(this, record.getId(), record.get('title'), record.get('path'));
}
}
box.sitemapConfig.callback = null;
box.close();
},
/**
* Call the callback function with null argument (no selection)
* @param {Ametys.window.DialogBox} box The current dialog box
* @private
*/
_delete: function (box)
{
if (box.sitemapConfig.callback)
{
if (box.sitemapConfig.multiple)
{
box.sitemapConfig.callback.call(this, []);
}
else
{
box.sitemapConfig.callback.call(this, null);
}
}
box.sitemapConfig.callback = null;
box.close();
},
/**
* @private
* Handler function invoked when the 'Cancel' button is clicked
*/
_cancel: function (box)
{
box.close();
},
/**
* @private
* Handler function invoked when the 'Close' button is clicked
*/
_close: function (box)
{
if (box.sitemapConfig.callback)
{
box.sitemapConfig.callback.call(this, undefined);
box.sitemapConfig.callback = null;
}
},
/**
* Create a page from the dialog box
* @param {Ametys.window.DialogBox} box The dialog box
* @private
*/
_createPage: function(box)
{
var tree = box.items.get(1);
var selection = tree.getSelectionModel().getSelection();
if (selection && selection.length > 0)
{
var parentId = selection[0].get('id');
Ametys.plugins.web.page.AddPageWizard.open (parentId, Ext.bind(this._createPageCb, this, [tree], true), {});
}
},
/**
* @private
* Callback function called after creating page
* @param {Ametys.web.page.Page} page The created page
* @param {Boolean} success creation is successfull
* @param {Ext.tree.Panel} tree The sitemap tree panel
*/
_createPageCb: function (page, success, tree)
{
var node = tree.getStore().getNodeById(page.getParentId());
if (node != null)
{
node.set('expanded', true);
tree.getStore().load({node: node, callback: Ext.bind (this._createPageCb2, this, [tree, page.getId()])});
}
},
/**
* @private
* Select the new created page
* @param {Ext.tree.Panel} tree The sitemap tree panel
* @param {String} pageId The page id to select
*/
_createPageCb2: function (tree, pageId)
{
var node = tree.getStore().getNodeById(pageId);
if (node != null)
{
tree.getSelectionModel().select(node);
}
},
/**
* Callback function after page creation
* @param {Ext.tree.Panel} tree The sitemap tree panel
* @param {String} pageId The page's id
* @param {String} parentId The page's parent id
* @private
*/
_onPageCreated: function (tree, pageId, parentId)
{
var node = tree.getStore().getNodeById(parentId);
if (node != null)
{
node.set('expanded', true);
tree.getStore().load({
node : node,
callback : function()
{
Ext.defer(tree.expandNode, 200, tree, [ node, false, null ]);
},
scope : this
});
}
}
});