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

/dev/tools/migration/Acl/Generator.php

https://bitbucket.org/sunil_nextbits/magento2
PHP | 749 lines | 401 code | 83 blank | 265 comment | 35 complexity | a7e8c9079e3873214cf0290b70d966e5 MD5 | raw file
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Magento
  22. * @package tools
  23. * @copyright Copyright (c) 2012 X.commerce, Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. require_once ( __DIR__ . '/Menu/Generator.php');
  27. require_once ( __DIR__ . '/FileManager.php');
  28. class Tools_Migration_Acl_Generator
  29. {
  30. /**
  31. * @var bool
  32. */
  33. protected $_printHelp = false;
  34. /**
  35. * Meta node names
  36. *
  37. * @var array
  38. */
  39. protected $_metaNodeNames = array();
  40. /**
  41. * Adminhtml files
  42. *
  43. * @var array|null
  44. */
  45. protected $_adminhtmlFiles = null;
  46. /**
  47. * Parsed dom list
  48. *
  49. * @var array
  50. */
  51. protected $_parsedDomList = array();
  52. /**
  53. * Map ACL resource xpath to id
  54. * @var array
  55. */
  56. protected $_aclResourceMaps = array();
  57. /**
  58. * Map Menu ids
  59. *
  60. * @var array
  61. */
  62. protected $_menuIdMaps = array();
  63. /**
  64. * Base application path
  65. *
  66. * @var string|null
  67. */
  68. protected $_basePath = null;
  69. /**
  70. * Adminhtml DOMDocument list
  71. *
  72. * @var array
  73. */
  74. protected $_adminhtmlDomList = array();
  75. /**
  76. * @var string
  77. */
  78. protected $_artifactsPath;
  79. /**
  80. * Is preview mode
  81. *
  82. * @var bool
  83. */
  84. protected $_isPreviewMode = false;
  85. /**
  86. * List of unique ACL ids
  87. *
  88. * @var array
  89. */
  90. protected $_uniqueName = array();
  91. /**
  92. * @var Tools_Migration_Acl_Formatter
  93. */
  94. protected $_xmlFormatter;
  95. /**
  96. * @var Tools_Migration_Acl_FileManager
  97. */
  98. protected $_fileManager;
  99. /**
  100. * @param Tools_Migration_Acl_Formatter $xmlFormatter
  101. * @param Tools_Migration_Acl_FileManager $fileManager
  102. * @param array $options configuration options
  103. */
  104. public function __construct(
  105. Tools_Migration_Acl_Formatter $xmlFormatter,
  106. Tools_Migration_Acl_FileManager $fileManager,
  107. $options = array()
  108. ) {
  109. $this->_xmlFormatter = $xmlFormatter;
  110. $this->_fileManager = $fileManager;
  111. $this->_printHelp = array_key_exists('h', $options);
  112. $this->_isPreviewMode = array_key_exists('p', $options);
  113. $this->_metaNodeNames = array(
  114. 'sort_order' => 'sortOrder',
  115. 'title' => 'title'
  116. );
  117. $this->_basePath = realpath(dirname(__FILE__) . '/../../../..');
  118. $this->_artifactsPath = realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
  119. }
  120. /**
  121. * Get module name from file name
  122. *
  123. * @param $fileName string
  124. * @return string
  125. */
  126. public function getModuleName($fileName)
  127. {
  128. $parts = array_reverse(explode(DIRECTORY_SEPARATOR, $fileName));
  129. $module = $parts[3] . '_' . $parts[2];
  130. return $module;
  131. }
  132. /**
  133. * Get is forward node
  134. *
  135. * @param string $nodeName
  136. * @return bool
  137. */
  138. public function isForwardNode($nodeName)
  139. {
  140. return in_array($nodeName, $this->getForwardNodeNames());
  141. }
  142. /**
  143. * Get is meta-info node
  144. *
  145. * @param string $nodeName
  146. * @return bool
  147. */
  148. public function isMetaNode($nodeName)
  149. {
  150. return isset($this->_metaNodeNames[$nodeName]);
  151. }
  152. /**
  153. * @return array
  154. */
  155. public function getForwardNodeNames()
  156. {
  157. return array(
  158. 'children',
  159. );
  160. }
  161. /**
  162. * @param array $metaNodeNames
  163. */
  164. public function setMetaNodeNames($metaNodeNames)
  165. {
  166. $this->_metaNodeNames = $metaNodeNames;
  167. }
  168. /**
  169. * @return array
  170. */
  171. public function getMetaNodeNames()
  172. {
  173. return $this->_metaNodeNames;
  174. }
  175. /**
  176. * Get is valid node type
  177. *
  178. * @param int $nodeType
  179. * @return bool
  180. */
  181. public function isValidNodeType($nodeType)
  182. {
  183. return in_array($nodeType, $this->getValidNodeTypes());
  184. }
  185. /**
  186. * Get valid node types
  187. *
  188. * @return array
  189. */
  190. public function getValidNodeTypes()
  191. {
  192. return array(
  193. 1, //DOMElement
  194. );
  195. }
  196. /**
  197. * Get etc directory pattern
  198. *
  199. * @param string $codePool
  200. * @param string $namespace
  201. * @return string
  202. */
  203. public function getEtcDirPattern($codePool = '*', $namespace = '*')
  204. {
  205. return $this->getBasePath() . DIRECTORY_SEPARATOR
  206. . 'app' . DIRECTORY_SEPARATOR
  207. . 'code' . DIRECTORY_SEPARATOR
  208. . $codePool . DIRECTORY_SEPARATOR //code pool
  209. . $namespace . DIRECTORY_SEPARATOR //namespace
  210. . '*' . DIRECTORY_SEPARATOR //module name
  211. . 'etc' . DIRECTORY_SEPARATOR;
  212. }
  213. /**
  214. * @param string $basePath
  215. */
  216. public function setBasePath($basePath)
  217. {
  218. $this->_basePath = $basePath;
  219. }
  220. /**
  221. * @return string
  222. */
  223. public function getBasePath()
  224. {
  225. return $this->_basePath;
  226. }
  227. /**
  228. * Create node
  229. *
  230. * @param DOMDocument $resultDom
  231. * @param string $nodeName
  232. * @param DOMNode $parent
  233. * @param string $moduleName
  234. *
  235. * @return DOMNode
  236. */
  237. public function createNode(DOMDocument $resultDom, $nodeName, DOMNode $parent, $moduleName)
  238. {
  239. $newNode = $resultDom->createElement('resource');
  240. $xpath = $parent->getAttribute('xpath');
  241. $newNode->setAttribute('xpath', $xpath . '/' . $nodeName);
  242. $parent->appendChild($newNode);
  243. $newNode->setAttribute('id', $this->generateId($newNode, $xpath, $nodeName));
  244. if ($moduleName) {
  245. $newNode->setAttribute('module', $moduleName);
  246. }
  247. return $newNode;
  248. }
  249. /**
  250. * Generate unique id for ACL item
  251. *
  252. * @param DOMNode $node
  253. * @param $xpath string
  254. * @param $resourceId string
  255. * @return mixed
  256. */
  257. public function generateId(DOMNode $node, $xpath, $resourceId)
  258. {
  259. if (isset($this->_uniqueName[$resourceId]) && $this->_uniqueName[$resourceId] != $xpath) {
  260. $parts = explode('/', $node->parentNode->getAttribute('xpath'));
  261. $suffix = end($parts);
  262. $resourceId = $this->generateId($node->parentNode, $xpath, $suffix . '_' . $resourceId);
  263. }
  264. $this->_uniqueName[$resourceId] = $xpath;
  265. return $resourceId;
  266. }
  267. /**
  268. * Set meta node
  269. *
  270. * @param DOMNode $node
  271. * @param DOMNode $dataNode
  272. * @param string $module
  273. */
  274. public function setMetaInfo(DOMNode $node, DOMNode $dataNode, $module)
  275. {
  276. $node->setAttribute($this->_metaNodeNames[$dataNode->nodeName], $dataNode->nodeValue);
  277. if ($dataNode->nodeName == 'title') {
  278. $node->setAttribute('moduleOwner', $module);
  279. $resourceId = $node->getAttribute('moduleOwner') . '::' . $node->getAttribute('id');
  280. $xpath = $node->getAttribute('xpath');
  281. $this->_aclResourceMaps[$xpath] = $resourceId;
  282. }
  283. }
  284. /**
  285. * @return array
  286. */
  287. public function getAclResourceMaps()
  288. {
  289. return $this->_aclResourceMaps;
  290. }
  291. /**
  292. * @return array
  293. */
  294. public function getAdminhtmlFiles()
  295. {
  296. if (null === $this->_adminhtmlFiles) {
  297. $localFiles = glob($this->getEtcDirPattern('local') . 'adminhtml.xml');
  298. $communityFiles = glob($this->getEtcDirPattern('community') . 'adminhtml.xml');
  299. $coreFiles = glob($this->getEtcDirPattern('core') . 'adminhtml.xml');
  300. $this->_adminhtmlFiles = array_merge($localFiles, $communityFiles, $coreFiles);
  301. }
  302. return $this->_adminhtmlFiles;
  303. }
  304. /**
  305. * @param array $adminhtmlFiles
  306. */
  307. public function setAdminhtmlFiles($adminhtmlFiles)
  308. {
  309. $this->_adminhtmlFiles = $adminhtmlFiles;
  310. }
  311. /**
  312. * @return array
  313. */
  314. public function getParsedDomList()
  315. {
  316. return $this->_parsedDomList;
  317. }
  318. /**
  319. * Parse node
  320. *
  321. * @param DOMNode $node - data source
  322. * @param DOMDocument $dom - result DOMDocument
  323. * @param DOMNode $parentNode - parent node from result document
  324. * @param $moduleName
  325. */
  326. public function parseNode(DOMNode $node, DOMDocument $dom, DOMNode $parentNode, $moduleName)
  327. {
  328. if ($this->isRestrictedNode($node->nodeName)) {
  329. return;
  330. }
  331. foreach ($node->childNodes as $item) {
  332. if (false == $this->isValidNodeType($item->nodeType) || $this->isRestrictedNode($item->nodeName)) {
  333. continue;
  334. }
  335. if ($this->isForwardNode($item->nodeName)) {
  336. $this->parseNode($item, $dom, $parentNode, $moduleName);
  337. } elseif ($this->isMetaNode($item->nodeName)) {
  338. $this->setMetaInfo($parentNode, $item, $moduleName);
  339. } else {
  340. $newNode = $this->createNode($dom, $item->nodeName, $parentNode, $item->getAttribute('module'));
  341. if ($item->childNodes->length > 0) {
  342. $this->parseNode($item, $dom, $newNode, $moduleName);
  343. }
  344. }
  345. }
  346. }
  347. /**
  348. * Check if node is restricted
  349. *
  350. * @param $nodeName string
  351. * @return bool
  352. */
  353. public function isRestrictedNode($nodeName)
  354. {
  355. return in_array($nodeName, $this->getRestrictedNodeNames());
  356. }
  357. /**
  358. * Print help message
  359. */
  360. public function printHelpMessage()
  361. {
  362. $output = './acl.php -- [-hp]' . PHP_EOL;
  363. $output .= 'additional parameters:' . PHP_EOL;
  364. $output .= ' -h print usage' . PHP_EOL;
  365. $output .= ' -p preview result' . PHP_EOL;
  366. echo $output;
  367. }
  368. /**
  369. * Get template for result DOMDocument
  370. *
  371. * @return DOMDocument
  372. */
  373. public function getResultDomDocument()
  374. {
  375. $resultDom = new DOMDocument();
  376. $resultDom->formatOutput = true;
  377. $config = $resultDom->createElement('config');
  378. $resultDom->appendChild($config);
  379. $acl = $resultDom->createElement('acl');
  380. $config->appendChild($acl);
  381. $parent = $resultDom->createElement('resources');
  382. $parent->setAttribute('xpath', 'config/acl/resources');
  383. $acl->appendChild($parent);
  384. return $resultDom;
  385. }
  386. /**
  387. * Parse adminhtml.xml files
  388. */
  389. public function parseAdminhtmlFiles()
  390. {
  391. foreach ($this->getAdminhtmlFiles() as $file) {
  392. $module = $this->getModuleName($file);
  393. $resultDom = $this->getResultDomDocument();
  394. $adminhtmlDom = new DOMDocument();
  395. $adminhtmlDom->load($file);
  396. $this->_adminhtmlDomList[$file] = $adminhtmlDom;
  397. $xpath = new DOMXPath($adminhtmlDom);
  398. $resourcesList = $xpath->query('//config/acl/*');
  399. /** @var $aclNode DOMNode **/
  400. foreach ($resourcesList as $aclNode) {
  401. $this->parseNode($aclNode, $resultDom, $resultDom->getElementsByTagName('resources')->item(0), $module);
  402. }
  403. $this->_parsedDomList[$file] = $resultDom;
  404. }
  405. }
  406. /**
  407. * Update ACL resource id
  408. */
  409. public function updateAclResourceIds()
  410. {
  411. /** @var $dom DOMDocument **/
  412. foreach ($this->_parsedDomList as $dom) {
  413. $list = $dom->getElementsByTagName('resources');
  414. /** @var $node DOMNode **/
  415. foreach ($list as $node) {
  416. $node->removeAttribute('xpath');
  417. if ($node->childNodes->length > 0) {
  418. $this->updateChildAclNodes($node);
  419. }
  420. }
  421. }
  422. }
  423. /**
  424. * @param $node DOMNode
  425. */
  426. public function updateChildAclNodes($node)
  427. {
  428. /** @var $item DOMNode **/
  429. foreach ($node->childNodes as $item) {
  430. if (false == $this->isValidNodeType($item->nodeType)) {
  431. continue;
  432. }
  433. $xpath = $item->getAttribute('xpath');
  434. $resourceId = $item->getAttribute('moduleOwner') . '::' . $item->getAttribute('id');
  435. if (isset($this->_aclResourceMaps[$xpath])) {
  436. $resourceId = $this->_aclResourceMaps[$xpath];
  437. }
  438. $item->setAttribute('id', $resourceId);
  439. $item->removeAttribute('xpath');
  440. $item->removeAttribute('moduleOwner');
  441. if ($item->childNodes->length > 0) {
  442. $this->updateChildAclNodes($item);
  443. }
  444. }
  445. }
  446. /**
  447. * @param array $aclResourceMaps
  448. */
  449. public function setAclResourceMaps($aclResourceMaps)
  450. {
  451. $this->_aclResourceMaps = $aclResourceMaps;
  452. }
  453. /**
  454. * Save ACL files
  455. *
  456. * @throws Exception if tidy extension is not installed
  457. */
  458. public function saveAclFiles()
  459. {
  460. if ($this->_isPreviewMode) {
  461. return;
  462. }
  463. /** @var $dom DOMDocument **/
  464. foreach ($this->_parsedDomList as $originFile => $dom) {
  465. $file = str_replace('adminhtml.xml', 'adminhtml' . DIRECTORY_SEPARATOR . 'acl.xml', $originFile);
  466. $dom->preserveWhiteSpace = false;
  467. $dom->formatOutput = true;
  468. $output = $this->_xmlFormatter->parseString($dom->saveXml(), array(
  469. 'indent' => true,
  470. 'input-xml' => true,
  471. 'output-xml' => true,
  472. 'add-xml-space' => false,
  473. 'indent-spaces' => 4,
  474. 'wrap' => 1000
  475. ));
  476. $this->_fileManager->write($file, $output);
  477. }
  478. }
  479. /**
  480. * @param array $parsedDomList
  481. */
  482. public function setParsedDomList($parsedDomList)
  483. {
  484. $this->_parsedDomList = $parsedDomList;
  485. }
  486. /**
  487. * @param array $adminhtmlDomList
  488. */
  489. public function setAdminhtmlDomList($adminhtmlDomList)
  490. {
  491. $this->_adminhtmlDomList = $adminhtmlDomList;
  492. }
  493. /**
  494. * @return array
  495. */
  496. public function getAdminhtmlDomList()
  497. {
  498. return $this->_adminhtmlDomList;
  499. }
  500. /**
  501. * Remove empty files
  502. */
  503. public function removeAdminhtmlFiles()
  504. {
  505. $output = array(
  506. 'removed' => array(),
  507. 'not_removed' => array(),
  508. );
  509. /** @var $dom DOMDocument **/
  510. foreach ($this->_adminhtmlDomList as $file => $dom) {
  511. $xpath = new DOMXpath($dom);
  512. $nodeList = $xpath->query('/config/acl');
  513. if ($nodeList->length == 0) {
  514. continue;
  515. }
  516. $acl = $nodeList->item(0);
  517. $countNodes = $acl->childNodes->length - 1;
  518. for ($i = $countNodes; $i >= 0 ; $i--) {
  519. $node = $acl->childNodes->item($i);
  520. if (in_array($node->nodeName, $this->getNodeToRemove())) {
  521. $acl->removeChild($node);
  522. }
  523. }
  524. if ($this->isNodeEmpty($acl)) {
  525. if (false == $this->_isPreviewMode) {
  526. $this->_fileManager->remove($file);
  527. }
  528. $output['removed'][] = $file;
  529. } else {
  530. $output['not_removed'][] = $file;
  531. }
  532. }
  533. $output['artifacts']['AclXPathToAclId.log'] = json_encode($this->_aclResourceMaps);
  534. return $output;
  535. }
  536. /**
  537. * Check if node is empty
  538. *
  539. * @param DOMNode $node
  540. * @return bool
  541. */
  542. public function isNodeEmpty(DOMNode $node)
  543. {
  544. $output = true;
  545. foreach ($node->childNodes as $item) {
  546. if ($this->isValidNodeType($item->nodeType)) {
  547. $output = false;
  548. break;
  549. }
  550. }
  551. return $output;
  552. }
  553. /**
  554. * @param string $artifactsPath
  555. */
  556. public function setArtifactsPath($artifactsPath)
  557. {
  558. $this->_artifactsPath = $artifactsPath;
  559. }
  560. /**
  561. * Run migration process
  562. */
  563. public function run()
  564. {
  565. if ($this->_printHelp) {
  566. $this->printHelpMessage();
  567. return;
  568. }
  569. $this->parseAdminhtmlFiles();
  570. $this->updateAclResourceIds();
  571. $this->saveAclFiles();
  572. $result = $this->removeAdminhtmlFiles();
  573. $menuResult = $this->processMenu();
  574. $artifacts = array_merge($result['artifacts'], $menuResult['artifacts']);
  575. $this->saveArtifacts($artifacts);
  576. $this->printStatistic($result, $menuResult, $artifacts);
  577. }
  578. /**
  579. * Print statistic
  580. *
  581. * @param $result
  582. * @param $menuResult
  583. * @param $artifacts
  584. */
  585. public function printStatistic($result, $menuResult, $artifacts)
  586. {
  587. $output = PHP_EOL;
  588. if (true == $this->_isPreviewMode) {
  589. $output .= '!!! PREVIEW MODE. ORIGIN DATA NOT CHANGED!!!' . PHP_EOL;
  590. }
  591. $output .= PHP_EOL;
  592. $output .= 'Removed adminhtml.xml: ' . count($result['removed']) . ' files ' . PHP_EOL;
  593. $output .= 'Not Removed adminhtml.xml: ' . count($result['not_removed']) . ' files ' . PHP_EOL;
  594. if (count($result['not_removed'])) {
  595. foreach ($result['not_removed'] as $fileName) {
  596. $output .= ' - ' . $fileName . PHP_EOL;
  597. }
  598. }
  599. $output .= PHP_EOL;
  600. $output .= 'Mapped Menu Items: ' . count($menuResult['mapped']) . PHP_EOL;
  601. $output .= 'Not Mapped Menu Items: ' .count($menuResult['not_mapped']) . PHP_EOL;
  602. if (count($menuResult['not_mapped'])) {
  603. foreach ($menuResult['not_mapped'] as $menuId) {
  604. $output .= ' - ' . $menuId . PHP_EOL;
  605. }
  606. }
  607. $output .= 'Menu Update Errors: ' .count($menuResult['menu_update_errors']) . PHP_EOL;
  608. if (count($menuResult['menu_update_errors'])) {
  609. foreach ($menuResult['menu_update_errors'] as $errorText) {
  610. $output .= ' - ' . $errorText . PHP_EOL;
  611. }
  612. }
  613. $output .= PHP_EOL;
  614. $output .= 'Artifacts: ' . PHP_EOL;
  615. foreach (array_keys($artifacts) as $file) {
  616. $output .= ' - ' . $this->_artifactsPath . $file . PHP_EOL;
  617. }
  618. echo $output;
  619. }
  620. /**
  621. * Save artifacts files
  622. *
  623. * @param $artifacts
  624. */
  625. public function saveArtifacts($artifacts)
  626. {
  627. foreach ($artifacts as $file => $data) {
  628. $this->_fileManager->write($this->_artifactsPath . $file, $data);
  629. }
  630. }
  631. /**
  632. * Run process of menu updating
  633. *
  634. * @return array
  635. */
  636. public function processMenu()
  637. {
  638. $menu = new Tools_Migration_Acl_Menu_Generator(
  639. $this->getBasePath(),
  640. $this->getValidNodeTypes(),
  641. $this->_aclResourceMaps,
  642. $this->_fileManager,
  643. $this->_isPreviewMode
  644. );
  645. return $menu->run();
  646. }
  647. /**
  648. * @return array
  649. */
  650. public function getRestrictedNodeNames()
  651. {
  652. return array(
  653. 'privilegeSets',
  654. );
  655. }
  656. /**
  657. * @return array
  658. */
  659. public function getNodeToRemove()
  660. {
  661. return array(
  662. 'resources',
  663. 'privilegeSets',
  664. );
  665. }
  666. }