setka-8.x-1.0/src/Controller/SetkaEditorApiController.php

src/Controller/SetkaEditorApiController.php
<?php

namespace Drupal\setka_editor\Controller;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Asset\CssCollectionOptimizer;
use Drupal\Core\Asset\JsCollectionOptimizer;
use Drupal\Core\Asset\LibraryDiscovery;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\DatabaseBackend;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\File\FileSystem;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\file\Entity\File;
use Drupal\file\FileUsage\FileUsageInterface;
use Drupal\setka_editor\SetkaEditorApi;
use Drupal\setka_editor\SetkaEditorHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

/**
 * Setka Editor API controller.
 */
class SetkaEditorApiController extends ControllerBase {

  const SETKA_ALLOWED_MIME_TYPES = [
    'image/jpeg',
    'image/pjpeg',
    'image/gif',
    'image/png',
    'image/svg+xml',
    'image/vnd.wap.wbmp',
  ];

  /**
   * Setka Editor api service.
   *
   * @var \Drupal\setka_editor\SetkaEditorApi
   */
  protected $editorApi;

  /**
   * Setka Editor config.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $setkaConfig;

  /**
   * Service to interact with $_SESSION.
   *
   * @var \Drupal\Core\TempStore\PrivateTempStore
   */
  protected $sessionStore;

  /**
   * Drupal file usage interface.
   *
   * @var \Drupal\file\FileUsage\FileUsageInterface
   */
  protected $fileUsage;

  /**
   * Drupal database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * Cache Discovery bin backend.
   *
   * @var \Drupal\Core\Cache\DatabaseBackend
   */
  protected $cacheDiscovery;

  /**
   * Drupal CSS optimizer service.
   *
   * @var \Drupal\Core\Asset\CssCollectionOptimizer
   */
  protected $cssOptimizer;

  /**
   * Drupal JS optimizer service.
   *
   * @var \Drupal\Core\Asset\JsCollectionOptimizer
   */
  protected $jsOptimizer;

  /**
   * Drupal file_system service.
   *
   * @var \Drupal\Core\File\FileSystem
   */
  protected $fileSystem;

  /**
   * Drupal queue factory.
   *
   * @var \Drupal\Core\Queue\QueueFactory
   */
  protected $queueFactory;

  /**
   * Library discovery service.
   *
   * @var \Drupal\Core\Asset\LibraryDiscovery
   */
  protected $libraryDiscovery;

  /**
   * Lock service.
   *
   * @var \Drupal\Core\Lock\LockBackendInterface
   */
  protected $lock;

