Browse Source

N°641 Portal: Browse and Manage bricks now have an optional <opening_target> tag to choose how to open items (modal, new tab, current window).

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4623 a333f486-631f-4898-b8df-5754b55c2be0
glajarige 8 years ago
parent
commit
cf730681e1

+ 2 - 1
datamodels/2.x/itop-portal-base/portal/src/controllers/browsebrickcontroller.class.inc.php

@@ -45,7 +45,8 @@ class BrowseBrickController extends BrickController
 
 	public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId, $sBrowseMode = null, $sDataLoading = null)
 	{
-		$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
+        /** @var \Combodo\iTop\Portal\Brick\BrowseBrick $oBrick */
+        $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
 
 		// Getting availables browse modes
 		$aBrowseModes = $oBrick->GetAvailablesBrowseModes();

+ 5 - 2
datamodels/2.x/itop-portal-base/portal/src/controllers/managebrickcontroller.class.inc.php

@@ -49,6 +49,7 @@ class ManageBrickController extends BrickController
 
 	public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId, $sGroupingTab, $sDataLoading = null)
 	{
+	    /** @var \Combodo\iTop\Portal\Brick\ManageBrick $oBrick */
 		$oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
 
 		$aData = array();
@@ -387,7 +388,8 @@ class ManageBrickController extends BrickController
 							$aActions[] = array(
 								'type' => $sActionType,
 								'class' => $sCurrentClass,
-								'id' => $oCurrentRow->GetKey()
+								'id' => $oCurrentRow->GetKey(),
+                                'opening_target' => $oBrick->GetOpeningTarget(),
 							);
 						}
 					}
@@ -406,7 +408,8 @@ class ManageBrickController extends BrickController
 								$aActions[] = array(
 									'type' => ManageBrick::ENUM_ACTION_VIEW,
 									'class' => $oAttDef->GetTargetClass(),
-									'id' => $oCurrentRow->Get($sItemAttr)
+									'id' => $oCurrentRow->Get($sItemAttr),
+                                    'opening_target' => $oBrick->GetOpeningTarget(),
 								);
 							}
 						}

+ 20 - 1
datamodels/2.x/itop-portal-base/portal/src/entities/browsebrick.class.inc.php

@@ -49,9 +49,13 @@ class BrowseBrick extends PortalBrick
 	const DEFAULT_LEVEL_NAME_ATT = 'name';
 	const DEFAULT_BROWSE_MODE = self::ENUM_BROWSE_MODE_LIST;
 	const DEFAULT_ACTION = self::ENUM_ACTION_DRILLDOWN;
+	const DEFAULT_ACTION_OPENING_TARGET = self::ENUM_OPENING_TARGET_MODAL;
 	const DEFAULT_COUNT_PER_PAGE_LIST = 20;
 
-	static $sRouteName = 'p_browse_brick';
+    static $aBrowseModes = array(self::ENUM_BROWSE_MODE_LIST, self::ENUM_BROWSE_MODE_TREE, self::ENUM_BROWSE_MODE_GRID);
+
+    static $sRouteName = 'p_browse_brick';
+
 	protected $aLevels;
 	protected $aAvailablesBrowseModes;
 	protected $sDefaultBrowseMode;
@@ -417,6 +421,21 @@ class BrowseBrick extends PortalBrick
 								{
 									$aTmpAction['icon_class'] = $oActionIconClassNode->GetText();
 								}
