d3yii2/d3files

View on GitHub
widgets/D3FilesPreviewWidget.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

namespace d3yii2\d3files\widgets;

use d3system\exceptions\D3Exception;
use d3yii2\d3files\D3FilesPreviewAsset;
use d3yii2\d3files\models\D3filesModelName;
use d3yii2\pdfobject\widgets\PDFObject;
use eaBlankonThema\assetbundles\AjaxAsset;
use Exception;
use yii\helpers\Html;
use yii\helpers\Json;
use yii\helpers\Url;
use Yii;
use d3yii2\d3files\components\D3Files;
use yii\web\View;
use function is_callable;

/**
 * Class D3FilesPreviewWidget
 * @package d3yii2\d3files\widgets
 * Usage by model \d3yii2\d3files\widgets\D3FilesPreviewWidget::widget(['model' => $model])
 * Usage by fileList (attachments are joined already) D3FilesPreviewWidget::widget(['fileList' => $fileList])
 * @property null|string $modalTitle
 * @property string $filesDropdown
 * @property string $modalToolbarContent
 * @property string $assetsUrl
 * @property string $prevNextFileButtons
 */
class D3FilesPreviewWidget extends D3FilesWidget
{
    /**
     * @var string $viewType
     * @var string $dialogWidgetClass
     * @var array $pdfObjectOptions
     * @var string VIEW_MODAL_BUTTON
     * @var string VIEW_INLINE_BUTTON
     * @var string VIEW_IFRAME
     * @var string VIEW_TYPE_MODAL
     * @var string VIEW_TYPE_INLINE
     * @var string EMBED_CONTENT_CLASS
     *
     * @property null|string $modalTitle
     * @property string $filesDropdown
     * @property string $modalToolbarContent
     * @property string $assetsUrl
     * @property bool $useInColumn
     */

    public $icon = self::DEFAULT_ICON;
    public $previewExtensions = '/(gif|pdf|jpe?g|png)$/i';
    public $viewExtension = ['pdf', 'jpg'];
    public $currentFile;
    public $showPrevNext = false;
    public $prevFile;
    public $nextFile;
    public $dialogWidgetClass = 'eaBlankonThema\widget\ThModal';
    public $pdfObjectOptions = [];
    public $showPrevNextButtons = false;
    public $viewType = self::VIEW_TYPE_MODAL;
    public $buttonView = self::VIEW_MODAL_BUTTON;
    public $contentTargetSelector = self::EMBED_CONTENT_CLASS;
    public $nextButtonLabel;
    public $prevButtonLabel;
    /** @var bool */
    public $useInColumn = false;

    public const DEFAULT_ICON = 'glyphicon glyphicon-eye-open';

    public const VIEW_DROPDOWN_LIST = 'dropdown-list';
    public const VIEW_MODAL_BUTTON = '_modal_button';
    public const VIEW_INLINE_BUTTON = '_inline_button';
    public const VIEW_IFRAME = '_iframe';

    public const VIEW_TYPE_MODAL = 'modal';
    public const VIEW_TYPE_NONE = 'none';
    public const VIEW_TYPE_INLINE = 'inline';

    public const EMBED_CONTENT_CLASS = 'd3files-embed-content';
    public const PREVIEW_BUTTON_CLASS = 'd3files-preview-button';

    private const MODAL_ID = 'D3FilesPreviewModal';

