mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°636 Portal: Action buttons can now be added to object details page through the iPopupMenuItemExtension
SVN:trunk[4600]
This commit is contained in:
@@ -308,6 +308,37 @@ interface iPopupMenuExtension
|
||||
* $param is null
|
||||
*/
|
||||
const MENU_USER_ACTIONS = 5;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object details page in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object currently displayed)
|
||||
*/
|
||||
const PORTAL_OBJDETAILS_ACTIONS = 7;
|
||||
|
||||
/**
|
||||
* Insert an item into the Actions menu of a list in the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object_set' => $oSet) containing DBObjectSet containing the list of objects
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_OBJLIST_ACTIONS = 6;
|
||||
/**
|
||||
* Insert an item into the user menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_USER_ACTIONS = 8;
|
||||
/**
|
||||
* Insert an item into the navigation menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_MENU_ACTIONS = 9;
|
||||
|
||||
/**
|
||||
* Get the list of items to be added to a menu.
|
||||
@@ -334,17 +365,21 @@ abstract class ApplicationPopupMenuItem
|
||||
protected $sUID;
|
||||
/** @ignore */
|
||||
protected $sLabel;
|
||||
/** @ignore */
|
||||
protected $aCssClasses;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
*/
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @param array $aCssClasses The CSS classes to add to the menu
|
||||
*/
|
||||
public function __construct($sUID, $sLabel)
|
||||
{
|
||||
$this->sUID = $sUID;
|
||||
$this->sLabel = $sLabel;
|
||||
$this->aCssClasses = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,6 +403,35 @@ abstract class ApplicationPopupMenuItem
|
||||
{
|
||||
return $this->sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS classes
|
||||
*
|
||||
* @return array
|
||||
* @ignore
|
||||
*/
|
||||
public function GetCssClasses()
|
||||
{
|
||||
return $this->aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aCssClasses
|
||||
*/
|
||||
public function SetCssClasses($aCssClasses)
|
||||
{
|
||||
$this->aCssClasses = $aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CSS class to the CSS classes that will be put on the menu item
|
||||
*
|
||||
* @param $sCssClass
|
||||
*/
|
||||
public function AddCssClass($sCssClass)
|
||||
{
|
||||
$this->aCssClasses[] = $sCssClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
@@ -415,7 +479,7 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array ('label' => $this->GetLabel(), 'url' => $this->sURL, 'target' => $this->sTarget);
|
||||
return array ('label' => $this->GetLabel(), 'url' => $this->sURL, 'target' => $this->sTarget, 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +515,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
public function GetMenuItem()
|
||||
{
|
||||
// Note: the semicolumn is a must here!
|
||||
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#');
|
||||
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
@@ -483,10 +547,34 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array ('label' => '<hr class="menu-separator">', 'url' => '');
|
||||
return array ('label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that browses to the given URL
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0
|
||||
*/
|
||||
class URLButtonItem extends URLPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that runs some JS code
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0
|
||||
*/
|
||||
class JSButtonItem extends JSPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any iTopWebPage
|
||||
*
|
||||
|
||||
@@ -734,7 +734,7 @@ class WebPage implements Page
|
||||
{
|
||||
foreach ($aActions as $aAction)
|
||||
{
|
||||
$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
|
||||
$sClass = isset($aAction['css_classes']) ? ' class="'.implode(' ', $aAction['css_classes']).'"' : '';
|
||||
$sOnClick = isset($aAction['onclick']) ? ' onclick="'.htmlspecialchars($aAction['onclick'], ENT_QUOTES, "UTF-8").'"' : '';
|
||||
$sTarget = isset($aAction['target']) ? " target=\"{$aAction['target']}\"" : "";
|
||||
if (empty($aAction['url']))
|
||||
|
||||
@@ -43,6 +43,9 @@ use \cmdbAbstractObject;
|
||||
use \AttributeEnum;
|
||||
use \AttributeFinalClass;
|
||||
use \UserRights;
|
||||
use \iPopupMenuExtension;
|
||||
use \URLButtonItem;
|
||||
use \JSButtonItem;
|
||||
use \Combodo\iTop\Portal\Helper\ApplicationHelper;
|
||||
use \Combodo\iTop\Portal\Helper\SecurityHelper;
|
||||
use \Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
|
||||
@@ -101,10 +104,13 @@ class ObjectController extends AbstractController
|
||||
// Add an edit button if user is allowed
|
||||
if (SecurityHelper::IsActionAllowed($oApp, UR_ACTION_MODIFY, $sObjectClass, $sObjectId))
|
||||
{
|
||||
$aData['form']['buttons']['links'][] = array(
|
||||
'label' => Dict::S('UI:Menu:Modify'),
|
||||
'url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId))
|
||||
);
|
||||
$oModifyButton = new URLButtonItem(
|
||||
'modify_object',
|
||||
Dict::S('UI:Menu:Modify'),
|
||||
$oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId))
|
||||
);
|
||||
// Putting this one first
|
||||
$aData['form']['buttons']['links'][] = $oModifyButton->GetMenuItem();
|
||||
}
|
||||
|
||||
// Preparing response
|
||||
@@ -467,7 +473,9 @@ class ObjectController extends AbstractController
|
||||
|
||||
// Preparing transitions only if we are currently going through one
|
||||
$aFormData['buttons'] = array(
|
||||
'transitions' => array()
|
||||
'transitions' => array(),
|
||||
'actions' => array(),
|
||||
'links' => array(),
|
||||
);
|
||||
if ($sMode !== 'apply_stimulus')
|
||||
{
|
||||
@@ -482,6 +490,25 @@ class ObjectController extends AbstractController
|
||||
$aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel();
|
||||
}
|
||||
}
|
||||
|
||||
// Add plugins buttons
|
||||
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
|
||||
{
|
||||
foreach($oExtensionInstance->EnumItems(iPopupMenuExtension::PORTAL_OBJDETAILS_ACTIONS, array('portal_id' => $oApp['combodo.portal.instance.id'], 'object' => $oObject)) as $oMenuItem)
|
||||
{
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
if($oMenuItem instanceof JSButtonItem)
|
||||
{
|
||||
$aFormData['buttons']['actions'][] = $oMenuItem->GetMenuItem() + array('js_files' => $oMenuItem->GetLinkedScripts());
|
||||
}
|
||||
elseif($oMenuItem instanceof URLButtonItem)
|
||||
{
|
||||
$aFormData['buttons']['links'][] = $oMenuItem->GetMenuItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Preparing callback urls
|
||||
$aCallbackUrls = $oApp['context_manipulator']->GetCallbackUrls($oApp, $aActionRules, $oObject, $bModal);
|
||||
@@ -1499,5 +1526,3 @@ class ObjectController extends AbstractController
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -21,6 +21,13 @@
|
||||
</div>
|
||||
<div class="form_buttons">
|
||||
{% block pFormButtons %}
|
||||
{# Misc. buttons #}
|
||||
{% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
|
||||
<div class="form_btn_misc">
|
||||
{% include 'itop-portal-base/portal/src/views/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{# Transition buttons #}
|
||||
{% if form.buttons is defined and form.buttons.transitions is defined and form.buttons.transitions|length > 0 %}
|
||||
<div class="form_btn_transitions">
|
||||
{% for sStimulusCode, sStimulusLabel in form.buttons.transitions %}
|
||||
@@ -29,6 +36,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form_btn_regular">
|
||||
{# If form has editable fields, we display cancel / submit buttons #}
|
||||
{% if form.editable_fields_count is defined and form.editable_fields_count > 0 %}
|
||||
<button class="btn btn-default form_btn_cancel" type="button" value="cancel" title="{{ 'Portal:Button:Cancel'|dict_s }}" data-dismiss="modal">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
@@ -39,6 +47,7 @@
|
||||
{{ 'Portal:Button:Submit'|dict_s }}
|
||||
</button>
|
||||
{% else %}
|
||||
{# Modal mode #}
|
||||
{% if tIsModal %}
|
||||
<input class="btn btn-default form_btn_cancel" type="button" value="{{ 'Portal:Button:Close'|dict_s }}" data-dismiss="modal">
|
||||
{% endif %}
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
{# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #}
|
||||
|
||||
{% block pFormButtons %}
|
||||
{% if form.buttons is defined and form.buttons.links is defined %}
|
||||
<div class="form_btn_transitions">
|
||||
{% for aLink in form.buttons.links %}
|
||||
<a class="btn btn-primary" href="{{ aLink.url }}">{{ aLink.label }}</a>
|
||||
{% endfor %}
|
||||
{# Misc. buttons #}
|
||||
{% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
|
||||
<div class="form_btn_misc">
|
||||
{% include 'itop-portal-base/portal/src/views/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if tIsModal is defined and tIsModal == true %}
|
||||
<div class="form_btn_regular">
|
||||
<input class="btn btn-primary form_btn_cancel" type="button" value="{{ 'Portal:Button:Close'|dict_s }}" data-dismiss="modal">
|
||||
<input class="btn btn-primary form_btn_cancel" type="button" value="{{ 'Portal:Button:Close'|dict_s }}" data-dismiss="modal" />
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,61 @@
|
||||
{% set iLinkButtonsCount = (aButtons.links is defined) ? aButtons.links|length : 0 %}
|
||||
{% set iActionButtonsCount = (aButtons.actions is defined) ? aButtons.actions|length : 0 %}
|
||||
{% set iButtonsCount = iLinkButtonsCount + iActionButtonsCount %}
|
||||
{% set iButtonsGroupThreshold = 2 %}
|
||||
{% set bGroupButtons = (iButtonsCount > iButtonsGroupThreshold) ? true : false %}
|
||||
|
||||
{% set sButtonCssClasses = (bGroupButtons) ? '' : 'btn btn-default' %}
|
||||
{% set aJSFiles = [] %}
|
||||
|
||||
{% if bGroupButtons == true %}
|
||||
<div class="dropup">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ 'UI:Menu:OtherActions'|dict_s }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% endif %}
|
||||
|
||||
{# JSs buttons #}
|
||||
{% if aButtons.actions is defined %}
|
||||
{% for aButton in aButtons.actions %}
|
||||
{# Adding button #}
|
||||
{% if bGroupButtons == true %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a class="{{ sButtonCssClasses }} {{ aButton.css_classes|join(' ') }}" href="{{ aButton.url }}" onclick="{{ aButton.onclick }}">{{ aButton.label }}</a>
|
||||
{% if bGroupButtons == true %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{# Preparing JS files to load #}
|
||||
{% set aJSFiles = aJSFiles|merge(aButton.js_files) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{# URLs buttons #}
|
||||
{% if aButtons.links is defined %}
|
||||
{% for aButton in aButtons.links %}
|
||||
{% if bGroupButtons == true %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a class="{{ sButtonCssClasses }} {{ aButton.css_classes|join(' ') }}" href="{{ aButton.url }}" target="{{ aButton.target }}">{{ aButton.label }}</a>
|
||||
{% if bGroupButtons == true %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if bGroupButtons == true %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Loading JS files #}
|
||||
{% if aJSFiles|length > 0 %}
|
||||
<script type="text/javascript">
|
||||
{% for sJSFile in aJSFiles %}
|
||||
console.log('loading {{ sJSFile }}');
|
||||
$.getScript('{{ sJSFile }}');
|
||||
{% endfor %}
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
@@ -1056,6 +1056,9 @@ table .group-actions {
|
||||
padding-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.form_buttons .form_btn_misc {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form_buttons .form_btn_transitions {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -1067,6 +1070,9 @@ table .group-actions {
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
/* Making regular button sticky */
|
||||
.form_buttons .form_btn_misc {
|
||||
float: left !important;
|
||||
}
|
||||
.form_buttons .form_btn_transitions {
|
||||
float: right !important;
|
||||
margin-left: 3px;
|
||||
|
||||
@@ -1126,6 +1126,9 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
padding-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.form_buttons .form_btn_misc {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form_buttons .form_btn_transitions{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -1136,6 +1139,9 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 768px){
|
||||
.form_buttons .form_btn_misc{
|
||||
float: left !important;
|
||||
}
|
||||
.form_buttons .form_btn_transitions{
|
||||
float: right !important;
|
||||
margin-left: 3px;
|
||||
|
||||
Reference in New Issue
Block a user