+								// Action opening target
+                                $oActionOpeningTargetNode = $oActionNode->GetOptionalElement('opening_target');
+								if($oActionOpeningTargetNode !== null)
+                                {
+                                    $aTmpAction['opening_target'] = $oActionOpeningTargetNode->GetText(static::DEFAULT_ACTION_OPENING_TARGET);
+                                }
+                                else
+                                {
+                                    $aTmpAction['opening_target'] = static::DEFAULT_ACTION_OPENING_TARGET;
+                                }
+                                // - Checking that opening target is among authorized modes
+                                if(!in_array($aTmpAction['opening_target'], static::$aOpeningTargets))
+                                {
+                                    throw new DOMFormatException('BrowseBrick :  ' . $sTagName . '/action/opening_target has a wrong value. "'.$aTmpAction['opening_target'].'" given, '.implode('|', static::$aOpeningTargets).' expected.', null, null, $oActionOpeningTargetNode);
+                                }
 								// Action rules
 								foreach ($oActionNode->GetNodes('./rules/rule') as $oRuleNode)
 								{

+ 29 - 31
datamodels/2.x/itop-portal-base/portal/src/entities/managebrick.class.inc.php

@@ -54,7 +54,7 @@ class ManageBrick extends PortalBrick
 		parent::__construct();
 
 		$this->sOql = static::DEFAULT_OQL;
-		$this->sOpeningMode = static::DEFAULT_OPENING_MODE;
+        $this->sOpeningMode = static::DEFAULT_OPENING_MODE;
 		$this->aGrouping = array();
 		$this->aFields = array();
 
@@ -72,15 +72,15 @@ class ManageBrick extends PortalBrick
 		return $this->sOql;
 	}
 
