N°1268 Add support for abstract classes creation in browse brick

* Refactored parts of the create brick into the object controller
This commit is contained in:
Molkobain
2019-07-23 11:45:03 +02:00
parent 60863c5fcf
commit b858ba3786
4 changed files with 170 additions and 115 deletions

View File

@@ -23,7 +23,6 @@
namespace Combodo\iTop\Portal\Controller;
use Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
use MetaModel;
use Symfony\Component\HttpFoundation\Request;
/**
@@ -52,16 +51,14 @@ class CreateBrickController extends BrickController
*/
public function DisplayAction(Request $oRequest, $sBrickId)
{
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
$oSecurityHelper = $this->get('security_helper');
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
$oBrickCollection = $this->get('brick_collection');
/** @var \Combodo\iTop\Portal\Brick\CreateBrick $oBrick */
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
$sObjectClass = $oBrick->GetClass();
$aRouteParams = array(
'sBrickId' => $sBrickId,
'sObjectClass' => $oBrick->GetClass(),
'ar_token' => null,
);
@@ -73,45 +70,7 @@ class CreateBrickController extends BrickController
$aRouteParams['ar_token'] = ContextManipulatorHelper::PrepareAndEncodeRulesToken($aRules);
}
// Checking if the target object class is abstract or not
// - If is not abstract, we redirect to object creation form
if (!MetaModel::IsAbstract($sObjectClass))
{
// Preparing redirection route
// - Adding brick id to the params
$aRouteParams['sBrickId'] = $sBrickId;
$oResponse = $this->ForwardFromRoute('p_object_create', $aRouteParams, $oRequest->query->all());
}
// - Else, we list the leaf classes as an intermediate step
else
{
$aData = array(
'oBrick' => $oBrick,
'sBrickId' => $sBrickId,
'aLeafClasses' => array(),
'ar_token' => $aRouteParams['ar_token'],
);
$aLeafClasses = array();
$aChildClasses = MetaModel::EnumChildClasses($sObjectClass);
foreach ($aChildClasses as $sChildClass)
{
if (!MetaModel::IsAbstract($sChildClass) && $oSecurityHelper->IsActionAllowed(UR_ACTION_CREATE, $sChildClass))
{
$aLeafClasses[] = array(
'id' => $sChildClass,
'name' => MetaModel::GetName($sChildClass),
);
}
}
$aData['aLeafClasses'] = $aLeafClasses;
$oResponse = $this->render($oBrick->GetPageTemplatePath(), $aData);
}
return $oResponse;
return $this->ForwardFromRoute('p_object_create', $aRouteParams, $oRequest->query->all());
}
}

View File

