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

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