/*
* 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.page.tool.PageTool', {
extend: 'Ametys.tool.Tool',
statics : {
/**
* @private
* @readonly
* @property {RegExp} __URL_REGEXP The regexp to match url
*/
__URL_REGEXP: new RegExp("^(https?:\/\/[^\/]+)(\/.*)?")
},
/**
* @private
* @property {Boolean} true if the displayed page is a page of another site
*/
_isOutOfSite: false,
/**
* @private
* @property {String} _lastZoneName The name of the zone lastly selected. Null if no zone was selected.
*/
_lastZoneName: null,
/**
* @private
* @property {String} _lastZoneWidth The width of the zone lastly selected. Null if no zone was selected or unknown.
*/
_lastZoneWidth: null,
/**
* @private
* @property {String} _lastZoneItemId The id of the zone item lastly selected. Null if no zone item was selected.
*/
_lastZoneItemId: null,
/**
* @private
* @property {String} _lastPageType The type of the page lastly selected.
*/
_lastPageType: null,
/**
* @private
* @property {Ext.Template} _nodeOrLinkTpl Template for page of type 'node' or 'link'
*/
_nodeOrLinkTpl: Ext.create('Ext.Template', '<div class="title">',
'<span class="{iconCls}"></span>',
"{title}",
'</div>',
'<div class="text">{text}</div>'),
/**
* @private
* @property {String} _baseUrl The url that the iframe should stay on
*/
/**
* @private
* @property {String[]} _contentIds The identifiers of contents in this page.
*/
/**
* @private
* @property {Boolean} _ctrlPressedOnLastLink Was the key CTRL pressed, the last time a link was clicked on this page? Read by PageToolFactory
*/
/**
* @private
* @property {Boolean} _openedAtStartup Was the tool opened during the UI startup
*/
constructor: function(config)
{
this.callParent(arguments);
this._tooltipDescriptionTpl = new Ext.Template(
"<u>{{i18n plugin.web:PLUGINS_WEB_TOOL_PAGE_PAGETOOL_TOOLTIPTEXT_SITE}}</u> : ",
"<b>{siteName}</b><br/>",
"<u>{{i18n plugin.web:PLUGINS_WEB_TOOL_PAGE_PAGETOOL_TOOLTIPTEXT_LANG}}</u> : ",
"<b>{lang}</b><br/>",
"<u>{{i18n plugin.web:PLUGINS_WEB_TOOL_PAGE_PAGETOOL_TOOLTIPTEXT_URL}}</u> : ",
"<b>{path}</b>"
);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDeleted, this);
},
createPanel: function ()
{
this._container = Ext.create("Ext.Container", {
scrollable: true,
cls: 'empty',
html: ''
});
this._iframe = Ext.create("Ext.ux.IFrame", {});
this._iframe.on ('render', this._onIframeRender, this);
this._iframe.on ('load', this._onIframeLoad, this);
return new Ext.Panel ({
layout: {
type: 'card',
deferredRender: false
},
cls: 'uitool-pagetool',
dockedItems: {
dock: 'top',
xtype: 'button',
hidden: true,
itemId: 'out-of-site',
ui: 'tool-hintmessage',
text: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_OUT_OF_SITE}}",
handler: this._openOutOfSitePage,
scope: this
},
items: [
this._container,
this._iframe
]
});
},
getType: function()
{
return Ametys.tool.Tool.TYPE_PAGE;
},
setParams: function (params)
{
this.callParent(arguments);
this._openedAtStartup = !Ametys.tool.ToolsManager.isInitialized();
this._pageId = params['pageId'] || params['id'];
this._pageMessageType = params['page-message-type'] || Ametys.message.MessageTarget.PAGE;
this.refresh();
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
refresh: function()
{
this.showRefreshing();
Ametys.web.page.PageDAO.getPage(this._pageId, Ext.bind(this._finalizeParams, this), null, false);
},
/**
* @private
* Finalize refreshment of tool
* @param {Ametys.web.page.Page} page The page or null if not found
*/
_finalizeParams: function (page)
{
if (page)
{
this._openedAtStartup = false;
this._siteName = page.getSiteName();
if (this._siteName != Ametys.getAppParameter('siteName'))
{
// Page will be in read-only
this._isOutOfSite = true;
this._showOutOfSitePanel();
}
this._updateUI (page);
this._updateInfos (page);
this._updateLastValues(page);
this.showRefreshed();
// Save in navigation history
this._saveToNavhistory(page);
}
else
{
if (!this._openedAtStartup)
{
// Page not found
var details = '';
var params = this.getParams();
for (var name in params)
{
details += name + " : " + params[name] + '\n';
}
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_PAGE_NOT_FOUND}}",
text: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_PAGE_NOT_FOUND_ERROR}}",
details: details,
category: this.self.getName()
});
}
else
{
Ametys.notify({
title: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_PAGE_NOT_FOUND}}",
description: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_PAGE_NOT_FOUND_ERROR}}",
type: "warn"
});
this.getLogger().error("Cannot open unexisting page");
}
this.close();
}
},
/**
* Show the top hint panel when the page is not a page of current site.
* @private
*/
_showOutOfSitePanel: function ()
{
this.getContentPanel().down("#out-of-site").show();
},
/**
* @private
* Open a page which is not a page of the current site
*/
_openOutOfSitePage: function ()
{
window.open("../" + this._siteName + "/index.html?uitool=uitool-page,id:'" + this._pageId + "'");
},
/**
* Update UI
* @param {Ametys.web.page.Page} page The page
* @private
*/
_updateUI: function (page)
{
switch (page.getType().toLowerCase())
{
case "node":
this.getContentPanel().getLayout().setActiveItem(0);
var link = "";
var url = page.getUrl();
var title = page.getUrlTitle();
if (url != null && title != null)
{
link = ": <a href='javascript: Ametys.tool.ToolsManager.openTool (\"uitool-page\", {id: \"" + url + "\", followNavigation: false, event: window.event}) && void(0);'>" + title + "</a>";
if (this.getParams().followNavigation)
{
Ametys.tool.ToolsManager.openTool ("uitool-page", {id: url, followNavigation: false});
}
}
var html = this._nodeOrLinkTpl.applyTemplate({
iconCls: this.getInitialConfig()['icon-glyph'] + (this.getInitialConfig()['icon-decorator'] ? " " + this.getInitialConfig()['icon-decorator'] : ""),
title: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_BLANKPAGE_TITLE}}",
text: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_BLANKPAGE}}" + link + "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_BLANKPAGE2}}"
});
this._container.update(html);
this.setGlyphIcon(this.getInitialConfig()['icon-glyph']);
this.setIconDecorator(this.getInitialConfig()['icon-decorator']);
break;
case "link":
this.getContentPanel().getLayout().setActiveItem(0);
var url = page.getUrl();
var urlType = page.getUrlType();
var link = "";
switch (urlType)
{
case "PAGE":
var title = page.getUrlTitle();
if (title != null)
{
if (this.getParams().followNavigation)
{
Ametys.tool.ToolsManager.openTool ("uitool-page", {id: url, followNavigation: false});
}
link = "<a href='javascript: Ametys.tool.ToolsManager.openTool (\"uitool-page\", {id: \"" + url + "\", followNavigation: false}) && void(0);'>" + title + "</a>";
}
else
{
link = "<span class='error'>{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_NOPAGE}}</span>";
}
break;
case "WEB":
default:
link = "<a target='_blank' href='" + url + "'>" + url + "</a>";
break;
}
var html = this._nodeOrLinkTpl.applyTemplate({
iconCls: this.getInitialConfig()['icon-glyph2'] + (this.getInitialConfig()['icon-decorator2'] ? " " + this.getInitialConfig()['icon-decorator2'] : ""),
title: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_REDIRECT_PAGE_TITLE}}",
text: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_REDIRECT_PAGE}}" + link + "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_REDIRECT_PAGE_2}}"
});
this._container.update(html);
this.setGlyphIcon(this.getInitialConfig()['icon-glyph2']);
this.setIconDecorator(this.getInitialConfig()['icon-decorator2']);
break;
case "container":
this.getContentPanel().getLayout().setActiveItem(1);
var url = "/" + page.getSiteName() + "/" + page.getLang() + "/" + page.getPath() + ".html";
this._iframe.load(Ametys.CONTEXT_PATH + url)
this._baseUrl = Ametys.CONTEXT_PATH + url;
this._contentIds = page.getContentIds();
this.setGlyphIcon(this.getInitialConfig()['icon-glyph3']);
this.setIconDecorator(this.getInitialConfig()['icon-decorator3']);
break;
default:
// Unknow type of page
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_UNKNOWN_PAGE_TYPE_TITLE}}",
text: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_UNKNOWN_PAGE_TYPE_TEXT}}",
details: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_UNKNOWN_PAGE_TYPE_DETAILS1}}" + page.getType() + "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_UNKNOWN_PAGE_TYPE_DETAILS2}}",
category: this.self.getName()
});
this.close();
}
},
/**
* Update tool information
* @param {Ametys.web.page.Page} page The page
* @private
*/
_updateInfos: function (page)
{
var values = {
siteName: page.getSiteName(),
lang: page.getLang(),
path: page.getPath()
}
this.setTitle(page.getTitle());
var description = this._tooltipDescriptionTpl.applyTemplate (values);
this.setDescription(description);
},
/**
* Update "last" values, ensuring that the corresponding objects still exist.
* @param {Ametys.web.page.Page} page The page
* @private
*/
_updateLastValues: function(page)
{
var zoneExists = false;
var ziExists = false;
var zones = page.getZones();
for (var i = 0; i < zones.length; i++)
{
if (zones[i].name == this._lastZoneName)
{
zoneExists = true;
}
if (this._lastZoneItemId != null)
{
var zoneItems = zones[i].zoneitems;
for (var j = 0; j < zoneItems.length; j++)
{
if (zoneItems[j].id == this._lastZoneItemId)
{
ziExists = true;
}
}
}
}
this._lastPageType = page.getType();
if (!zoneExists)
{
this._lastZoneName = null;
this._lastZoneWidth = null;
}
if (!ziExists)
{
this._lastZoneItemId = null;
}
},
/**
* Saves the accessed page in navigation history
* @param {Ametys.web.page.Page} page The page
* @private
*/
_saveToNavhistory: function (page)
{
var i18nDesc = "{{i18n PLUGINS_WEB_UITOOL_PAGE_NAVHISTORY_DESC}}";
var toolId = this.getFactory().getId();
var currentToolParams = Ext.clone(this.getParams());
currentToolParams.followNavigation = false; // Click from history should not automatically open redirection
Ametys.navhistory.HistoryDAO.addEntry({
id: this.getId(),
objectId: this.getId(),
label: this.getTitle(),
description: Ext.String.format(i18nDesc, Ext.String.escapeHtml(page.getTitle()), page.getLang()),
iconGlyph: this.getGlyphIcon(),
iconSmall: this.getSmallIcon(),
iconMedium: this.getMediumIcon(),
iconLarge: this.getLargeIcon(),
type: Ametys.navhistory.HistoryDAO.PAGE_TYPE,
action: Ext.bind(Ametys.tool.ToolsManager.openTool, Ametys.tool.ToolsManager, [toolId, currentToolParams], false)
});
},
sendCurrentSelection: function()
{
var me = this;
if (!this._isOutOfSite)
{
Ext.create("Ametys.message.Message", {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: {
id: this._pageMessageType,
parameters: {
'ids': [ this._pageId ],
'zone-name': this._lastZoneName,
'zoneitem-id': this._lastZoneItemId,
'zone-width': this._lastZoneWidth,
'errorMessage': this._openedAtStartup ? false : undefined
}
},
beforeFireCallback: function(message)
{
if (me._lastZoneItemId && message.getTarget("content"))
{
message.setParameters(Ext.apply({ creation: 'content' }, message.getParameters()));
}
message.getTargets()[0]
}
});
}
else
{
Ext.create("Ametys.message.Message", {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: []
});
}
},
/**
* @private
* Listener called when the iframe is rendered.
* Protects the iframe by handling links of the internal frame by setting a onclick on every one
* @param {Ext.ux.IFrame} iframe The iframe
*/
_onIframeRender: function (iframe)
{
this._dropZone = new Ametys.relation.dd.AmetysDropZone(iframe.getEl(), {setAmetysDropZoneInfos: Ext.bind(this.getDropInfo, this)});
},
/**
* @private
* This event is thrown before the beforeDrop event and create the target of the drop operation relation.
* @param {Object} target 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.
*/
getDropInfo: function(target, item)
{
item.target = {
relationTypes: [Ametys.relation.Relation.MOVE, Ametys.relation.Relation.REFERENCE, Ametys.relation.Relation.COPY],
targets: {
id: this._pageMessageType,
parameters: { ids: [this._pageId] }
}
};
},
/**
* @private
* Listener called when the iframe is loaded.
* Protects the iframe by handling links of the internal frame by setting a onclick on every one
* @param {Ext.ux.IFrame} iframe The iframe
*/
_onIframeLoad: function (iframe)
{
if (window.event && window.event.srcElement.readyState && !/loaded|complete/.test(window.event.srcElement.readyState))
{
return;
}
Ametys.plugins.web.page.tool.PageTool.__URL_REGEXP.test(window.location.href);
var win = this._iframe.getWin();
var outside = false;
try
{
win.location.href
}
catch (e)
{
outside = true;
}
if (outside || (win.location.href != 'about:blank' && win.location.href.indexOf(RegExp.$1 + this._baseUrl) != 0))
{
var outsideUrl = win.location.href;
// Back to iframe base url
win.location.href = this._baseUrl;
// Open ouside url in a new window
window.open(outsideUrl);
}
else
{
this._iframe.getWin().onfocus = Ext.bind(this._onIframeFocus, this);
this._handleZones();
this._handleLinks();
}
},
/**
* @private
* Listener when iframe takes the focus.<br/>
* Force the focus on tool.
*/
_onIframeFocus: function ()
{
// Force focus on tool
this.focus();
},
/**
* @private
* Prepare the iframe to have the onZoneSelected method
*/
_handleZones: function()
{
this._iframe.getWin().onZoneSelected = Ext.bind(this._onInternalZoneSelected, this);
},
/**
* @private
* Take care of all existing links in the page to wrap onclick event with #_handleLink
*/
_handleLinks: function()
{
var iframe = this._iframe.getEl().dom;
var links = this._iframe.getWin().document.getElementsByTagName("a");
for (var a = 0; a < links.length; a++)
{
if (links[a].getAttribute("internal") == null && links[a].href && links[a].href.length > 0)
{
if (links[a].onclick)
{
links[a].onclickold = links[a].onclick;
}
links[a].onclick = Ext.bind(this._handleLink, this, [links[a]], 0);
}
}
},
/**
* @private
* Listener for the internal frame, when a zone is selected
*/
_onInternalZoneSelected: function(zoneName, zoneItemId, zoneWidth)
{
this._iframe.getWin().focus();
if (!this._isOutOfSite)
{
this._lastZoneName = zoneName;
this._lastZoneItemId = zoneItemId;
this._lastZoneWidth = zoneWidth;
}
this.sendCurrentSelection();
},
/**
* @private
* Lazy handling of links. Each times a link is clicked, check if we should open it in a window, open another tool...
* @param {HTMLElement} link The clicked link
* @param {Event} event The browser event
*/
_handleLink: function(link, event)
{
this._ctrlPressedOnLastLink = (new Ext.event.Event(event)).ctrlKey;
if (link.onclickold)
{
var ret = link.onclickold();
if (ret === false)
{
return false;
}
}
var currentURI = this._iframe.getWin().location.href;
var absHref = link.href;
var relHref = null;
if (/^(https?:\/\/[^\/]+)(\/.*)?/.test(absHref) && absHref.indexOf(RegExp.$1 + Ametys.CONTEXT_PATH) == 0)
{
relHref = absHref.substring(absHref.indexOf(RegExp.$1 + Ametys.CONTEXT_PATH) + (RegExp.$1 + Ametys.CONTEXT_PATH).length);
}
// Internal link
if (absHref.indexOf(currentURI) == 0) return true;
// JS Link
if (absHref.indexOf("javascript:") == 0) return true;
// Download link
if (relHref != null && relHref.indexOf("/plugins/explorer/download/") == 0) return true;
// Pagelink
if (relHref != null && /^\/([^\/]*)\/([a-z]{2})\/(.*)\.html$/.test(relHref))
{
// We have a path but want an id
var siteName = RegExp.$1,
lang = RegExp.$2,
path = RegExp.$3;
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.SitemapDAO",
methodName: 'convertPathToId',
parameters: [siteName, lang, path],
callback: {
handler: this._getPathCb,
scope: this,
arguments: {
siteName: siteName,
lang: lang,
path: path
}
},
errorMessage: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR}}"
});
return false;
}
// Unknown link : open in a new window
window.open(absHref);
return false;
},
/**
* @private
* Function called after checking if the page with this path exist in sitemap
* @param {String} pageId The page id or null if not found
* @param {Object} args The callback arguments
*/
_getPathCb: function (pageId, args)
{
if (pageId != null)
{
// Existing id: we open the page
Ametys.tool.ToolsManager.openTool ("uitool-page", {id: pageId});
}
else
{
var detailMsg = "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR_NOPAGE_SITENAME}}" + args.siteName
+ "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR_NOPAGE_LANG}}" + args.lang
+ "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR_NOPAGE_PATH}}" + args.path;
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR_NOPAGE_TITLE}}",
text: "{{i18n PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR_NOPAGE_TEXT}}",
details: detailMsg,
category: this.self.getName()
});
}
},
/**
* Listener on {@link Ametys.message.Message#MODIFIED} message. If the current page is concerned, the tool will be out-of-date.
* @param {Ametys.message.Message} message The edited message.
* @protected
*/
_onModified: function (message)
{
var me = this;
var pageTargets = message.getTargets(function (target) {return me._pageMessageType == target.getId();});
for (var i=0; i < pageTargets.length; i++)
{
if (this._pageId == pageTargets[i].getParameters().id)
{
if (pageTargets[i].getParameters()['zone-name'] !== undefined)
{
// The modification concerned of a zone or a zone item of the page
var selectionWillChange = false;
if (this._lastZoneName != pageTargets[i].getParameters()['zone-name'])
{
this._lastZoneName = pageTargets[i].getParameters()['zone-name'];
selectionWillChange = true;
}
if (this._lastZoneItemId != pageTargets[i].getParameters()['zoneitem-id'])
{
this._lastZoneItemId = pageTargets[i].getParameters()['zoneitem-id'];
selectionWillChange = true;
}
if (this._lastPageType != pageTargets[i].getParameters()['type'])
{
this._lastPageType = pageTargets[i].getParameters()['type'];
selectionWillChange = true;
}
if (selectionWillChange)
{
this.sendCurrentSelection();
}
}
this.showOutOfDate();
}
}
var contentTargets = message.getTargets(Ametys.message.MessageTarget.CONTENT);
for (var i=0; i < contentTargets.length; i++)
{
if (this._contentIds && Ext.Array.contains (this._contentIds, contentTargets[i].getParameters().id))
{
this.showOutOfDate();
}
}
},
/**
* Listener on {@link Ametys.message.Message#DELETED} message. If the current page is concerned, the tool will be closed.
* @param {Ametys.message.Message} message The deleted message.
* @protected
*/
_onDeleted: function (message)
{
var me = this;
var pageTargets = message.getTargets(function (target) {return me._pageMessageType == target.getId();});
for (var i=0; i < pageTargets.length; i++)
{
if (this._pageId == pageTargets[i].getParameters().id)
{
this.close();
// Remove the deleted page from the navigation history
Ametys.navhistory.HistoryDAO.removeEntry (this.getId());
}
}
}
});