Widget.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Widget\Model;
  7. /**
  8. * Widget model for different purposes
  9. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  10. *
  11. * @api
  12. * @since 100.0.2
  13. */
  14. class Widget
  15. {
  16. /**
  17. * @var \Magento\Widget\Model\Config\Data
  18. */
  19. protected $dataStorage;
  20. /**
  21. * @var \Magento\Framework\App\Cache\Type\Config
  22. */
  23. protected $configCacheType;
  24. /**
  25. * @var \Magento\Framework\View\Asset\Repository
  26. */
  27. protected $assetRepo;
  28. /**
  29. * @var \Magento\Framework\View\Asset\Source
  30. */
  31. protected $assetSource;
  32. /**
  33. * @var \Magento\Framework\View\FileSystem
  34. */
  35. protected $viewFileSystem;
  36. /**
  37. * @var \Magento\Framework\Escaper
  38. */
  39. protected $escaper;
  40. /**
  41. * @var array
  42. */
  43. protected $widgetsArray = [];
  44. /**
  45. * @var \Magento\Widget\Helper\Conditions
  46. */
  47. protected $conditionsHelper;
  48. /**
  49. * @var \Magento\Framework\Math\Random
  50. */
  51. private $mathRandom;
  52. /**
  53. * @param \Magento\Framework\Escaper $escaper
  54. * @param \Magento\Widget\Model\Config\Data $dataStorage
  55. * @param \Magento\Framework\View\Asset\Repository $assetRepo
  56. * @param \Magento\Framework\View\Asset\Source $assetSource
  57. * @param \Magento\Framework\View\FileSystem $viewFileSystem
  58. * @param \Magento\Widget\Helper\Conditions $conditionsHelper
  59. */
  60. public function __construct(
  61. \Magento\Framework\Escaper $escaper,
  62. \Magento\Widget\Model\Config\Data $dataStorage,
  63. \Magento\Framework\View\Asset\Repository $assetRepo,
  64. \Magento\Framework\View\Asset\Source $assetSource,
  65. \Magento\Framework\View\FileSystem $viewFileSystem,
  66. \Magento\Widget\Helper\Conditions $conditionsHelper
  67. ) {
  68. $this->escaper = $escaper;
  69. $this->dataStorage = $dataStorage;
  70. $this->assetRepo = $assetRepo;
  71. $this->assetSource = $assetSource;
  72. $this->viewFileSystem = $viewFileSystem;
  73. $this->conditionsHelper = $conditionsHelper;
  74. }
  75. /**
  76. * Get math random
  77. *
  78. * @return \Magento\Framework\Math\Random
  79. *
  80. * @deprecated 100.0.10
  81. */
  82. private function getMathRandom()
  83. {
  84. if ($this->mathRandom === null) {
  85. $this->mathRandom = \Magento\Framework\App\ObjectManager::getInstance()
  86. ->get(\Magento\Framework\Math\Random::class);
  87. }
  88. return $this->mathRandom;
  89. }
  90. /**
  91. * Return widget config based on its class type
  92. *
  93. * @param string $type Widget type
  94. * @return null|array
  95. */
  96. public function getWidgetByClassType($type)
  97. {
  98. $widgets = $this->getWidgets();
  99. /** @var array $widget */
  100. foreach ($widgets as $widget) {
  101. if (isset($widget['@'])) {
  102. if (isset($widget['@']['type'])) {
  103. if ($type === $widget['@']['type']) {
  104. return $widget;
  105. }
  106. }
  107. }
  108. }
  109. return null;
  110. }
  111. /**
  112. * Return widget XML configuration as \Magento\Framework\DataObject and makes some data preparations
  113. *
  114. * @param string $type Widget type
  115. * @return null|\Magento\Framework\Simplexml\Element
  116. *
  117. * @deprecated 101.0.0
  118. */
  119. public function getConfigAsXml($type)
  120. {
  121. return $this->getXmlElementByType($type);
  122. }
  123. /**
  124. * Return widget XML configuration as \Magento\Framework\DataObject and makes some data preparations
  125. *
  126. * @param string $type Widget type
  127. * @return \Magento\Framework\DataObject
  128. */
  129. public function getConfigAsObject($type)
  130. {
  131. $widget = $this->getWidgetByClassType($type);
  132. $object = new \Magento\Framework\DataObject();
  133. if ($widget === null) {
  134. return $object;
  135. }
  136. $widget = $this->getAsCanonicalArray($widget);
  137. // Save all nodes to object data
  138. $object->setData($widget);
  139. $object->setType($type);
  140. // Correct widget parameters and convert its data to objects
  141. $newParams = $this->prepareWidgetParameters($object);
  142. $object->setData('parameters', $newParams);
  143. return $object;
  144. }
  145. /**
  146. * Prepare widget parameters
  147. *
  148. * @param \Magento\Framework\DataObject $object
  149. * @return array
  150. */
  151. protected function prepareWidgetParameters(\Magento\Framework\DataObject $object)
  152. {
  153. $params = $object->getData('parameters');
  154. $newParams = [];
  155. if (is_array($params)) {
  156. $sortOrder = 0;
  157. foreach ($params as $key => $data) {
  158. if (is_array($data)) {
  159. $data = $this->prepareDropDownValues($data, $key, $sortOrder);
  160. $data = $this->prepareHelperBlock($data);
  161. $newParams[$key] = new \Magento\Framework\DataObject($data);
  162. $sortOrder++;
  163. }
  164. }
  165. }
  166. uasort($newParams, [$this, 'sortParameters']);
  167. return $newParams;
  168. }
  169. /**
  170. * Prepare drop-down values
  171. *
  172. * @param array $data
  173. * @param string $key
  174. * @param int $sortOrder
  175. * @return array
  176. */
  177. protected function prepareDropDownValues(array $data, $key, $sortOrder)
  178. {
  179. $data['key'] = $key;
  180. $data['sort_order'] = isset($data['sort_order']) ? (int)$data['sort_order'] : $sortOrder;
  181. $values = [];
  182. if (isset($data['values']) && is_array($data['values'])) {
  183. foreach ($data['values'] as $value) {
  184. if (isset($value['label']) && isset($value['value'])) {
  185. $values[] = $value;
  186. }
  187. }
  188. }
  189. $data['values'] = $values;
  190. return $data;
  191. }
  192. /**
  193. * Prepare helper block
  194. *
  195. * @param array $data
  196. * @return array
  197. */
  198. protected function prepareHelperBlock(array $data)
  199. {
  200. if (isset($data['helper_block'])) {
  201. $helper = new \Magento\Framework\DataObject();
  202. if (isset($data['helper_block']['data']) && is_array($data['helper_block']['data'])) {
  203. $helper->addData($data['helper_block']['data']);
  204. }
  205. if (isset($data['helper_block']['type'])) {
  206. $helper->setType($data['helper_block']['type']);
  207. }
  208. $data['helper_block'] = $helper;
  209. }
  210. return $data;
  211. }
  212. /**
  213. * Return filtered list of widgets
  214. *
  215. * @param array $filters Key-value array of filters for widget node properties
  216. * @return array
  217. */
  218. public function getWidgets($filters = [])
  219. {
  220. $widgets = $this->dataStorage->get();
  221. $result = $widgets;
  222. // filter widgets by params
  223. if (is_array($filters) && count($filters) > 0) {
  224. foreach ($widgets as $code => $widget) {
  225. try {
  226. foreach ($filters as $field => $value) {
  227. if (!isset($widget[$field]) || (string)$widget[$field] != $value) {
  228. throw new \Exception();
  229. }
  230. }
  231. } catch (\Exception $e) {
  232. unset($result[$code]);
  233. continue;
  234. }
  235. }
  236. }
  237. return $result;
  238. }
  239. /**
  240. * Return list of widgets as array
  241. *
  242. * @param array $filters Key-value array of filters for widget node properties
  243. * @return array
  244. */
  245. public function getWidgetsArray($filters = [])
  246. {
  247. if (empty($this->widgetsArray)) {
  248. $result = [];
  249. foreach ($this->getWidgets($filters) as $code => $widget) {
  250. $result[$widget['name']] = [
  251. 'name' => __((string)$widget['name']),
  252. 'code' => $code,
  253. 'type' => $widget['@']['type'],
  254. 'description' => __((string)$widget['description']),
  255. ];
  256. }
  257. usort($result, [$this, "sortWidgets"]);
  258. $this->widgetsArray = $result;
  259. }
  260. return $this->widgetsArray;
  261. }
  262. /**
  263. * Return widget presentation code in WYSIWYG editor
  264. *
  265. * @param string $type Widget Type
  266. * @param array $params Pre-configured Widget Params
  267. * @param bool $asIs Return result as widget directive(true) or as placeholder image(false)
  268. * @return string Widget directive ready to parse
  269. */
  270. public function getWidgetDeclaration($type, $params = [], $asIs = true)
  271. {
  272. $directive = '{{widget type="' . $type . '"';
  273. $widget = $this->getConfigAsObject($type);
  274. foreach ($params as $name => $value) {
  275. // Retrieve default option value if pre-configured
  276. if ($name == 'conditions') {
  277. $name = 'conditions_encoded';
  278. $value = $this->conditionsHelper->encode($value);
  279. } elseif (is_array($value)) {
  280. $value = implode(',', $value);
  281. } elseif (trim($value) == '') {
  282. $parameters = $widget->getParameters();
  283. if (isset($parameters[$name]) && is_object($parameters[$name])) {
  284. $value = $parameters[$name]->getValue();
  285. }
  286. }
  287. if (isset($value)) {
  288. $directive .= sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false));
  289. }
  290. }
  291. $directive .= $this->getWidgetPageVarName($params);
  292. $directive .= sprintf(' type_name="%s"', $widget['name']);
  293. $directive .= '}}';
  294. if ($asIs) {
  295. return $directive;
  296. }
  297. $html = sprintf(
  298. '<img id="%s" src="%s" title="%s">',
  299. $this->idEncode($directive),
  300. $this->getPlaceholderImageUrl($type),
  301. $this->escaper->escapeUrl($directive)
  302. );
  303. return $html;
  304. }
  305. /**
  306. * Get widget page varname
  307. *
  308. * @param array $params
  309. * @return string
  310. * @throws \Magento\Framework\Exception\LocalizedException
  311. */
  312. private function getWidgetPageVarName($params = [])
  313. {
  314. $pageVarName = '';
  315. if (array_key_exists('show_pager', $params) && (bool)$params['show_pager']) {
  316. $pageVarName = sprintf(
  317. ' %s="%s"',
  318. 'page_var_name',
  319. 'p' . $this->getMathRandom()->getRandomString(5, \Magento\Framework\Math\Random::CHARS_LOWERS)
  320. );
  321. }
  322. return $pageVarName;
  323. }
  324. /**
  325. * Get image URL of WYSIWYG placeholder image
  326. *
  327. * @param string $type
  328. * @return string
  329. */
  330. public function getPlaceholderImageUrl($type)
  331. {
  332. $placeholder = false;
  333. $widget = $this->getWidgetByClassType($type);
  334. if (is_array($widget) && isset($widget['placeholder_image'])) {
  335. $placeholder = (string)$widget['placeholder_image'];
  336. }
  337. if ($placeholder) {
  338. $asset = $this->assetRepo->createAsset($placeholder);
  339. $placeholder = $this->assetSource->getFile($asset);
  340. if ($placeholder) {
  341. return $asset->getUrl();
  342. }
  343. }
  344. return $this->assetRepo->getUrl('Magento_Widget::placeholder.png');
  345. }
  346. /**
  347. * Get a list of URLs of WYSIWYG placeholder images
  348. *
  349. * Returns array(<type> => <url>)
  350. *
  351. * @return array
  352. */
  353. public function getPlaceholderImageUrls()
  354. {
  355. $result = [];
  356. $widgets = $this->getWidgets();
  357. /** @var array $widget */
  358. foreach ($widgets as $widget) {
  359. if (isset($widget['@'])) {
  360. if (isset($widget['@']['type'])) {
  361. $type = $widget['@']['type'];
  362. $result[$type] = $this->getPlaceholderImageUrl($type);
  363. }
  364. }
  365. }
  366. return $result;
  367. }
  368. /**
  369. * Remove attributes from widget array and emulate work of \Magento\Framework\Simplexml\Element::asCanonicalArray
  370. *
  371. * @param array $inputArray
  372. * @return array
  373. */
  374. protected function getAsCanonicalArray($inputArray)
  375. {
  376. if (array_key_exists('@', $inputArray)) {
  377. unset($inputArray['@']);
  378. }
  379. foreach ($inputArray as $key => $value) {
  380. if (!is_array($value)) {
  381. continue;
  382. }
  383. $inputArray[$key] = $this->getAsCanonicalArray($value);
  384. }
  385. return $inputArray;
  386. }
  387. /**
  388. * Encode string to valid HTML id element, based on base64 encoding
  389. *
  390. * @param string $string
  391. * @return string
  392. */
  393. protected function idEncode($string)
  394. {
  395. return strtr(base64_encode($string), '+/=', ':_-');
  396. }
  397. /**
  398. * User-defined widgets sorting by Name
  399. *
  400. * @param array $firstElement
  401. * @param array $secondElement
  402. * @return bool
  403. */
  404. protected function sortWidgets($firstElement, $secondElement)
  405. {
  406. return strcmp($firstElement["name"], $secondElement["name"]);
  407. }
  408. /**
  409. * Widget parameters sort callback
  410. *
  411. * @param \Magento\Framework\DataObject $firstElement
  412. * @param \Magento\Framework\DataObject $secondElement
  413. * @return int
  414. */
  415. protected function sortParameters($firstElement, $secondElement)
  416. {
  417. $aOrder = (int)$firstElement->getData('sort_order');
  418. $bOrder = (int)$secondElement->getData('sort_order');
  419. return $aOrder < $bOrder ? -1 : ($aOrder > $bOrder ? 1 : 0);
  420. }
  421. }