MedienRenderer und MedienHelper in TYPO3 8

Es ist ja bekanntlich inzwischen möglich innerhalb eines Datensatzes bzw eines Inhaltselements auch Videos von YouTube und Vimeo per URL hinzuzufügen. Dieses verhalten wollte ich gerne nachbauen – mit einem Instagram Link.

Ich habe mich dafür der Instagram-Api bedient und lasse damit das Widget für die Instagram-URLs anzeigen.

Wie geht man sowas insgesamt an? Ich habe mir meinen Editor (PhpStorm) geschnappt und habe den TYPO3 Code nach „youtube“ durchsucht. Ich wurde dadurch auf die Resource Renderer und Online Media Helper aufmerksam. Der Online Media Helper ist dafür zuständig das die Bilder erkannt werden, ein Vorschaubild erstellt wird und die Metadaten abgegriffen werden. Der Renderer ist – wie das Wort schon andeutet – für die Darstellung der Medien zuständig.

In diesem Blog gehe ich davon aus, das bereits eine Extension vorhanden ist, welche ergänzt wird.

Machen wir uns also erstmal auf den Weg und machen es möglich Instagram URLs hinzuzufügen. Dazu legen wir in unserer Extension den InstagramHelper an. Ich habe diesen unter „Classes/Resources/OnlineMedia/Helpers/InstagramHelper.php“ abgelegt und folgenden Code eingesetzt:

<?php
  namespace Vendor\MyExtension\Resource\OnlineMedia\Helpers;

  /*
   * It is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, either version 2
   * of the License, or any later version.
   */

  use TYPO3\CMS\Core\Resource\File;
  use TYPO3\CMS\Core\Resource\Folder;
  use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\AbstractOEmbedHelper;
  use TYPO3\CMS\Core\Utility\GeneralUtility;

  /**
   * Instagram helper class
   */
  class InstagramHelper extends AbstractOEmbedHelper
  {

    /**
     * File extension bind to the OnlineMedia helper
     *
     * @var string
     */
    protected $extension = 'instagram';

    /**
     * Get oEmbed url to retrieve oEmbed data
     *
     * @param string $mediaId
     * @param string $format
     *
     * @return string
     */
    protected function getOEmbedUrl($mediaId, $format = 'json')
    {
      return sprintf('https://api.instagram.com/oembed?url=%s', urlencode(sprintf('http://instagr.am/p/%s/', $mediaId)));
    }

    /**
     * Get local absolute file path to preview image
     *
     * @param File $file
     *
     * @return string
     */
    public function getPreviewImage(File $file)
    {
      $mediaId           = $this->getOnlineMediaId($file);
      $temporaryFileName = $this->getTempFolderPath() . 'instagram_' . md5($mediaId) . '.jpg';

      if (!file_exists($temporaryFileName)) {
        $previewImage = GeneralUtility::getUrl(sprintf('https://instagram.com/p/%s/media/?size=m', $mediaId));
        if ($previewImage !== false) {
          file_put_contents($temporaryFileName, $previewImage);
          GeneralUtility::fixPermissions($temporaryFileName);
        }
      }

      return $temporaryFileName;
    }

    /**
     * Get public url
     *
     * @param File $file
     * @param bool $relativeToCurrentScript
     *
     * @return string|NULL
     */
    public function getPublicUrl(File $file, $relativeToCurrentScript = false)
    {
      $instagramId = $this->getOnlineMediaId($file);
      return sprintf('https://instagr.am/p/%s/', $instagramId);
    }

    /**
     * Try to transform given URL to a File
     *
     * @param string $url
     * @param Folder $targetFolder
     *
     * @return File|NULL
     */
    public function transformUrlToFile($url, Folder $targetFolder)
    {
      $mediaId = null;
      if (preg_match('%(?:www.instagram.com|instagr.am)/p/(.*?)/%i', $url, $match)) {
        $mediaId = $match[1];
      }
      if (empty($mediaId)) {
        return null;
      }
      return $this->transformMediaIdToFile($mediaId, $targetFolder, $this->extension);
    }
  }

Im Grunde habe ich alles was mit YouTube zu tun hat gegen den entsprechenden Instagram Code ausgetauscht 😉

Anschließend müssen wir TYPO3 noch sagen wie damit umgegangen werden soll. Dazu fügen wir folgende Zeilen in die „ext_localconf.php“ unserer Extension hinzu:

# Add instagram to allowed mediafile extensions
$GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext'] .= ',instagram';
# Add instagram helper
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['onlineMediaHelpers']['instagram'] = \Vendor\MyExtension\Resource\OnlineMedia\Helpers\InstagramHelper::class;

Wer noch ein „schickes“ Icon im Datei-Manager für Instagram-Links haben will fügt noch folgende Zeilen in seine „ext_localconf.php“ hinzu:

# Add instagram as own mimetype
$GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['instagram'] = 'image/instagram';

# Add icon
$iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
$iconRegistry->registerIcon('mimetypes-media-image-instagram', \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, ['source' => 'EXT:my_extensions/Resources/Public/Icons/mimetypes-media-image-instagram.svg']);
$iconRegistry->registerFileExtension('instagram', 'mimetypes-media-image-instagram');

Als Icon habe ich mich einfach an dem Instagram Logo bedient (bspw. aus dem entsprechenden Wikipedia Eintrag).