-	/**
-	 * Returns the brick's objects opening mode (edit or view)
-	 *
-	 * @return string
-	 */
-	public function GetOpeningMode()
-	{
-		return $this->sOpeningMode;
-	}
+    /**
+     * Returns the brick's objects opening mode (edit or view)
+     *
+     * @return string
+     */
+    public function GetOpeningMode()
+    {
+        return $this->sOpeningMode;
+    }
 
 	/**
 	 * Returns the brick grouping
@@ -114,17 +114,17 @@ class ManageBrick extends PortalBrick
 		return $this;
 	}
 
-	/**
-	 * Sets the brick's objects opening mode
-	 *
-	 * @param string $sOpeningMode
-	 * @return \Combodo\iTop\Portal\Brick\ManageBrick
-	 */
-	public function SetOpeningMode($sOpeningMode)
-	{
-		$this->sOpeningMode = $sOpeningMode;
-		return $this;
-	}
+    /**
+     * Sets the brick's objects opening mode
+     *
+     * @param string $sOpeningMode
+     * @return \Combodo\iTop\Portal\Brick\ManageBrick
+     */
+    public function SetOpeningMode($sOpeningMode)
+    {
+        $this->sOpeningMode = $sOpeningMode;
+        return $this;
+    }
 
 	/**
 	 * Sets the grouping of the brick
@@ -329,15 +329,15 @@ class ManageBrick extends PortalBrick
 					$this->SetOql($sOql);
 					break;
 
-				case 'opening_mode':
-					$sOpeningMode = $oBrickSubNode->GetText(static::DEFAULT_OPENING_MODE);
-					if (!in_array($sOpeningMode, array(static::ENUM_ACTION_VIEW, static::ENUM_ACTION_EDIT)))
-					{
-						throw new DOMFormatException('ManageBrick : opening_mode tag value must be edit|view ("' . $sOpeningMode . '" given)', null, null, $oBrickSubNode);
-					}
+                case 'opening_mode':
+                    $sOpeningMode = $oBrickSubNode->GetText(static::DEFAULT_OPENING_MODE);
+                    if (!in_array($sOpeningMode, array(static::ENUM_ACTION_VIEW, static::ENUM_ACTION_EDIT)))
+                    {
+                        throw new DOMFormatException('ManageBrick : opening_mode tag value must be edit|view ("' . $sOpeningMode . '" given)', null, null, $oBrickSubNode);
+                    }
 
-					$this->SetOpeningMode($sOpeningMode);
-					break;
+                    $this->SetOpeningMode($sOpeningMode);
+                    break;
 
 				case 'fields':
 					foreach ($oBrickSubNode->GetNodes('./field') as $oFieldNode)
@@ -426,5 +426,3 @@ class ManageBrick extends PortalBrick
 	}
 
 }
-
-?>

+ 49 - 0
datamodels/2.x/itop-portal-base/portal/src/entities/portalbrick.class.inc.php

@@ -32,6 +32,10 @@ use \Combodo\iTop\Portal\Brick\AbstractBrick;
  */
 abstract class PortalBrick extends AbstractBrick
 {
+    const ENUM_OPENING_TARGET_MODAL = 'modal';
+    const ENUM_OPENING_TARGET_SELF = 'self';
+    const ENUM_OPENING_TARGET_NEW = 'new';
+
 	const DEFAULT_WIDTH = 1;
 	const DEFAULT_HEIGHT = 1;
 	const DEFAULT_MODAL = false;
@@ -41,8 +45,11 @@ abstract class PortalBrick extends AbstractBrick
 	const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = '';
 	const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/tile.html.twig';
 	const DEFAULT_TILE_CONTROLLER_ACTION = null;
+    const DEFAULT_OPENING_TARGET = self::ENUM_OPENING_TARGET_MODAL;
 
 	static $sRouteName = null;
+    static $aOpeningTargets = array(self::ENUM_OPENING_TARGET_MODAL, self::ENUM_OPENING_TARGET_SELF, self::ENUM_OPENING_TARGET_NEW);
+
 	protected $iWidth;
 	protected $iHeight;
 	protected $bModal;
@@ -52,6 +59,7 @@ abstract class PortalBrick extends AbstractBrick
 	protected $sDecorationClassNavigationMenu;
 	protected $sTileTemplatePath;
 	protected $sTileControllerAction;
+    protected $sOpeningTarget;
 	// Vars below are itemization from parent class
 	protected $fRankHome;
 	protected $fRankNavigationMenu;
@@ -79,6 +87,7 @@ abstract class PortalBrick extends AbstractBrick
 		$this->sDecorationClassNavigationMenu = static::DEFAULT_DECORATION_CLASS_NAVIGATION_MENU;
 		$this->sTileTemplatePath = static::DEFAULT_TILE_TEMPLATE_PATH;
 		$this->sTileControllerAction = static::DEFAULT_TILE_CONTROLLER_ACTION;
+        $this->sOpeningTarget = static::DEFAULT_OPENING_TARGET;
 	}
 
 	/**
@@ -211,6 +220,16 @@ abstract class PortalBrick extends AbstractBrick
 		return $this->sTileTemplatePath;
 	}
 
+    /**
+     * Returns the brick's objects opening target (modal, new tab, current window)
+     *
+     * @return string
+     */
+    public function GetOpeningTarget()
+    {
+        return $this->sOpeningTarget;
+    }
+
 	/**
 	 * Sets the width of the brick
 	 *
@@ -354,6 +373,18 @@ abstract class PortalBrick extends AbstractBrick
 		return $this;
 	}
 
+    /**
+     * Sets the brick's objects opening target
+     *
+     * @param string $sOpeningTarget
+     * @return \Combodo\iTop\Portal\Brick\PortalBrick
+     */
+    public function SetOpeningTarget($sOpeningTarget)
+    {
+        $this->sOpeningTarget = $sOpeningTarget;
+        return $this;
+    }
+
 	/**
 	 * Load the brick's data from the xml passed as a ModuleDesignElement.
 	 * This is used to set all the brick attributes at once.
@@ -373,13 +404,16 @@ abstract class PortalBrick extends AbstractBrick
 				case 'width':
 					$this->SetWidth((int) $oBrickSubNode->GetText(static::DEFAULT_WIDTH));
 					break;
+
 				case 'height':
 					$this->SetHeight((int) $oBrickSubNode->GetText(static::DEFAULT_HEIGHT));
 					break;
+
 				case 'modal':
 					$bModal = ($oBrickSubNode->GetText(static::DEFAULT_MODAL) === 'true');
 					$this->SetModal($bModal);
 					break;
+
 				case 'visible':
 					// Default value
 					$oOptionalNode = $oBrickSubNode->GetOptionalElement('default');
@@ -404,6 +438,7 @@ abstract class PortalBrick extends AbstractBrick
 						$this->SetVisibleNavigationMenu($optionalNodeValue);
 					}
 					break;
+
 				case 'templates':
 					$oTemplateNodeList = $oBrickSubNode->GetNodes('template[@id=' . ModuleDesign::XPathQuote('tile') . ']');
 					if ($oTemplateNodeList->length > 0)
@@ -411,6 +446,7 @@ abstract class PortalBrick extends AbstractBrick
 						$this->SetTileTemplatePath($oTemplateNodeList->item(0)->GetText(static::DEFAULT_TILE_TEMPLATE_PATH));
 					}
 					break;
+
 				case 'rank':
 					// Setting value from parent attribute
 					$this->SetRankHome($this->fRank);
@@ -438,6 +474,7 @@ abstract class PortalBrick extends AbstractBrick
 						$this->SetRankNavigationMenu($optionalNodeValue);
 					}
 					break;
+
 				case 'title':
 					// Setting value from parent attribute
 					$this->SetTitleHome($this->sTitle);
@@ -466,6 +503,7 @@ abstract class PortalBrick extends AbstractBrick
 						$this->SetTitle($optionalNodeValue);
 					}
 					break;
+
 				case 'decoration_class':
 					// Setting value from parent attribute
 					$this->SetDecorationClassHome(static::DEFAULT_DECORATION_CLASS_HOME);
@@ -493,9 +531,20 @@ abstract class PortalBrick extends AbstractBrick
 						$this->SetDecorationClassNavigationMenu($optionalNodeValue);
 					}
 					break;
+
 				case 'tile_controller_action':
 					$this->SetTileControllerAction($oBrickSubNode->GetText(static::DEFAULT_TILE_CONTROLLER_ACTION));
 					break;
+
+                case 'opening_target':
+                    $sOpeningTarget = $oBrickSubNode->GetText(static::DEFAULT_OPENING_TARGET);
+                    if (!in_array($sOpeningTarget, array(static::ENUM_OPENING_TARGET_MODAL, static::ENUM_OPENING_TARGET_NEW, static::ENUM_OPENING_TARGET_SELF)))
+                    {
+                        throw new DOMFormatException('PortalBrick : opening_target tag value must be modal|new|self ("' . $sOpeningTarget . '" given)', null, null, $oBrickSubNode);
+                    }
+
+                    $this->SetOpeningTarget($sOpeningTarget);
+                    break;
 			}
 		}
 

+ 12 - 9
datamodels/2.x/itop-portal-base/portal/src/views/bricks/browse/mode_grid.html.twig

@@ -220,17 +220,20 @@
 						break;
 					case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
 						url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-						aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
-						break;
+                        SetActionUrl(aElem, url);
+                        SetActionOpeningTarget(aElem, levelPrimaryAction.opening_target);
+                        break;
 					case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_EDIT') }}':
 						url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-						aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
-						break;
+                        SetActionUrl(aElem, url);
+                        SetActionOpeningTarget(aElem, levelPrimaryAction.opening_target);
+                        break;
 					case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
 						url = levelPrimaryAction.url.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
 						url = AddParameterToUrl(url, 'ar_token', item.action_rules_token[levelPrimaryAction.type]);
-						aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
-						break;
+                        SetActionUrl(aElem, url);
+                        SetActionOpeningTarget(aElem, levelPrimaryAction.opening_target);
+                        break;
 					default:
 						//console.log('Action "'+levelPrimaryAction.type+'" not implemented for primary action');
 						break;
@@ -272,21 +275,21 @@
 						{
 							case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
 								url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-								actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 								break;
 							case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_EDIT') }}':
 								url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-								actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 								break;
 							case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
 								url = action.url.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
 								url = AddParameterToUrl(url, 'ar_token', item.action_rules_token[action.type]);
-								actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 								break;
 							default:
+							    url = '#';
 								//console.log('Action "'+action.type+'" not implemented for secondary action');
 								break;
 						}
+                        SetActionUrl(actionElem, url);
+                        SetActionOpeningTarget(actionElem, action.opening_target);
 						
 						// Adding title if present
 						if(action.title !== undefined)

+ 7 - 7
datamodels/2.x/itop-portal-base/portal/src/views/bricks/browse/mode_list.html.twig

@@ -89,21 +89,21 @@
 							{
 								case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
 									url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
-									cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
-									break;
+                                    break;
 								case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_EDIT') }}':
 									url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
-									cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 									break;
 								case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
 									url = levelPrimaryAction.url.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
 									url = AddParameterToUrl(url, 'ar_token', data.action_rules_token[levelPrimaryAction.type]);
-									cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 									break;
 								default:
+								    url = '#';
 									//console.log('Action "'+levelPrimaryAction.type+'" not implemented');
 									break;
 							}
+                            SetActionUrl(cellElem, url);
+                            SetActionOpeningTarget(cellElem, levelPrimaryAction.opening_target);
 
 							// - Secondary actions
 							if(levelActionsKeys.length > 1)
@@ -142,21 +142,21 @@
 									{
 										case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
 											url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
-											actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 											break;
 										case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_EDIT') }}':
 											url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
-											actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 											break;
 										case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
 											url = action.url.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
 											url = AddParameterToUrl(url, 'ar_token', data.action_rules_token[action.type]);
-											actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 											break;
 										default:
+										    url = '#';
 											//console.log('Action "'+action.type+'" not implemented for secondary action');
 											break;
 									}
+                                    SetActionUrl(actionElem, url);
+                                    SetActionOpeningTarget(actionElem, action.opening_target);
 									
 									// Adding title if present
 									if(action.title !== undefined)

+ 13 - 10
datamodels/2.x/itop-portal-base/portal/src/views/bricks/browse/mode_tree.html.twig

@@ -215,16 +215,19 @@
 						break;
 					case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
 						url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-						aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
-						break;
+						SetActionUrl(aElem, url);
+                        SetActionOpeningTarget(aElem, levelPrimaryAction.opening_target);
+                        break;
 					case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_EDIT') }}':
 						url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-						aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
-						break;
+						SetActionUrl(aElem, url);
+                        SetActionOpeningTarget(aElem, levelPrimaryAction.opening_target);
+                        break;
 					case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
 						url = levelPrimaryAction.url.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
 						url = AddParameterToUrl(url, 'ar_token', item.action_rules_token[levelPrimaryAction.type]);
-						aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
+						SetActionUrl(aElem, url);
+                        SetActionOpeningTarget(aElem, levelPrimaryAction.opening_target);
 						break;
 					default:
 						//console.log('Action "'+levelPrimaryAction.type+'" not implemented for primary action');
@@ -267,23 +270,23 @@
 						{
 							case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
 								url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-								actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 								break;
 							case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_EDIT') }}':
 								url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
-								actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 								break;
 							case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
 								url = action.url.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
 								url = AddParameterToUrl(url, 'ar_token', item.action_rules_token[action.type]);
-								actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 								break;
 							default:
+							    url = '#';
 								//console.log('Action "'+action.type+'" not implemented for secondary action');
 								break;
 						}
-						
-						// Adding title if present
+                        SetActionUrl(actionElem, url);
+                        SetActionOpeningTarget(actionElem, action.opening_target);
+
+                        // Adding title if present
 						if(action.title !== undefined)
 						{
 							actionElem.attr('title', action.title);

+ 25 - 0
datamodels/2.x/itop-portal-base/portal/src/views/bricks/layout.html.twig

@@ -30,4 +30,29 @@
 	{% block pMainContentHolder%}
 	{% endblock %}
 </div>
+{% endblock %}
+
+{% block pPageLiveScriptHelpers %}
+    {{ parent() }}
+
+	var SetActionUrl = function(oElem, sUrl)
+	{
+		oElem.attr('href', sUrl);
+	};
+
+	var SetActionOpeningTarget = function(oElem, sMode)
+	{
+		if(sMode === '{{ constant('Combodo\\iTop\\Portal\\Brick\\PortalBrick::ENUM_OPENING_TARGET_MODAL') }}')
+		{
+			oElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all');
+		}
+		else if(sMode === '{{ constant('Combodo\\iTop\\Portal\\Brick\\PortalBrick::ENUM_OPENING_TARGET_SELF') }}')
+		{
+			oElem.attr('target', '_self');
+		}
+		else if(sMode === '{{ constant('Combodo\\iTop\\Portal\\Brick\\PortalBrick::ENUM_OPENING_TARGET_NEW') }}')
+		{
+			oElem.attr('target', '_blank');
+		}
+	};
 {% endblock %}

+ 3 - 2
datamodels/2.x/itop-portal-base/portal/src/views/bricks/manage/layout.html.twig

@@ -131,16 +131,17 @@
 							{
 								case '{{ constant('Combodo\\iTop\\Portal\\Brick\\ManageBrick::ENUM_ACTION_VIEW') }}':
 									url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, itemPrimaryAction.class).replace(/-objectId-/, itemPrimaryAction.id);
-									cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 									break;
 								case '{{ constant('Combodo\\iTop\\Portal\\Brick\\ManageBrick::ENUM_ACTION_EDIT') }}':
 									url = '{{ app.url_generator.generate('p_object_edit', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, itemPrimaryAction.class).replace(/-objectId-/, itemPrimaryAction.id);
-									cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
 									break;
 								default:
+								    url = '#';
 									//console.log('Action "'+itemPrimaryAction+'" not implemented');
 									break;
 							}
+                            SetActionUrl(cellElem, url);
+                            SetActionOpeningTarget(cellElem, itemPrimaryAction.opening_target);
 							
 							// - Secondary actions
 							// Not done for now, only the data structure is ready in case we need it later

+ 15 - 13
datamodels/2.x/itop-portal-base/portal/src/views/layout.html.twig

@@ -313,19 +313,21 @@
 	
 	{% block pPageLiveScripts %}
 		<script type="text/javascript">
-			var GetAbsoluteUrlAppRoot = function()
-			{
-				return '{{ app['combodo.absolute_url'] }}';
-			};
-			var AddParameterToUrl = function(sUrl, sParamName, sParamValue)
-			{
-				sUrl += (sUrl.split('?')[1] ? '&':'?') + sParamName + '=' + sParamValue;
-				return sUrl;
-			};
-			var GetContentLoaderTemplate = function()
-			{
-				return '<div class="content_loader"><div class="icon glyphicon glyphicon-refresh"></div><div class="message">{{ 'Page:PleaseWait'|dict_s }}</div></div>';
-			}
+			{% block pPageLiveScriptHelpers %}
+				var GetAbsoluteUrlAppRoot = function()
+				{
+					return '{{ app['combodo.absolute_url'] }}';
+				};
+				var AddParameterToUrl = function(sUrl, sParamName, sParamValue)
+				{
+					sUrl += (sUrl.split('?')[1] ? '&':'?') + sParamName + '=' + sParamValue;
+					return sUrl;
+				};
+				var GetContentLoaderTemplate = function()
+				{
+					return '<div class="content_loader"><div class="icon glyphicon glyphicon-refresh"></div><div class="message">{{ 'Page:PleaseWait'|dict_s }}</div></div>';
+				}
+			{% endblock %}
 			
 			$(document).ready(function(){
 				{% block pPageReadyScripts %}

+ 4 - 0
datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml

@@ -1088,6 +1088,8 @@
 													<class>UserRequest</class>
 													<!-- Optional tag that can be used on any action type -->
 													<!--<title>Créer un ticket</title>-->
+													<!-- Optional tag to define if the action should be done in a modal window ("modal"), a new window ("new") or the current window ("self") -->
+													<!--<opening_target>modal</opening_target>-->
 													<icon_class>fc fc-new-request fc-1-6x fc-flip-horizontal</icon_class>
 													<rules>
 														<rule id="contact-to-userrequest"/>
@@ -1129,6 +1131,8 @@
 						<default>fc fc-ongoing-request fc-2x</default>
 					</decoration_class>
 					<oql><![CDATA[SELECT Ticket]]></oql>
+					<!-- Optional tag to define if the action should be done in a modal window ("modal"), a new window ("new") or the current window ("self") -->
+					<!--<opening_target>modal</opening_target>-->
 					<!-- Optional tag to define the how the objects should be opened. Values can be edit|view. Note that even if this is set to edit, objects not allowed in edition mode for the user (cf. scopes and security layers) will open in view mode -->
 					<!-- <opening_mode>edit</opening_mode> -->
 					<!-- Can be either a class tag with the class name or an oql tag with the query -->