@@ -22,34 +22,35 @@
namespace Combodo\iTop\Portal\Controller;
use AttributeEnum;
use AttributeFinalClass;
use AttributeFriendlyName;
use AttributeImage;
use BinaryExpression;
use Combodo\iTop\Portal\Brick\CreateBrick;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
use Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
use DBObject;
use DBObjectSearch;
use DBObjectSet;
use DBSearch;
use Dict;
use Exception;
use FalseExpression;
use FieldExpression;
use FileUploadException;
use IssueLog;
use ListExpression;
use MetaModel;
use ScalarExpression;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Exception;
use FileUploadException;
use utils;
use Dict;
use IssueLog;
use MetaModel;
use DBObject;
use DBSearch;
use DBObjectSearch;
use FalseExpression;
use BinaryExpression;
use FieldExpression;
use VariableExpression;
use ListExpression;
use ScalarExpression;
use DBObjectSet;
use AttributeEnum;
use AttributeImage;
use AttributeFinalClass;
use AttributeFriendlyName;
use UserRights;
use URLButtonItem;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
use Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
use UserRights;
use utils;
use VariableExpression;
/**
* Class ObjectController
@@ -279,55 +280,17 @@ class ObjectController extends BrickController
*/
public function CreateAction(Request $oRequest, $sObjectClass)
{
/** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */
$oRequestManipulator = $this->get('request_manipulator');
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
$oObjectFormHandler = $this->get('object_form_handler');
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
$oSecurityHelper = $this->get('security_helper');
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
$oBrickCollection = $this->get('brick_collection');
// Checking security layers
if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_CREATE, $sObjectClass))
$oResponse = null;
// Checking if the target object class is abstract or not
// - If is not abstract, we redirect to object creation form
if (!MetaModel::IsAbstract($sObjectClass))
{
IssueLog::Warning(__METHOD__.' at line '.__LINE__.' : User #'.UserRights::GetUserId().' not allowed to create '.$sObjectClass.' object.');
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
}
$sOperation = $oRequestManipulator->ReadParam('operation', '');
$aData = array('sMode' => 'create');
$aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass);
$aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Create:Title', MetaModel::GetName($sObjectClass));
// Preparing response
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if (empty($sOperation))
{
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
}
else
{
$oResponse = new JsonResponse($aData);
}
$oResponse = $this->DisplayCreationForm($oRequest, $sObjectClass);
}
// - Else, we list the leaf classes as an intermediate step
else
{
// Adding brick if it was passed
$sBrickId = $oRequestManipulator->ReadParam('sBrickId', '');
if (!empty($sBrickId))
{
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
if ($oBrick !== null)
{
$aData['oBrick'] = $oBrick;
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
$oResponse = $this->DisplayLeafClassesForm($sObjectClass);
}
return $oResponse;
@@ -1390,4 +1353,137 @@ class ObjectController extends BrickController
return $aObjectData;
}
/**
* Displays the creation form of an instantiable class
*
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param string $sObjectClass
*
* @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\Response
* @throws \ArchivedObjectException
* @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
protected function DisplayCreationForm(Request $oRequest, $sObjectClass)
{
/** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */
$oRequestManipulator = $this->get('request_manipulator');
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
$oObjectFormHandler = $this->get('object_form_handler');
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
$oSecurityHelper = $this->get('security_helper');
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
$oBrickCollection = $this->get('brick_collection');
// Checking security layers
if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_CREATE, $sObjectClass))
{
IssueLog::Warning(__METHOD__.' at line '.__LINE__.' : User #'.UserRights::GetUserId().' not allowed to create '.$sObjectClass.' object.');
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
}
$sOperation = $oRequestManipulator->ReadParam('operation', '');
$aData = array('sMode' => 'create');
$aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass);
$aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Create:Title', MetaModel::GetName($sObjectClass));
// Preparing response
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if (empty($sOperation))
{
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/modal.html.twig', $aData);
}
else
{
$oResponse = new JsonResponse($aData);
}
}
else
{
// Adding brick if it was passed
$sBrickId = $oRequestManipulator->ReadParam('sBrickId', '');
if (!empty($sBrickId))
{
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
if ($oBrick !== null)
{
$aData['oBrick'] = $oBrick;
}
}
$aData['sPageTitle'] = $aData['form']['title'];
$oResponse = $this->render('itop-portal-base/portal/templates/bricks/object/layout.html.twig', $aData);
}
return $oResponse;
}
/**
* Displays a list of leaf classes from the abstract $sObjectClass which will lead to the actual creation form.
*
* @param string $sObjectClass
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
protected function DisplayLeafClassesForm($sObjectClass)
{
/** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */
$oRequestManipulator = $this->get('request_manipulator');
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
$oSecurityHelper = $this->get('security_helper');
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
$oBrickCollection = $this->get('brick_collection');
$aData = array(
'aLeafClasses' => array(),
'sPageTitle' => Dict::Format('Brick:Portal:Object:Form:Create:Title', MetaModel::GetName($sObjectClass)),
'sLeafClassesListId' => 'leaf_classes_list_' . uniqid(),
'ar_token' => $oRequestManipulator->ReadParam('ar_token', ''),
);
$sTemplatePath = CreateBrick::DEFAULT_PAGE_TEMPLATE_PATH;
$sBrickId = $oRequestManipulator->ReadParam('sBrickId', '');
if (!empty($sBrickId))
{
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
$sTemplatePath = $oBrick->GetPageTemplatePath();
$aData['sBrickId'] = $sBrickId;
$aData['oBrick'] = $oBrick;
$aData['sPageTitle'] = $oBrick->GetTitle();
}
$aLeafClasses = array();
$aChildClasses = MetaModel::EnumChildClasses($sObjectClass);
foreach ($aChildClasses as $sChildClass)
{
if (!MetaModel::IsAbstract($sChildClass) && $oSecurityHelper->IsActionAllowed(UR_ACTION_CREATE, $sChildClass))
{
$aLeafClasses[] = array(
'id' => $sChildClass,
'name' => MetaModel::GetName($sChildClass),
);
}
}
$aData['aLeafClasses'] = $aLeafClasses;
$oResponse = $this->render($sTemplatePath, $aData);
return $oResponse;
}
}

View File

@@ -194,7 +194,7 @@ class BrowseBrickHelper
switch ($aAction['type'])
{
case BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS:
// We can only make translate a dictionnary entry with a class placeholder when the action has a class tag. if it has a factory method, we don't know yet what class is going to be created
// We can only make translate a dictionary entry with a class placeholder when the action has a class tag. if it has a factory method, we don't know yet what class is going to be created
if ($aAction['factory']['type'] === BrowseBrick::ENUM_FACTORY_TYPE_CLASS)
{
$aAction['title'] = Dict::Format('Brick:Portal:Browse:Action:CreateObjectFromThis',

View File

@@ -3,12 +3,12 @@
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% block pModalTitle %}
{{ oBrick.GetTitle()|dict_s }}
{{ sPageTitle|dict_s }}
{% endblock %}
{% block pModalBody %}
<p>{{ 'Brick:Portal:Create:ChooseType'|dict_s }}</p>
<ul id="{{ sBrickId }}_leaf_classes">
<ul id="{{ sLeafClassesListId }}">
{% for aLeafClass in aLeafClasses %}
<li><a href="#" data-target-class="{{ aLeafClass.id }}">{{ aLeafClass.name }}</a></li>
{% endfor %}
@@ -16,7 +16,7 @@
<script type="text/javascript">
$(document).ready(function(){
$('#{{ sBrickId }}_leaf_classes a').off('click').on('click', function(oEvent){
$('#{{ sLeafClassesListId }} a').off('click').on('click', function(oEvent){
oEvent.preventDefault();
var sUrl = '{{ app['url_generator'].generate('p_object_create', {sObjectClass : '-sObjectClass-'})|raw }}';