    /**
     * @throws Exception
     */
    public function init(): void
    {
        parent::init();

        // Backward compatibility
        if ($this->viewByFancyBox) {
            $this->viewType = self::VIEW_TYPE_MODAL;
        }

        $hasPreviewFiles = false;
        $hasPdf = false;
        $hasAjax = false;

        if (self::VIEW_TYPE_NONE !== $this->viewType) {
            if (self::VIEW_TYPE_MODAL !== $this->viewType) {
                $this->buttonView = self::VIEW_INLINE_BUTTON;
                $this->pdfObjectOptions = [
                    'wrapperHtmlOptions' => ['style' => 'height:800px'],
                    //'targetElementClass' => self::EMBED_CONTENT_CLASS,
                ];
            }

            // Check for PDF and AJAX loaded attachments to  assets and assign preview attributes
            foreach ($this->fileList as $file) {
                $ext = D3Files::getFileExtension($file);
                if ('pdf' === $ext) {
                    $hasPdf = true;
                } else {
                    $hasAjax = true;
                }

                if (D3Files::hasFileWithExtension([$file], $this->previewExtensions)) {
                    $hasPreviewFiles = true;
                }
            }

            D3FilesPreviewAsset::register(Yii::$app->view);

            // If AJAX load required, register the assets
            if ($hasAjax && !isset(Yii::$app->view->params['AjaxAssetRegistered'])) {
                AjaxAsset::register(Yii::$app->view);

                // Avoid the asets registering multiple times by widget second calls
                Yii::$app->view->params['AjaxAssetRegistered'] = true;
            }
        }

        // Render the PdfObject content in the footer if the files have PDF extension
        // Another preview types (e.g. images are also loaded via PdfObject and some optimization is @TODO)
        if (($hasPdf || $hasPreviewFiles) && !isset(Yii::$app->view->params['PdfObjectRendered'])) {
            if ((self::VIEW_TYPE_INLINE === $this->viewType)) {
                $pdfObjectOptions = array_merge(
                    [
                        'closeButtonOptions' => ['label' => Yii::t('d3files', 'Close')],
                        'targetElementClass' => self::EMBED_CONTENT_CLASS,
                    ],
                    $this->pdfObjectOptions
                );
                echo PDFObject::widget($pdfObjectOptions);
            } else {
                PDFObject::widget($this->pdfObjectOptions);
            }
            Yii::$app->view->params['PdfObjectRendered'] = true;
        }

        // Ensure modal preview is enabled and the layout rendered once
        if (self::VIEW_TYPE_MODAL === $this->viewType && !isset(Yii::$app->view->params['D3FilesModalRendered'])) {
            if (is_callable($this->dialogWidgetClass)) {
                throw new D3Exception('Invalid Modal Dialog class: ' . $this->dialogWidgetClass);
            }

            $modalOptions = [];

            // Force modal width/height
            $modalOptions['dialogHtmlOptions'] = ['style' => 'width:70%;height:80%'];
            $modalOptions['contentHtmlOptions'] = ['style' => 'height:100%'];
            $modalOptions['id'] = self::MODAL_ID;
            $modalOptions['contentClass'] = PDFObject::CONTENT_CLASS;
            $modalOptions['title'] = $this->getModalTitle();
            $modalOptions['toolbarContent'] = $this->getModalToolbarContent();

            $this->dialogWidgetClass::widget($modalOptions);
        }

        Yii::$app->getView()->registerJs(
            'document.D3FP = new $.D3FilesPreview();
                document.D3FP.setOption("prevNextButtons",  ' . ($this->showPrevNextButtons ? 'true' : 'false') . ');
                document.D3FP.reflow();',
            View::POS_READY,
            'd3fp'
        );

        Yii::$app->getView()->registerJsVar(
            'D3FilesPreviewJsVars',
            ['assetUrl' => $this->getAssetsUrl()]
        );
    }

    /**
     * @return array
     * @throws \yii\db\Exception
     */
    public function initFilesList(): array
    {
        parent::initFilesList();
    
        if (!$this->nameModel) {
            $this->nameModel = D3filesModelName::findOne(['name' => $this->model_name]);
        }

        // Rebuild the list adding some preview params to files
        foreach ($this->fileList as $i => $file) {
            if (D3Files::hasFileWithExtension([$file], $this->previewExtensions)) {
                $file['src'] = Url::to([
                    $this->urlPrefix . 'd3filesopen',
                    'model_name_id' => $this->nameModel->id,
                ]);
                $this->fileList[$i] = $file;
            }
        }
        return $this->fileList;
    }

    /**
     * @return string
     */
    public function getAssetsUrl(): string
    {
        return Yii::$app->getAssetManager()->getBundle(D3FilesPreviewAsset::class)->baseUrl;
    }

    /**
     * @return string|null
     */
    public function getModalTitle(): ?string
    {
        return $this->title;
    }