Nun können wir bereits die entsprechenden Dateien im Backend Dialog hochladen:

Medien nach URL hinzufügen

Nun weiß TYPO3 noch nicht wie es mit der Datei umgehen soll. Dazu erstellen wir nun den InstagramRenderer. Diesen habe ich unter „Classes/Resources/Rendering/InstagramRenderer.php“ angelegt und folgenden Code eingesetzt:

<?php
  namespace Vendor\MyExt\Resource\Rendering;

  /*
   * It is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, either version 2
   * of the License, or any later version.
   */

  use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
  use TYPO3\CMS\Core\Resource\File;
  use TYPO3\CMS\Core\Resource\FileInterface;
  use TYPO3\CMS\Core\Resource\FileReference;
  use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperInterface;
  use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
  use TYPO3\CMS\Core\Resource\Rendering\FileRendererInterface;
  use TYPO3\CMS\Core\Utility\GeneralUtility;
  use TYPO3\CMS\Core\Utility\MathUtility;

  /**
   * Instagram renderer class
   */
  class InstagramRenderer implements FileRendererInterface
  {

    /**
     * @var OnlineMediaHelperInterface
     */
    protected $onlineMediaHelper;

    /**
     * Check if given File(Reference) can be rendered
     *
     * @param FileInterface $file File of FileReference to render
     *
     * @return bool
     */
    public function canRender(FileInterface $file)
    {
      return ($file->getMimeType() === 'image/instagram' || $file->getExtension() === 'instagram') && $this->getOnlineMediaHelper($file) !== false;
    }

    /**
     * Returns the priority of the renderer
     * This way it is possible to define/overrule a renderer
     * for a specific file type/context.
     * For example create a video renderer for a certain storage/driver type.
     * Should be between 1 and 100, 100 is more important than 1
     *
     * @return int
     */
    public function getPriority()
    {
      return 1;
    }

    /**
     * Render for given File(Reference) html output
     *
     * @param FileInterface $file
     * @param int|string    $width                            TYPO3 known format; examples: 220, 200m or 200c
     * @param int|string    $height                           TYPO3 known format; examples: 220, 200m or 200c
     * @param array         $options
     * @param bool          $usedPathsRelativeToCurrentScript See $file->getPublicUrl()
     *
     * @return string
     */
    public function render(FileInterface $file, $width, $height, array $options = null, $usedPathsRelativeToCurrentScript = false)
    {
      if ($file instanceof FileReference) {
        $orgFile = $file->getOriginalFile();
      } else {
        $orgFile = $file;
      }

      $mediaId   = $this->getOnlineMediaHelper($file)->getOnlineMediaId($orgFile);
      $arguments = ['url' => sprintf('http://instagr.am/p/%s/', $mediaId)];

      if ((int)$width > 0) {
        $arguments['maxWidth'] = MathUtility::forceIntegerInRange($width, 320);
      } else if (array_key_exists('maxWidth', $options) && $options['maxWidth'] && MathUtility::canBeInterpretedAsInteger($options['maxWidth'])) {
        $arguments['maxWidth'] = MathUtility::forceIntegerInRange($options['maxWidth'], 320);
      }

      if (array_key_exists('hideCaption', $options) && $options['hideCaption']) {
        $arguments['hidecaption'] = 1;
      }
      if (array_key_exists('omitScript', $options) && $options['omitScript']) {
        $arguments['omitscript'] = 1;
      }
      if (array_key_exists('callBack', $options) && $options['callBack']) {
        $arguments['callback'] = $options['callBack'];
      }

      $oEmbed = GeneralUtility::getUrl(sprintf('https://api.instagram.com/oembed?%s', http_build_query($arguments)));

      if ($oEmbed) {
        $oEmbed = json_decode($oEmbed, true);
      }

      return $oEmbed['html'];
    }

    /**
     * Get online media helper
     *
     * @param FileInterface $file
     *
     * @return bool|OnlineMediaHelperInterface
     */
    protected function getOnlineMediaHelper(FileInterface $file)
    {
      if ($this->onlineMediaHelper === null) {
        $orgFile = $file;
        if ($orgFile instanceof FileReference) {
          $orgFile = $orgFile->getOriginalFile();
        }
        if ($orgFile instanceof File) {
          $this->onlineMediaHelper = OnlineMediaHelperRegistry::getInstance()->getOnlineMediaHelper($orgFile);
        } else {
          $this->onlineMediaHelper = false;
        }
      }
      return $this->onlineMediaHelper;
    }
  }

Damit TYPO3 auch von dieser Datei erfährt müssen in der „ext_localconf.php“ noch folgende Zeilen hinzugefügt werden:

# ImageRenderer/Helper
$rendererRegistry = \TYPO3\CMS\Core\Resource\Rendering\RendererRegistry::getInstance();
$rendererRegistry->registerRendererClass(\Vendor\MyExtension\Resource\Rendering\InstagramRenderer::class);

Fügen wir zum Beispiel bei einer News ein Instagram Link als Bild hinzu erhalten wir nun folgendes Ergebnis:

Solltet ihr Anregungen haben, wie man das eine oder andere anders Umsetzen kann so meldet euch gerne bei mir 😉

Ein Gedanke zu „MedienRenderer und MedienHelper in TYPO3 8

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.