navView(); // TODO: header like in Storage_AclUsage UI::startTag('div', [ 'class' => 'container-fluid' ]); try { $namespace = V::get('namespace', '', $_GET); if (empty($namespace)) throw new Exception("Missing param namespace"); // [idZasob] => 25872 // [idDatabase] => 36 // [namespace] => default_db/CRM_PROCES/CRM_PROCES // [_rootTableName] => CRM_PROCES // [_type] => AntAcl // [hasStruct] => 1 // [isStructInstalled] => 1 // [isObjectActive] => 1 // [description] => // [name] => CRM_PROCES // [nsPrefix] => default_db__x3A__CRM_PROCES // [typeName] => default_db__x3A__CRM_PROCES:CRM_PROCES // [reinstallLink] => https://biuro.biall-net.pl/dev-pl/se-master/index.php?_route=Storage_AclReinstall&namespace=default_db/CRM_PROCES/CRM_PROCES $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]); switch ($item['_type']) { case 'TableAcl': $this->tableStructView($item, $item['name'], $item['idDatabase']); break; case 'AntAcl': $this->objectStructView($item); break; case 'StorageAcl': $this->objectStructView($item); break; default: throw new Exception("Not implemented type"); break; } } catch (Exception $e) { UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage()); DBG::log($e); } UI::endTag('div'); // .container-fluid UI::dol(); } public function tableStructAction() { UI::gora(); UI::menu(); // Router::getRoute('Storage')->navView(); // TODO: header like in Storage_AclUsage try { $idStorage = V::get('idStorage', 0, $_REQUEST, 'int'); if (empty($idStorage)) throw new Exception("Missing id storage"); $storageList = Router::getRoute('Storage')->getStorageList(); if (empty($storageList)) throw new Exception("No storage defined"); if (!array_key_exists($idStorage, $storageList)) throw new Exception("Storage not exists"); $tblName = V::get('table', '', $_REQUEST, 'word'); if (empty($tblName)) throw new Exception("No table name"); $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]); $this->tableStructView($item, $tblName, $idStorage); } catch (Exception $e) { UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage()); } UI::dol(); } public function objectStructureAction() { // objectStructAction UI::gora(); UI::menu(); // Router::getRoute('Storage')->navView(); // TODO: header like in Storage_AclUsage UI::startTag('div', [ 'class' => 'container-fluid' ]); try { $namespace = V::get('namespace', '', $_GET); if (empty($namespace)) throw new Exception("Missing param namespace"); $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]); $this->objectStructView($item); } catch (Exception $e) { UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage()); DBG::log($e); } UI::endTag('div'); // .container-fluid UI::dol(); } public function tableStructView($item, $tblName, $idStorage) { $storagePdo = DB::getStorage($idStorage); $tblStruct = $storagePdo->getTableStruct($tblName); $idTable = Router::getRoute('Storage')->fetchTableId($idStorage, $tblName); if ($idTable <= 0) { UI::alert('warning', "Zasob tabela '{$tblName}' nie istnieje");// TODO: add p5UI btn DBG::table("tblStruct", $tblStruct, __CLASS__, __FUNCTION__, __LINE__); throw new Exception("Zasob tabela '{$tblName}' nie istnieje"); } $cellZasobList = array(); foreach (DB::getPDO()->fetchAllByKey(" select z.ID, z.`DESC`, z.`TYPE` as ZASOB_TYPE, z.A_STATUS, z.SORT_PRIO from CRM_LISTA_ZASOBOW z where z.PARENT_ID = '{$idTable}' ", $key = 'DESC') as $ind => $row) { $cellZasobList[strtolower($ind)] = $row; } $emptyItem = array(); $emptyItem['sort'] = null; $emptyItem['name'] = ''; $emptyItem['id_zasob'] = ''; $emptyItem['zasob_type'] = ''; $emptyItem['uwagi'] = ''; $emptyItem['type'] = ''; $emptyItem['is_nullable'] = ''; $emptyItem['default_value'] = ''; $emptyItem['default_is_null'] = ''; $emptyItem['max_length'] = ''; $emptyItem['num_precision'] = ''; $emptyItem['num_scale'] = ''; $emptyItem['char_encoding'] = ''; $emptyItem['char_collation'] = ''; $emptyItem['extra'] = ''; $emptyItem['raw_storage_type'] = ''; $tableList = array(); foreach ($tblStruct as $row) { $cellName = $row['name']; $tblItem = V::cloneArray($emptyItem); $tblItem['name'] = $cellName; foreach ($row as $fldName => $fldVal) { if (array_key_exists($fldName, $tblItem)) $tblItem[$fldName] = $fldVal; } $tblItem['uwagi'] = ''; $lowerCellName = strtolower($cellName); $tblZasob = V::get($lowerCellName, '', $cellZasobList); if ($tblZasob) { $cellZasobList[$lowerCellName]['_checked'] = true; $tblItem['id_zasob'] = $tblZasob['ID']; $tblItem['zasob_type'] = $tblZasob['ZASOB_TYPE']; $tblItem['sort'] = $tblZasob['SORT_PRIO']; } else { $tblItem['uwagi'] .= '!Zasob';//'TODO: ADD ZASOB'; $ajaxAddZasobLink = Router::getRoute('Storage')->getLink('addCellToZasoby', [ 'idStorage' => $idStorage, 'tblName' => $tblName, 'cellName' => $cellName]); $onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:addCellToZasoby', { href: '{$ajaxAddZasobLink}' })"; $tblItem['id_zasob'] = 'TODO: ADD ZASOB'; } $tableList[] = $tblItem; } foreach ($cellZasobList as $cellName => $row) { if ('URL_ACTION' == $row['ZASOB_TYPE']) continue; if (!$row['_checked']) { $tblItem = V::cloneArray($emptyItem); $tblItem['name'] = $cellName; $tblItem['id_zasob'] = $row['ID']; $tblItem['zasob_type'] = $row['ZASOB_TYPE']; $tblItem['uwagi'] = '!DB';//'TODO: nie istnieje w bazie danych'; $tableList[] = $tblItem; } } { // sortBy $sortByArgName = 'sortBy'; $sortByCol = V::get($sortByArgName, 'name', $_GET); $sortByDir = V::get("{$sortByArgName}Dir", 'asc', $_GET); if ('name' === $sortByCol) { usort($tableList, function($rowA, $rowB) { $a = $rowA['name']; $b = $rowB['name']; // if ('ID' == $a) return -1; // if ('ID' == $b) return 1; if ($a == $b) return 0; $a1 = substr($a, 0, 1); $b1 = substr($b, 0, 1); if (('_' == $a1 || '_' == $b1) && $a1 != $b1) { return ($a1 < $b1) ? 1 : -1; } return ($a < $b) ? -1 : 1; }); } else if ('sort' === $sortByCol) { usort($tableList, function ($a, $b) use ($sortByDir) { if (null === $a['sort'] && null === $b['sort']) return 0; if (null === $a['sort'] && null != $b['sort']) return 1; if (null !== $a['sort'] && null === $b['sort']) return -1; if ($a['sort'] > $b['sort']) return 'asc' === $sortByDir ? 1 : -1; if ($a['sort'] < $b['sort']) return 'asc' === $sortByDir ? -1 : 1; return 0; }); } echo UI::h('style', [ 'type' => "text/css" ], " .ta-ordering { cursor:pointer } .ta-ordering-desc:after { content:\"\\e252\"; font-family:\"Glyphicons Halflings\"; line-height:1; margin:0 0 0 3px; display:inline-block; color:#bbb } .ta-ordering-asc:after { content:\"\\e253\"; font-family:\"Glyphicons Halflings\"; line-height:1; margin:0 0 0 3px; display:inline-block; color:#bbb } .ta-sortable-item { } .ta-sortable-item-handle { cursor:pointer } "); echo UI::h('script', [ 'src' => "static/URI.js" ]); echo UI::h('script', [], " function p5UI_table_sortBy_get(n, argName, colName, dir) { var newUri = URI(window.location.href).setQuery(argName, colName).setQuery(argName+'Dir', dir).build().toString(); window.location.href = newUri } "); } UI::table([ '__html_id' => 'struct_table', 'caption' => UI::h('span', [], [ "Struktura tabeli {$tblName} [{$idTable}] ", UI::h('a', [ 'href' => "index.php?_route=ViewTableAjax&namespace=default_db/{$tblName}", 'class' => "btn btn-md btn-link" ], "Przeglądaj tabelę"), UI::h('a', [ 'href' => "procesy5.php?task=CRM_LISTA_ZASOBOW&filtr_id={$idTable}&filtr_ids=%2B&filtr_ob=%2B", 'class' => "btn btn-md btn-link", 'title' => "Struktura aktualnie przeglądanej tabeli" ], "Zasoby"), UI::h('a', [ 'href' => "index.php?FUNCTION_INIT=PROCES_MENU&HEADER_NOT_INIT=YES&_task=PROCES_FOR_TABLE&tblId={$idTable}", 'class' => "btn btn-md btn-link", 'title' => "Procesy dla aktualnie przeglądanej tabeli" ], "Procesy"), UI::h('a', [ 'href' => Router::getRoute('Storage_AclUsage')->getLink('', [ 'namespace' => "default_db/{$tblName}" ]), 'class' => "btn btn-md btn-link", 'title' => "Uprawnienia - analiza użycia komórek w procesach" ], "Uprawnienia (analiza użycia)"), UI::h('a', [ // $idStorage = V::get('idStorage', '', $_GET); // $tblName = V::get('table', '', $_GET, 'word'); 'href' => Router::getRoute('Storage')->getLink('tableXsd', [ 'idStorage' => $idStorage, 'table' => "{$tblName}" ]), 'class' => "btn btn-md btn-link", ], "xsd"), ]), 'cols_label' => [ 'sort' => UI::h('nobr', [ 'class' => "ta-ordering" . ('sort' === $sortByCol ? " ta-ordering-" . ( 'asc' === $sortByDir ? 'asc' : 'desc' ) : '' ), 'onClick' => "return p5UI_table_sortBy_get(this, '{$sortByArgName}', 'sort', '" . ('sort' === $sortByCol ? ( 'asc' === $sortByDir ? 'desc' : 'asc' ) : 'asc' ) . "')", 'title' => "Sortuj wg pola SORT_PRIO z zasobów", ], "sort"), 'name' => UI::h('nobr', [ 'class' => "ta-ordering" . ('name' === $sortByCol ? " ta-ordering-" . ( 'asc' === $sortByDir ? 'asc' : 'desc' ) : '' ), 'onClick' => "return p5UI_table_sortBy_get(this, '{$sortByArgName}', 'name', '" . ('name' === $sortByCol ? ( 'asc' === $sortByDir ? 'desc' : 'asc' ) : 'asc' ) . "')", 'title' => "Sortuj wg nazwy", ], "namespace"), ], 'cols' => array_keys($emptyItem), 'rows' => array_map(function ($item) use ($sortByCol) { if ('sort' === $sortByCol && null !== $item['sort'] && !empty($item['id_zasob'])) { $item['@class'] = "ta-sortable-item"; $item['@class[sort]'] = "ta-sortable-item-handle"; $item['@data']['sort_prio'] = $item['sort']; $item['@data']['id_zasob'] = $item['id_zasob']; $item['sort'] = ":: {$item['sort']}"; } return $item; }, $tableList), ]); echo UI::h('script', [], " jQuery(document).on('p5UIBtnAjax:Storage:addCellToZasoby:click', function(e, n, payload) { console.log('event p5UIBtnAjax:Storage:addCellToZasoby:click', n, payload); }); jQuery(document).on('p5UIBtnAjax:Storage:addCellToZasoby:ajaxLoaded', function(e, n, payload) { console.log('event p5UIBtnAjax:Storage:addCellToZasoby:ajaxLoaded', n, payload); if ('success' == payload.type && payload.body && payload.body.id > 0) { var cellUwagiJQNode = jQuery(n).parents('td').next('td'); cellUwagiJQNode.text(cellUwagiJQNode.text().replace('!Zasob', '')) jQuery(n).parents('td').text(payload.body.id); jQuery(n).remove(); } jQuery.notify(payload.msg, payload.type); }); "); $ajaxAddZasobLink = Router::getRoute('Storage')->getLink('addGeomEtykietaCells', [ 'idStorage' => $idStorage, 'tblName' => $tblName]); $onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:addGeomEtykietaCells', { href: '{$ajaxAddZasobLink}' })"; UI::tag('a', ['onclick'=>$onClick, 'class'=>"btn btn-xs btn-default", 'href'=>"#"], "Dodaj komórki etykiet", true); echo "(`etykieta_x`, `etykieta_y`, `etykieta_obrot`)"; echo UI::h('script', [], " jQuery(document).on('p5UIBtnAjax:Storage:addGeomEtykietaCells:ajaxLoaded', function(e, n, payload) { console.log('event p5UIBtnAjax:Storage:addGeomEtykietaCells:ajaxLoaded', n, payload); if ('success' == payload.type && payload.body && payload.body.id > 0) { var cellUwagiJQNode = jQuery(n).parents('td').next('td'); cellUwagiJQNode.text(cellUwagiJQNode.text().replace('!Zasob', '')) jQuery(n).parents('td').text(payload.body.id); jQuery(n).remove(); } jQuery.notify(payload.msg, payload.type); }); "); $tableActions = array_filter($cellZasobList, function ($row) { return ('URL_ACTION' == $row['ZASOB_TYPE']); }); UI::table([ 'caption' => "tableActions", // 'cols' => array_keys($emptyItem), 'rows' => array_map(function ($item) { $sqlIdAction = DB::getPDO()->quote($item['ID'], PDO::PARAM_INT); $args = DB::getPDO()->fetchAll(" select z.`DESC` , a.ID as ALIAS_ID, a.`DESC` as ALIAS_DESC, a.OPIS as ALIAS_OPIS from CRM_LISTA_ZASOBOW z left join CRM_LISTA_ZASOBOW a on(a.ID = z.ALIAS_ID) where z.PARENT_ID = {$sqlIdAction} and z.`TYPE` = 'PARAM_IN' "); $definitionArgs = DB::getPDO()->fetchAll(" select p.ID, p.`DESC` from CRM_LISTA_ZASOBOW z join CRM_LISTA_ZASOBOW d on(d.ID = z.ALIAS_ID) left join CRM_LISTA_ZASOBOW p on(p.PARENT_ID = d.ID) where z.ID = {$sqlIdAction} and p.`TYPE` = 'PARAM_IN' "); $flatDefinitionArgs = implode(";", array_map(function ($arg) { return "{$arg['ID']}={$arg['DESC']}"; }, $definitionArgs)); return [ 'label' => DB::getPDO()->fetchValue(" select z.OPIS from CRM_LISTA_ZASOBOW z where z.ID = {$sqlIdAction} ") . " " . UI::h('i', [ 'class' => "glyphicon glyphicon-pencil", 'style' => "cursor:pointer", 'onClick' => "return Storage__tableStruct__editActionLabel(this, {$sqlIdAction})"], ''), 'args' => implode("
&", array_map(function ($item) { return (NULL === $item['ALIAS_ID']) ? $item['DESC'] : "{$item['DESC']}=" . '{$row["' . $item['ALIAS_DESC'] . '"]}';// TODO: add rmParam btn }, $args)) . " " . UI::h('i', [ 'class' => "glyphicon glyphicon-plus-sign", 'style' => "cursor:pointer", 'title' => "Dodaj PARAM_IN", 'onClick' => "return Storage__tableStruct__addParamAction(this, {$sqlIdAction}, '{$flatDefinitionArgs}')"], ''), // 'args_info' => '
' . var_export($args, true) . '
', 'ID' => $item['ID'], 'DESC' => $item['DESC'], 'A_STATUS' => $item['A_STATUS'], 'definition args' => implode("", array_map(function ($item) { return '
' . "[{$item['ID']}] {$item['DESC']}" . '
'; }, $definitionArgs)), ]; }, $tableActions) ]); echo UI::h('button', [ 'onClick'=>"Storage__tableStruct__addAction()", 'class'=>"btn btn-xs btn-default" ], "Dodaj Akcję"); echo UI::h('link', ['rel'=>"stylesheet", 'type'=>"text/css", 'href'=>"static/sweetalert2.min.css"]); echo UI::h('script', ['src'=>"static/sweetalert2.min.js"]); echo UI::h('style', [], " .swal2-radio.p5-swal-radio-as-list { text-align:left } .swal2-radio.p5-swal-radio-as-list > label { display:block; margin-left:20px } "); UI::inlineJS(__FILE__ . '.tableActions.js', [ 'ID_STORAGE' => $idStorage, 'TABLE_NAME' => $tblName, 'FETCH_URL' => Router::getRoute('Storage')->getLink('fetchActionListAjax'), 'ADD_ACTION_URL' => Router::getRoute('Storage')->getLink('addActionAjax') ]); echo '
'; $ajaxAddBaseProcesLink = Router::getRoute('Storage')->getLink('addTableBaseProces', [ 'idStorage' => $idStorage, 'tblName' => $tblName ]); $onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:addTableBaseProces', { href: '{$ajaxAddBaseProcesLink}' })"; UI::tag('a', ['onclick'=>$onClick, 'class'=>"btn btn-xs btn-default", 'href'=>"#"], "Dodaj podstawowy proces dla tabeli '{$tblName}' - read only", true); echo UI::h('script', [], " jQuery(document).on('p5UIBtnAjax:Storage:addTableBaseProces:ajaxLoaded', function(e, n, payload) { jQuery.notify(payload.msg, payload.type); }); "); if ('sort' === $sortByCol) { echo UI::h('script', [], " jQuery(document).ready(function (){ var tblNode$ = jQuery('#struct_table > tbody') var getOrder = function () { var orderIds = [] jQuery('#struct_table > tbody').children('.ta-sortable-item').each(function (idx, trNode) { orderIds.push(jQuery(trNode).data('id_zasob')) }) return orderIds } var sortOrder = getOrder() console.log('sortOrder - init', sortOrder) tblNode$.sortable({ axis: 'y', handle: '.ta-sortable-item-handle', items: '.ta-sortable-item', update: function (event, ui) { sortOrder = getOrder() tblNode$.sortable('disable') tblNode$.find('.ta-sortable-item-handle').css({color:'silver'}) console.log('TODO: disable sortable while ajax') // window.setTimeout(function () { // tblNode$.find('.ta-sortable-item-handle').css({color:'black'}) // console.log('TODO: ajax fetch result') // tblNode$.sortable('enable') // tblNode$.sortable('cancel') // }, 1000) window.fetch( URI(window.location.href) .setSearch('_task', 'updateAntAclSortAjax') .setSearch('idZasobOrder', sortOrder) .build().toString(), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' // query string }, credentials: 'same-origin', body: JSON.stringify({ namespace: URI.parseQuery(window.location.search).namespace, sortBy: '{$sortByCol}', sortByDir: '{$sortByDir}', sortOrder: sortOrder, }) } ).then(function(response) { return response.json() }).then(function(payload) { console.log('updateAntAclSortAjax response', payload); tblNode$.sortable('enable') if ('success' !== payload.type) { tblNode$.sortable('cancel') } tblNode$.sortable('enable') tblNode$.find('.ta-sortable-item-handle').css({color:'black'}) p5UI__notifyAjaxCallback({ type: payload.type || 'error', msg: payload.msg || 'Request error' }); if ('success' === payload.type) { window.location.reload(true) } }).catch(function(e) { tblNode$.sortable('enable') tblNode$.sortable('cancel') tblNode$.find('.ta-sortable-item-handle').css({color:'black'}) p5UI__notifyAjaxCallback({ type: 'error', msg: 'Request error ' + e }); }) } }); }); "); } } public function objectStructView($item) { $namespace = $item['namespace']; { // sortBy $sortByArgName = 'sortBy'; $sortByCol = V::get($sortByArgName, 'namespace', $_GET); $sortByDir = V::get("{$sortByArgName}Dir", 'asc', $_GET); { // add SORT_PRIO $item['field'] = array_map(function ($field) { $field['SORT_PRIO'] = ($field['idZasob'] > 0) ? DB::getPDO()->fetchValue(" select z.SORT_PRIO from CRM_LISTA_ZASOBOW z where z.ID = :id ", [ ':id' => $field['idZasob'] ]) : null ; return $field; }, $item['field']); } if ('namespace' === $sortByCol) { usort($item['field'], function ($a, $b) use ($sortByDir) { if ($a['fieldNamespace'] > $b['fieldNamespace']) return 'asc' === $sortByDir ? 1 : -1; if ($a['fieldNamespace'] < $b['fieldNamespace']) return 'asc' === $sortByDir ? -1 : 1; return 0; }); } else if ('sort' === $sortByCol) { usort($item['field'], function ($a, $b) use ($sortByDir) { if (null === $a['SORT_PRIO'] && null === $b['SORT_PRIO']) return 0; if (null === $a['SORT_PRIO'] && null != $b['SORT_PRIO']) return 1; if (null !== $a['SORT_PRIO'] && null === $b['SORT_PRIO']) return -1; if ($a['SORT_PRIO'] > $b['SORT_PRIO']) return 'asc' === $sortByDir ? 1 : -1; if ($a['SORT_PRIO'] < $b['SORT_PRIO']) return 'asc' === $sortByDir ? -1 : 1; return 0; }); } echo UI::h('style', [ 'type' => "text/css" ], " .ta-ordering { cursor:pointer } .ta-ordering-desc:after { content:\"\\e252\"; font-family:\"Glyphicons Halflings\"; line-height:1; margin:0 0 0 3px; display:inline-block; color:#bbb } .ta-ordering-asc:after { content:\"\\e253\"; font-family:\"Glyphicons Halflings\"; line-height:1; margin:0 0 0 3px; display:inline-block; color:#bbb } .ta-sortable-item { } .ta-sortable-item-handle { cursor:pointer } "); echo UI::h('script', [ 'src' => "static/URI.js" ]); echo UI::h('script', [], " function p5UI_table_sortBy_get(n, argName, colName, dir) { var newUri = URI(window.location.href).setQuery(argName, colName).setQuery(argName+'Dir', dir).build().toString(); window.location.href = newUri } "); } if ('setFieldRefConfig' === V::get('_postTask', '', $_POST)) { $field = V::get('field', '', $_POST); $source = V::get('source', '', $_POST); switch ($source) { case 'view': $refSelect = ACL::generateRefSelectSqlByFlatRelationCache($namespace, $field); ACL::setRefSource($namespace, $field, 'view', $refSelect); break; case 'table': ACL::setRefSource($namespace, $field, 'table'); break; } } if ('preview' === V::get('_postTask', '', $_POST)) { try { $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]); $localFields = array_filter($item['field'], function ($field) { return $field['isLocal']; }); $refFields = array_filter($item['field'], function ($field) { return 'ref:' === substr($field['xsdType'], 0, 4); }); $activeRefFields = array_filter($refFields, function ($field) { $refNamespace = str_replace(['__x3A__', ':'], '/', substr($field['xsdType'], strlen('ref:'))); return (1 == DB::getPDO()->fetchValue(" select t.isObjectActive from `CRM_#CACHE_ACL_OBJECT` t where t.`namespace` = '{$refNamespace}' ")); }); $previewItems = ACL::getAclByNamespace($namespace)->buildQuery([])->getItems([ 'limit' => 5 ]); DBG::nicePrint($previewItems, 'items (default query) - preview (limit 5)'); $query = [ 'cols' => array_merge( array_map( function ($field) { return $field['fieldNamespace']; }, $localFields ), array_map( function ($field) { return "{$field['fieldNamespace']}/*"; }, $activeRefFields ) ) ]; DBG::nicePrint($query, 'items with ref/* - query'); $previewItems = ACL::getAclByNamespace($namespace)->buildQuery($query)->getItems([ 'limit' => 5 ]); DBG::nicePrint($previewItems, 'items with ref/* - preview (limit 5)'); $query = [ 'cols' => array_merge( array_map( function ($field) { return $field['fieldNamespace']; }, $localFields ), array_map( function ($field) { return "{$field['fieldNamespace']}"; }, $activeRefFields ) ) ]; DBG::nicePrint($query, 'items with ref as xlink - query'); $previewItems = ACL::getAclByNamespace($namespace)->buildQuery($query)->getItems([ 'limit' => 5 ]); DBG::nicePrint($previewItems, 'items with ref as xlink - preview (limit 5)'); } catch (Exception $e) { DBG::log($e); UI::alert('danger', $e->getMessage()); } } echo UI::h('details', ['style'=>"margin-bottom:24px; padding:0 10px; background-color:#eee", 'open' => "open"], [ UI::h('summary', ['style'=>"font-size:1.4em; line-height:2em; cursor:pointer; outline:none"], [ "Struktura obiektu '{$namespace}' ", // UI::h('small', ['style'=>"font-size:0.8em; font-style:italic; color:#aaa"], " więcej...") ]), UI::h('div', ['style'=>"padding:4px 24px; border-top:1px solid #fff"], [ UI::h('span', ['style'=>"margin-right:12px"], [ ( ($item['idZasob'] > 0) ? UI::h('span', [ 'title' => "Nr zasobu '{$item['idZasob']}'" ], "Nr zasobu [{$item['idZasob']}]") : UI::h('span', [ 'title' => "Brak nr zasobu - dodaj do zasobów" ], [ UI::hButtonAjax("+ do zasobów", 'addAclObjectToZasobyAjax', [ 'class' => "btn btn-xs btn-primary", 'href' => Router::getRoute('Storage')->getLink('addAclObjectToZasobyAjax'), 'data' => [ 'idStorage' => $item['idDatabase'], 'namespace' => $item['namespace'], ] ]) ]) ), ]), UI::h('span', ['style'=>"margin:0 12px"], [ ( ($item['isObjectActive'] > 0) ? UI::h('span', [ 'class' => "label label-success", 'title' => "Namespace active" ], "Active") : UI::h('span', [ 'title' => "Namespace not active" ], [ ($item['idZasob'] > 0) ? UI::hButtonAjax("Aktywuj", 'activateObjectAjax', [ 'class' => "btn btn-xs btn-primary", 'href' => Router::getRoute('Storage')->getLink('activateObjectAjax'), 'data' => [ 'namespace' => $item['namespace'], ] ]) : '', ]) ), ]), UI::h('a', [ 'href' => "index.php?_route=ViewTableAjax&namespace={$item['namespace']}", 'class' => "btn btn-sm btn-link" ], "Przeglądaj tabelę"), ( ($item['idZasob'] > 0) ? UI::h('a', [ 'href' => "procesy5.php?task=CRM_LISTA_ZASOBOW&filtr_id={$item['idZasob']}&filtr_ids=%2B&filtr_ob=%2B", 'class' => "btn btn-sm btn-link", 'title' => "Struktura tabeli w drzewie zasobów" ], "Zasoby") : '' ), ( ($item['idZasob'] > 0) ? UI::h('a', [ 'href' => "index.php?FUNCTION_INIT=PROCES_MENU&HEADER_NOT_INIT=YES&_task=PROCES_FOR_TABLE&tblId={$item['idZasob']}", 'class' => "btn btn-sm btn-link", 'title' => "Procesy dla aktualnie przeglądanej tabeli" ], "Procesy") : '' ), UI::h('a', [ 'href' => Router::getRoute('Storage_AclReinstall')->getLink('', ['namespace' => $item['namespace']]), 'class' => 'btn btn-sm btn-link', 'style' => "color:#f00", 'title' => "Zainstaluje ponownie obiekt" ], "reinstall object"), UI::h('a', [ 'href' => Router::getRoute('Storage_AclUsage')->getLink('', [ 'namespace' => $item['namespace'] ]), 'class' => "btn btn-sm btn-link", 'title' => "Uprawnienia - analiza użycia komórek w procesach" ], "Uprawnienia (analiza użycia)"), UI::hButtonPost("(podgląd z relacjami)", [ 'data' => [ '_postTask' => 'preview' ], 'class' => 'btn btn-sm btn-link', 'style' => "font-style:italic", 'title' => "Podgląd kilku ostatnich obiektów wraz z relacjami" ]), ]) ]); $thisGetLink = [ $this, 'getLink' ]; { // not installed ref $refFields = array_filter($item['field'], function ($field) { return ('ref:' === substr($field['xsdType'], 0, 4) && $field['isActive']); }); UI::table([ 'caption' => UI::h('span', [], "Obiekty powiązane (TODO backRef)"), 'rows' => array_map(function ($field) use ($thisGetLink) { $refNamespace = str_replace(['__x3A__', ':'], '/', substr($field['xsdType'], strlen('ref:'))); $isInstalled = (1 == DB::getPDO()->fetchValue(" select t.isObjectActive from `CRM_#CACHE_ACL_OBJECT` t where t.`namespace` = '{$refNamespace}' ")); $refSource = null; if ($isInstalled) { try { $refTable = ACL::getRefTable($field['objectNamespace'], $field['fieldNamespace']); DBG::log($refTable, 'array', "getRefTable('{$field['objectNamespace']}', '{$field['fieldNamespace']}')"); $refSource = ACL::getRefSource($field['objectNamespace'], $field['fieldNamespace']); DBG::log($refSource, 'array', "ACL::getRefSource('{$field['objectNamespace']}', '{$field['fieldNamespace']}')"); } catch (Exception $e) { DBG::log($e); } } return [ 'fieldName' => $field['fieldNamespace'], // 'xsdType' => $field['xsdType'], // always === "ref:{$field['fieldNamespace']}" 'ref object' => UI::h('a', [ 'class' => "btn btn-xs btn-link", 'href' => $thisGetLink('', [ 'namespace' => $refNamespace ]) ], "objectStruct ({$refNamespace})"), 'ref source' => UI::h('div', [], (null === $refSource) ? [ "ref object not installed" ] // TODO: link to install? is table struct : [ // UI::hButtonAjax("Tabela ref", "setFieldRefConfig", [ // 'class' => "btn btn-xs btn-default" . ( 'table' === $refSource ? ' disabled' : '' ), // 'href' => $thisGetLink('setFieldRefConfig'), // 'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], 'do' => 'table' ] // ]), // UI::hButtonAjax("Widok (cache)", "setFieldRefConfig", [ // 'title' => "Według flat_relation_cache", // 'class' => "btn btn-xs btn-default" . ( 'view' === $refSource ? ' disabled' : '' ), // 'href' => $thisGetLink('setFieldRefConfig'), // 'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], 'do' => 'view' ] // ]), UI::hButtonPost("Tabela ref", [ 'title' => "Według wygenerowanej tabeli REF", 'class' => "btn btn-xs btn-default" . ( 'table' === $refSource ? ' disabled' : '' ), // 'href' => $thisGetLink('setFieldRefConfig'), 'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], '_postTask' => 'setFieldRefConfig', 'source' => 'table' ] ]), UI::hButtonPost("Widok (cache)", [ 'title' => "Według flat_relation_cache", 'class' => "btn btn-xs btn-default" . ( 'view' === $refSource ? ' disabled' : '' ), // 'href' => $thisGetLink('setFieldRefConfig'), 'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], '_postTask' => 'setFieldRefConfig', 'source' => 'view' ] ]), ] ), '@class' => ($isInstalled) ? "success" : "danger", ]; // TODO: link to install object }, $refFields) ]); } $thisGetLink = [ $this, 'getLink' ]; UI::table([ '__html_id' => 'struct_table', 'caption' => UI::h('span', [], [ UI::h('span', [ 'style' => "margin-right:6px;color:#000" ], "Struktura obiektu '{$item['namespace']}'"), ]), // 'sortBy' => [ // 'method' => 'get', // 'argName' => '_sortBy', // 'allow' => ['sort', 'namespace'], // 'default' => ['namespace', 'asc'], // ], 'cols_label' => [ 'sort' => UI::h('nobr', [ 'class' => "ta-ordering" . ('sort' === $sortByCol ? " ta-ordering-" . ( 'asc' === $sortByDir ? 'asc' : 'desc' ) : '' ), 'onClick' => "return p5UI_table_sortBy_get(this, '{$sortByArgName}', 'sort', '" . ('sort' === $sortByCol ? ( 'asc' === $sortByDir ? 'desc' : 'asc' ) : 'asc' ) . "')", 'title' => "Sortuj wg pola SORT_PRIO z zasobów", ], "sort"), 'namespace' => UI::h('nobr', [ 'class' => "ta-ordering" . ('namespace' === $sortByCol ? " ta-ordering-" . ( 'asc' === $sortByDir ? 'asc' : 'desc' ) : '' ), 'onClick' => "return p5UI_table_sortBy_get(this, '{$sortByArgName}', 'namespace', '" . ('namespace' === $sortByCol ? ( 'asc' === $sortByDir ? 'desc' : 'asc' ) : 'asc' ) . "')", 'title' => "Sortuj wg nazwy pola", ], "namespace"), ], 'rows' => array_map(function ($field) use ($item, $thisGetLink, $sortByCol) { $tblItem = []; if ('sort' === $sortByCol && null !== $field['SORT_PRIO'] && !empty($field['idZasob'])) { $tblItem['@class'] = "ta-sortable-item"; $tblItem['@class[sort]'] = "ta-sortable-item-handle"; $tblItem['@data']['sort_prio'] = $field['SORT_PRIO']; $tblItem['@data']['id_zasob'] = $field['idZasob']; $field['SORT_PRIO'] = ":: {$field['SORT_PRIO']}"; } $tblItem['sort'] = V::get('SORT_PRIO', null, $field); foreach ($field as $k => $v) { if ('SORT_PRIO' === $k) continue; $tblItem[$k] = $v; } $tblItem['namespace'] = UI::h('span', [], [ UI::h('span', ['style' => "color:#aaa"], substr($field['namespace'], 0, strlen($field['objectNamespace']) + 1)), UI::h('span', ['style' => "color:#000"], substr($field['namespace'], strlen($field['objectNamespace']) + 1)), ]); $tblItem['idZasob'] = ($field['idZasob'] > 0) ? $field['idZasob'] : ( ($item['idZasob'] > 0) ? UI::hButtonAjax("+ do zasobów", 'addFieldToZasobyAjax', [ 'class' => "btn btn-xs btn-primary", 'href' => $thisGetLink('addFieldToZasobyAjax'), 'data' => [ 'namespace' => $item['namespace'], 'fieldNamespace' => $field['namespace'], ] ]) : UI::h('span', ['title'=>"Brak Nr zasobu dla obiektu", 'class'=>"btn btn-xs btn-danger"], "Brak nr zasobu obiektu") ) ; unset($tblItem['objectNamespace']); unset($tblItem['fieldNamespace']); return $tblItem; }, array_filter($item['field'], function ($field) { return $field['isActive']; })) ]); $removedFields = array_filter($item['field'], function ($field) { return !$field['isActive']; }); if (!empty($removedFields)) { // echo UI::h('details', [ 'style' => "padding:6px; background-color:#333; color:#fff", 'open' => "open" ], [ // UI::h('summary', [ 'style' => "padding:0 3px; outline:none; cursor:pointer" ], "WFS Response converted to JSON"), // UI::h('pre', [ 'id' => 'wfsResponse', 'style' => "margin:0; font-size:x-small; border-radius:0" ], 'loading...'), echo UI::h('details', [ 'style' => "margin-bottom:12px; padding:6px; background-color:#dedede; color:#000" ], [ UI::h('summary', [ 'style' => "padding:0 3px; outline:none; cursor:pointer" ], "Pola w koszu (".count($removedFields).")"), UI::h('table', [ 'style' => "margin:6px 0 0 0; background-color:#fff; font-size:x-small", 'class' => "table table-bordered table-hover table-condensed" ], [ UI::h('thead', [], [ UI::h('tr', [], [ UI::h('th', [], "#"), UI::h('th', [], "namespace"), UI::h('th', [], "xsdType"), UI::h('th', [], "xsdRestrictions"), UI::h('th', [], "appInfo"), UI::h('th', [], "minOccurs"), UI::h('th', [], "maxOccurs"), UI::h('th', [], "isActive"), ]), ]), UI::h('tbody', [], array_map(function ($field) use ($item, $thisGetLink) { return UI::h('tr', [], [ UI::h('td', [], [ UI::hButtonAjax("usuń", 'removeFieldFromTrashAjax', [ 'class' => "btn btn-xs btn-danger", 'href' => $thisGetLink('removeFieldFromTrashAjax'), 'data' => [ 'namespace' => $item['namespace'], 'fieldNamespace' => $field['namespace'], ] ]) ]), UI::h('td', [], $field['fieldNamespace']), UI::h('td', [], $field['xsdType']), UI::h('td', [], $field['xsdRestrictions']), UI::h('td', [], $field['appInfo']), UI::h('td', [], $field['minOccurs']), UI::h('td', [], $field['maxOccurs']), UI::h('td', [], $field['isActive']), ]); }, $removedFields)), ]), ]); } UI::hButtonAjaxOnResponse('removeFieldFromTrashAjax', /* payload, n */ " if (!payload.type) return false jQuery.notify(payload.msg, payload.type) if ('success' == payload.type) { var trJqNode = jQuery(n).closest('tr') var tbodyJqNode = trJqNode.parent() var detailsJqNode = trJqNode.closest('details') trJqNode.remove() if (!tbodyJqNode.children().length) { detailsJqNode.remove() } } "); UI::hButtonAjaxOnResponse('addFieldToZasobyAjax', /* payload, n */ " if (!payload.type) return false if (payload.body && payload.body.id && payload.body.id > 0) { // if ('success' == payload.type) { n.parentNode.replaceChild(document.createTextNode(payload.body.id), n) } jQuery.notify(payload.msg, payload.type) "); UI::hButtonAjaxOnResponse('addAclObjectToZasobyAjax', /* payload, n */ " if (!payload.type) return false if ('success' === payload.type || 'info' === payload.type) { if (payload.body && payload.body.id && payload.body.id > 0) { n.parentNode.replaceChild(document.createTextNode(payload.body.id), n) } else { console.log('TODO: addAclObjectToZasobyAjax unknown response', payload); } window.location.reload() } "); UI::hButtonAjaxOnResponse('activateObjectAjax', /* payload, n */ " jQuery.notify(payload.msg, payload.type) if (!payload.type) return false if ('success' === payload.type || 'info' === payload.type) { if (payload.body && payload.body.isObjectActive && payload.body.isObjectActive > 0) { n.parentNode.replaceChild(document.createTextNode('Active'), n) } else { console.log('TODO: activateObjectAjax unknown response', payload); } } "); if ('sort' === $sortByCol) { echo UI::h('script', [], " jQuery(document).ready(function (){ var tblNode$ = jQuery('#struct_table > tbody') var getOrder = function () { var orderIds = [] jQuery('#struct_table > tbody').children('.ta-sortable-item').each(function (idx, trNode) { orderIds.push(jQuery(trNode).data('id_zasob')) }) return orderIds } var sortOrder = getOrder() console.log('sortOrder - init', sortOrder) tblNode$.sortable({ axis: 'y', handle: '.ta-sortable-item-handle', items: '.ta-sortable-item', update: function (event, ui) { sortOrder = getOrder() tblNode$.sortable('disable') tblNode$.find('.ta-sortable-item-handle').css({color:'silver'}) console.log('TODO: disable sortable while ajax') // window.setTimeout(function () { // tblNode$.find('.ta-sortable-item-handle').css({color:'black'}) // console.log('TODO: ajax fetch result') // tblNode$.sortable('enable') // tblNode$.sortable('cancel') // }, 1000) window.fetch( URI(window.location.href) .setSearch('_task', 'updateAntAclSortAjax') .setSearch('idZasobOrder', sortOrder) .build().toString(), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' // query string }, credentials: 'same-origin', body: JSON.stringify({ namespace: URI.parseQuery(window.location.search).namespace, sortBy: '{$sortByCol}', sortByDir: '{$sortByDir}', sortOrder: sortOrder, }) } ).then(function(response) { return response.json() }).then(function(payload) { console.log('updateAntAclSortAjax response', payload); tblNode$.sortable('enable') if ('success' !== payload.type) { tblNode$.sortable('cancel') } tblNode$.sortable('enable') tblNode$.find('.ta-sortable-item-handle').css({color:'black'}) p5UI__notifyAjaxCallback({ type: payload.type || 'error', msg: payload.msg || 'Request error' }); if ('success' === payload.type) { window.location.reload(true) } }).catch(function(e) { tblNode$.sortable('enable') tblNode$.sortable('cancel') tblNode$.find('.ta-sortable-item-handle').css({color:'black'}) p5UI__notifyAjaxCallback({ type: 'error', msg: 'Request error ' + e }); }) } }); }); "); } if ($item['isObjectActive']) { echo UI::hButtonAjax("Dodaj podstawowy proces dla obiektu '{$item['namespace']}' - read only (TODO)", 'addObjectBaseProcesAjax', [ 'class' => "btn btn-xs btn-default", 'href' => Router::getRoute('Storage')->getLink('addObjectBaseProcesAjax'), 'data' => [ 'namespace' => $item['namespace'] ] ]); UI::hButtonAjaxOnResponse('addObjectBaseProcesAjax', /* payload, n */ " jQuery.notify(payload.msg, payload.type) "); } DBG::nicePrint($item, '$item'); } public function updateAntAclSortAjaxAction() { return Response::sendTryCatchJson(array($this, 'updateAntAclSortAjax'), $args = 'JSON_FROM_REQUEST_BODY'); } public function updateAntAclSortAjax($args) { DBG::log($args, 'array', "args"); $namespace = V::get('namespace', '', $args); if (empty($namespace)) throw new Exception("Missing param namespace"); $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]); DBG::log($item, 'array', "\$item"); $sortByArgName = 'sortBy'; $sortByCol = V::get($sortByArgName, 'namespace', $args); $sortByDir = V::get("{$sortByArgName}Dir", 'asc', $args); if ('sort' !== $sortByCol) throw new Exception("Only sort by SORT_PRIO allowed to update"); if (!in_array($sortByDir, ['asc', 'desc'])) throw new Exception("Wrong order by dir arg"); if ('asc' !== $sortByDir) throw new Exception("Only sort asc is allowed"); // DBG $sortPrioIdsFromZasoby = DB::getPDO()->fetchAll(" select z.ID, z.SORT_PRIO from CRM_LISTA_ZASOBOW z where z.PARENT_ID = :p_id order by z.SORT_PRIO asc, z.ID asc -- default order in Zasoby tree ", [ ':p_id' => $item['idZasob'] ]); DBG::log($sortPrioIdsFromZasoby, 'array', "\$sortPrioIdsFromZasoby"); $idZasobCurrentOrder = array_map(function ($orderRow) { return (int)$orderRow['ID']; }, $sortPrioIdsFromZasoby); DBG::log($idZasobCurrentOrder, 'array', "\$idZasobCurrentOrder"); DBG::log(array_diff_assoc($args['sortOrder'], $idZasobCurrentOrder), 'array', "array_diff_assoc(\$args['sortOrder'], \$idZasobCurrentOrder)"); DBG::log(array_diff_assoc($idZasobCurrentOrder, $args['sortOrder']), 'array', "array_diff_assoc(\$idZasobCurrentOrder, \$args['sortOrder'])"); $orderDiff = array_diff_assoc($args['sortOrder'], $idZasobCurrentOrder); foreach ($orderDiff as $sortPrio => $idZasob) { DB::getPDO()->update('CRM_LISTA_ZASOBOW', 'ID', $idZasob, [ 'SORT_PRIO' => $sortPrio, ]); } return [ 'type' => 'success', 'msg' => "OK", 'DBG__args' => $args ]; } public function removeFieldFromTrashAjaxAction() { DBG::log($_REQUEST, 'array', '$_REQUEST'); Response::sendTryCatchJson(array($this, 'removeFieldFromTrashAjax'), $_REQUEST); } public function removeFieldFromTrashAjax($args) { $namespace = V::get('namespace', '', $args); if (empty($namespace)) throw new HttpException("Missing namespace"); $fieldNamespace = V::get('fieldNamespace', '', $args); if (empty($fieldNamespace)) throw new HttpException("Missing fieldNamespace"); $fieldItem = SchemaFactory::loadDefaultObject('SystemObjectField')->getItem($fieldNamespace); if (!$fieldItem) throw new HttpException("Field not found '{$fieldNamespace}'", 404); DBG::log($fieldItem, 'array', "\$fieldItem"); switch ($fieldItem['xsdType']) { case 'p5:enum': throw new Exception("TODO: remove enum values first"); break; // CRM_#CACHE_ACL_OBJECT_FIELD_enum default: { if ('ref:' === substr($fieldItem['xsdType'], 0, 4)) { // OK, remove } else throw new Exception("TODO: remove xsdType: {$fieldItem['xsdType']}"); } } DB::getPDO()->execSql(" DELETE from `CRM_#CACHE_ACL_OBJECT_FIELD` where namespace = :namespace and objectNamespace = :objectNamespace and _rootTableName = :rootTableName and isActive = 0 limit 1 ", [ 'namespace' => $fieldItem['namespace'], 'objectNamespace' => $fieldItem['objectNamespace'], 'rootTableName' => $fieldItem['_rootTableName'], ]); return [ 'type' => "success", 'msg' => "Usunięto pole {$fieldItem['fieldNamespace']}", ]; } public function addFieldToZasobyAjaxAction() { DBG::log($_REQUEST, 'array', '$_REQUEST'); Response::sendTryCatchJson(array($this, 'addFieldToZasobyAjax'), $_REQUEST); } public function addFieldToZasobyAjax($args) { $namespace = V::get('namespace', '', $args); if (empty($namespace)) throw new HttpException("Missing namespace"); $fieldNamespace = V::get('fieldNamespace', '', $args); if (empty($fieldNamespace)) throw new HttpException("Missing fieldNamespace"); $fieldItem = SchemaFactory::loadDefaultObject('SystemObjectField')->getItem($fieldNamespace); if (!$fieldItem) throw new HttpException("Field not found '{$fieldNamespace}'", 404); DBG::log($fieldItem, 'array', "\$fieldItem"); if ($fieldItem['idZasob'] > 0) throw (new AlertSuccessException("Field already added to Zasoby '{$fieldItem['idZasob']}'"))->setBody([ 'id' => $fieldItem['idZasob'] ]); $objectItem = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace); if (!$objectItem) throw new HttpException("Object not found '{$namespace}'", 404); DBG::log($objectItem, 'array', "\$objectItem"); if (!$objectItem['idZasob']) throw new Exception("Missing Object idZasob"); $zasobItem = DB::getPDO()->fetchFirst(" select zField.ID, zField.`DESC`, zField.`TYPE`, zField.PARENT_ID from CRM_LISTA_ZASOBOW as zField join CRM_LISTA_ZASOBOW as zTable on(zTable.ID = zField.PARENT_ID) where zTable.`ID` = '{$objectItem['idZasob']}' and zTable.`TYPE` = 'TABELA' and zField.`DESC` = '{$fieldItem['fieldNamespace']}' and zField.`TYPE` = 'KOMORKA' "); if ($zasobItem) { SchemaFactory::loadDefaultObject('SystemObjectField')->updateItem([ 'namespace' => $fieldItem['namespace'], 'idZasob' => $zasobItem['ID'] ]); throw (new AlertSuccessException("Zasob już istnieje"))->setBody([ 'id' => $zasobItem['ID'] ]); } $newFieldItem = [ 'PARENT_ID' => $objectItem['idZasob'], 'TYPE' => 'KOMORKA', 'DESC' => $fieldItem['fieldNamespace'], 'DESC_PL' => $fieldItem['fieldNamespace'], ]; DBG::log($newFieldItem, 'array', "add new field item"); try { $acl = User::getAcl()->getObjectAcl('default_db', 'crm_lista_zasobow'); } catch (Exception $e) { throw new Exception("Brak dostępu do tabeli Zasoby"); } $createdId = $acl->addItem($newFieldItem); if (!$createdId) throw new Exception("Nie udało się utworzyć nowego rekordu!"); SchemaFactory::loadDefaultObject('SystemObjectField')->updateItem([ 'namespace' => $fieldItem['namespace'], 'idZasob' => $createdId ]); throw (new AlertSuccessException("Utworzono pomyślnie rekord nr {$createdId}"))->setBody([ 'id' => $createdId ]); } }