N°956 Portal: Add an icon to copy object name and url next to the form title

* Add a generic utility to build iTop clipboard widget
* Can be used in portal, console and extensions
This commit is contained in:
Stephen Abello
2019-08-06 12:29:02 +02:00
parent 16c123df49
commit e69275c6c5
25 changed files with 248 additions and 7 deletions

View File

@@ -75,6 +75,8 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/clipboard.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/clipboardwidget.js');
$this->add_dict_entries('UI:Combo');

View File

@@ -133,6 +133,9 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Uloženo',
'Brick:Portal:Object:Search:Regular:Title' => 'Vybrat %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Vybrat %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved~~',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -126,11 +126,14 @@ Dict::Add('EN US', 'English', 'English', array(
'Brick:Portal:Object:Name' => 'Object',
'Brick:Portal:Object:Form:Create:Title' => 'New %1$s',
'Brick:Portal:Object:Form:Edit:Title' => 'Updating %2$s (%1$s)',
'Brick:Portal:Object:Form:View:Title' => '%1$s : %2$s',
'Brick:Portal:Object:Form:View:Title' => '%1$s: %2$s',
'Brick:Portal:Object:Form:Stimulus:Title' => 'Please, complete the following information:',
'Brick:Portal:Object:Form:Message:Saved' => 'Saved',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied'
));
// CreateBrick brick

View File

@@ -129,6 +129,9 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Guardado',
'Brick:Portal:Object:Search:Regular:Title' => 'Selección %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Selección %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -129,6 +129,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Enregistré',
'Brick:Portal:Object:Search:Regular:Title' => 'Sélection de %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Sélection de %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s',
'Brick:Portal:Object:Copy:Tooltip' => 'Copier l\'url de l\'objet',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copié'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved~~',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved~~',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved~~',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Opgeslagen',
'Brick:Portal:Object:Search:Regular:Title' => 'Geselecteerd %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Selecteer %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -278,6 +278,21 @@ footer {
margin: 7em 0em;
text-align: center;
}
/*******************/
/* Clipboard icons */
/*******************/
.url-to-clipboard.url-to-clipboard-icon {
opacity: 0.5;
cursor: pointer;
transition: opacity 0.2s linear;
}
.url-to-clipboard.url-to-clipboard-icon:hover {
opacity: 1;
}
.url-to-clipboard-tooltip-copied {
color: green;
margin-right: 3px;
}
/**************************/
/* MagnificPopup settings */
/**************************/
@@ -292,7 +307,7 @@ footer {
cursor: zoom-out;
}
/********************/
/* Typeahed setting */
/* Typeahead setting */
/********************/
.twitter-typeahead .tt-menu {
max-height: 200px;

View File

@@ -293,6 +293,25 @@ footer{
text-align: center;
}
/*******************/
/* Clipboard icons */
/*******************/
.url-to-clipboard{
&.url-to-clipboard-icon {
opacity: 0.5;
cursor: pointer;
transition: opacity 0.2s linear;
&:hover {
opacity: 1;
}
}
}
// Used for clipboard's tooltip, which is not part of .url-to-clipboard element
.url-to-clipboard-tooltip-copied {
color: green;
margin-right: 3px;
}
/**************************/
/* MagnificPopup settings */
/**************************/
@@ -308,7 +327,7 @@ footer{
}
/********************/
/* Typeahed setting */
/* Typeahead setting */
/********************/
.twitter-typeahead .tt-menu{
max-height: 200px;

View File

@@ -0,0 +1,38 @@
/*
*
* * Copyright (C) 2013-2019 Combodo SARL
* *
* * This file is part of iTop.
* *
* * iTop is free software; you can redistribute it and/or modify
* * it under the terms of the GNU Affero General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * iTop 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 Affero General Public License for more details.
* *
* * You should have received a copy of the GNU Affero General Public License
*
*/
/*
* Initialize every modal DOM objects with class url-to-clipboard with itop.clipboard widget
*/
$(document).ready(function()
{
// Bootstrap modal: initialize .url-to-clipboard and set the widget container to the modal
$('body').on('loaded.bs.modal', function (oEvent) {
var oModalsElem = $(this).find('.modal.in');
oModalsElem.each(function()
{
var oModalElem = $(this);
oModalElem.find('.url-to-clipboard').each(function()
{
$(this).clipboard({'container': oModalElem[0]});
});
});
});
});

View File

@@ -23,6 +23,7 @@
namespace Combodo\iTop\Portal\Helper;
use ApplicationContext;
use Combodo\iTop\Portal\Form\ObjectFormManager;
use Combodo\iTop\Portal\Twig\AppExtension;
use Combodo\iTop\Renderer\Bootstrap\BsFormRenderer;
@@ -363,7 +364,16 @@ class ObjectFormHandlerHelper
$aFormData['object_state'] = $oFormManager->GetObject()->GetState();
$aFormData['fieldset'] = $aFieldSetData;
$aFormData['display_mode'] = (isset($aFormProperties['properties'])) ? $aFormProperties['properties']['display_mode'] : ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE;
// - Set a text to be copied on title if the object is not in creation
if($sMode !== static::ENUM_MODE_CREATE)
{
$aFormData['title_clipboard_text'] = Dict::Format(
'Brick:Portal:Object:Copy:TextToCopy',
$aFormData['object_name'],
ApplicationContext::MakeObjectUrl($sObjectClass, $sObjectId)
);
}
return $aFormData;
}

View File

@@ -7,7 +7,18 @@
{% block pMainHeader %}
<div class="col-xs-12" id="main-header-title">
{% if form.title is defined %}
<h2>{{ form.title|raw }}</h2>
<h2>
{% if form.title_clipboard_text is defined %}
<i class="url-to-clipboard url-to-clipboard-icon fas fa-link fa-xs"
data-clipboard-text="{{ form.title_clipboard_text }}"
data-toggle="tooltip"
title="{{ 'Brick:Portal:Object:Copy:Tooltip' | dict_s }}"
data-copied-title="{{ 'Brick:Portal:Object:Copy:CopiedTooltip' | dict_s }}"
data-copied-icon="fas fa-check">
</i>
{% endif %}
{{ form.title|raw }}
</h2>
{% endif %}
</div>
{% endblock %}

View File

@@ -3,6 +3,15 @@
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% block pModalTitle %}
{% if form.title_clipboard_text is defined %}
<i class="url-to-clipboard url-to-clipboard-icon fas fa-link fa-xs"
data-clipboard-text="{{ form.title_clipboard_text }}"
data-toggle="tooltip"
title="{{ 'Brick:Portal:Object:Copy:Tooltip' | dict_s }}"
data-copied-title="{{ 'Brick:Portal:Object:Copy:CopiedTooltip' | dict_s }}"
data-copied-icon="fas fa-check">
</i>
{% endif %}
{% if form.title is defined %}
{{ form.title|raw }}
{% endif %}

