From 9c8720a0d9cc3612886b8e8473f4b0b0223ffba6 Mon Sep 17 00:00:00 2001 From: Tooweb Date: Sat, 3 May 2025 04:32:37 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B5=D0=B4=D1=80=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursorrules | 18 ++ assets/css/pdf-viewer-widget.css | 75 ++++++ assets/js/pdf-viewer-widget.js | 108 +++++++++ includes/dynamic-tags/pdf-url.php | 43 ++++ includes/widgets/pdf-viewer-widget.php | 310 +++++++++++++++++++++++++ languages/pdf-viewer-elementor.pot | 0 pdf-viewer-elementor.php | 149 ++++++++++++ readme.txt | 113 +++++++++ uninstall.php | 18 ++ 9 files changed, 834 insertions(+) create mode 100644 .cursorrules create mode 100644 assets/css/pdf-viewer-widget.css create mode 100644 assets/js/pdf-viewer-widget.js create mode 100644 includes/dynamic-tags/pdf-url.php create mode 100644 includes/widgets/pdf-viewer-widget.php create mode 100644 languages/pdf-viewer-elementor.pot create mode 100644 pdf-viewer-elementor.php create mode 100644 readme.txt create mode 100644 uninstall.php diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..cb3611c --- /dev/null +++ b/.cursorrules @@ -0,0 +1,18 @@ +Не ломай рабочий код проекта! +Старайся добавлять комментарии к коду, который ты вставляешь" +Анализируй все файлы проекта и работай так, чтбы ничего не сломать. + +Задача проекта: +Мне нужен плагин для последней версии Wordpress и плагина Elementor который будет выполнять следующий функционал: +1 Для конструктора Elementor и Elementor PRO. +Необходимо чтобы при редактировании через конструктор Elementor добавился виджет, который называется «PDF Viewer». +При перетаскивании виджета в рабочую область страницы, виджет должен отображать демо страницу формата PDF. +Сам же виджет должен иметь настройки аналогично дизайну других настроек плагина Elementor. Первой, это поле для выбора PDF файла из медиатеки wordpress. Так же в поле загрузки плагин должен иметь возможность подтягивать динамические данные. +После этого поля должен быть ряд настроек: +1. Поле для ввода числового значения, которое будет влиять на отображаемое количество страниц на странице – соответственно если в настройке указана только одна страница, а сам файл имеет 2 и более страниц, то на странице должна отображаться только одна страница. Если указать цифру 2, то отображаться на странице 2 страницы и так далее. По умолчанию отображается одна страница. +2. Кнопка on/off, которая может переопределять предыдущую настройку. Если ее включить, то виджет должен отображать все страницы в файле pdf. +3. Кнопка on/off, которая позволяет включать полосу прокрутки при просмотре pdf файла. + +При необходимости размещай структуру файлов плагина так, чтобы я потом мог загрузить и отправить плагин на модерацию в репозиторий Wordpress.org. +Автор плагина: Александр Головин +Сайт автора: tooweb.ru diff --git a/assets/css/pdf-viewer-widget.css b/assets/css/pdf-viewer-widget.css new file mode 100644 index 0000000..bc8b42a --- /dev/null +++ b/assets/css/pdf-viewer-widget.css @@ -0,0 +1,75 @@ +.pdf-viewer-widget { + margin: 0 auto; +} + +.pdf-viewer-container { + width: 100%; + margin: 0 auto; + transition: all 0.3s ease; +} + +.pdf-pages-container { + width: 100%; +} + +.pdf-page-container { + display: inline-block; +} + +.pdf-page-container:last-child { + margin-bottom: 0 !important; +} + +.pdf-canvas { + width: 100%; + height: auto; + display: block; +} + +/* Стили для режима прокрутки */ +.pdf-viewer-container[data-scrollbar="true"] { + overflow-y: auto; +} + +/* Медиа-запросы для адаптивности */ +@media (max-width: 767px) { + .pdf-viewer-container { + margin: 0; + } + + .pdf-page-container { + width: 100% !important; + } +} + +/* Стили для контрола выбора файла */ +.elementor-control-media { + padding: 7px; + border: 1px solid #d5dadf; + border-radius: 3px; + background-color: #fff; +} + +.elementor-control-media__content { + display: flex; + align-items: center; + justify-content: space-between; +} + +.elementor-control-media__preview { + flex: 1; + padding: 0 10px; +} + +.elementor-control-media-upload-button { + margin-right: 10px; +} + +.elementor-control-media__tools { + display: flex; + align-items: center; +} + +.elementor-control-dynamic-switcher { + margin-left: 5px; +} \ No newline at end of file diff --git a/assets/js/pdf-viewer-widget.js b/assets/js/pdf-viewer-widget.js new file mode 100644 index 0000000..59c4880 --- /dev/null +++ b/assets/js/pdf-viewer-widget.js @@ -0,0 +1,108 @@ +(function($) { + 'use strict'; + + class PDFViewer { + constructor(element) { + this.element = element; + this.container = element.querySelector('.pdf-viewer-container'); + this.pagesContainer = element.querySelector('.pdf-pages-container'); + this.pdfUrl = element.dataset.pdfUrl; + this.pages = element.dataset.pages; + this.showAll = element.dataset.showAll === 'true'; + this.scrollbar = element.dataset.scrollbar === 'true'; + this.layout = element.dataset.layout || 'horizontal'; + this.pdfDoc = null; + this.scale = 1.0; + + this.init(); + } + + async init() { + const pdfjsLib = window['pdfjs-dist/build/pdf']; + pdfjsLib.GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; + + try { + this.pdfDoc = await pdfjsLib.getDocument(this.pdfUrl).promise; + await this.renderPages(); + } catch (error) { + console.error('Error loading PDF:', error); + } + } + + async renderPages() { + this.pagesContainer.innerHTML = ''; + + const totalPages = this.pdfDoc.numPages; + const pagesToRender = this.showAll ? totalPages : Math.min(parseInt(this.pages), totalPages); + + // Получаем первую страницу для определения масштаба + const firstPage = await this.pdfDoc.getPage(1); + const containerWidth = this.container.clientWidth; + const viewport = firstPage.getViewport({ scale: 1 }); + + // Настраиваем масштаб и стили контейнера в зависимости от layout + if (!this.showAll && pagesToRender > 1 && this.layout === 'horizontal') { + this.scale = (containerWidth / viewport.width) * 0.49; + this.pagesContainer.style.display = 'flex'; + this.pagesContainer.style.flexWrap = 'wrap'; + this.pagesContainer.style.justifyContent = 'space-between'; + } else { + this.scale = containerWidth / viewport.width; + this.pagesContainer.style.display = this.layout === 'vertical' ? 'block' : 'flex'; + this.pagesContainer.style.flexWrap = 'wrap'; + } + + // Настройка прокрутки + if (this.showAll && this.scrollbar) { + this.container.style.overflowY = 'auto'; + this.container.style.maxHeight = '800px'; + } else { + this.container.style.overflowY = 'hidden'; + this.container.style.maxHeight = 'none'; + } + + // Рендерим страницы + for (let pageNum = 1; pageNum <= pagesToRender; pageNum++) { + const pageContainer = document.createElement('div'); + pageContainer.classList.add('pdf-page-container'); + + if (!this.showAll && pagesToRender > 1) { + if (this.layout === 'horizontal') { + pageContainer.style.width = '49%'; + pageContainer.style.marginBottom = '10px'; + } else { + pageContainer.style.width = '100%'; + pageContainer.style.marginBottom = '20px'; + } + } + + const canvas = document.createElement('canvas'); + canvas.classList.add('pdf-canvas'); + pageContainer.appendChild(canvas); + this.pagesContainer.appendChild(pageContainer); + + const page = await this.pdfDoc.getPage(pageNum); + const scaledViewport = page.getViewport({ scale: this.scale }); + + canvas.height = scaledViewport.height; + canvas.width = scaledViewport.width; + + const renderContext = { + canvasContext: canvas.getContext('2d'), + viewport: scaledViewport + }; + + await page.render(renderContext).promise; + } + } + } + + // Инициализация виджетов + $(window).on('elementor/frontend/init', () => { + elementorFrontend.hooks.addAction('frontend/element_ready/pdf_viewer.default', ($element) => { + const widgets = $element[0].querySelectorAll('.pdf-viewer-widget'); + widgets.forEach(widget => new PDFViewer(widget)); + }); + }); + +})(jQuery); \ No newline at end of file diff --git a/includes/dynamic-tags/pdf-url.php b/includes/dynamic-tags/pdf-url.php new file mode 100644 index 0000000..ce6b9a1 --- /dev/null +++ b/includes/dynamic-tags/pdf-url.php @@ -0,0 +1,43 @@ +start_controls_section( + 'content_section', + [ + 'label' => 'Настройки PDF', + 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'pdf_source', + [ + 'label' => 'Источник PDF', + 'type' => \Elementor\Controls_Manager::SELECT, + 'default' => 'media', + 'options' => [ + 'media' => 'Медиафайл', + 'dynamic' => 'Динамический', + ], + ] + ); + + $this->add_control( + 'pdf_file', + [ + 'label' => 'PDF Файл', + 'type' => \Elementor\Controls_Manager::MEDIA, + 'media_types' => ['application/pdf'], + 'default' => [ + 'url' => '', + 'id' => null, + ], + 'condition' => [ + 'pdf_source' => 'media', + ], + ] + ); + + $this->add_control( + 'pdf_dynamic', + [ + 'label' => 'Динамический PDF', + 'type' => \Elementor\Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + \Elementor\Modules\DynamicTags\Module::URL_CATEGORY, + \Elementor\Modules\DynamicTags\Module::POST_META_CATEGORY, + \Elementor\Modules\DynamicTags\Module::MEDIA_CATEGORY, + ], + ], + 'condition' => [ + 'pdf_source' => 'dynamic', + ], + ] + ); + + $this->add_control( + 'show_all_pages', + [ + 'label' => 'Показать все страницы', + 'type' => \Elementor\Controls_Manager::SWITCHER, + 'label_on' => 'Да', + 'label_off' => 'Нет', + 'default' => '', + ] + ); + + $this->add_control( + 'pages_to_show', + [ + 'label' => 'Количество страниц для показа', + 'type' => \Elementor\Controls_Manager::NUMBER, + 'min' => 1, + 'max' => 100, + 'step' => 1, + 'default' => 1, + 'condition' => [ + 'show_all_pages' => '', + ], + ] + ); + + $this->add_control( + 'pages_layout', + [ + 'label' => 'Расположение страниц', + 'type' => \Elementor\Controls_Manager::SELECT, + 'default' => 'horizontal', + 'options' => [ + 'horizontal' => 'Горизонтально', + 'vertical' => 'Вертикально', + ], + 'condition' => [ + 'pages_to_show!' => '1', + ], + ] + ); + + $this->add_control( + 'enable_scrollbar', + [ + 'label' => 'Включить полосу прокрутки', + 'type' => \Elementor\Controls_Manager::SWITCHER, + 'label_on' => 'Да', + 'label_off' => 'Нет', + 'default' => '', + 'condition' => [ + 'show_all_pages' => 'yes', + ], + ] + ); + + $this->add_control( + 'pdf_file_style', + [ + 'label' => '', + 'type' => \Elementor\Controls_Manager::HIDDEN, + 'selectors' => [ + '{{WRAPPER}} .elementor-control-media' => 'border: 1px solid #d5dadf; padding: 7px; border-radius: 3px;', + '{{WRAPPER}} .elementor-control-media__content' => 'display: flex; align-items: center;', + '{{WRAPPER}} .elementor-control-media__preview' => 'height: 40px; width: auto; max-width: 100%; margin-right: 10px;', + '{{WRAPPER}} .elementor-control-media-upload-button' => 'margin-right: 10px;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'style_section', + [ + 'label' => 'Стили', + 'tab' => \Elementor\Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'background_color', + [ + 'label' => 'Цвет фона', + 'type' => \Elementor\Controls_Manager::COLOR, + 'default' => '#525659', + 'selectors' => [ + '{{WRAPPER}} .pdf-viewer-container' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'viewer_width', + [ + 'label' => 'Ширина виджета', + 'type' => \Elementor\Controls_Manager::SLIDER, + 'size_units' => ['px', '%'], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 2000, + 'step' => 1, + ], + '%' => [ + 'min' => 0, + 'max' => 100, + 'step' => 1, + ], + ], + 'default' => [ + 'unit' => '%', + 'size' => 100, + ], + 'selectors' => [ + '{{WRAPPER}} .pdf-viewer-widget' => 'width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + \Elementor\Group_Control_Border::get_type(), + [ + 'name' => 'border', + 'label' => 'Граница', + 'selector' => '{{WRAPPER}} .pdf-viewer-container', + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => 'Скругление углов', + 'type' => \Elementor\Controls_Manager::DIMENSIONS, + 'size_units' => ['px', '%'], + 'default' => [ + 'top' => 4, + 'right' => 4, + 'bottom' => 4, + 'left' => 4, + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .pdf-viewer-container' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + \Elementor\Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'label' => 'Тень', + 'selector' => '{{WRAPPER}} .pdf-viewer-container', + 'fields_options' => [ + 'box_shadow_type' => [ + 'default' => 'yes', + ], + 'box_shadow' => [ + 'default' => [ + 'horizontal' => 0, + 'vertical' => 2, + 'blur' => 5, + 'spread' => 0, + 'color' => 'rgba(0,0,0,0.2)', + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'padding', + [ + 'label' => 'Внутренний отступ', + 'type' => \Elementor\Controls_Manager::DIMENSIONS, + 'size_units' => ['px', 'em', '%'], + 'selectors' => [ + '{{WRAPPER}} .pdf-pages-container' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'default' => [ + 'top' => 20, + 'right' => 20, + 'bottom' => 20, + 'left' => 20, + 'unit' => 'px', + 'isLinked' => true, + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + $pdf_url = ''; + + if ($settings['pdf_source'] === 'media' && !empty($settings['pdf_file']['url'])) { + $pdf_url = $settings['pdf_file']['url']; + } elseif ($settings['pdf_source'] === 'dynamic' && !empty($settings['pdf_dynamic'])) { + $pdf_url = $settings['pdf_dynamic']; + } + + if (empty($pdf_url)) { + echo 'Пожалуйста, выберите PDF файл или настройте динамический источник'; + return; + } + + $show_all_pages = $settings['show_all_pages'] === 'yes'; + $pages_to_show = $show_all_pages ? 'all' : intval($settings['pages_to_show']); + $enable_scrollbar = $settings['enable_scrollbar'] === 'yes'; + $pages_layout = $settings['pages_layout']; + + ?> +
+
+
+
+
+
+ =')) { + add_action('admin_notices', [$this, 'admin_notice_minimum_elementor_version']); + return; + } + + // Проверка версии PHP + if (version_compare(PHP_VERSION, self::MINIMUM_PHP_VERSION, '<')) { + add_action('admin_notices', [$this, 'admin_notice_minimum_php_version']); + return; + } + + // Добавляем поддержку динамических тегов + add_action('elementor/dynamic_tags/register', [$this, 'register_dynamic_tags']); + + // Регистрируем виджет + add_action('elementor/widgets/register', [$this, 'register_widgets']); + + // Регистрируем скрипты и стили + add_action('elementor/frontend/after_register_scripts', [$this, 'register_scripts']); + add_action('elementor/frontend/after_register_styles', [$this, 'register_styles']); + } + + public function register_widgets($widgets_manager) { + require_once(__DIR__ . '/includes/widgets/pdf-viewer-widget.php'); + $widgets_manager->register(new \PDF_Viewer_Widget()); + } + + public function register_scripts() { + wp_register_script( + 'pdf-viewer-widget', + plugins_url('assets/js/pdf-viewer-widget.js', __FILE__), + ['jquery', 'pdfjs'], + self::VERSION, + true + ); + } + + public function register_styles() { + wp_register_style( + 'pdf-viewer-widget', + plugins_url('assets/css/pdf-viewer-widget.css', __FILE__), + [], + self::VERSION + ); + } + + public function register_dynamic_tags($dynamic_tags_manager) { + // Регистрируем категорию для PDF + \Elementor\Plugin::$instance->dynamic_tags->register_group( + 'pdf', + [ + 'title' => 'PDF' + ] + ); + + // Подключаем и регистрируем наш тег + require_once(__DIR__ . '/includes/dynamic-tags/pdf-url.php'); + $dynamic_tags_manager->register(new \PDF_Viewer_URL_Tag()); + } + + public function admin_notice_missing_main_plugin() { + if (isset($_GET['activate'])) unset($_GET['activate']); + + $message = sprintf( + esc_html__('"%1$s" requires "%2$s" to be installed and activated.', 'pdf-viewer-elementor'), + '' . esc_html__('PDF Viewer for Elementor', 'pdf-viewer-elementor') . '', + '' . esc_html__('Elementor', 'pdf-viewer-elementor') . '' + ); + + printf('

