diff --git a/js/DI/app.js b/js/DI/app.js index 1972a7955..cad1fe004 100644 --- a/js/DI/app.js +++ b/js/DI/app.js @@ -1,7 +1,7 @@ /** * Application handling. * - * @returns {{init: init}} + * @returns {{init: init, handleTooltips: handleTooltips}} * @constructor */ const App = function(){ @@ -12,24 +12,32 @@ const App = function(){ }; /** - * init. + * initialization. * */ function init(){ + // dark theme button $(aSelectors.darkModeButton).on('click', function(){ $('body').attr('data-bs-theme', this.ariaPressed === 'true' ? 'dark' : 'light'); }); + // dark theme button state if (window.matchMedia('(prefers-color-scheme: dark)').matches) { $('body').attr('data-bs-theme', 'dark'); $(aSelectors.darkModeButton).attr('aria-pressed', 'true'); $(aSelectors.darkModeButton).toggleClass('active', true); } + // handle tooltips handleTooltips(document); } + /** + * Bootstrap tooltip initialization. + * + * @param oElement + */ function handleTooltips(oElement){ const tooltips = oElement.querySelectorAll("[data-bs-toggle='tooltip']"); tooltips.forEach((el) => { diff --git a/js/DI/collection.js b/js/DI/collection.js index f2dda7f94..244ea096a 100644 --- a/js/DI/collection.js +++ b/js/DI/collection.js @@ -9,17 +9,23 @@ */ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ + // dom selectors + const aSelectors = { + addItem: '.add_item_link', + createItem: '.create_item_link', + removeItem: '.btn-remove-link', + linkSetContainer: '.link_set_widget_container', + }; + /** * Listen for add item buttons. * * @param oContainer */ function listenAddItem (oContainer) { - - oContainer.querySelectorAll('.add_item_link').forEach(btn => { - btn.addEventListener("click", addFormToCollection) + oContainer.querySelectorAll(aSelectors.addItem).forEach(btn => { + btn.addEventListener('click', addFormToCollection) }); - } /** @@ -28,11 +34,9 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ * @param oContainer */ function listenCreateItem (oContainer) { - - oContainer.querySelectorAll('.create_item_link').forEach(btn => { - btn.addEventListener("click", createObject) + oContainer.querySelectorAll(aSelectors.createItem).forEach(btn => { + btn.addEventListener('click', createObject) }); - } /** @@ -41,20 +45,9 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ * @param oContainer */ function listenRemoveItem(oContainer){ - - oContainer.querySelectorAll('.btn-remove-link').forEach(btn => { - btn.addEventListener("click", (e) => { - - const oContainer = e.currentTarget.closest('.link_set_widget_container'); - - btn.closest('tr').remove() - - if(oContainer.querySelectorAll('tbody tr').length === 1) { - oContainer.querySelector('.no_data').style.display = 'table-row'; - } - }) + oContainer.querySelectorAll(aSelectors.removeItem).forEach(btn => { + btn.addEventListener('click', removeItem); }); - } /** @@ -67,7 +60,7 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ // retrieve link set container const oContainer = e.currentTarget.closest('.link_set_widget_container'); - // retrieve collection holder + // retrieve collection holder (replace ':' character otherwise the selector is invalid) const exp = e.currentTarget.dataset.collectionHolderClass.replaceAll(/:/g, '\\:'); const collectionHolder = oContainer.querySelector('.' + exp); @@ -145,12 +138,33 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ handleElement(formEl); oApp.handleTooltips(formEl); }); + + listenSaveModalObject(myModalAlternative); }) .catch(function (error) { console.error(error); }); } + /** + * Remove an item. + * + * @param e + */ + function removeItem(e) + { + // retrieve link set container + const oContainer = e.currentTarget.closest(aSelectors.linkSetContainer); + + // remove row + e.currentTarget.closest('tr').remove(); + + // handle no data row visibility + if(oContainer.querySelectorAll('tbody tr').length === 1) { + oContainer.querySelector('.no_data').style.display = 'table-row'; + } + } + /** * Handle collection on the provided container element. * @@ -163,8 +177,11 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ listenRemoveItem(oContainer); } - - function listenSaveModalobject(){ + /** + * + */ + function listenSaveModalObject(myModalAlternative) + { const oSave = document.querySelector('[data-action="save_modal_object"]'); oSave.addEventListener('click', function(e){ @@ -194,25 +211,29 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ .then((response) => response.json()) .then((data) => { - let form = $(data.template); + if(data.succeeded){ - console.log(form); - // - // console.log(oForm.dataset.attCode); - // - // const fragment = oToolkit.createElementFromHtml(data.template); - // const inner = fragment.querySelector('form').innerHTML; - // const el = oToolkit.createElementFromHtml(inner); - // console.log(el); - // - const myModalAlternative = new bootstrap.Modal('#object_modal', { - hide: true - }); + let form = $(data.template); + console.log(form); + // + // console.log(oForm.dataset.attCode); + // + // const fragment = oToolkit.createElementFromHtml(data.template); + // const inner = fragment.querySelector('form').innerHTML; + // const el = oToolkit.createElementFromHtml(inner); + // console.log(el); + // + myModalAlternative.hide(); - console.log($(`[data-att-code="${oForm.dataset.attCode}"] tbody`)); + console.log($(`[data-att-code="${oForm.dataset.attCode}"] tbody`)); - $(`[data-att-code="${oForm.dataset.attCode}"] tbody`).append($(form.innerHTML)); + $(`[data-att-code="${oForm.dataset.attCode}"] tbody`).append($(form.innerHTML)); + + } + else{ + console.error('Error while saving object'); + } }) .catch(function (error) { @@ -222,7 +243,6 @@ const Collection = function(oForm, objectFormUrl, objectSaveUrl){ }); } - listenSaveModalobject(); return { handleElement diff --git a/js/DI/form.js b/js/DI/form.js index 842811d0c..e33c7e4fe 100644 --- a/js/DI/form.js +++ b/js/DI/form.js @@ -193,7 +193,13 @@ const Form = function(oWidget, oDynamic){ for(let sContainerId in aMapDependencies) { // retrieve object container - const oObjectContainer = document.querySelector(`[data-container-id="${sContainerId}"]`); + let oObjectContainer = null; + if(oElement.dataset !== undefined && oElement.dataset.containerId === sContainerId){ + oObjectContainer = oElement; + } + else{ + oObjectContainer = oElement.querySelector(`[data-container-id="${sContainerId}"]`); + } const aMapContainer = aMapDependencies[sContainerId]; diff --git a/js/DI/widget.js b/js/DI/widget.js index d463daa00..8dcc520ed 100644 --- a/js/DI/widget.js +++ b/js/DI/widget.js @@ -22,8 +22,8 @@ const Widget = function(){ // initialize widget const sWidgetName = widgetField.dataset.widget; const oWidget = eval(`$(widgetField).${sWidgetName}()`); - console.log('Init widget: ' + sWidgetName); - console.log(oWidget); + console.debug('Init widget: ' + sWidgetName); + console.debug(oWidget); }); } diff --git a/sources/DI/Controller/ObjectController.php b/sources/DI/Controller/ObjectController.php index d561ba78a..6a5b6db99 100644 --- a/sources/DI/Controller/ObjectController.php +++ b/sources/DI/Controller/ObjectController.php @@ -2,6 +2,7 @@ namespace Combodo\iTop\DI\Controller; +use Combodo\iTop\DI\Form\Manager\ObjectFormManager; use Combodo\iTop\DI\Form\Type\Compound\PartialObjectType; use Combodo\iTop\DI\Form\Type\Compound\ObjectType; use Combodo\iTop\DI\Services\ObjectService; @@ -25,8 +26,9 @@ class ObjectController extends AbstractController /** - * @Route ("/{class<\w+>}/{id<\d+>}/view", name="object_view") + * Return object view page with object data printed with key value representation. * + * @Route ("/{class<\w+>}/{id<\d+>}/view", name="object_view") */ public function objectView(string $class, int $id) : Response { @@ -35,7 +37,7 @@ class ObjectController extends AbstractController $oObject = MetaModel::GetObject($class, $id); } catch(Exception $e){ - throw $this->createNotFoundException("The $class $id does not exist"); + throw $this->createNotFoundException("The $class $id does not exist", $e); } // return object view @@ -47,16 +49,18 @@ class ObjectController extends AbstractController } /** + * Return object view as JSon response. + * * @Route ("/{class<\w+>}/{id<\d+>}/json", name="object_json") */ - public function objectJSon(string $class, int $id) : Response + public function objectJSon(string $class, int $id) : JsonResponse { // retrieve object try{ $oObject = MetaModel::GetObject($class, $id); } catch(Exception $e){ - throw $this->createNotFoundException("The $class $id does not exist"); + throw $this->createNotFoundException("The $class $id does not exist", $e); } // return object as json response @@ -66,6 +70,11 @@ class ObjectController extends AbstractController } /** + * Return object edition view page. + * The form is constructed with the ObjectType form type @see ObjectType + * + * @todo perform database DbInsert, DbDelete, DbUpdate on links + * * @Route("/{class<\w+>}/{id<\d+>}/edit", name="object_edit") */ public function objectEdit(Request $request, string $class, int $id, ObjectService $oObjectService) : Response @@ -91,7 +100,7 @@ class ObjectController extends AbstractController try { - // handle link set (apply DbInsert, DbDelete, DbUpdate) could be automatic ? + // handle link set (apply DbInsert, DbDelete, DbUpdate) should be automatic ? handle by host object ? $oObjectService->handleLinkSetDB($oObject); // save object @@ -104,7 +113,7 @@ class ObjectController extends AbstractController } catch(Exception $e){ - throw new HttpException(500, 'Error while trying to save object'); + throw new HttpException(500, 'Error while trying to save object', $e); } // redirect to view object @@ -125,6 +134,8 @@ class ObjectController extends AbstractController } /** + * Return an object form view. + * * @Route("/{class<\w+>}/{id<\d+>}/{name<\w+>}/form", name="object_form", methods={"POST"}) */ public function objectForm(Request $request, string $name, string $class, int $id, ObjectService $oObjectService, FormFactoryInterface $oFormFactory) : Response @@ -134,7 +145,7 @@ class ObjectController extends AbstractController $oObject = $oObjectService->getObject($class, $id); } catch(Exception $e){ - throw $this->createNotFoundException("The $class $id does not exist"); + throw $this->createNotFoundException("The $class $id does not exist", $e); } // decode data @@ -176,16 +187,18 @@ class ObjectController extends AbstractController } /** + * Save object into database and return its list representation. + * * @Route("/{class<\w+>}/{id<\d+>}/{name<\w+>}/save", name="object_save", methods={"POST"}) */ - public function objectSave(Request $request, string $name, string $class, int $id, ObjectService $oObjectService, FormFactoryInterface $oFormFactory) : Response + public function objectSave(Request $request, string $name, string $class, int $id, ObjectService $oObjectService, FormFactoryInterface $oFormFactory, ObjectFormManager $oObjectFormManager) : Response { // retrieve object try{ $oObject = $oObjectService->getObject($class, $id); } catch(Exception $e){ - throw $this->createNotFoundException("The $class $id does not exist"); + throw $this->createNotFoundException("The $class $id does not exist", $e); } // create object form @@ -196,13 +209,8 @@ class ObjectController extends AbstractController // handle HTTP request $oForm->handleRequest($request); - // locked attributes - $aValue = $request->get('new'); - $sLockedAttributes = $aValue['locked_attributes']; - $aLockedAttributes = json_decode($sLockedAttributes); - foreach($aLockedAttributes as $sKey => $sValue){ - $oObject->Set($sKey, $sValue); - } + // apply locked attributes to object + $oObjectFormManager->applyRequestLockedAttributesToObject($request, $oObject, 'new'); // submitted and valid if ($oForm->isSubmitted() && $oForm->isValid()) { @@ -217,7 +225,7 @@ class ObjectController extends AbstractController } } catch(Exception $e){ - throw new HttpException(500, 'Error while trying to save object'); + throw new HttpException(500, 'Error while trying to save object', $e); } // create object form @@ -229,7 +237,7 @@ class ObjectController extends AbstractController // return object form return new JsonResponse([ 'succeeded' => true, - 'template' => $this->renderView('DI/form.html.twig', [ + 'template' => $this->renderView('DI/form/form.html.twig', [ 'id' => $id, 'class' => $class, 'form' => $oForm->createView(), @@ -244,6 +252,10 @@ class ObjectController extends AbstractController } /** + * Actualize a piece of the form. + * The first form, used to apply modifications, contains all dependencies attributes and dependent attributes. + * The second form, used for new fields templates, contains only the dependent attributes. + * * @Route("/{class<\w+>}/{id<\d+>}/reload", name="object_reload") */ public function objectReload(Request $request, string $class, int $id, ObjectService $oObjectService) : Response @@ -253,7 +265,7 @@ class ObjectController extends AbstractController $oObject = $oObjectService->getObject($class, $id); } catch(Exception $e){ - throw $this->createNotFoundException("The $class $id does not exist"); + throw $this->createNotFoundException("The $class $id does not exist", $e); } $aDependencyAttCodes = explode(',', $request->get('dependency_att_codes')); diff --git a/sources/DI/Form/Manager/ObjectFormManager.php b/sources/DI/Form/Manager/ObjectFormManager.php new file mode 100644 index 000000000..cd7b8465b --- /dev/null +++ b/sources/DI/Form/Manager/ObjectFormManager.php @@ -0,0 +1,33 @@ +get($sFormName); + $sLockedAttributes = $aValue['locked_attributes']; + $aLockedAttributes = json_decode($sLockedAttributes); + foreach($aLockedAttributes as $sKey => $sValue){ + $oDbObject->Set($sKey, $sValue); + } + } + catch(Exception $e){ + + } + + } +} \ No newline at end of file diff --git a/templates/DI/form/theme/classic_theme.html.twig b/templates/DI/form/theme/classic_theme.html.twig index 458ae78e2..9a38d54ed 100644 --- a/templates/DI/form/theme/classic_theme.html.twig +++ b/templates/DI/form/theme/classic_theme.html.twig @@ -149,10 +149,12 @@ {% if objectId == -1 %} {% set objectId = 0 %} {% endif %} + {% else %} + {% set objectId = 0 %} {% endif %} {% if z_list == 'list' %} - + {% for child in form %} {{ form_widget(child) }} @@ -163,7 +165,7 @@ {% else %} -
+
{{ form_widget(form) }}
{% endif %} diff --git a/templates/DI/object/edit.html.twig b/templates/DI/object/edit.html.twig index 4fa725a5b..71e0dc35f 100644 --- a/templates/DI/object/edit.html.twig +++ b/templates/DI/object/edit.html.twig @@ -76,7 +76,7 @@ {% block modals %} -