vendor/pimcore/pimcore/models/Document/Editable/Link.php line 26

Open in your IDE?
  1. <?php
  2. /**
  3. * Pimcore
  4. *
  5. * This source file is available under two different licenses:
  6. * - GNU General Public License version 3 (GPLv3)
  7. * - Pimcore Commercial License (PCL)
  8. * Full copyright and license information is available in
  9. * LICENSE.md which is distributed with this source code.
  10. *
  11. * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12. * @license http://www.pimcore.org/license GPLv3 and PCL
  13. */
  14. namespace Pimcore\Model\Document\Editable;
  15. use Pimcore\Logger;
  16. use Pimcore\Model;
  17. use Pimcore\Model\Asset;
  18. use Pimcore\Model\Document;
  19. /**
  20. * @method \Pimcore\Model\Document\Editable\Dao getDao()
  21. */
  22. class Link extends Model\Document\Editable implements IdRewriterInterface, EditmodeDataInterface
  23. {
  24. /**
  25. * Contains the data for the link
  26. *
  27. * @internal
  28. *
  29. * @var array|null
  30. */
  31. protected $data;
  32. /**
  33. * {@inheritdoc}
  34. */
  35. public function getType()
  36. {
  37. return 'link';
  38. }
  39. /**
  40. * {@inheritdoc}
  41. */
  42. public function getData()
  43. {
  44. // update path if internal link
  45. $this->updatePathFromInternal(true);
  46. return $this->data;
  47. }
  48. /**
  49. * {@inheritdoc}
  50. */
  51. public function getDataEditmode() /** : mixed */
  52. {
  53. // update path if internal link
  54. $this->updatePathFromInternal(true, true);
  55. return $this->data;
  56. }
  57. /**
  58. * {@inheritdoc}
  59. */
  60. protected function getEditmodeElementClasses($options = []): array
  61. {
  62. // we don't want the class attribute being applied to the editable container element (<div>, only to the <a> tag inside
  63. // the default behavior of the parent method is to include the "class" attribute
  64. $classes = [
  65. 'pimcore_editable',
  66. 'pimcore_editable_' . $this->getType(),
  67. ];
  68. return $classes;
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function frontend()
  74. {
  75. $url = $this->getHref();
  76. if (strlen($url) > 0) {
  77. if (!is_array($this->config)) {
  78. $this->config = [];
  79. }
  80. $prefix = '';
  81. $suffix = '';
  82. $noText = false;
  83. if (array_key_exists('textPrefix', $this->config)) {
  84. $prefix = $this->config['textPrefix'];
  85. unset($this->config['textPrefix']);
  86. }
  87. if (array_key_exists('textSuffix', $this->config)) {
  88. $suffix = $this->config['textSuffix'];
  89. unset($this->config['textSuffix']);
  90. }
  91. if (isset($this->config['noText']) && $this->config['noText'] == true) {
  92. $noText = true;
  93. unset($this->config['noText']);
  94. }
  95. // add attributes to link
  96. $allowedAttributes = [
  97. 'charset',
  98. 'coords',
  99. 'hreflang',
  100. 'name',
  101. 'rel',
  102. 'rev',
  103. 'shape',
  104. 'target',
  105. 'accesskey',
  106. 'class',
  107. 'dir',
  108. 'draggable',
  109. 'dropzone',
  110. 'contextmenu',
  111. 'id',
  112. 'lang',
  113. 'style',
  114. 'tabindex',
  115. 'title',
  116. 'media',
  117. 'download',
  118. 'ping',
  119. 'type',
  120. 'referrerpolicy',
  121. 'xml:lang',
  122. 'onblur',
  123. 'onclick',
  124. 'ondblclick',
  125. 'onfocus',
  126. 'onmousedown',
  127. 'onmousemove',
  128. 'onmouseout',
  129. 'onmouseover',
  130. 'onmouseup',
  131. 'onkeydown',
  132. 'onkeypress',
  133. 'onkeyup',
  134. ];
  135. $defaultAttributes = [];
  136. if (!is_array($this->data)) {
  137. $this->data = [];
  138. }
  139. $availableAttribs = array_merge($defaultAttributes, $this->data, $this->config);
  140. // add attributes to link
  141. $attribs = [];
  142. foreach ($availableAttribs as $key => $value) {
  143. if ((is_string($value) || is_numeric($value))
  144. && (strpos($key, 'data-') === 0 ||
  145. strpos($key, 'aria-') === 0 ||
  146. in_array($key, $allowedAttributes))) {
  147. if (!empty($this->data[$key]) && !empty($this->config[$key])) {
  148. $attribs[] = $key.'="'. $this->data[$key] .' '. $this->config[$key] .'"';
  149. } elseif (!empty($value)) {
  150. $attribs[] = $key.'="'.$value.'"';
  151. }
  152. }
  153. }
  154. $attribs = array_unique($attribs);
  155. if (array_key_exists('attributes', $this->data) && !empty($this->data['attributes'])) {
  156. $attribs[] = $this->data['attributes'];
  157. }
  158. return '<a href="'.$url.'" '.implode(' ', $attribs).'>' . $prefix . ($noText ? '' : htmlspecialchars($this->data['text'])) . $suffix . '</a>';
  159. }
  160. return '';
  161. }
  162. /**
  163. * {@inheritdoc}
  164. */
  165. public function checkValidity()
  166. {
  167. $sane = true;
  168. if (is_array($this->data) && isset($this->data['internal']) && $this->data['internal']) {
  169. if ($this->data['internalType'] == 'document') {
  170. $doc = Document::getById($this->data['internalId']);
  171. if (!$doc) {
  172. $sane = false;
  173. Logger::notice(
  174. 'Detected insane relation, removing reference to non existent document with id ['.$this->getDocumentId(
  175. ).']'
  176. );
  177. $this->data = null;
  178. }
  179. } elseif ($this->data['internalType'] == 'asset') {
  180. $asset = Asset::getById($this->data['internalId']);
  181. if (!$asset) {
  182. $sane = false;
  183. Logger::notice(
  184. 'Detected insane relation, removing reference to non existent asset with id ['.$this->getDocumentId(
  185. ).']'
  186. );
  187. $this->data = null;
  188. }
  189. } elseif ($this->data['internalType'] == 'object') {
  190. $object = Model\DataObject\Concrete::getById($this->data['internalId']);
  191. if (!$object) {
  192. $sane = false;
  193. Logger::notice(
  194. 'Detected insane relation, removing reference to non existent object with id ['.$this->getDocumentId(
  195. ).']'
  196. );
  197. $this->data = null;
  198. }
  199. }
  200. }
  201. return $sane;
  202. }
  203. /**
  204. * @return string
  205. */
  206. public function getHref()
  207. {
  208. $this->updatePathFromInternal();
  209. $url = $this->data['path'] ?? '';
  210. if (strlen($this->data['parameters'] ?? '') > 0) {
  211. $url .= (strpos($url, '?') !== false ? '&' : '?') . str_replace('?', '', $this->getParameters());
  212. }
  213. if (strlen($this->data['anchor'] ?? '') > 0) {
  214. $anchor = $this->getAnchor();
  215. $anchor = str_replace('"', urlencode('"'), $anchor);
  216. $url .= '#' . str_replace('#', '', $anchor);
  217. }
  218. return $url;
  219. }
  220. /**
  221. * @param bool $realPath
  222. * @param bool $editmode
  223. */
  224. private function updatePathFromInternal($realPath = false, $editmode = false)
  225. {
  226. $method = 'getFullPath';
  227. if ($realPath) {
  228. $method = 'getRealFullPath';
  229. }
  230. if (isset($this->data['internal']) && $this->data['internal']) {
  231. if ($this->data['internalType'] == 'document') {
  232. if ($doc = Document::getById($this->data['internalId'])) {
  233. if ($editmode || (!Document::doHideUnpublished() || $doc->isPublished())) {
  234. $this->data['path'] = $doc->$method();
  235. } else {
  236. $this->data['path'] = '';
  237. }
  238. }
  239. } elseif ($this->data['internalType'] == 'asset') {
  240. if ($asset = Asset::getById($this->data['internalId'])) {
  241. $this->data['path'] = $asset->$method();
  242. }
  243. } elseif ($this->data['internalType'] == 'object') {
  244. if ($object = Model\DataObject::getById($this->data['internalId'])) {
  245. if ($editmode) {
  246. $this->data['path'] = $object->getFullPath();
  247. } else {
  248. if ($object instanceof Model\DataObject\Concrete) {
  249. if ($linkGenerator = $object->getClass()->getLinkGenerator()) {
  250. if ($realPath) {
  251. $this->data['path'] = $object->getFullPath();
  252. } else {
  253. $this->data['path'] = $linkGenerator->generate(
  254. $object,
  255. [
  256. 'document' => $this->getDocument(),
  257. 'context' => $this,
  258. ]
  259. );
  260. }
  261. }
  262. }
  263. }
  264. }
  265. }
  266. }
  267. // deletes unnecessary attribute, which was set by mistake in earlier versions, see also
  268. // https://github.com/pimcore/pimcore/issues/7394
  269. if (isset($this->data['type'])) {
  270. unset($this->data['type']);
  271. }
  272. }
  273. /**
  274. * @return string
  275. */
  276. public function getText()
  277. {
  278. return $this->data['text'] ?? '';
  279. }
  280. /**
  281. * @param string $text
  282. */
  283. public function setText($text)
  284. {
  285. $this->data['text'] = $text;
  286. }
  287. /**
  288. * @return string
  289. */
  290. public function getTarget()
  291. {
  292. return $this->data['target'] ?? '';
  293. }
  294. /**
  295. * @return string
  296. */
  297. public function getParameters()
  298. {
  299. return $this->data['parameters'] ?? '';
  300. }
  301. /**
  302. * @return string
  303. */
  304. public function getAnchor()
  305. {
  306. return $this->data['anchor'] ?? '';
  307. }
  308. /**
  309. * @return string
  310. */
  311. public function getTitle()
  312. {
  313. return $this->data['title'] ?? '';
  314. }
  315. /**
  316. * @return string
  317. */
  318. public function getRel()
  319. {
  320. return $this->data['rel'] ?? '';
  321. }
  322. /**
  323. * @return string
  324. */
  325. public function getTabindex()
  326. {
  327. return $this->data['tabindex'] ?? '';
  328. }
  329. /**
  330. * @return string
  331. */
  332. public function getAccesskey()
  333. {
  334. return $this->data['accesskey'] ?? '';
  335. }
  336. /**
  337. * @return mixed
  338. */
  339. public function getClass()
  340. {
  341. return $this->data['class'] ?? '';
  342. }
  343. /**
  344. * @return mixed
  345. */
  346. public function getAttributes()
  347. {
  348. return $this->data['attributes'] ?? '';
  349. }
  350. /**
  351. * {@inheritdoc}
  352. */
  353. public function setDataFromResource($data)
  354. {
  355. $this->data = \Pimcore\Tool\Serialize::unserialize($data);
  356. if (!is_array($this->data)) {
  357. $this->data = [];
  358. }
  359. return $this;
  360. }
  361. /**
  362. * {@inheritdoc}
  363. */
  364. public function setDataFromEditmode($data)
  365. {
  366. if (!is_array($data)) {
  367. $data = [];
  368. }
  369. $path = $data['path'] ?? null;
  370. if (!empty($path)) {
  371. $target = null;
  372. if ($data['linktype'] == 'internal' && $data['internalType']) {
  373. $target = Model\Element\Service::getElementByPath($data['internalType'], $path);
  374. if ($target) {
  375. $data['internal'] = true;
  376. $data['internalId'] = $target->getId();
  377. }
  378. }
  379. if (!$target) {
  380. if ($target = Document::getByPath($path)) {
  381. $data['internal'] = true;
  382. $data['internalId'] = $target->getId();
  383. $data['internalType'] = 'document';
  384. } elseif ($target = Asset::getByPath($path)) {
  385. $data['internal'] = true;
  386. $data['internalId'] = $target->getId();
  387. $data['internalType'] = 'asset';
  388. } elseif ($target = Model\DataObject\Concrete::getByPath($path)) {
  389. $data['internal'] = true;
  390. $data['internalId'] = $target->getId();
  391. $data['internalType'] = 'object';
  392. } else {
  393. $data['internal'] = false;
  394. $data['internalId'] = null;
  395. $data['internalType'] = null;
  396. $data['linktype'] = 'direct';
  397. }
  398. if ($target) {
  399. $data['linktype'] = 'internal';
  400. }
  401. }
  402. }
  403. $this->data = $data;
  404. return $this;
  405. }
  406. /**
  407. * {@inheritdoc}
  408. */
  409. public function isEmpty()
  410. {
  411. return strlen($this->getHref()) < 1;
  412. }
  413. /**
  414. * {@inheritdoc}
  415. */
  416. public function resolveDependencies()
  417. {
  418. $dependencies = [];
  419. $isInternal = $this->data['internal'] ?? false;
  420. if (is_array($this->data) && $isInternal) {
  421. if ((int)$this->data['internalId'] > 0) {
  422. if ($this->data['internalType'] == 'document') {
  423. if ($doc = Document::getById($this->data['internalId'])) {
  424. $key = 'document_'.$doc->getId();
  425. $dependencies[$key] = [
  426. 'id' => $doc->getId(),
  427. 'type' => 'document',
  428. ];
  429. }
  430. } elseif ($this->data['internalType'] == 'asset') {
  431. if ($asset = Asset::getById($this->data['internalId'])) {
  432. $key = 'asset_'.$asset->getId();
  433. $dependencies[$key] = [
  434. 'id' => $asset->getId(),
  435. 'type' => 'asset',
  436. ];
  437. }
  438. }
  439. }
  440. }
  441. return $dependencies;
  442. }
  443. /**
  444. * { @inheritdoc }
  445. */
  446. public function rewriteIds($idMapping) /** : void */
  447. {
  448. if (isset($this->data['internal']) && $this->data['internal']) {
  449. $type = $this->data['internalType'];
  450. $id = (int)$this->data['internalId'];
  451. if (array_key_exists($type, $idMapping)) {
  452. if (array_key_exists($id, $idMapping[$type])) {
  453. $this->data['internalId'] = $idMapping[$type][$id];
  454. $this->getHref();
  455. }
  456. }
  457. }
  458. }
  459. }