mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
N°952 Portal: Added UI extension APIs similar to those used in the console (Experimental!)
SVN:trunk[4852]
This commit is contained in:
@@ -308,12 +308,18 @@ interface iPopupMenuExtension
|
||||
* $param is null
|
||||
*/
|
||||
const MENU_USER_ACTIONS = 5;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object item in an objects list in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object on the current line)
|
||||
*/
|
||||
const PORTAL_OBJLISTITEM_ACTIONS = 7;
|
||||
/**
|
||||
* 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;
|
||||
const PORTAL_OBJDETAILS_ACTIONS = 8;
|
||||
|
||||
/**
|
||||
* Insert an item into the Actions menu of a list in the portal
|
||||
@@ -330,7 +336,7 @@ interface iPopupMenuExtension
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_USER_ACTIONS = 8;
|
||||
const PORTAL_USER_ACTIONS = 9;
|
||||
/**
|
||||
* Insert an item into the navigation menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
@@ -338,7 +344,7 @@ interface iPopupMenuExtension
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_MENU_ACTIONS = 9;
|
||||
const PORTAL_MENU_ACTIONS = 10;
|
||||
|
||||
/**
|
||||
* Get the list of items to be added to a menu.
|
||||
@@ -616,6 +622,128 @@ interface iPageUIExtension
|
||||
public function GetBannerHtml(iTopWebPage $oPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any enhanced portal page
|
||||
*
|
||||
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.4
|
||||
*/
|
||||
interface iPortalUIExtension
|
||||
{
|
||||
const ENUM_PORTAL_EXT_UI_BODY = 'Body';
|
||||
const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
|
||||
const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
|
||||
|
||||
/**
|
||||
* Returns an array of CSS file urls
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
public function GetCSSFiles(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns inline (raw) CSS
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetCSSInline(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns an array of JS file urls
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
public function GetJSFiles(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw JS code
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetJSInline(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the <body> tag
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetBodyHTML(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #main-wrapper element
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetMainContentHTML(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Silex\Application $oApp);
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
|
||||
*/
|
||||
abstract class AbstractPortalUIExtension implements iPortalUIExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSFiles(\Silex\Application $oApp)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSInline(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSFiles(\Silex\Application $oApp)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSInline(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetBodyHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetMainContentHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add new operations to the REST/JSON web service
|
||||
*
|
||||
|
||||
@@ -1718,7 +1718,7 @@ abstract class MetaModel
|
||||
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension');
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension');
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClasses[$sInterface] = array();
|
||||
|
||||
@@ -106,6 +106,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
'Brick:Portal:Manage:Name' => 'Spravovat položky',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Žádná položka',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -98,9 +98,11 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Brick:Portal:Browse:Filter:NoData' => 'Kein Eintrag',
|
||||
));
|
||||
|
||||
// ManageBrick brick
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Brick:Portal:Manage:Name' => 'Einträge managen',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Kein Eintrag.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -102,6 +102,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'Brick:Portal:Manage:Name' => 'Manage items',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'No item.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -102,6 +102,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Brick:Portal:Manage:Name' => 'Administrar elementos',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Sin objeto.',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -102,6 +102,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Brick:Portal:Manage:Name' => 'Gestion d\'éléments',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Aucun élément',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -17,7 +17,7 @@ SetupWebPage::AddModule(
|
||||
'portal/src/entities/portalbrick.class.inc.php',
|
||||
'portal/src/controllers/abstractcontroller.class.inc.php',
|
||||
'portal/src/controllers/brickcontroller.class.inc.php',
|
||||
'portal/src/routers/abstractrouter.class.inc.php',
|
||||
'portal/src/routers/abstractrouter.class.inc.php',
|
||||
),
|
||||
'webservice' => array(
|
||||
//'webservices.itop-portal-base.php',
|
||||
@@ -40,4 +40,4 @@ SetupWebPage::AddModule(
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Brick:Portal:Manage:Name' => 'Beheer items',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Geen gegevens',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
@@ -41,6 +41,9 @@ use \VariableExpression;
|
||||
use \SQLExpression;
|
||||
use \UnaryExpression;
|
||||
use \Dict;
|
||||
use \iPopupMenuExtension;
|
||||
use \URLButtonItem;
|
||||
use \JSButtonItem;
|
||||
use \Combodo\iTop\Portal\Helper\ApplicationHelper;
|
||||
use \Combodo\iTop\Portal\Helper\SecurityHelper;
|
||||
use \Combodo\iTop\Portal\Brick\AbstractBrick;
|
||||
@@ -381,6 +384,7 @@ class ManageBrickController extends BrickController
|
||||
|
||||
// Retrieving and preparing data for rendering
|
||||
$aGroupingAreasData = array();
|
||||
$bHasObjectListItemExtension = false;
|
||||
foreach ($aSets as $sKey => $oSet)
|
||||
{
|
||||
// Set properties
|
||||
@@ -402,7 +406,6 @@ class ManageBrickController extends BrickController
|
||||
|
||||
// Getting items
|
||||
$aItems = array();
|
||||
$aItemsIds = array();
|
||||
// ... For each item
|
||||
/** @var DBObject $oCurrentRow */
|
||||
while ($oCurrentRow = $oSet->Fetch())
|
||||
@@ -433,12 +436,12 @@ class ManageBrickController extends BrickController
|
||||
// - Then set allowed action
|
||||
if ($sActionType !== null)
|
||||
{
|
||||
$aActions[] = array(
|
||||
'type' => $sActionType,
|
||||
'class' => $sCurrentClass,
|
||||
'id' => $oCurrentRow->GetKey(),
|
||||
$aActions[] = array(
|
||||
'type' => $sActionType,
|
||||
'class' => $sCurrentClass,
|
||||
'id' => $oCurrentRow->GetKey(),
|
||||
'opening_target' => $oBrick->GetOpeningTarget(),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,25 +482,50 @@ class ManageBrickController extends BrickController
|
||||
'actions' => $aActions
|
||||
);
|
||||
}
|
||||
|
||||
// ... Checking menu extensions
|
||||
$aItemButtons = array();
|
||||
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
|
||||
{
|
||||
foreach($oExtensionInstance->EnumItems(iPopupMenuExtension::PORTAL_OBJLISTITEM_ACTIONS, array('portal_id' => $oApp['combodo.portal.instance.id'], 'object' => $oCurrentRow)) as $oMenuItem)
|
||||
{
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
if($oMenuItem instanceof JSButtonItem)
|
||||
{
|
||||
$aItemButtons[] = $oMenuItem->GetMenuItem() + array('js_files' => $oMenuItem->GetLinkedScripts(), 'type' => 'button');
|
||||
}
|
||||
elseif($oMenuItem instanceof URLButtonItem)
|
||||
{
|
||||
$aItemButtons[] = $oMenuItem->GetMenuItem() + array('type' => 'link');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... And item's properties
|
||||
$aItems[] = array(
|
||||
'id' => $oCurrentRow->GetKey(),
|
||||
'class' => $sCurrentClass,
|
||||
'attributes' => $aItemAttrs,
|
||||
'highlight_class' => $oCurrentRow->GetHilightClass()
|
||||
'highlight_class' => $oCurrentRow->GetHilightClass(),
|
||||
'actions' => $aItemButtons,
|
||||
);
|
||||
$aItemsIds = $oCurrentRow->GetKey();
|
||||
|
||||
if(!empty($aItemButtons))
|
||||
{
|
||||
$bHasObjectListItemExtension = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we retrieved items, we check which can be edited, which can be view and which cannot be opened
|
||||
//
|
||||
// Note: Now that we do checks here and not through the SecurityHelper while fetching objects, we might bypass datamodel security regarding the object class!
|
||||
// $oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sCurrentClass, UR_ACTION_MODIFY);
|
||||
// if($oSearchEditableItems !== null)
|
||||
// {
|
||||
// $oSearchEditableItems->A
|
||||
// }
|
||||
// Adding an extra column for object list item extensions
|
||||
if($bHasObjectListItemExtension === true)
|
||||
{
|
||||
$aColumnsDefinition['_ui_extensions'] = array(
|
||||
'title' => Dict::S('Brick:Portal:Manage:Table:ItemActions'),
|
||||
'type' => 'html',
|
||||
);
|
||||
}
|
||||
|
||||
$aGroupingAreasData[$sKey] = array(
|
||||
'sId' => $sKey,
|
||||
|
||||
@@ -30,11 +30,13 @@ use \Dict;
|
||||
use \utils;
|
||||
use \IssueLog;
|
||||
use \UserRights;
|
||||
use \CMDBSource;
|
||||
use \DOMFormatException;
|
||||
use \ModuleDesign;
|
||||
use \MetaModel;
|
||||
use \DBObjectSearch;
|
||||
use \DBObjectSet;
|
||||
use \iPortalUIExtension;
|
||||
use \Combodo\iTop\Portal\Brick\AbstractBrick;
|
||||
|
||||
/**
|
||||
@@ -200,7 +202,7 @@ class ApplicationHelper
|
||||
*/
|
||||
static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
|
||||
{
|
||||
// A filter to translate a string via the Dict::S function
|
||||
// Filter to translate a string via the Dict::S function
|
||||
// Usage in twig : {{ 'String:ToTranslate'|dict_s }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s', function($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
@@ -208,7 +210,7 @@ class ApplicationHelper
|
||||
})
|
||||
);
|
||||
|
||||
// A filter to format a string via the Dict::Format function
|
||||
// Filter to format a string via the Dict::Format function
|
||||
// Usage in twig : {{ 'String:ToTranslate'|dict_format() }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format', function($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null)
|
||||
{
|
||||
@@ -216,12 +218,12 @@ class ApplicationHelper
|
||||
})
|
||||
);
|
||||
|
||||
// Filters to enable base64 encode/decode
|
||||
// Filter to enable base64 encode/decode
|
||||
// Usage in twig : {{ 'String to encode'|base64_encode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
|
||||
|
||||
// Filters to enable json decode (encode already exists)
|
||||
// Filter to enable json decode (encode already exists)
|
||||
// Usage in twig : {{ aSomeArray|json_decode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function($sJsonString, $bAssoc = false)
|
||||
{
|
||||
@@ -243,6 +245,22 @@ class ApplicationHelper
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Filter to add a module's version to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function($sUrl, $sModuleName){
|
||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
||||
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl . "?moduleversion=" . $sModuleVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl . "&moduleversion=" . $sModuleVersion;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,8 +416,15 @@ class ApplicationHelper
|
||||
),
|
||||
'portals' => array(),
|
||||
'forms' => array(),
|
||||
'ui_extensions' => array(
|
||||
'css_files' => array(),
|
||||
'css_inline' => null,
|
||||
'js_files' => array(),
|
||||
'js_inline' => null,
|
||||
'html' => array(),
|
||||
),
|
||||
'bricks' => array(),
|
||||
'bricks_total_width' => 0
|
||||
'bricks_total_width' => 0,
|
||||
);
|
||||
// - Global portal properties
|
||||
foreach ($oDesign->GetNodes('/module_design/properties/*') as $oPropertyNode)
|
||||
@@ -510,6 +535,8 @@ class ApplicationHelper
|
||||
static::LoadLifecycleConfiguration($oApp, $oDesign);
|
||||
// - Presentation lists
|
||||
$aPortalConf['lists'] = static::LoadListsConfiguration($oApp, $oDesign);
|
||||
// - UI extensions
|
||||
$aPortalConf['ui_extensions'] = static::LoadUIExtensions($oApp);
|
||||
// - Action rules
|
||||
static::LoadActionRulesConfiguration($oApp, $oDesign);
|
||||
// - Generating CSS files
|
||||
@@ -1242,4 +1269,68 @@ class ApplicationHelper
|
||||
return $aClassesLists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads portal UI extensions
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
static protected function LoadUIExtensions(Application $oApp)
|
||||
{
|
||||
$aUIExtensions = array(
|
||||
'css_files' => array(),
|
||||
'css_inline' => null,
|
||||
'js_files' => array(),
|
||||
'js_inline' => null,
|
||||
'html' => array(),
|
||||
);
|
||||
$aUIExtensionHooks = array(
|
||||
iPortalUIExtension::ENUM_PORTAL_EXT_UI_BODY,
|
||||
iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU,
|
||||
iPortalUIExtension::ENUM_PORTAL_EXT_UI_MAIN_CONTENT,
|
||||
);
|
||||
|
||||
/** @var iPortalUIExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iPortalUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
// Adding CSS files
|
||||
$aUIExtensions['css_files'] = array_merge($aUIExtensions['css_files'], $oExtensionInstance->GetCSSFiles($oApp));
|
||||
|
||||
// Adding CSS inline
|
||||
$sCSSInline = $oExtensionInstance->GetCSSInline($oApp);
|
||||
if($sCSSInline !== null)
|
||||
{
|
||||
$aUIExtensions['css_inline'] .= "\n\n" . $sCSSInline;
|
||||
}
|
||||
|
||||
// Adding JS files
|
||||
$aUIExtensions['js_files'] = array_merge($aUIExtensions['js_files'], $oExtensionInstance->GetJSFiles($oApp));
|
||||
|
||||
// Adding JS inline
|
||||
$sJSInline = $oExtensionInstance->GetJSInline($oApp);
|
||||
if($sJSInline !== null)
|
||||
{
|
||||
// Note: Semi-colon is to prevent previous script that would have omitted it.
|
||||
$aUIExtensions['js_inline'] .= "\n\n;\n" . $sJSInline;
|
||||
}
|
||||
|
||||
// Adding HTML for each hook
|
||||
foreach($aUIExtensionHooks as $sUIExtensionHook)
|
||||
{
|
||||
$sFunctionName = 'Get'.$sUIExtensionHook.'HTML';
|
||||
$sHTML = $oExtensionInstance->$sFunctionName($oApp);
|
||||
if($sHTML !== null)
|
||||
{
|
||||
if(!array_key_exists($sUIExtensionHook, $aUIExtensions['html']))
|
||||
{
|
||||
$aUIExtensions['html'][$sUIExtensionHook] = '';
|
||||
}
|
||||
$aUIExtensions['html'][$sUIExtensionHook] .= "\n\n" . $sHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aUIExtensions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -107,54 +107,130 @@
|
||||
|
||||
for(key in tableProperties)
|
||||
{
|
||||
columnsDefinition.push({
|
||||
"width": "auto",
|
||||
"searchable": true,
|
||||
"sortable": (sDataLoading === '{{ constant('Combodo\\iTop\\Portal\\Brick\\AbstractBrick::ENUM_DATA_LOADING_FULL') }}'),
|
||||
"title": tableProperties[key].title,
|
||||
"defaultContent": "",
|
||||
"type": "html",
|
||||
"data": "attributes."+key+".att_code",
|
||||
"render": function(att_code, type, row){
|
||||
var cellElem;
|
||||
var itemActions;
|
||||
var itemPrimarayAction;
|
||||
|
||||
// Preparing action on the cell
|
||||
// Note : For now we will use only one action, the secondary actions are therefore not implemented. Only the data structure is done.
|
||||
itemActions = row.attributes[att_code].actions;
|
||||
|
||||
// Preparing the cell data
|
||||
cellElem = (itemActions.length > 0) ? $('<a></a>') : $('<span></span>');
|
||||
cellElem.html(row.attributes[att_code].value);
|
||||
// Building actions
|
||||
if(itemActions.length > 0)
|
||||
{
|
||||
// - Primary action
|
||||
itemPrimaryAction = itemActions[0];
|
||||
switch(itemPrimaryAction.type)
|
||||
// Regular attribute columns
|
||||
if(key !== '_ui_extensions') {
|
||||
columnsDefinition.push({
|
||||
"width": "auto",
|
||||
"searchable": true,
|
||||
"sortable": (sDataLoading === '{{ constant('Combodo\\iTop\\Portal\\Brick\\AbstractBrick::ENUM_DATA_LOADING_FULL') }}'),
|
||||
"title": tableProperties[key].title,
|
||||
"defaultContent": "",
|
||||
"type": "html",
|
||||
"data": "attributes." + key + ".att_code",
|
||||
"render": function (att_code, type, row) {
|
||||
var cellElem;
|
||||
var itemActions;
|
||||
var itemPrimarayAction;
|
||||
|
||||
// Preparing action on the cell
|
||||
// Note : For now we will use only one action, the secondary actions are therefore not implemented. Only the data structure is done.
|
||||
itemActions = row.attributes[att_code].actions;
|
||||
|
||||
// Preparing the cell data
|
||||
cellElem = (itemActions.length > 0) ? $('<a></a>') : $('<span></span>');
|
||||
cellElem.html(row.attributes[att_code].value);
|
||||
// Building actions
|
||||
if (itemActions.length > 0) {
|
||||
// - Primary action
|
||||
itemPrimaryAction = itemActions[0];
|
||||
switch (itemPrimaryAction.type) {
|
||||
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);
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
return cellElem.prop('outerHTML');
|
||||
},
|
||||
});
|
||||
}
|
||||
// UI extensions buttons
|
||||
else
|
||||
{
|
||||
columnsDefinition.push({
|
||||
"width": "auto",
|
||||
"searchable": false,
|
||||
"sortable": (sDataLoading === '{{ constant('Combodo\\iTop\\Portal\\Brick\\AbstractBrick::ENUM_DATA_LOADING_FULL') }}'),
|
||||
"title": tableProperties[key].title,
|
||||
"defaultContent": "",
|
||||
"type": "html",
|
||||
"data": "attributes." + key + ".att_code",
|
||||
"render": function (att_code, type, row) {
|
||||
var cellElem = $('<div class="group-actions-wrapper"></div>');
|
||||
var actionsCount = row.actions.length;
|
||||
|
||||
// Adding menu wrapper in case there are several actions
|
||||
var actionsElem = $('<div></div>');
|
||||
actionsElem.appendTo(cellElem);
|
||||
if(actionsCount > 1) {
|
||||
actionsElem.addClass('group-actions pull-right');
|
||||
|
||||
// Adding hamburger icon toggler
|
||||
actionsElem.append(
|
||||
$('<a class="glyphicon glyphicon-menu-hamburger" data-toggle="collapse" data-target="#item-actions-menu-' + row.id + '"></a>')
|
||||
);
|
||||
|
||||
// Adding sub menu
|
||||
var actionsSSMenuElem = $('<div id="item-actions-menu-' + row.id + '" class="item-action-wrapper panel panel-default"></div>')
|
||||
.appendTo(actionsElem);
|
||||
var actionsSSMenuContainerElem = $('<div class="panel-body"></div>')
|
||||
.appendTo(actionsSSMenuElem);
|
||||
}
|
||||
|
||||
// Adding actions
|
||||
for(var i in row.actions)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
url = '#';
|
||||
//console.log('Action "'+itemPrimaryAction+'" not implemented');
|
||||
break;
|
||||
var actionDef = row.actions[i];
|
||||
var actionElem = $('<a></a>')
|
||||
.attr('href', actionDef.url)
|
||||
.append( $('<span></span>').html(actionDef.label) );
|
||||
|
||||
// Adding css classes to action
|
||||
for(var j in actionDef.css_classes)
|
||||
{
|
||||
actionElem.addClass(actionDef.css_classes[j]);
|
||||
}
|
||||
|
||||
// Performing specific treatment regarding the action type
|
||||
if(actionDef.type === 'button')
|
||||
{
|
||||
// External files
|
||||
// Note: Not supported yet
|
||||
|
||||
// On click callback
|
||||
actionElem.attr('onclick', actionDef.onclick);
|
||||
}
|
||||
else if(actionDef.type === 'link')
|
||||
{
|
||||
actionElem.attr('target', actionDef.target);
|
||||
}
|
||||
|
||||
if(actionsCount > 1)
|
||||
{
|
||||
actionsSSMenuContainerElem.append( $('<p></p>').append(actionElem) );
|
||||
}
|
||||
else
|
||||
{
|
||||
actionsElem.append( actionElem );
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
return cellElem.prop('outerHTML');
|
||||
}
|
||||
|
||||
return cellElem.prop('outerHTML');
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return columnsDefinition;
|
||||
@@ -275,6 +351,11 @@
|
||||
}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
// Auto collapse item actions popup
|
||||
$('body').click(function(){
|
||||
$('table .item-action-wrapper.collapse.in').collapse('hide');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -23,7 +23,7 @@
|
||||
{% if bGroupButtons == true %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a class="{{ sButtonCssClasses }} {{ aButton.css_classes|join(' ') }}" href="{{ aButton.url }}" onclick="{{ aButton.onclick }}">{{ aButton.label }}</a>
|
||||
<a class="{{ sButtonCssClasses }} {{ aButton.css_classes|join(' ') }}" href="{{ aButton.url }}" onclick="{{ aButton.onclick }}">{{ aButton.label|raw }}</a>
|
||||
{% if bGroupButtons == true %}
|
||||
</li>
|
||||
{% endif %}
|
||||
@@ -37,7 +37,7 @@
|
||||
{% if bGroupButtons == true %}
|
||||
<li>
|
||||
{% endif %}
|
||||
<a class="{{ sButtonCssClasses }} {{ aButton.css_classes|join(' ') }}" href="{{ aButton.url }}" target="{{ aButton.target }}">{{ aButton.label }}</a>
|
||||
<a class="{{ sButtonCssClasses }} {{ aButton.css_classes|join(' ') }}" href="{{ aButton.url }}" target="{{ aButton.target }}">{{ aButton.label|raw }}</a>
|
||||
{% if bGroupButtons == true %}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
{% endblock %}
|
||||
<title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - {{ constant('ITOP_APPLICATION') }}{% else %}{{ 'Page:DefaultTitle'|dict_s }}{% endif %}{% endblock %}</title>
|
||||
<link rel="shortcut icon" href="{{ app['combodo.absolute_url'] ~ 'images/favicon.ico'|add_itop_version }}" />
|
||||
|
||||
{% block pPageStylesheets %}
|
||||
{# First bootstrap core, lib themes, then bootstrap theme, portal adjustements #}
|
||||
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap/css/bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
|
||||
@@ -47,6 +48,12 @@
|
||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.bootstrap|add_itop_version }}" rel="stylesheet" id="css_bootstrap_theme">
|
||||
{# - Portal adjustments for BS theme #}
|
||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.portal|add_itop_version }}" rel="stylesheet" id="css_portal">
|
||||
{# UI Extensions CSS, in an undefined order #}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.css_files is defined %}
|
||||
{% for css_file in app['combodo.portal.instance.conf'].ui_extensions.css_files %}
|
||||
<link href="{{ css_file|add_itop_version }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{# Custom CSS that is supposed to do adjustments to the portal #}
|
||||
{% if app['combodo.portal.instance.conf'].properties.themes.custom is defined %}
|
||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.custom|add_itop_version }}" rel="stylesheet">
|
||||
@@ -58,6 +65,16 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block pStyleinline %}
|
||||
{# UI Extensions inline CSS #}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.css_inline is not null %}
|
||||
<style>
|
||||
{{ app['combodo.portal.instance.conf'].ui_extensions.css_inline|raw }}
|
||||
</style>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block pPageScripts %}
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery/jquery-1.11.3.min.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery-ui/jquery-ui-1.11.4.min.js'|add_itop_version }}"></script>
|
||||
@@ -100,6 +117,12 @@
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_handler.js'|add_itop_version }}"></script>
|
||||
<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>
|
||||
{# 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 %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body class="{% block pPageBodyClass %}{% endblock %}">
|
||||
@@ -179,6 +202,12 @@
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% block pPageUIExtensionNavigationMenuTopbar %}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU')] is defined %}
|
||||
{{ app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU')]|raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
@@ -235,6 +264,13 @@
|
||||
</ul>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
{% block pPageUIExtensionNavigationMenuSidebar %}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU')] is defined %}
|
||||
{{ app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU')]|raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% if app['combodo.portal.instance.conf'].properties.logo is not null %}
|
||||
<div class="logo">
|
||||
{% block pNavigationSideMenuLogo %}
|
||||
@@ -268,6 +304,12 @@
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block pPageUIExtensionMainContent %}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_MAIN_CONTENT')] is defined %}
|
||||
{{ app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_MAIN_CONTENT')]|raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -313,6 +355,12 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pPageUIExtensionBody %}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_BODY')] is defined %}
|
||||
{{ app['combodo.portal.instance.conf'].ui_extensions.html[constant('iPortalUIExtension::ENUM_PORTAL_EXT_UI_BODY')]|raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block pPageLiveScripts %}
|
||||
@@ -410,5 +458,14 @@
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block pPageExtensionsScripts %}
|
||||
{# UI Extensions inline JS #}
|
||||
{% if app['combodo.portal.instance.conf'].ui_extensions.js_inline is not null %}
|
||||
<script type="text/javascript">
|
||||
{{ app['combodo.portal.instance.conf'].ui_extensions.js_inline|raw }}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -582,6 +582,9 @@ footer {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
/* Secondary actions */
|
||||
.list-group-item-actions .group-actions-wrapper, .mosaic-group-item-actions .group-actions-wrapper, table .group-actions-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
table .group-actions {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -612,6 +612,11 @@ footer{
|
||||
}
|
||||
|
||||
/* Secondary actions */
|
||||
.list-group-item-actions .group-actions-wrapper,
|
||||
.mosaic-group-item-actions .group-actions-wrapper,
|
||||
table .group-actions-wrapper{
|
||||
text-align: center;
|
||||
}
|
||||
table .group-actions{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ require_once APPROOT . '/core/moduledesign.class.inc.php';
|
||||
require_once APPROOT . '/application/loginwebpage.class.inc.php';
|
||||
require_once APPROOT . '/sources/autoload.php';
|
||||
// Portal
|
||||
// Note: This could be prevented by adding namespaces to composer
|
||||
require_once __DIR__ . '/../src/providers/urlgeneratorserviceprovider.class.inc.php';
|
||||
require_once __DIR__ . '/../src/helpers/urlgeneratorhelper.class.inc.php';
|
||||
require_once __DIR__ . '/../src/providers/contextmanipulatorserviceprovider.class.inc.php';
|
||||
@@ -109,6 +110,7 @@ $oApp->before(function(Symfony\Component\HttpFoundation\Request $oRequest, Silex
|
||||
$oApp['debug'] = $bDebug;
|
||||
$oApp['combodo.current_environment'] = utils::GetCurrentEnvironment();
|
||||
$oApp['combodo.absolute_url'] = utils::GetAbsoluteUrlAppRoot();
|
||||
$oApp['combodo.modules.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment();
|
||||
$oApp['combodo.portal.base.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/web/';
|
||||
$oApp['combodo.portal.base.absolute_path'] = MODULESROOT . '/itop-portal-base/portal/web/';
|
||||
$oApp['combodo.portal.instance.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/' . PORTAL_MODULE_ID . '/';
|
||||
|
||||
@@ -84,6 +84,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'Brick:Portal:Manage:Name' => 'Управление элементами',
|
||||
'Brick:Portal:Manage:Table:NoData' => 'Нет элементов',
|
||||
'Brick:Portal:Manage:Table:ItemActions' => 'Actions~~',
|
||||
));
|
||||
|
||||
// ObjectBrick brick
|
||||
|
||||
Reference in New Issue
Block a user