%1$s

', $message); + } + + public function add_pdf_mime_type($mimes) { + $mimes['pdf'] = 'application/pdf'; + return $mimes; + } + + public function update_pdf_attachment_details($response, $attachment) { + if ($response['mime'] === 'application/pdf') { + $response['icon'] = includes_url('images/media/document.png'); + $response['sizes'] = [ + 'full' => [ + 'url' => $response['url'], + ], + ]; + } + return $response; + } +} + +PDF_Viewer_Elementor::instance(); \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..42b791d --- /dev/null +++ b/readme.txt @@ -0,0 +1,113 @@ +=== PDF Viewer for Elementor === +Contributors: Alexander Golovin +Tags: elementor, pdf, viewer, pdf viewer, elementor widget +Requires at least: 5.0 +Tested up to: 6.8 +Requires PHP: 7.0 +Stable tag: 1.0.1 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +Добавляет виджет PDF Viewer для Elementor с расширенными возможностями просмотра PDF файлов. + +== Description == +PDF Viewer for Elementor позволяет легко добавлять и настраивать отображение PDF файлов на вашем сайте с помощью удобного виджета Elementor. + += Основные возможности = + +Выбор источника PDF файла: +* Загрузка из медиабиблиотеки WordPress +* Поддержка динамических данных (мета-поля, ACF поля, URL) +* Интеграция с медиабиблиотекой WordPress + +Настройки отображения страниц: +* Выбор количества отображаемых страниц +* Возможность показа всех страниц файла +* Два режима отображения страниц: + - Горизонтальное (страницы в ряд) + - Вертикальное (страницы друг под другом) +* Настраиваемая полоса прокрутки при просмотре всех страниц + +Гибкие настройки стилей: +* Настройка ширины виджета (пиксели или проценты) +* Настройка цвета фона +* Настройка границ: + - Толщина границ + - Цвет границ + - Стиль границ +* Настройка скругления углов +* Настройка тени: + - Горизонтальное и вертикальное смещение + - Размытие + - Растяжение + - Цвет тени +* Настройка внутренних отступов + += Особенности работы = +* Автоматическое масштабирование PDF под размер контейнера +* Поддержка адаптивного дизайна +* Оптимизированная загрузка страниц +* Интуитивно понятный интерфейс в стиле Elementor +* Полная интеграция с динамическими тегами Elementor + += Использование = +1. Установите и активируйте плагин +2. В редакторе Elementor найдите виджет "PDF Viewer" +3. Перетащите виджет в нужное место на странице +4. Выберите PDF файл из медиабиблиотеки или настройте динамический источник +5. Настройте параметры отображения и стили + += Поддержка динамического контента = +Плагин поддерживает различные источники динамического контента: +* Произвольные поля WordPress +* Поля ACF (при наличии плагина Advanced Custom Fields) +* URL-адреса +* Мета-поля постов и страниц +* Другие источники данных, поддерживаемые Elementor + += Технические требования = +* WordPress 5.0 или выше +* Elementor 3.0.0 или выше +* PHP 7.0 или выше +* Поддержка JavaScript в браузере + +== Installation == +1. Загрузите плагин в папку `/wp-content/plugins/` +2. Активируйте плагин через меню 'Плагины' в WordPress +3. Используйте виджет 'PDF Viewer' в редакторе Elementor + +== Frequently Asked Questions == + += Какие форматы файлов поддерживаются? = +Плагин поддерживает работу с файлами в формате PDF. + += Можно ли ограничить количество отображаемых страниц? = +Да, в настройках виджета можно указать конкретное количество страниц для отображения или показать все страницы документа. + += Поддерживается ли адаптивный дизайн? = +Да, виджет полностью адаптивен и корректно отображается на всех устройствах. + += Можно ли использовать PDF файлы из внешних источников? = +Да, через систему динамических тегов можно использовать PDF файлы из любых доступных источников. + +== Screenshots == +1. Интерфейс виджета в редакторе Elementor +2. Настройки отображения PDF +3. Настройки стилей +4. Пример отображения на сайте + +== Changelog == += 1.0.0 = +* Первый релиз +* Базовый функционал просмотра PDF +* Поддержка динамического контента +* Настройки отображения страниц +* Настройки стилей + +== Upgrade Notice == += 1.0.0 = +Первый релиз плагина. + +== Additional Info == +Автор: Александр Головин +Сайт автора: tooweb.ru \ No newline at end of file diff --git a/uninstall.php b/uninstall.php new file mode 100644 index 0000000..6917ab8 --- /dev/null +++ b/uninstall.php @@ -0,0 +1,18 @@ +query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_elementor_data' AND meta_value LIKE '%pdf_viewer%'"); + +// Очищаем кэш +wp_cache_flush(); \ No newline at end of file