View File

@@ -136,8 +136,13 @@
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_field.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_field_html.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_field_set.js'|add_itop_version }}"></script>
{# UI Extensions JS, in an undefined order #}
{% if app['combodo.portal.instance.conf'].ui_extensions.js_files is defined %}
{# Clipboard helper #}
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/clipboard.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/clipboardwidget.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal-clipboard.js'|add_itop_version }}"></script>
{# UI Extensions JS, in an undefined order #}
{% if app['combodo.portal.instance.conf'].ui_extensions.js_files is defined %}
{% for js_file in app['combodo.portal.instance.conf'].ui_extensions.js_files %}
<script type="text/javascript" src="{{ js_file|add_itop_version }}"></script>
{% endfor %}

View File

@@ -129,6 +129,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Salvo',
'Brick:Portal:Object:Search:Regular:Title' => 'Selecionar %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Selecinar %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -118,6 +118,9 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Сохранено',
'Brick:Portal:Object:Search:Regular:Title' => 'Выбрать %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Выбрать %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved~~',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Brick:Portal:Object:Form:Message:Saved' => 'Saved~~',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)~~',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

View File

@@ -130,6 +130,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Brick:Portal:Object:Form:Message:Saved' => '已保存',
'Brick:Portal:Object:Search:Regular:Title' => '选择 %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => '选择 %1$s (%2$s)',
'Brick:Portal:Object:Copy:TextToCopy' => '%1$s: %2$s~~',
'Brick:Portal:Object:Copy:Tooltip' => 'Copy object link~~',
'Brick:Portal:Object:Copy:CopiedTooltip' => 'Copied~~'
));
// CreateBrick brick

7
js/clipboard.min.js vendored Normal file

File diff suppressed because one or more lines are too long

80
js/clipboardwidget.js Normal file
View File

@@ -0,0 +1,80 @@
/*
*
* * Copyright (C) 2013-2019 Combodo SARL
* *
* * This file is part of iTop.
* *
* * iTop is free software; you can redistribute it and/or modify
* * it under the terms of the GNU Affero General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * iTop 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 Affero General Public License for more details.
* *
* * You should have received a copy of the GNU Affero General Public License
*
*/
$(function() {
// the widget definition, where "itop" is the namespace,
// "clipboard" the widget name
$.widget("itop.clipboard",
{
// default options
options: {
standard_title: '',
standard_icon: '',
copied_title: '',
copied_icon: '',
container: '',
},
_create: function () {
var me = this;
var sTitle = this.element.attr('title');
var sDataTitleIcon = this.element.attr('data-title-icon');
var sDataCopiedTitle = this.element.attr('data-copied-title');
var sDataCopiedIcon = this.element.attr('data-copied-icon');
this.options.standard_title = (typeof sTitle === 'undefined' ? this.options.standard_title : sTitle);
this.options.standard_icon = (typeof sDataTitleIcon === 'undefined' ? this.options.standard_icon : sDataTitleIcon);
this.options.copied_title = (typeof sDataCopiedTitle === 'undefined' ? this.options.copied_title : sDataCopiedTitle);
this.options.copied_icon = (typeof sDataCopiedIcon === 'undefined' ? this.options.copied_icon : sDataCopiedIcon);
this.element.addClass('url-to-clipboard');
//initialize clipboard widget and set a container if provided (eg: bootstrap modal)
var aInitParams = {};
if (this.options.container !== '')
{
aInitParams['container'] = this.options.container;
}
new ClipboardJS(this.element[0], aInitParams);
//initialize tooltip with mouse interaction
this.element.on('click',function(){
var sOriginalTitle = (me.options.copied_icon !== '' ? '<i class="'+me.options.copied_icon+' url-to-clipboard-tooltip-copied"></i>' : '') + me.options.copied_title;
$(this).attr('data-original-title', sOriginalTitle).tooltip('show');
});
this.element.on('mouseout',function(){
var sOriginalTitle = (me.options.standard_icon !== '' ? '<i class="'+me.options.standard_icon+' url-to-clipboard-tooltip-copied"></i>' : '') + me.options.standard_title;
$(this).attr('data-original-title', sOriginalTitle);
$(this).removeClass('url-to-clipboard-copied');
});
this.element.tooltip({'html': true});
}
}
);
});
/*
* Initialize every DOM objects on page ready with class url-to-clipboard with itop.clipboard widget
*/
$(document).ready(function()
{
//Initialize every corresponding DOM element with clipboard.js widget
$('.url-to-clipboard').clipboard();
});