PageRenderTime 29ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/sale/lib/location/admin/helper.php

https://gitlab.com/alexprowars/bitrix
PHP | 792 lines | 596 code | 139 blank | 57 comment | 102 complexity | 22a274fb94de1ef9f24578073909c62b MD5 | raw file
  1. <?
  2. /**
  3. * This class is for internal use only, not a part of public API.
  4. * It can be changed at any time without notification.
  5. *
  6. * @access private
  7. */
  8. namespace Bitrix\Sale\Location\Admin;
  9. use Bitrix\Main;
  10. use Bitrix\Main\Config;
  11. use Bitrix\Main\Localization\Loc;
  12. use Bitrix\Sale\Location;
  13. abstract class Helper
  14. {
  15. const DEBUG_MODE_OPT = 'location2_debug_mode';
  16. const IMPORT_PAGE_URL = 'sale_location_import.php';
  17. const REINDEX_PAGE_URL = 'sale_location_reindex.php';
  18. const MIGRATION_PAGE_URL = 'sale_location_migration.php';
  19. const LOCATION_LINK_DATA_CACHE_TAG = 'sale-location-data';
  20. #####################################
  21. #### Entity settings
  22. #####################################
  23. abstract public static function getEntityRoadMap();
  24. public static function getEntityRoadCode()
  25. {
  26. return 'main';
  27. }
  28. // this should be overlapped for each ancestor
  29. public static function getColumns($page)
  30. {
  31. // in the middle of extension, should be like this:
  32. //return array_merge(parent::getColumns(), self::getMap());
  33. return self::getMap($page);
  34. }
  35. // get part of the whole field map for responsibility zone of the current entity
  36. // call this only with self::
  37. public static function getMap($page)
  38. {
  39. static $flds;
  40. if($flds == null)
  41. $flds = static::readMap(self::getEntityRoadCode(), $page);
  42. return $flds;
  43. }
  44. #####################################
  45. #### CRUD wrappers
  46. #####################################
  47. // columns shown in all grids
  48. public static function getListGridColumns()
  49. {
  50. $columns = static::getColumns('list');
  51. foreach($columns as &$col)
  52. $col['DEFAULT'] = true;
  53. return $columns;
  54. }
  55. // columns shown in all filters
  56. public static function getFilterColumns()
  57. {
  58. $columns = static::getColumns('list');
  59. foreach($columns as &$col)
  60. $col['DEFAULT'] = true;
  61. return $columns;
  62. }
  63. // columns shown in all forms
  64. public static function getDetailPageRows()
  65. {
  66. return static::getColumns('list');
  67. }
  68. // generalized filter to orm filter proxy
  69. public static function getParametersForList($proxed)
  70. {
  71. $columns = self::getMap('list'); // columns only for 'main' class
  72. $parameters = array();
  73. // filter
  74. $filter = array();
  75. if(is_array($proxed['FILTER']) && !empty($proxed['FILTER']))
  76. {
  77. foreach($columns as $code => $fld)
  78. {
  79. if($fld['data_type'] == 'integer' || $fld['data_type'] == 'float')
  80. {
  81. // range or list expected
  82. if(is_array($proxed['FILTER'][$code]))
  83. {
  84. if(mb_strlen($proxed['FILTER'][$code]['FROM']) && mb_strlen($proxed['FILTER'][$code]['TO'])) // range
  85. {
  86. $filter['><'.$code] = array($proxed['FILTER'][$code]['FROM'], $proxed['FILTER'][$code]['TO']);
  87. }
  88. elseif(mb_strlen($proxed['FILTER'][$code]['FROM'])) // greather than
  89. {
  90. $filter['>='.$code] = $proxed['FILTER'][$code]['FROM'];
  91. }
  92. elseif(mb_strlen($proxed['FILTER'][$code]['TO'])) // less than
  93. {
  94. $filter['<='.$code] = $proxed['FILTER'][$code]['TO'];
  95. }
  96. }
  97. elseif(mb_strlen($proxed['FILTER'][$code]))
  98. {
  99. $filter['='.$code] = (string)$proxed['FILTER'][$code];
  100. }
  101. }
  102. else
  103. {
  104. if($proxed['FILTER'][$code] <> '')
  105. {
  106. $filter[static::getFilterModifier($fld['data_type']).$code] = $proxed['FILTER'][$code];
  107. }
  108. }
  109. }
  110. }
  111. if(!empty($filter))
  112. $parameters['filter'] = $filter;
  113. // select
  114. foreach($columns as $code => $col)
  115. $parameters['select'][] = $code;
  116. // order
  117. if(is_array($proxed['ORDER']) && !empty($proxed['ORDER']))
  118. $parameters['order'] = $proxed['ORDER'];
  119. // nav (unused)
  120. if(($page = intval($proxed['NAV']['PAGE_NUM'])) && ($lop = intval($proxed['NAV']['LOP'])))
  121. {
  122. $roadMap = static::getEntityRoadMap();
  123. $road = $roadMap[self::getEntityRoadCode()]['name'];
  124. $class = $road.'Table';
  125. $count = $class::getList(array(
  126. 'filter' => is_array($parameters['filter']) ? $parameters['filter'] : array(),
  127. 'select' => array('CNT'),
  128. 'runtime' => array(
  129. 'CNT' => array(
  130. 'data_type' => 'integer',
  131. 'expression' => array(
  132. 'count(%u)',
  133. 'ID'
  134. )
  135. )
  136. )
  137. ))->fetch();
  138. $bounds = Main\DB\Paginator::calculateQueryLimits($count['CNT'], $page, $lop);
  139. $parameters['offset'] = $bounds[0];
  140. $parameters['limit'] = $bounds[1];
  141. }
  142. return $parameters;
  143. }
  144. /*
  145. * $parameters: array of keys: FILTER (generalized), ID, OPERATION
  146. */
  147. public static function performGridOperations($parameters)
  148. {
  149. $result = array(
  150. 'sucess' => true,
  151. 'errors' => array()
  152. );
  153. @set_time_limit(0);
  154. if(is_array($parameters['ID']) && !empty($parameters['ID']))
  155. {
  156. $parameters['ID'] = array_unique($parameters['ID']);
  157. foreach($parameters['ID'] as $id)
  158. {
  159. $res = static::delete($id);
  160. if(!$res['success'])
  161. {
  162. $result['success'] = false;
  163. $result['errors'] = array_merge($result['errors'], $res['errors']);
  164. }
  165. }
  166. }
  167. else if(is_array($parameters['FILTER'])) // filter can be empty
  168. {
  169. $entityClass = static::getEntityClass();
  170. $parameters = Helper::getParametersForList($parameters); // from generalized to orm
  171. $glParams = array('select' => array('ID'));
  172. if(is_array($parameters['filter']) && !empty($parameters['filter']))
  173. $glParams['filter'] = $parameters['filter'];
  174. $resItems = $entityClass::getList($glParams);
  175. while ($item = $resItems->fetch())
  176. {
  177. /* Locations have tree-style structure so
  178. * we could have deleted some of them
  179. * during previous iterations. Let's check this.
  180. */
  181. if(!$entityClass::getById($item['ID'])->fetch())
  182. continue;
  183. /**/
  184. $res = static::delete($item['ID']);
  185. if(!$res['success'])
  186. {
  187. $result['success'] = false;
  188. $result['errors'] = array_merge($result['errors'], $res['errors']);
  189. }
  190. }
  191. }
  192. return $result;
  193. }
  194. // get data to display in a form
  195. public static function getFormData($id)
  196. {
  197. $parameters = static::proxyListRequest('detail');
  198. $parameters['filter']['='.static::getPrimaryFieldName()] = $id;
  199. $formData = static::getList($parameters)->fetch();
  200. if(!is_array($formData) || empty($formData))
  201. throw new Main\SystemException(Loc::getMessage('SALE_LOCATION_E_ITEM_NOT_FOUND'));
  202. return $formData;
  203. }
  204. public static function makeSafeDisplay(&$value, $code)
  205. {
  206. $columns = static::getColumns('');
  207. if(!empty($columns[$code]))
  208. {
  209. if(!mb_strlen($value) && mb_strlen($columns[$code]['default']))
  210. $value = $columns[$code]['default'];
  211. switch($columns[$code]['data_type'])
  212. {
  213. case 'integer':
  214. $value = intval($value);
  215. break;
  216. case 'float':
  217. $value = floatval($value);
  218. break;
  219. default:
  220. $value = htmlspecialcharsbx($value);
  221. }
  222. }
  223. else
  224. $value = htmlspecialcharsbx($value);
  225. return $value;
  226. }
  227. ##############################################
  228. ##############################################
  229. ##############################################
  230. public static function validateUpdateRequest($data)
  231. {
  232. return array();
  233. }
  234. // this function could be much more complicated in the derivative classes
  235. public static function proxyUpdateRequest($data)
  236. {
  237. unset($data['ID']); // drop id if presents
  238. $proxed = array();
  239. $columns = static::getColumns('list');
  240. foreach($columns as $code => $void)
  241. {
  242. if(isset($data[$code]))
  243. $proxed[$code] = $data[$code];
  244. }
  245. return $proxed;
  246. }
  247. // an adapter from CAdminList + CAdminFilter to ORM getList() logic
  248. // deprecated: too strong relation with admin grid, replaced with getParametersForList
  249. public static function proxyListRequest($page)
  250. {
  251. global $by;
  252. global $order;
  253. $columns = self::getMap($page); // columns only for 'main' class
  254. $parameters = array('filter' => array());
  255. foreach($columns as $code => $col)
  256. $parameters['select'][] = $code;
  257. $filter = array();
  258. if(self::checkUseFilter())
  259. {
  260. foreach($columns as $code => $fld)
  261. {
  262. $from = 'find_'.$code.'_1';
  263. $to = 'find_'.$code.'_2';
  264. if($fld['data_type'] == 'integer' && (isset($GLOBALS[$from]) || isset($GLOBALS[$to])))
  265. {
  266. // range expected
  267. if(mb_strlen($GLOBALS[$from]) && mb_strlen($GLOBALS[$to])) // range
  268. {
  269. $filter['><'.$code] = array($GLOBALS[$from], $GLOBALS[$to]);
  270. }
  271. elseif(mb_strlen($GLOBALS[$from])) // greather than
  272. {
  273. $filter['>='.$code] = $GLOBALS[$from];
  274. }
  275. elseif(mb_strlen($GLOBALS[$to])) // less than
  276. {
  277. $filter['<='.$code] = $GLOBALS[$to];
  278. }
  279. }
  280. else
  281. {
  282. if($GLOBALS['find_'.$code] <> '')
  283. {
  284. $filter[static::getFilterModifier($fld['data_type']).$code] = $GLOBALS['find_'.$code];
  285. }
  286. }
  287. }
  288. }
  289. if(!empty($filter))
  290. $parameters['filter'] = $filter;
  291. if($by <> '')
  292. {
  293. $columns = static::getColumns($page); // check if that column really exists, for the whole extension hierarchy
  294. if(isset($columns[$by]))
  295. {
  296. $parameters['order'] = array($by => isset($order)? $order : 'asc');
  297. }
  298. }
  299. return $parameters;
  300. }
  301. // crud over entity: add
  302. public static function add($data)
  303. {
  304. $success = true;
  305. $id = false;
  306. $entityClass = static::getEntityClass();
  307. $data = static::convertToArray($data);
  308. $data = static::proxyUpdateRequest($data);
  309. $errors = static::validateUpdateRequest($data);
  310. if(empty($errors))
  311. {
  312. $res = $entityClass::add($data);
  313. if(!$res->isSuccess())
  314. {
  315. $success = false;
  316. $errors = $res->getErrorMessages();
  317. }
  318. else
  319. $id = $res->getId();
  320. }
  321. else
  322. $success = false;
  323. return array(
  324. 'success' => $success,
  325. 'errors' => $errors,
  326. 'id' => $id
  327. );
  328. }
  329. // crud over entity: update
  330. public static function update($primary, $data)
  331. {
  332. $success = true;
  333. $entityClass = static::getEntityClass();
  334. $data = static::convertToArray($data);
  335. $data = static::proxyUpdateRequest($data);
  336. $errors = static::validateUpdateRequest($data);
  337. if(empty($errors))
  338. {
  339. $res = $entityClass::update($primary, $data);
  340. if(!$res->isSuccess())
  341. {
  342. $success = false;
  343. $errors = $res->getErrorMessages();
  344. }
  345. }
  346. else
  347. $success = false;
  348. return array(
  349. 'success' => $success,
  350. 'errors' => $errors
  351. );
  352. }
  353. // crud over entity: delete
  354. public static function delete($primary)
  355. {
  356. $success = true;
  357. $errors = array();
  358. $entityClass = static::getEntityClass();
  359. $res = $entityClass::delete($primary);
  360. if(!$res->isSuccess())
  361. {
  362. $success = false;
  363. $errors = $res->getErrorMessages();
  364. }
  365. return array(
  366. 'success' => $success,
  367. 'errors' => $errors
  368. );
  369. }
  370. // function calculates limit and offset for sql select query, based on current request and session
  371. // variables, then forms fake old-style database result
  372. public static function getList($parameters = array(), $tableId = false, $navigation = 20, $params = array())
  373. {
  374. $entityClass = static::getEntityClass();
  375. $navNum = $GLOBALS['NavNum'] + 1;
  376. $unique = md5($GLOBALS['APPLICATION']->GetCurPage());
  377. $showAll = $_SESSION[$unique.'SESS_ALL_'.$navNum] || $_GET['SHOWALL_'.$navNum];
  378. $isAdminSection = defined('ADMIN_SECTION') && ADMIN_SECTION === true;
  379. if ($params["uiMode"])
  380. {
  381. $result = new \CSaleProxyAdminUiResult($parameters, $entityClass, $tableId);
  382. }
  383. elseif($isAdminSection && mb_strlen($tableId))
  384. {
  385. $result = new \CSaleProxyAdminResult($parameters, $entityClass, $tableId); // being in admin and knowing table, do admin result api call
  386. }
  387. else
  388. {
  389. $result = new \CSaleProxyResult($parameters, $entityClass); // otherwise - public api call
  390. }
  391. if(!$showAll && $navigation !== false)
  392. {
  393. if($navigation === true)
  394. {
  395. $result->NavStart();
  396. }
  397. else
  398. {
  399. $result->NavStart($navigation);
  400. }
  401. }
  402. else
  403. {
  404. $result->NavStart();
  405. }
  406. // temporal fix
  407. $result->bShowAll = false;
  408. return $result;
  409. }
  410. public static function convertToArray($data)
  411. {
  412. if(!is_array($data))
  413. {
  414. $converted = array();
  415. foreach($data as $key => $value)
  416. $converted[$key] = $value;
  417. $data = $converted;
  418. }
  419. foreach($data as &$value)
  420. {
  421. if(is_string($value))
  422. $value = trim($value);
  423. }
  424. return $data;
  425. }
  426. // deprecated: not optimal
  427. public static function getIdsByFilter($listFilter)
  428. {
  429. $ids = array();
  430. $entityClass = static::getEntityClass();
  431. $res = $entityClass::getList(array(
  432. 'select' => array('ID'),
  433. 'filter' => is_array($listFilter) ? $listFilter : array()
  434. ));
  435. while($item = $res->fetch())
  436. {
  437. $ids[] = intval($item['ID']);
  438. }
  439. return $ids;
  440. }
  441. public static function getPrimaryFieldName()
  442. {
  443. $map = static::getEntityRoadMap();
  444. return $map['main']['primaryFieldName'] <> ''? $map['main']['primaryFieldName'] : 'ID';
  445. }
  446. // returns element name by it`s primary
  447. public static function getNameToDisplay($id)
  448. {
  449. if(!($id = intval($id)))
  450. return '';
  451. $entityClass = static::getEntityClass('main');
  452. $item = $entityClass::getById($id)->fetch();
  453. return $item['CODE'];
  454. }
  455. public static function getListUrl($parameters = array())
  456. {
  457. return self::getUrl(static::LIST_PAGE_URL, $parameters);
  458. }
  459. public static function getEditUrl($parameters = array())
  460. {
  461. return self::getUrl(static::EDIT_PAGE_URL, $parameters);
  462. }
  463. public static function getImportUrl()
  464. {
  465. return self::getUrl(static::IMPORT_PAGE_URL, array());
  466. }
  467. public static function getReindexUrl()
  468. {
  469. return self::getUrl(static::REINDEX_PAGE_URL, array());
  470. }
  471. public static function getMigrationUrl()
  472. {
  473. return self::getUrl(static::MIGRATION_PAGE_URL, array());
  474. }
  475. public static function getUrl($page, $parameters = array())
  476. {
  477. if(!is_array($parameters))
  478. $parameters = array();
  479. $parameters['lang'] = LANGUAGE_ID;
  480. $selfFolderUrl = (defined("SELF_FOLDER_URL") ? SELF_FOLDER_URL : "/bitrix/admin/");
  481. $packed = self::packUrlParameters($parameters);
  482. return $selfFolderUrl.$page.($packed <> ''? '?'.$packed : '');
  483. }
  484. #####################################
  485. #### Utilily methods for CRUD
  486. #####################################
  487. // deprecated: too strong relation with admin grid
  488. public static function checkUseFilter()
  489. {
  490. return $GLOBALS['filter'] == 'Y' && !$GLOBALS['del_filter'];
  491. }
  492. public static function readMap($entityRoadCode, $page = 'list')
  493. {
  494. $roads = static::getEntityRoadMap();
  495. $road = $roads[$entityRoadCode];
  496. if(!$road['name'])
  497. throw new Main\SystemException('Undefined entity name in entity map');
  498. if($page == '')
  499. $page = 'list';
  500. $flds = array();
  501. $class = $road['name'].'Table';
  502. $excluded = $road['pages'][$page]['excludedColumns'];
  503. $included = $road['pages'][$page]['includedColumns'];
  504. $map = $class::getMap();
  505. if(is_array($road['additional']) && !empty($road['additional']))
  506. $map = array_merge($map, $road['additional']);
  507. foreach($map as $fldCode => $fldDesc)
  508. {
  509. if((mb_strlen($fldDesc['title']) || $fldDesc['required'] || $fldDesc['primary'] || $fldCode == 'ID'))
  510. {
  511. if(is_array($excluded) && in_array($fldCode, $excluded))
  512. continue;
  513. if(is_array($included) && !in_array($fldCode, $included))
  514. continue;
  515. $fldDesc['title'] = $fldDesc['title'] <> ''? htmlspecialcharsbx($fldDesc['title']) : $fldCode;
  516. $fldDesc['ownerEntity'] = $road['name']; // map can be cumulative, from several entites, so we need to know who is an owner
  517. $flds[$fldCode] = $fldDesc;
  518. }
  519. }
  520. return $flds;
  521. }
  522. protected static function getFilterModifier($type)
  523. {
  524. return $type == 'string' ? '?' : '=';
  525. }
  526. protected static function packUrlParameters($parameters = array())
  527. {
  528. $params = array();
  529. foreach($parameters as $param => $value)
  530. {
  531. if($value <> '')
  532. {
  533. if(mb_strpos($param, '=') === 0)
  534. {
  535. // value goes as-is, unsafe
  536. $param = mb_substr($param, 1);
  537. }
  538. else
  539. {
  540. $value = urlencode($value);
  541. }
  542. $params[] = urlencode($param).'='.$value;
  543. }
  544. }
  545. return implode('&', $params);
  546. }
  547. protected static function getEntityClass($code = '')
  548. {
  549. $entityRoad = static::getEntityRoadMap();
  550. $entityName = $entityRoad[$code <> ''? $code : self::getEntityRoadCode()]['name'];
  551. if(!$entityName)
  552. throw new Main\SystemException('Undefined entity name in helper');
  553. return $entityName.'Table';
  554. }
  555. public static function getWidgetAppearance()
  556. {
  557. $appearance = Config\Option::get("sale", "sale_location_selector_appearance");
  558. if(!mb_strlen($appearance) || !in_array($appearance, array('search', 'steps')))
  559. return 'steps';
  560. return $appearance;
  561. }
  562. protected static function normalizeList($list, $expectNumeric = true)
  563. {
  564. $list = array_unique(array_values($list));
  565. foreach($list as $i => $id)
  566. {
  567. if($expectNumeric)
  568. {
  569. if(intval($id) != $id)
  570. unset($list[$i]);
  571. $list[$i] = intval($id);
  572. if(!$list[$i])
  573. unset($list[$i]);
  574. }
  575. else
  576. {
  577. if($list[$i] == '')
  578. unset($list[$i]);
  579. }
  580. }
  581. return $list;
  582. }
  583. // proxy between $_REQUEST and resulting array to save links between entites and locations
  584. public static function prepareLinksForSaving($connectorClass, $links)
  585. {
  586. $useIds = !$connectorClass::getUseCodes();
  587. $useGroups = $connectorClass::getUseGroups();
  588. $l = $connectorClass::DB_LOCATION_FLAG;
  589. $g = $connectorClass::DB_GROUP_FLAG;
  590. if(isset($links[$l]))
  591. {
  592. if(is_string($links[$l]))
  593. $links[$l] = explode(':', $links[$l]);
  594. }
  595. else
  596. $links[$l] = array();
  597. $links[$l] = self::normalizeList($links[$l], $useIds);
  598. if(!$useGroups)
  599. unset($links[$g]);
  600. else
  601. {
  602. if(isset($links[$g]))
  603. {
  604. if(is_string($links[$g]))
  605. $links[$g] = explode(':', $links[$g]);
  606. }
  607. else
  608. $links[$g] = array();
  609. $links[$g] = self::normalizeList($links[$g], $useIds);
  610. }
  611. return $links;
  612. }
  613. public static function resetLocationsForEntity($entityId, $locations, $entityName, $expectCodes = false)
  614. {
  615. $locList = array();
  616. if(is_array($locations) && !empty($locations))
  617. {
  618. foreach($locations as $loc)
  619. {
  620. if($loc['LOCATION_TYPE'] == 'L')
  621. $locList[Location\Connector::DB_LOCATION_FLAG][] = $loc['LOCATION_ID'];
  622. elseif($loc['LOCATION_TYPE'] == 'G')
  623. $locList[Location\Connector::DB_GROUP_FLAG][] = $loc['LOCATION_ID'];
  624. }
  625. }
  626. $entityClass = $entityName.'Table';
  627. try
  628. {
  629. if(!empty($locList) && !$expectCodes)
  630. {
  631. $locList[Location\Connector::DB_LOCATION_FLAG] = $entityClass::normalizeLocationList($locList[Location\Connector::DB_LOCATION_FLAG]);
  632. $gf = Location\Connector::DB_GROUP_FLAG;
  633. if(!empty($locList[$gf]))
  634. {
  635. $groupCodes = array();
  636. $locList[$gf] = array_flip($locList[$gf]);
  637. // here we must get codes by ids for groups. There will be no thousands of groups, so we can do the following:
  638. $res = Location\GroupTable::getList(array('select' => array('ID', 'CODE')));
  639. while($item = $res->fetch())
  640. {
  641. if(isset($locList[$gf][$item['ID']]))
  642. $groupCodes[$item['CODE']] = 1;
  643. }
  644. $locList[$gf] = array_keys($groupCodes);
  645. }
  646. }
  647. $entityClass::resetMultipleForOwner($entityId, $locList);
  648. }
  649. catch(Exception $e)
  650. {
  651. }
  652. }
  653. }