    /**
     * @return string
     * @throws Exception
     */
    public function getModalToolbarContent(): string
    {
        $previewButtons = $this->showPrevNextButtons ? $this->getPrevNextFileButtons() : '';

        return '
            <div class="row">
                <div class="col-sm-4">
                    <div class="d3preview-counter pull-left">
                        <span class="d3preview-counter-i"></span>
                        <span class="d3preview-counter-from">' . Yii::t('d3files', 'from') . '</span>
                        <span class="d3preview-counter-total"></span>
                    </div>
                    <span class="d3preview-model-files pull-left"></span>
                </div>
                <div class="col-sm-5 text-center">' . $previewButtons . '</div>
           </div>
           <div class="row">
                <div class="d3preview-image-content" style="display: none"></div>
           </div>
           ';
    }

    public function getFilesDropdown(): string
    {
        return '<span class="pull-right">
                <label for="getFilesDropdown">Files:</label>'
            . Html::dropDownList('getFilesDropdown', false, [], ['class' => 'd3files-preview-dropdown'])
            . '</span>';
    }

    /**
     * @return array|null
     */
    public function getViewParams(): ?array
    {
        $params = [
            'showPrevNext' => $this->showPrevNext,
            'viewType' => $this->viewType,
            'previewButton' => $this->buttonView,
            'hasPreview' => true,
            'previewExtensions' => $this->previewExtensions,
            'previewFileList' => D3Files::getPreviewFilesList(
                $this->fileList,
                $this->previewExtensions,
                [
                    $this->urlPrefix . 'd3filesopen',
                    'model_name_id' => $this->nameModel->id ?? null,
                ]
            ),
        ];

        if (self::VIEW_MODAL_BUTTON === $this->view || self::VIEW_INLINE_BUTTON === $this->view) {
            foreach ($this->viewExtension as $extension) {
                if($params['file'] = D3Files::getFirstFileHavingExt($this->fileList, $extension)) {
                    break;
                }
            }
        }

        return array_merge(
            parent::getViewParams(),
            $params
        );
    }

    /**
     * Get element data attributes for modal or inline box scripts
     * @param array $file
     * @param array $files
     * @return array
     */
    public static function getPreviewButtonDataAttributes(array $file, array $files): array
    {
        return [
            'data-d3files-preview' => Json::encode(['files' => $files, 'modelId' => $file['model_id']]),
            'data-file-id' => $file['id'],
            'data-model-id' => $file['model_id'],
        ];
    }
    
    /**
     * @param array $file
     * @param array $files
     * @return array
     */
    public static function getPreviewInlineButtonAttributes(array $file, array $files = []): array
    {
        $attrs = self::getPreviewButtonDataAttributes($file, $files);
        $attrs['title'] = Yii::t('d3files', 'Preview atachment');
        $attrs['class'] = 'd3files-preview-widget-load ';
        
        return $attrs;
    }

    /**
     * @param array $file
     * @param array $files
     * @return array
     */
    public static function getPreviewModalButtonAttributes(array $file, array $files = []): array
    {
        $attrs = self::getPreviewButtonDataAttributes($file, $files);
        $attrs['title'] = Yii::t('d3files', 'Preview atachment');
        $attrs['class'] = 'd3files-preview-widget-load ';
        $attrs['data-toggle'] = 'modal';
        $attrs['data-target'] = '#' . self::MODAL_ID;

        return $attrs;
    }

    /**
     * @param array $options
     * @return string
     * @throws Exception
     */
    public function getPdfContent(array $options = []): string
    {
        $pdfObjectOptions = array_merge($this->pdfObjectOptions, $options);

        return PDFObject::widget($pdfObjectOptions);
    }

    /**
     * @return string
     * @throws Exception
     */
    public function getPrevNextFileButtons(): string
    {
        $prevButtonLabel = $this->prevButtonLabel ?? Yii::t('d3files', 'Previous Attachment');
        $nextButtonLabel = $this->nextButtonLabel ?? Yii::t('d3files', 'Next Attachment');

        $buttons = '<a id="w80" class="btn btn-success d3files-preview-prev-button">' . $prevButtonLabel . '</a>';

        $buttons .= '<a id="w80" class="btn btn-success d3files-preview-next-button">' . $nextButtonLabel . '</a>';

        return $buttons;
    }
}