ajax_handler = new ADVAIMG_Ajax_Handler(); $this->ai_edit = new ADVAIMG_AI_Edit(); add_action('admin_menu', [$this, 'add_menu']); add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']); // Add plugin action links add_filter('plugin_action_links_' . ADVAIMG_PLUGIN_BASENAME, [$this, 'add_plugin_action_links']); // Add "Advanced Edit" link to Media Library list view row actions add_filter('media_row_actions', [$this, 'add_media_row_action'], 10, 2); } /** * Add action links to plugin row * * @param array $links Existing plugin action links * @return array Modified links array */ public function add_plugin_action_links($links) { $editor_link = '' . esc_html__('Editor', 'advanced-pixel-editor') . ''; $settings_link = '' . esc_html__('Settings', 'advanced-pixel-editor') . ''; array_unshift($links, $settings_link); array_unshift($links, $editor_link); return $links; } /** * Add "Advanced Edit" link to Media Library list view row actions * * @param array $actions Existing row actions * @param \WP_Post $post The attachment post object * @return array Modified actions array */ public function add_media_row_action($actions, $post) { if (wp_attachment_is_image($post->ID)) { $url = admin_url('upload.php?page=' . self::MENU_SLUG . '&attachment_id=' . $post->ID); $link = '' . esc_html__('Advanced Edit', 'advanced-pixel-editor') . ''; // Insert after "edit" to appear right next to Edit $reordered = []; foreach ($actions as $key => $value) { $reordered[$key] = $value; if ($key === 'edit') { $reordered['advaimg_edit'] = $link; } } return $reordered; } return $actions; } /** * Add editor and settings pages under the Media menu */ public function add_menu() { add_media_page( esc_html__('Advanced Pixel Editor', 'advanced-pixel-editor'), esc_html__('Advanced Pixel Editor', 'advanced-pixel-editor'), 'upload_files', self::MENU_SLUG, [$this, 'render_page'] ); } /** * Get the current tab from the request * * @return string Current tab slug */ private function get_current_tab() { $tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'editor'; $valid = apply_filters('advaimg_valid_tabs', self::VALID_TABS); return in_array($tab, $valid, true) ? $tab : 'editor'; } /** * Enqueue admin assets (CSS and JS) * * @param string $hook Current admin page hook */ public function enqueue_assets($hook) { // Attachment edit page - add "Advanced Editor" button next to "Edit Image" if ($hook === 'post.php') { $post = get_post(); if ($post && wp_attachment_is_image($post->ID)) { $url = admin_url('upload.php?page=' . self::MENU_SLUG . '&attachment_id=' . $post->ID); // Inserted as HTML by jQuery, so escape for HTML first, then for the JS string. $label = esc_js(esc_html__('Advanced Editor', 'advanced-pixel-editor')); wp_add_inline_script('jquery', sprintf( 'jQuery(function($){var b=$("[id^=imgedit-open-btn-]");if(b.length){b.after(" %s");}});', esc_url($url), $label )); } } // Media Library page - add "Advanced Editor" button to grid view modal if ($hook === 'upload.php') { $ml_js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/media-library.js'; $ml_js_version = file_exists($ml_js_path) ? filemtime($ml_js_path) : self::VERSION; wp_enqueue_script( 'advaimg-media-library-js', ADVAIMG_PLUGIN_URL . 'assets/js/media-library.js', ['jquery', 'media-views'], $ml_js_version, true ); wp_localize_script('advaimg-media-library-js', 'ADVAIMG_MEDIA', [ 'editor_url' => admin_url('upload.php?page=' . self::MENU_SLUG), 'i18n' => [ // media-library.js inserts this as HTML. 'advanced_editor' => esc_html__('Advanced Editor', 'advanced-pixel-editor'), ], ]); } // Plugin page, enqueue admin CSS for all tabs if ($hook !== 'media_page_' . self::MENU_SLUG) { return; } $css_path = ADVAIMG_PLUGIN_DIR . 'assets/css/admin.css'; $css_version = file_exists($css_path) ? filemtime($css_path) : self::VERSION; wp_enqueue_style( 'advaimg-admin-css', ADVAIMG_PLUGIN_URL . 'assets/css/admin.css', [], $css_version ); // Only enqueue editor assets on the Editor tab $current_tab = $this->get_current_tab(); if ($current_tab !== 'editor') { return; } wp_enqueue_media(); // Enables WP media modal // Get file modification time for cache busting $js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/editor.js'; $js_version = file_exists($js_path) ? filemtime($js_path) : self::VERSION; // Enqueue JS wp_enqueue_script( 'advaimg-editor-js', ADVAIMG_PLUGIN_URL . 'assets/js/editor.js', ['jquery'], $js_version, true ); // Enqueue pure transform geometry helpers. $geometry_js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/transform-geometry.js'; $geometry_js_version = file_exists($geometry_js_path) ? filemtime($geometry_js_path) : self::VERSION; wp_enqueue_script( 'advaimg-transform-geometry-js', ADVAIMG_PLUGIN_URL . 'assets/js/transform-geometry.js', [], $geometry_js_version, true ); // Enqueue transform (crop & resize) JS $transform_js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/editor-transform.js'; $transform_js_version = file_exists($transform_js_path) ? filemtime($transform_js_path) : self::VERSION; wp_enqueue_script( 'advaimg-editor-transform-js', ADVAIMG_PLUGIN_URL . 'assets/js/editor-transform.js', ['jquery', 'advaimg-editor-js', 'advaimg-transform-geometry-js'], $transform_js_version, true ); // Enqueue rotate JS (depends on transform JS for the shared params object) $rotate_js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/editor-rotate.js'; $rotate_js_version = file_exists($rotate_js_path) ? filemtime($rotate_js_path) : self::VERSION; wp_enqueue_script( 'advaimg-editor-rotate-js', ADVAIMG_PLUGIN_URL . 'assets/js/editor-rotate.js', ['jquery', 'advaimg-editor-transform-js'], $rotate_js_version, true ); // Enqueue flip JS (depends on transform JS for the shared params object) $flip_js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/editor-flip.js'; $flip_js_version = file_exists($flip_js_path) ? filemtime($flip_js_path) : self::VERSION; wp_enqueue_script( 'advaimg-editor-flip-js', ADVAIMG_PLUGIN_URL . 'assets/js/editor-flip.js', ['jquery', 'advaimg-editor-transform-js'], $flip_js_version, true ); // Enqueue AI Edit JS $ai_js_path = ADVAIMG_PLUGIN_DIR . 'assets/js/editor-ai.js'; $ai_js_version = file_exists($ai_js_path) ? filemtime($ai_js_path) : self::VERSION; wp_enqueue_script( 'advaimg-editor-ai-js', ADVAIMG_PLUGIN_URL . 'assets/js/editor-ai.js', ['jquery', 'advaimg-editor-js', 'advaimg-editor-transform-js'], $ai_js_version, true ); // Localize script with translations and AJAX data $localize_data = [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('advaimg_nonce'), 'i18n' => [ 'select_image' => __('Select an Image to Edit', 'advanced-pixel-editor'), 'use_this_image' => __('Use This Image', 'advanced-pixel-editor'), 'saving' => __('Saving...', 'advanced-pixel-editor'), 'processing' => __('Processing...', 'advanced-pixel-editor'), 'no_image' => __('Please select an image first', 'advanced-pixel-editor'), 'save_success' => __('Image saved successfully!', 'advanced-pixel-editor'), 'preview_failed' => __('Failed to generate preview', 'advanced-pixel-editor'), 'network_error' => __('Network error - please try again', 'advanced-pixel-editor'), 'save_failed' => __('Failed to save image', 'advanced-pixel-editor'), 'confirm_save' => __('Save this edited image to your media library?', 'advanced-pixel-editor'), 'view_edited' => __('Would you like to view the edited image?', 'advanced-pixel-editor'), 'crop_not_ready' => __('The preview has not finished loading yet. Wait for it and apply the crop again.', 'advanced-pixel-editor'), 'unknown_error' => __('Unknown error occurred', 'advanced-pixel-editor'), 'rate_limit_error' => __('Too many requests. Please wait a moment before trying again.', 'advanced-pixel-editor'), 'reset_confirm' => __('Reset all filters to default values?', 'advanced-pixel-editor'), 'save_status_enabled' => __('Ready to save edited image', 'advanced-pixel-editor'), 'reset_status_enabled' => __('Ready to reset filters', 'advanced-pixel-editor'), 'save_button' => __('Save Edited Image', 'advanced-pixel-editor'), 'replace_button' => __('Replace Original', 'advanced-pixel-editor'), 'confirm_replace' => __('Replace the original image? A backup will be saved automatically.', 'advanced-pixel-editor'), 'restore_confirm' => __('Restore the original image? The current edited version will be replaced.', 'advanced-pixel-editor'), 'restore_success' => __('Original image restored successfully.', 'advanced-pixel-editor'), 'restore_failed' => __('Failed to restore original image.', 'advanced-pixel-editor'), 'replacing' => __('Replacing...', 'advanced-pixel-editor'), 'restoring' => __('Restoring...', 'advanced-pixel-editor'), 'crop' => __('Crop', 'advanced-pixel-editor'), 'resize' => __('Resize', 'advanced-pixel-editor'), 'dpi' => __('Resolution (DPI)', 'advanced-pixel-editor'), 'aspect_free' => _x('Free', 'crop aspect ratio preset without constraint', 'advanced-pixel-editor'), 'apply_crop' => __('Apply Crop', 'advanced-pixel-editor'), 'clear_crop' => __('Clear', 'advanced-pixel-editor'), 'lock_aspect' => __('Lock aspect ratio', 'advanced-pixel-editor'), 'width' => __('Width', 'advanced-pixel-editor'), 'height' => __('Height', 'advanced-pixel-editor'), 'resample' => __('Resample (scale pixels)', 'advanced-pixel-editor'), 'crop_hint' => __('Click on the image to start cropping', 'advanced-pixel-editor'), 'apply_resize' => __('Apply Resize', 'advanced-pixel-editor'), 'clear_resize' => __('Clear', 'advanced-pixel-editor'), 'apply_dpi' => __('Apply DPI', 'advanced-pixel-editor'), 'clear_dpi' => __('Clear', 'advanced-pixel-editor'), 'ai_generating' => __('Generating... this can take a minute.', 'advanced-pixel-editor'), 'ai_failed' => __('AI Edit failed', 'advanced-pixel-editor'), 'ai_prompt_empty' => __('Describe the change you want first.', 'advanced-pixel-editor'), 'ai_confirm' => __('Run AI Edit? Your AI provider bills each request, and your other adjustments will be reset.', 'advanced-pixel-editor'), 'ai_discard_confirm' => __('Discard the AI result and go back to the original image?', 'advanced-pixel-editor'), 'ai_open_connectors' => __('Open Settings > Connectors', 'advanced-pixel-editor'), 'ai_generate' => __('Generate', 'advanced-pixel-editor'), 'ai_refine' => __('Refine', 'advanced-pixel-editor'), ], 'ai' => [ 'available' => ADVAIMG_AI_Edit::is_available(), 'connectors_url' => ADVAIMG_AI_Edit::get_connectors_url(), 'source_field' => ADVAIMG_AI_Edit::SOURCE_FIELD, 'max_length' => ADVAIMG_AI_Edit::PROMPT_MAX_LENGTH, ], ]; $localize_data = apply_filters('advaimg_editor_localize_data', $localize_data); wp_localize_script('advaimg-editor-js', 'ADVAIMG_AJAX', $localize_data); } /** * Render the main plugin page with tabs */ public function render_page() { if (!current_user_can('upload_files')) { wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'advanced-pixel-editor')); } $current_tab = $this->get_current_tab(); // Settings tab requires manage_options if ($current_tab === 'settings' && !current_user_can('manage_options')) { wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'advanced-pixel-editor')); } $base_url = admin_url('upload.php?page=' . self::MENU_SLUG); // Preserve attachment_id across tab switches so the selected image persists $attachment_id = isset($_GET['attachment_id']) ? absint($_GET['attachment_id']) : 0; if ($attachment_id > 0) { $base_url = add_query_arg('attachment_id', $attachment_id, $base_url); } $tabs = [ 'editor' => __('Editor', 'advanced-pixel-editor'), 'settings' => __('Settings', 'advanced-pixel-editor'), 'about' => __('About', 'advanced-pixel-editor'), ]; // Only show Settings tab to users who can manage options if (!current_user_can('manage_options')) { unset($tabs['settings']); } $tabs = apply_filters('advaimg_admin_tabs', $tabs); ?>
About tab for more information about ImageMagick.', 'advanced-pixel-editor'), ['a' => ['href' => []]] ), esc_url(admin_url('upload.php?page=' . self::MENU_SLUG . '&tab=about')) ); ?>