  /**
   * {@inheritdoc}
   */
  public function __construct(SetkaEditorApi $editorApi,
                              ConfigFactory $configFactory,
                              PrivateTempStoreFactory $privateTempstore,
                              FileUsageInterface $fileUsage,
                              Connection $database,
                              DatabaseBackend $cacheDiscovery,
                              CssCollectionOptimizer $cssOptimizer,
                              JsCollectionOptimizer $jsOptimizer,
                              FileSystem $fileSystem,
                              QueueFactory $queueFactory,
                              LibraryDiscovery $libraryDiscovery,
                              LockBackendInterface $lock) {
    $this->editorApi = $editorApi;
    $this->configFactory = $configFactory;
    $this->setkaConfig = $configFactory->get('setka_editor.settings');
    $this->sessionStore = $privateTempstore->get('setka_editor');
    $this->fileUsage = $fileUsage;
    $this->database = $database;
    $this->cacheDiscovery = $cacheDiscovery;
    $this->cssOptimizer = $cssOptimizer;
    $this->jsOptimizer = $jsOptimizer;
    $this->fileSystem = $fileSystem;
    $this->queueFactory = $queueFactory;
    $this->libraryDiscovery = $libraryDiscovery;
    $this->lock = $lock;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('setka_editor.api'),
      $container->get('config.factory'),
      $container->get('user.private_tempstore'),
      $container->get('file.usage'),
      $container->get('database'),
      $container->get('cache.discovery'),
      $container->get('asset.css.collection_optimizer'),
      $container->get('asset.js.collection_optimizer'),
      $container->get('file_system'),
      $container->get('queue'),
      $container->get('library.discovery'),
      $container->get('lock')
    );
  }

  /**
   * API gate to get setka editor files.
   */
  public function editorConfig(Request $request) {
    $token = $request->request->get('token');
    $data = $request->request->get('data');
    $licenseKey = $this->setkaConfig->get('setka_license_key');
    $setkaUseCdn = $this->setkaConfig->get('setka_use_cdn');
    $downloadFiles = (empty($setkaUseCdn) && SetkaEditorHelper::checkSetkaFolderPermissions($this->fileSystem));
    if (!empty($data) && !empty($token) && $licenseKey == $token) {
      if (!empty($data['plugins']) && !empty($data['content_editor_version'])
        && !empty($data['public_token']) && !empty($data['theme_files']) && !empty($data['content_editor_files'])) {
        $newSettings = SetkaEditorHelper::parseStyleManagerData($data);
      }
      if (!empty($newSettings['setka_editor_js_cdn']) && !empty($newSettings['setka_editor_css_cdn']) &&
        !empty($newSettings['setka_company_css_cdn']) && !empty($newSettings['setka_company_json_cdn'] &&
          !empty($newSettings['setka_public_js_cdn']))) {
        if ($downloadFiles) {
          $queue = $this->queueFactory->get('update_setke_editor');
          if (!$queue->numberOfItems()) {
            $queue->createQueue();
          }
          $queue->createItem(['newSettings' => $newSettings]);
          if ($this->lock->acquire('setka_editor_files_update')) {
            while ($newSettingsItem = $queue->claimItem()) {
              $newSettingsData = $newSettingsItem->data['newSettings'];
              SetkaEditorHelper::buildSetkaFilesUpdateTask($this->setkaConfig, $this->state(), $newSettingsData);
              $this->configFactory->getEditable('setka_editor.settings')
                ->set('setka_editor_version', $newSettingsData['setka_editor_version'])
                ->set('setka_editor_public_token', $newSettingsData['setka_editor_public_token'])
                ->set('setka_company_meta_data', $newSettingsData['setka_company_meta_data'])
                ->set('setka_editor_js_cdn', $newSettingsData['setka_editor_js_cdn'])
                ->set('setka_editor_css_cdn', $newSettingsData['setka_editor_css_cdn'])
                ->set('setka_company_css_cdn', $newSettingsData['setka_company_css_cdn'])
                ->set('setka_company_json_cdn', $newSettingsData['setka_company_json_cdn'])
                ->set('setka_public_js_cdn', $newSettingsData['setka_public_js_cdn'])
                ->save();
              $this->libraryDiscovery->clearCachedDefinitions();
              $this->configFactory->reset('setka_editor.settings');
              SetkaEditorHelper::runSetkaFilesUpdateTask($this->state());
              $queue->deleteItem($newSettingsItem);
            }
            foreach (Cache::getBins() as $cache_backend) {
              $cache_backend->deleteAll();
            }
            $this->libraryDiscovery->clearCachedDefinitions();
            $this->configFactory->reset('setka_editor.settings');
            $this->cacheDiscovery->deleteAll();
            $this->cssOptimizer->deleteAll();
            $this->jsOptimizer->deleteAll();
            _drupal_flush_css_js();

            $this->lock->release('setka_editor_files_update');
          }
        }
        else {
          $this->configFactory->getEditable('setka_editor.settings')
            ->set('setka_editor_version', $newSettings['setka_editor_version'])
            ->set('setka_editor_public_token', $newSettings['setka_editor_public_token'])
            ->set('setka_company_meta_data', $newSettings['setka_company_meta_data'])
            ->set('setka_editor_js_cdn', $newSettings['setka_editor_js_cdn'])
            ->set('setka_editor_css_cdn', $newSettings['setka_editor_css_cdn'])
            ->set('setka_company_css_cdn', $newSettings['setka_company_css_cdn'])
            ->set('setka_company_json_cdn', $newSettings['setka_company_json_cdn'])
            ->set('setka_public_js_cdn', $newSettings['setka_public_js_cdn'])
            ->save();
          $this->state()->setMultiple(
            [
              'setka_editor_js' => FALSE,
              'setka_editor_css' => FALSE,
              'setka_company_css' => FALSE,
              'setka_company_json' => FALSE,
              'setka_public_js' => FALSE,
            ]
          );
          $this->getLogger('setka_editor')->info('Setka Editor config update: successful update!');
          foreach (Cache::getBins() as $cache_backend) {
            $cache_backend
              ->deleteAll();
          }
          $this->libraryDiscovery->clearCachedDefinitions();
          $this->configFactory->reset('setka_editor.settings');
          $this->cacheDiscovery->deleteAll();
          $this->cssOptimizer->deleteAll();
          $this->jsOptimizer->deleteAll();
          _drupal_flush_css_js();
        }
      }
      else {
        $this->getLogger('setka_editor')->error('Setka Editor config update error: required request data does not exist!');
      }
    }

    return new JsonResponse();
  }

  /**
   * Gate for Setka Editor API to upload images.
   */
  public function uploadImages(Request $request) {
    $validUuids = $this->sessionStore->get('setka_editor_valid_uuids');
    $entityUuid = $request->request->get('entityUuid');
    $entityId = $request->request->get('entityId');
    $entityType = $request->request->get('entityType');
    if ($this->checkEntityEditAccess($entityId, $entityType)) {
      if ($validUuids && $entityUuid && in_array($entityUuid, $validUuids)) {
        if (mb_strpos($request->headers->get('Content-Type'), 'multipart/form-data;') !== 0) {
          $res = new Response();
          $res->setStatusCode(400, $this->t('Unsupported content type.'));
          return $res;
        }
        $uploadError = FALSE;
        switch ($_FILES['file']['error']) {
          case UPLOAD_ERR_INI_SIZE:
            $uploadError = $this->t('File to large. Max file size: @size',
              ['@size' => ini_get('upload_max_filesize')]
            );
            break;

          case UPLOAD_ERR_FORM_SIZE:
            $uploadError = $this->t('File to large. Max file size: @size',
              ['@size' => ini_get('upload_max_filesize')]
            );
            break;

          case UPLOAD_ERR_PARTIAL:
            $uploadError = $this->t('File has uploaded partially.');
            break;

          case UPLOAD_ERR_NO_FILE:
            $uploadError = $this->t('No file uploaded.');
            break;

          case UPLOAD_ERR_NO_TMP_DIR:
            $uploadError = $this->t('Tmp directory does not exists.');
            break;

          case UPLOAD_ERR_CANT_WRITE:
            $uploadError = $this->t('Write error.');
            break;

          case UPLOAD_ERR_EXTENSION:
            $uploadError = $this->t('Extension has stopped upload.');
            break;
        }
        if ($uploadError) {
          $res = new Response();
          $res->setStatusCode(400, $uploadError);
          return $res;
        }
        $mime = $_FILES['file']['type'];
        if (!in_array($mime, self::SETKA_ALLOWED_MIME_TYPES)) {
          $res = new Response();
          $res->setStatusCode(400, $this->t('Unsupported mime type.'));
          return $res;
        }
        $directory = $this->fileSystem->realpath("public://setka");
        $directoryError = FALSE;
        if (!SetkaEditorHelper::checkSetkaFolderPermissions($this->fileSystem)) {
          if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
            $directoryError = TRUE;
            $this->getLogger('setka_editor')->error('The directory %directory does not exist or is not writable.', ['%directory' => $directory]);
          }
        }
        if ($directoryError) {
          $res = new Response();
          $res->setStatusCode(400, $this->t('Image directory permissions error.'));
          return $res;
        }
        else {
          $fileData = file_get_contents($_FILES['file']['tmp_name']);
          $file = file_save_data($fileData, 'public://setka/' . $_FILES['file']['name'], FILE_EXISTS_RENAME);
          $imageId = $file->id();
          $imageUrl = file_create_url($file->getFileUri());

          if ($entityId && $entityType && $entityId != 'null') {
            $this->fileUsage->add($file, 'setka_editor', $entityType, $entityId);
          }
          else {
            $setkaEditorImages = $this->sessionStore->get('setka_editor_images') ?? [];
            $setkaEditorImages[$entityUuid][] = $imageId;
            $this->sessionStore->set('setka_editor_images', $setkaEditorImages);
          }

          return new JsonResponse([
            'id' => $imageId,
            'url' => $imageUrl,
          ]);
        }
      }
    }
    $res = new Response();
    $res->setStatusCode(400, $this->t('Unknown error.'));
    return $res;
  }

  /**
   * Gate for Setka Editor API to edit image alt.
   */
  public function putImage($id, Request $request) {
    $requestContent = $request->getContent();
    if ($requestPayload = Json::decode($requestContent)) {
      $imageId = (int) $id;
      if (($requestPayload['entityId'] || $requestPayload['entityUuid']) && !empty($requestPayload['entityType']) && $requestPayload['alt'] && $imageId > 0) {
        if ($this->checkEntityEditAccess($requestPayload['entityId'], $requestPayload['entityType'])) {
          /** @var \Drupal\file\Entity\File $imageEntity */
          $imageEntity = $this->entityTypeManager()->getStorage('file')->load($imageId);
          if ($this->checkImageAttachedToEntity($imageEntity, $requestPayload['entityId'], $requestPayload['entityUuid'])) {
            $imageUri = $imageEntity->getFileUri();
            $imageUrl = file_create_url($imageUri);

            $query = $this->database->update('file_managed');
            $query->fields(['alt' => $requestPayload['alt']]);
            $query->condition('fid', $id);
            $query->execute();
            return new JsonResponse([
              'id' => $imageId,
              'url' => $imageUrl,
              'thumbUrl' => $imageUrl,
            ]);
          }
        }
        else {
          $res = new Response();
          $res->setStatusCode(400, $this->t('Permissions error.'));
          return $res;
        }
      }
    }
    $res = new Response();
    $res->setStatusCode(400, $this->t('Unknown error.'));
    return $res;
  }

  /**
   * Gate for Setka Editor API to delete image.
   */
  public function delImage($id, Request $request) {
    $requestContent = $request->getContent();
    if ($requestPayload = Json::decode($requestContent)) {
      $imageId = (int) $id;
      if (($requestPayload['entityId'] || $requestPayload['entityUuid']) && !empty($requestPayload['entityType']) && $imageId > 0) {
        if ($this->checkEntityEditAccess($requestPayload['entityId'], $requestPayload['entityType'])) {
          $imageEntity = $this->entityTypeManager()->getStorage('file')->load($imageId);
          if ($this->checkImageAttachedToEntity($imageEntity, $requestPayload['entityId'], $requestPayload['entityUuid'])) {
            if ($imageEntity) {
              $imageEntity->delete();
            }
            $setkaEditorImages = $this->sessionStore->get('setka_editor_images') ?? [];
            if (!empty($setkaEditorImages[$requestPayload['entityUuid']])) {
              $imageKey = array_search($imageId, $setkaEditorImages[$requestPayload['entityUuid']]);
              unset($setkaEditorImages[$requestPayload['entityUuid']][$imageKey]);
              $this->sessionStore->set('setka_editor_images', $setkaEditorImages);
            }
          }
          return new JsonResponse();
        }
      }
    }
    $res = new Response();
    $res->setStatusCode(400, $this->t('Unknown error.'));
    return $res;
  }

  /**
   * Checks if user has access to edit/create entity.
   *
   * @param int|null $entityId
   *   Entity id.
   * @param string $entityType
   *   Entity type.
   *
   * @return bool|\Drupal\Core\Access\AccessResultInterface
   *   Check access result.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function checkEntityEditAccess($entityId, $entityType) {
    $account = $this->currentUser();
    if ($entityId > 0) {
      $entity = $this->entityTypeManager()->getStorage($entityType)->load($entityId);
      return $entity->access('edit', $account);
    }
    else {
      return $account->hasPermission('create ' . $entityType . ' content');
    }
  }

  /**
   * Checks if image attached to entity or not.
   *
   * @param \Drupal\file\Entity\File $imageFile
   *   Image file.
   * @param int $entityId
   *   Entity id.
   * @param string $entityUuid
   *   Entity uuid.
   *
   * @return bool
   *   Image attach status.
   */
  protected function checkImageAttachedToEntity(File $imageFile, $entityId, $entityUuid) {
    if (!$imageFile) {
      return FALSE;
    }
    if ($entityId) {
      $listUsage = $this->fileUsage->listUsage($imageFile);
      if (!empty($listUsage['setka_editor'])) {
        foreach ($listUsage['setka_editor'] as $entityTypeUsage) {
          if ($entityTypeUsage[$entityId]) {
            return TRUE;
          }
        }
      }
      return FALSE;
    }
    elseif ($entityUuid) {
      $setkaEditorImages = $this->sessionStore->get('setka_editor_images') ?? [];
      if (!empty($setkaEditorImages[$entityUuid])) {
        return in_array($imageFile->id(), $setkaEditorImages[$entityUuid]);
      }
    }
    return FALSE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc