| Server IP : 104.21.74.147 / Your IP : 216.73.217.154 Web Server : nginx/1.24.0 System : Linux wordpress-sites 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : www-data ( 33) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/rebeccaone.com/wp-content/plugins/envira-gallery/src/Admin/ |
Upload File : |
<?php
/**
* Admin Container
*
* @since 1.7.0
*
* @package Envira_Gallery
* @author Envira Gallery Team
*/
namespace Envira\Admin;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Envira\Admin\Settings;
use Envira\Admin\Metaboxes;
use Envira\Admin\Addons;
use Envira\Admin\Importers;
use Envira\Admin\Table;
use Envira\Admin\Posttype;
use Envira\Admin\Editor;
use Envira\Admin\License;
use Envira\Admin\Notices;
use Envira\Admin\Media_View;
use Envira\Admin\Support;
use Envira\Admin\Gutenberg;
use Envira\Admin\Tools;
use Envira\Utils\Copy;
use Envira\Admin\LicenseCheck;
use Envira\Admin\Permissions;
use Envira\Admin\Onboarding_Wizard;
use Envira\Admin\Menu_Nudge;
use Envira\Admin\Envira_Notifications;
use Envira\Admin\Deactivation_Survey;
// use Envira\Admin\Debug.
/**
* Admin Container
*
* @since 1.7.0
*
* @package Envira_Gallery
* @author Envira Gallery Team <[email protected]>
*/
class Admin_Container {
/**
* Holds all dismissed notices.
*
* @since 1.3.5
*
* @var array
*/
public $notices;
/**
* __construct function.
*
* @access public
* @return void
*/
public function __construct() {
// Actions.
add_action( 'admin_init', [ $this, 'add_capabilities' ] );
add_filter( 'wp_handle_upload', [ $this, 'fix_image_orientation' ] );
// Handle any necessary DB upgrades.
add_action( 'admin_init', [ $this, 'db_upgrade' ] );
// Add new section in WordPress media settings page.
add_action( 'admin_init', [ $this, 'envira_gallery_media_settings_add_new_section' ] );
// Load admin assets.
add_action( 'admin_enqueue_scripts', [ $this, 'admin_styles' ], 20 );
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
add_action( 'admin_head', [ $this, 'custom_admin_style' ], 1 );
add_action( 'admin_menu', [ $this, 'cdn_admin_menu' ], 12 );
// Delete any gallery association on attachment deletion. Also delete any extra cropped images.
add_action( 'delete_attachment', [ $this, 'delete_gallery_association' ] );
// Ensure gallery display is correct when trashing/untrashing galleries.
add_action( 'wp_trash_post', [ $this, 'trash_gallery' ] );
add_action( 'untrash_post', [ $this, 'untrash_gallery' ] );
// Delete attachments, if setting enabled, when a gallery is permanently deleted.
add_action( 'before_delete_post', [ $this, 'delete_gallery' ] );
// Prevent plugins from breaking Envira in admin.
add_action( 'wp_print_scripts', [ $this, 'plugin_humility' ], 1 );
add_action( 'admin_init', [ $this, 'fix_conflicts' ], 99 );
if ( is_plugin_active( 'enable-media-replace/enable-media-replace.php' ) ) {
add_action( 'enable-media-replace-upload-done', [ $this, 'emr_clear_envira_cache' ], 10, 3 );
}
// Populate $notices.
$this->notices = get_option( 'envira_gallery_notices' );
if ( ! is_array( $this->notices ) ) {
$this->notices = [];
}
$license_check = new LicenseCheck();
$posttype = new Posttype();
$settings = new Settings();
$metaboxes = new Metaboxes();
$importers = new Importers();
$table = new Table();
$editor = new Editor();
$license = new License();
$notices = new Notices();
$addons = new Addons();
$media_view = new Media_View();
$support = new Support();
$tools = new Tools();
$gutenberg = new Gutenberg();
$copy = new Copy();
$permissions = new Permissions();
$onboarding = new Onboarding_Wizard();
$notifications = new Envira_Notifications();
$notifications->hooks();
$onboarding->hooks();
$menu_nudge = new Menu_Nudge();
$menu_nudge->hooks();
$deactivation_survey = new Deactivation_Survey();
// Add action to output JS for Envira CDN menu item.
add_action( 'admin_footer', [ $this, 'admin_cdn_menu_target_blank' ] );
}
/**
* Add Beta Feedback Menu Item
*
* @since 1.9.0b
*
* @return void
*/
public function beta_admin_menu() {
global $submenu;
$url = 'https://enviragallery.com/account/beta-support/';
$submenu['edit.php?post_type=envira'][] = [ '<span style="color:#ad0023">Beta Feedback</span>', 'manage_options', $url ];
}
/**
* Add CDN Menu Item.
*
* @since 1.11.1-0
*
* @return void
*/
public function cdn_admin_menu() {
global $submenu;
// Only add CDN submenu if user has permission to edit Envira galleries
if ( ! current_user_can( 'edit_envira_galleries' ) ) {
return;
}
$utm = '?utm_source=envira-gallery&utm_medium=pro-plugin&utm_campaign=admin-menu&utm_content=' . ENVIRA_VERSION;
$url = 'https://enviragallery.com/cdn/' . $utm;
$cdn_menu = [
'Envira CDN<span class="cdn_new_badge">NEW</span>',
'edit_envira_galleries',
$url,
'envira-cdn-menu-item',
];
$submenu['edit.php?post_type=envira'][] = $cdn_menu;
}
/**
* Attempts to fix conflicts from other plugins.
*
* @since 1.1.0
*
* @return void
*/
public function fix_conflicts() {
global $pagenow;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required here.
if ( 'post.php' !== $pagenow && isset( $_GET['post'] ) && 'envira' !== get_post_type( sanitize_key( $_GET['post'] ) ) ) {
return;
}
if ( class_exists( 'WRGF' ) ) {
// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
global $WRGF;
remove_action( 'admin_print_scripts-post.php', [ $WRGF, 'wrgf_admin_print_scripts' ] );
remove_action( 'admin_print_scripts-post-new.php', [ $WRGF, 'wrgf_admin_print_scripts' ] );
// phpcs:enable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
}
/* Accordion Pro By Wpshopmart */
if ( function_exists( 'wpsm_ac_pro_editor_popup_content' ) ) {
remove_action( 'admin_footer', 'wpsm_ac_pro_editor_popup_content' );
}
/* Tabs Pro wpshopmart */
if ( function_exists( 'wpsm_tabs_pro_editor_popup_content' ) ) {
remove_action( 'admin_footer', 'wpsm_tabs_pro_editor_popup_content' );
}
/* Service Showcase Pro */
if ( function_exists( 'wpsm_service_box_pro_editor_popup_content' ) ) {
remove_action( 'admin_footer', 'wpsm_service_box_pro_editor_popup_content' );
}
/* Counter Number Showcase by wpshopmart */
if ( function_exists( 'wpsm_cns_editor_popup_content' ) ) {
remove_action( 'admin_footer', 'wpsm_cns_editor_popup_content' );
}
}
/**
* Checks if a given notice has been dismissed or not
*
* @since 1.3.5
*
* @param string $notice Programmatic Notice Name.
* @return bool Notice Dismissed
*/
public function is_dismissed( $notice ) {
if ( ! isset( $this->notices[ $notice ] ) ) {
return false;
}
return true;
}
/**
* Marks the given notice as dismissed
*
* @since 1.3.5
*
* @param string $notice Programmatic Notice Name.
*/
public function dismiss( $notice ) {
$this->notices[ $notice ] = true;
update_option( 'envira_gallery_notices', $this->notices );
}
/**
* Marks a notice as not dismissed
*
* @since 1.3.5
*
* @param string $notice Programmatic Notice Name.
*/
public function undismiss( $notice ) {
unset( $this->notices[ $notice ] );
update_option( 'envira_gallery_notices', $this->notices );
}
/**
* Displays an inline notice with some Envira styling.
*
* @since 1.3.5
*
* @param string $notice Programmatic Notice Name.
* @param string $title Title.
* @param string $message Message.
* @param string $type Message Type (updated|warning|error) - green, yellow/orange and red respectively.
* @param string $button_text Button Text (optional).
* @param string $button_url Button URL (optional).
* @param bool $is_dismissible User can Dismiss Message (default: true).
*/
public function display_inline_notice( $notice, $title, $message, $type = 'success', $button_text = '', $button_url = '', $is_dismissible = true ) {
// Check if the notice is dismissible, and if so has been dismissed.
if ( $is_dismissible && $this->is_dismissed( $notice ) ) {
// Nothing to show here, return!
return;
}
// Display inline notice.
?>
<div class="envira-notice <?php echo esc_attr( $type ) . ( $is_dismissible ? ' is-dismissible' : '' ); ?>" data-notice="<?php echo esc_attr( $notice ); ?>">
<?php
// Title.
if ( ! empty( $title ) ) {
?>
<p class="envira-intro"><?php echo esc_html( $title ); ?></p>
<?php
}
// Message.
if ( ! empty( $message ) ) {
?>
<p><?php echo esc_html( $message ); ?></p>
<?php
}
// Button.
if ( ! empty( $button_text ) && ! empty( $button_url ) ) {
?>
<a href="<?php echo esc_url( $button_url ); ?>" target="_blank" rel="noopener" class="button button-primary"><?php echo esc_html( $button_text ); ?></a>
<?php
}
// Dismiss Button.
if ( $is_dismissible ) {
?>
<button type="button" class="notice-dismiss">
<span class="screen-reader-text">
<?php esc_html_e( 'Dismiss this notice', 'envira-gallery' ); ?>
</span>
</button>
<?php
}
?>
</div>
<?php
}
/**
* Registers Envira Gallery capabilities for each Role, if they don't already exist.
*
* If capabilities don't exist, they're copied from Posts. This ensures users prior to 1.3.7
* get like-for-like behaviour in Envira and don't notice the new capabilities.
*
* @since 1.0.0
*/
public function add_capabilities() {
// Bail out if it is settings page update action.
// phpcs:ignore WordPress.Security.NonceVerification
if ( isset( $_POST['envira-permissions-nonce'] ) && sanitize_text_field( wp_unslash( $_POST['envira-permissions-nonce'] ) ) !== null ) {
return;
}
// Todo: Refactor this duplicate method to run only once, and not on every admin_init.
// Grab the administrator role, and if it already has an Envira capability key defined, bail
// as we only need to register our capabilities once.
$administrator = get_role( 'administrator' );
if ( $administrator->has_cap( 'edit_other_envira_galleries' ) ) {
return;
}
// If here, we need to assign capabilities
// Define the roles we want to assign capabilities to...
$roles = [
'administrator',
'editor',
'author',
'contributor',
'subscriber',
];
// Iterate through roles.
foreach ( $roles as $role_name ) {
// Properly get the role as WP_Role object.
$role = get_role( $role_name );
if ( ! is_object( $role ) ) {
continue;
}
// Map this Role's Post capabilities to our Envira Gallery capabilities.
$caps = [
'edit_envira_gallery' => $role->has_cap( 'edit_posts' ),
'read_envira_gallery' => $role->has_cap( 'read' ),
'delete_envira_gallery' => $role->has_cap( 'delete_posts' ),
'edit_envira_galleries' => $role->has_cap( 'edit_posts' ),
'edit_other_envira_galleries' => $role->has_cap( 'edit_others_posts' ),
'edit_others_envira_galleries' => $role->has_cap( 'edit_others_posts' ),
'publish_envira_galleries' => $role->has_cap( 'publish_posts' ),
'read_private_envira_galleries' => $role->has_cap( 'read_private_posts' ),
'delete_envira_galleries' => $role->has_cap( 'delete_posts' ),
'delete_private_envira_galleries' => $role->has_cap( 'delete_private_posts' ),
'delete_published_envira_galleries' => $role->has_cap( 'delete_published_posts' ),
'delete_others_envira_galleries' => $role->has_cap( 'delete_others_posts' ),
'edit_private_envira_galleries' => $role->has_cap( 'edit_private_posts' ),
'edit_published_envira_galleries' => $role->has_cap( 'edit_published_posts' ),
'create_envira_galleries' => $role->has_cap( 'edit_posts' ),
];
// Add the above Envira capabilities to this Role.
foreach ( $caps as $envira_cap => $value ) {
// Don't add if value is false.
if ( ! $value ) {
continue;
}
$role->add_cap( $envira_cap );
}
}
}
/**
* Check if the EXIF orientation flag matches one of the values we're looking for
* http://www.impulseadventure.com/photo/exif-orientation.html
*
* If it does, this means we need to rotate the image based on the orientation flag and then remove the flag.
* This will ensure the image has the correct orientation, regardless of where it's displayed.
*
* Whilst most browsers and applications will read this flag to perform the rotation on displaying just the image, it's
* not possible to do this in some situations e.g. displaying an image within a lightbox, or when the image is
* within HTML markup.
*
* Orientation flags we're looking for:
* 8: We need to rotate the image 90 degrees counter-clockwise
* 3: We need to rotate the image 180 degrees
* 6: We need to rotate the image 90 degrees clockwise (270 degrees counter-clockwise)
*
* @since 1.3.8.2
*
* @param array $file Uploaded File.
* @return array Uploaded File
*/
public function fix_image_orientation( $file ) {
// Check we have a file.
if ( ! file_exists( $file['file'] ) ) {
return $file;
}
// Check we have a JPEG.
if ( 'image/jpg' !== $file['type'] && 'image/jpeg' !== $file['type'] ) {
return $file;
}
// Attempt to read EXIF data from the image.
$exif_data = wp_read_image_metadata( $file['file'] );
if ( ! $exif_data ) {
return $file;
}
// Check if an orientation flag exists.
if ( ! isset( $exif_data['orientation'] ) ) {
return $file;
}
// Check if the orientation flag matches one we're looking for.
$required_orientations = [ 8, 3, 6 ];
if ( ! in_array( $exif_data['orientation'], $required_orientations, true ) ) {
return $file;
}
// If here, the orientation flag matches one we're looking for
// Load the WordPress Image Editor class.
$image = wp_get_image_editor( $file['file'] );
if ( is_wp_error( $image ) ) {
// Something went wrong - abort.
return $file;
}
// Store the source image EXIF and IPTC data in a variable, which we'll write
// back to the image once its orientation has changed
// This is required because when we save an image, it'll lose its metadata.
$source_size = getimagesize( $file['file'], $image_info );
// Depending on the orientation flag, rotate the image.
switch ( $exif_data['orientation'] ) {
/**
* Rotate 90 degrees counter-clockwise
*/
case 8:
$image->rotate( 90 );
break;
/**
* Rotate 180 degrees
*/
case 3:
$image->rotate( 180 );
break;
/**
* Rotate 270 degrees counter-clockwise ($image->rotate always works counter-clockwise)
*/
case 6:
$image->rotate( 270 );
break;
}
// Save the image, overwriting the existing image
// This will discard the EXIF and IPTC data.
$image->save( $file['file'] );
// Drop the EXIF orientation flag, otherwise applications will try to rotate the image
// before display it, and we don't need that to happen as we've corrected the orientation
// Write the EXIF and IPTC metadata to the revised image.
$result = $this->transfer_iptc_exif_to_image( $image_info, $file['file'], $exif_data['orientation'] );
if ( ! $result ) {
return $file;
}
// Read the image again to see if the EXIF data was preserved.
$exif_data = wp_read_image_metadata( $file['file'] );
// Finally, return the data that's expected.
return $file;
}
/**
* Transfers IPTC and EXIF data from a source image which contains either/both,
* and saves it into a destination image's headers that might not have this IPTC
* or EXIF data
*
* Useful for when you edit an image through PHP and need to preserve IPTC and EXIF
* data
*
* @since 1.3.8.2
*
* @source http://php.net/iptcembed - ebashkoff at gmail dot com
*
* @param string $image_info EXIF and IPTC image information from the source image, using getimagesize().
* @param string $destination_image Path and File of Destination Image, which needs IPTC and EXIF data.
* @param int $original_orientation The image's original orientation, before we changed it.
* Used when we replace this orientation in the EXIF data.
* @return bool Success
*/
private function transfer_iptc_exif_to_image( $image_info, $destination_image, $original_orientation ) {
// Check destination exists.
if ( ! file_exists( $destination_image ) ) {
return false;
}
// Get EXIF data from the image info, and create the IPTC segment.
$exif_data = ( ( is_array( $image_info ) && key_exists( 'APP1', $image_info ) ) ? $image_info['APP1'] : null );
if ( $exif_data ) {
// Find the image's original orientation flag, and change it to 1
// This prevents applications and browsers re-rotating the image, when we've already performed that function.
$exif_data = str_replace( chr( dechex( $original_orientation ) ), chr( 0x1 ), $exif_data );
$exif_length = strlen( $exif_data ) + 2;
if ( $exif_length > 0xFFFF ) {
return false;
}
// Construct EXIF segment.
$exif_data = chr( 0xFF ) . chr( 0xE1 ) . chr( ( $exif_length >> 8 ) & 0xFF ) . chr( $exif_length & 0xFF ) . $exif_data;
}
// Get IPTC data from the source image, and create the IPTC segment.
$iptc_data = ( ( is_array( $image_info ) && key_exists( 'APP13', $image_info ) ) ? $image_info['APP13'] : null );
if ( $iptc_data ) {
$iptc_length = strlen( $iptc_data ) + 2;
if ( $iptc_length > 0xFFFF ) {
return false;
}
// Construct IPTC segment.
$iptc_data = chr( 0xFF ) . chr( 0xED ) . chr( ( $iptc_length >> 8 ) & 0xFF ) . chr( $iptc_length & 0xFF ) . $iptc_data;
}
// Get the contents of the destination image.
$remote_path = wp_remote_get( $destination_image );
$destination_image_contents = wp_remote_retrieve_body( $remote_path );
if ( ! $destination_image_contents ) {
return false;
}
if ( strlen( $destination_image_contents ) === 0 ) {
return false;
}
// Build the EXIF and IPTC data headers.
$destination_image_contents = substr( $destination_image_contents, 2 );
$portion_to_add = chr( 0xFF ) . chr( 0xD8 ); // Variable accumulates new & original IPTC application segments.
$exif_added = ! $exif_data;
$iptc_added = ! $iptc_data;
while ( ( substr( $destination_image_contents, 0, 2 ) & 0xFFF0 ) === 0xFFE0 ) {
$segment_length = ( substr( $destination_image_contents, 2, 2 ) & 0xFFFF );
$iptc_segment_number = ( substr( $destination_image_contents, 1, 1 ) & 0x0F ); // Last 4 bits of second byte is IPTC segment #.
if ( $segment_length <= 2 ) {
return false;
}
$thisexistingsegment = substr( $destination_image_contents, 0, $segment_length + 2 );
if ( ( 1 <= $iptc_segment_number ) && ( ! $exif_added ) ) {
$portion_to_add .= $exif_data;
$exif_added = true;
if ( 1 === $iptc_segment_number ) {
$thisexistingsegment = '';
}
}
if ( ( 13 <= $iptc_segment_number ) && ( ! $iptc_added ) ) {
$portion_to_add .= $iptc_data;
$iptc_added = true;
if ( 13 === $iptc_segment_number ) {
$thisexistingsegment = '';
}
}
$portion_to_add .= $thisexistingsegment;
$destination_image_contents = substr( $destination_image_contents, $segment_length + 2 );
}
// Write the EXIF and IPTC data to the new file.
if ( ! $exif_added ) {
$portion_to_add .= $exif_data;
}
if ( ! $iptc_added ) {
$portion_to_add .= $iptc_data;
}
$output_file = fopen( $destination_image, 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
if ( $output_file ) {
return fwrite( $output_file, $portion_to_add . $destination_image_contents ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
}
return false;
}
/**
* Handles any necessary DB upgrades for Envira.
*
* @since 1.0.0
*/
public function db_upgrade() {
// Upgrade to allow captions (v1.1.6).
$captions = get_option( 'envira_gallery_116' );
if ( ! $captions ) {
$galleries = ( class_exists( 'Envira_Gallery' ) ? \Envira_Gallery::get_instance()->_get_galleries() : \Envira_Gallery_Lite::get_instance()->_get_galleries() );
if ( $galleries ) {
foreach ( $galleries as $gallery ) {
foreach ( (array) $gallery['gallery'] as $id => $item ) {
$gallery['gallery'][ $id ]['caption'] = ! empty( $item['title'] ) ? $item['title'] : '';
update_post_meta( $gallery['id'], '_eg_gallery_data', $gallery );
envira_flush_gallery_caches( $gallery['id'], $gallery['config']['slug'] );
}
}
}
update_option( 'envira_gallery_116', true );
}
// 1.2.1: Convert all non-Envira Post Type galleries into Envira CPT galleries.
$cpt_galleries = get_option( 'envira_gallery_121' );
if ( ! $cpt_galleries ) {
// Get Post Types, excluding our own
// We don't use post_status => 'any', as this doesn't include CPTs where exclude_from_search = true.
$post_types = get_post_types(
[
'public' => true,
]
);
$excluded_post_types = [ 'envira', 'envira_album', 'attachment' ];
foreach ( $post_types as $key => $post_type ) {
if ( in_array( $post_type, $excluded_post_types, true ) ) {
unset( $post_types[ $key ] );
}
}
// Get all Posts that have _eg_gallery_data set.
$in_post_galleries = new \WP_Query(
[
'post_type' => $post_types,
'post_status' => 'any',
'posts_per_page' => -1,
'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
[
'key' => '_eg_gallery_data',
'compare' => 'EXISTS',
],
],
]
);
// Check if any Posts with galleries exist.
if ( count( $in_post_galleries->posts ) > 0 ) {
$migrated_galleries = 0;
// Iterate through Posts with Galleries.
foreach ( $in_post_galleries->posts as $post ) {
// Check if this is an Envira or Envira Album CPT
// If so, skip it.
if ( 'envira' === $post->post_type || 'envira_album' === $post->post_type ) {
continue;
}
// Get metadata.
$data = get_post_meta( $post->ID, '_eg_gallery_data', true );
$in = get_post_meta( $post->ID, '_eg_in_gallery', true );
// Check if there is at least one image in the gallery
// Some Posts save Envira config data but don't have images - we don't want to migrate those,
// as we would end up with blank Envira CPT galleries.
if ( ! isset( $data['gallery'] ) || ! is_array( $data['gallery'] ) ) {
continue;
}
// If here, we need to create a new Envira CPT.
$cpt_args = [
'post_title' => ( ! empty( $data['config']['title'] ) ? $data['config']['title'] : $post->post_title ),
'post_status' => $post->post_status,
'post_type' => 'envira',
'post_author' => $post->post_author,
];
if ( ! empty( $data['config']['slug'] ) ) {
$cpt_args['post_name'] = $data['config']['slug'];
}
$envira_gallery_id = wp_insert_post( $cpt_args );
// Check gallery creation was successful.
if ( is_wp_error( $envira_gallery_id ) ) {
// @TODO how to handle errors?
continue;
}
// Get Envira Gallery Post.
$envira_post = get_post( $envira_gallery_id );
// Map the title and slug of the post object to the custom fields if no value exists yet.
$data['config']['title'] = trim( wp_strip_all_tags( $envira_post->post_title ) );
$data['config']['slug'] = sanitize_text_field( $envira_post->post_name );
// Store post metadata.
update_post_meta( $envira_gallery_id, '_eg_gallery_data', $data );
update_post_meta( $envira_gallery_id, '_eg_in_gallery', $in );
update_post_meta( $envira_gallery_id, '_eg_gallery_old', $post->ID );
if ( ! empty( $data['config']['slug'] ) ) {
update_post_meta( $envira_gallery_id, '_eg_gallery_old_slug', $data['config']['slug'] );
}
// Remove post metadata from the original Post.
delete_post_meta( $post->ID, '_eg_gallery_data' );
delete_post_meta( $post->ID, '_eg_in_gallery' );
// Search for the envira shortcode in the Post content, and change its ID to the new Envira Gallery ID.
if ( has_shortcode( $post->post_content, 'envira-gallery' ) ) {
$pattern = get_shortcode_regex();
if ( preg_match_all( '/' . $pattern . '/s', $post->post_content, $matches ) ) {
foreach ( $matches[2] as $key => $shortcode ) {
if ( 'envira-gallery' === $shortcode ) {
// Found an envira-gallery shortcode
// Change the ID.
$original_shortcode = $matches[0][ $key ];
$replacement_shortcode = str_replace( 'id="' . $post->ID . '"', 'id="' . $envira_gallery_id . '"', $original_shortcode );
$post->post_content = str_replace( $original_shortcode, $replacement_shortcode, $post->post_content );
wp_update_post( $post );
}
}
}
}
// Store a relationship between the gallery and this Post.
update_post_meta( $post->ID, '_eg_gallery_id', $envira_gallery_id );
// Increment the counter.
++$migrated_galleries;
}
// Display a one time admin notice so the user knows their in-page galleries were migrated.
if ( $migrated_galleries > 0 ) {
add_action( 'admin_notices', [ $this, 'notice_galleries_migrated' ] );
}
}
// Force the tags addon to convert any tags to the new CPT system for any galleries that have been converted to Envira post type.
delete_option( 'envira_tags_taxonomy_migrated' );
// Mark upgrade as complete.
update_option( 'envira_gallery_121', true );
}
}
/**
* Displays a notice on screen when a user upgrades from Lite to Pro or Lite to Lite 1.5.x,
* telling them that their in-page galleries have been migrated.
*
* @since 1.5.0
*/
public function notice_galleries_migrated() {
?>
<div class="notice updated">
<p><strong><?php esc_html_e( 'Envira Gallery:', 'envira-gallery' ); ?></strong><?php esc_html_e( ' Your existing in-page Galleries can now be found by clicking on Envira Gallery in the WordPress Admin menu.', 'envira-gallery' ); ?></p>
</div>
<?php
}
/**
* Loads styles for all Envira-based Administration Screens.
*
* @since 1.3.1
*/
public function admin_styles() {
// Get current screen.
$screen = get_current_screen();
// if we are on the widget page, load the admin css.
if ( 'widgets' === $screen->id || 'customize' === $screen->id ) {
wp_register_style( ENVIRA_SLUG . '-admin-style', plugins_url( 'assets/css/admin.css', ENVIRA_FILE ), [], ENVIRA_VERSION );
wp_enqueue_style( ENVIRA_SLUG . '-admin-style' );
} elseif ( 'envira' !== $screen->post_type && 'envira_album' !== $screen->post_type ) {
// If we're not on the Envira Post Type screen, only load the modal and minimum css then bail.
wp_register_style( ENVIRA_SLUG . '-admin-min-style', plugins_url( 'assets/css/admin-min.css', ENVIRA_FILE ), [], ENVIRA_VERSION );
wp_enqueue_style( ENVIRA_SLUG . '-admin-min-style' );
wp_register_style( ENVIRA_SLUG . '-admin-modal-style', plugins_url( 'assets/css/admin-modal.css', ENVIRA_FILE ), [], ENVIRA_VERSION );
wp_enqueue_style( ENVIRA_SLUG . '-admin-modal-style' );
} else {
// Proceed loading remaining admin CSS necessary admin styles.
wp_register_style( ENVIRA_SLUG . '-admin-style', plugins_url( 'assets/css/admin.css', ENVIRA_FILE ), [], ENVIRA_VERSION );
wp_enqueue_style( ENVIRA_SLUG . '-admin-style' );
wp_register_style( ENVIRA_SLUG . '-admin-modal-style', plugins_url( 'assets/css/admin-modal.css', ENVIRA_FILE ), [], ENVIRA_VERSION );
wp_enqueue_style( ENVIRA_SLUG . '-admin-modal-style' );
}
// Fire a hook to load in custom admin styles.
do_action( 'envira_gallery_admin_styles' );
}
/**
* Force special css to load at the top of the page before most anything else
*
* @since 1.8.1
*
* @return null
*/
public function custom_admin_style() {
// Add CDN menu item's styles.
echo '<style>
ul#adminmenu li.menu-icon-envira ul.wp-submenu span.cdn_new_badge {
color:#f18200;
vertical-align:super;
font-size:9px;
margin-left:3px;
}
</style>';
// Get current screen.
$screen = get_current_screen();
// Bail if we're not on the Envira Post Type screen or in widgets.
if ( 'envira' !== $screen->post_type && 'envira_album' !== $screen->post_type ) {
return;
}
echo '<style>
#screen-meta-links {
display: none;
}
</style>';
}
/**
* Loads scripts for all Envira-based Administration Screens.
*
* @since 1.3.5
*
* @param string $hook Page Hook.
* @return null Return early if not on the proper screen.
*/
public function admin_scripts( $hook ) {
// Get current screen.
$screen = get_current_screen();
// Include notice js, because notices might need to be displayed on pages other than Envira's.
wp_register_script( ENVIRA_SLUG . '-admin-notice-script', plugins_url( 'assets/js/min/notices-min.js', ENVIRA_FILE ), [ 'jquery' ], ENVIRA_VERSION, false );
wp_enqueue_script( ENVIRA_SLUG . '-admin-notice-script' );
wp_localize_script(
ENVIRA_SLUG . '-admin-notice-script',
'envira_gallery_admin',
[
'ajax' => admin_url( 'admin-ajax.php' ),
'dismiss_notice_nonce' => wp_create_nonce( 'envira-gallery-dismiss-notice' ),
]
);
wp_enqueue_style( 'envira-notice-css', plugins_url( 'assets/css/notices.css', ENVIRA_FILE ), false, ENVIRA_VERSION );
// Bail if we're not on the Envira Post Type screen or in widgets.
if ( 'envira' !== $screen->post_type && 'envira_album' !== $screen->post_type && 'widgets' !== $screen->base ) {
return;
}
// Load necessary admin scripts.
wp_register_script( ENVIRA_SLUG . '-admin-script', envira_script( 'assets/js/min/admin-min.js', ENVIRA_FILE ), [ 'jquery' ], ENVIRA_VERSION, [] );
wp_enqueue_script( ENVIRA_SLUG . '-choice-js', plugins_url( 'assets/js/lib/choices.min.js', ENVIRA_FILE ), [ 'jquery' ], ENVIRA_VERSION, false );
if ( 'envira_page_envira-gallery-settings' === $hook ) {
$permissions = new Permissions();
$permissions->envira_permissions_localize();
// For permisson modal.
wp_register_script( ENVIRA_SLUG . '-permissions-modal-js', plugins_url( 'assets/js/lib/a11y-dialog.min.js', ENVIRA_FILE ), [], ENVIRA_VERSION, false );
wp_enqueue_script( ENVIRA_SLUG . '-permissions-modal-js' );
}
wp_enqueue_script( ENVIRA_SLUG . '-admin-script' );
wp_localize_script(
ENVIRA_SLUG . '-admin-script',
'envira_gallery_admin_notifications',
[
'dismiss_notification_nonce' => wp_create_nonce( 'envira_dismiss_notification' ),
'dismiss_upsell_nonce' => wp_create_nonce( 'envira-dismiss-upsell' ),
]
);
if ( 'widgets.php' === $hook || 'post.php' === $hook || 'envira_page_envira-gallery-settings' === $hook || ( 'post-new.php' === $hook && isset( $_GET['post_type'] ) && 'envira' === $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_enqueue_style( 'envira-choice-css', plugins_url( 'assets/css/choices.css', ENVIRA_FILE ), false, ENVIRA_VERSION );
}
// Load gallery convert script.
wp_register_script( ENVIRA_SLUG . '-convert-gallery-script', envira_script( 'assets/js/min/gallery-convert-min.js', ENVIRA_FILE ), [ 'jquery' ], ENVIRA_VERSION, false );
wp_enqueue_script( ENVIRA_SLUG . '-convert-gallery-script' );
wp_localize_script(
ENVIRA_SLUG . '-convert-gallery-script',
'envira_gallery_convert',
[
'bulk_convert_rest_url' => rest_url( 'envira-convert/v1/bulk-convert' ),
'process_item_rest_url' => rest_url( 'envira-convert/v1/process-gallery' ),
'gallery_convert_rest_nonce' => wp_create_nonce( 'wp_rest' ),
'bulk_confirmation_alert' => __( 'Are you sure you want to convert? This action cannot be reversed.', 'envira-gallery' ),
'bulk_conversion_started' => __( 'Gallery conversion in progress... Please do not close this window or refresh the page until the process is complete.', 'envira-gallery' ),
'found_posts_text' => __( 'posts found containing WordPress galleries.', 'envira-gallery' ),
'conversion_completed' => __( 'Gallery conversion completed.', 'envira-gallery' ),
'failed_conversion_logs' => __( 'Failed Conversion Logs:', 'envira-gallery' ),
'post_id_text' => __( 'Post ID', 'envira-gallery' ),
'edit_post_text' => __( 'Edit Post', 'envira-gallery' ),
]
);
// Fire a hook to load in custom admin scripts.
do_action( 'envira_gallery_admin_scripts' );
}
/**
* Deletes the Envira gallery association for the image being deleted.
*
* @since 1.0.0
*
* @param int $attach_id The attachment ID being deleted.
*/
public function delete_gallery_association( $attach_id ) {
$has_gallery = get_post_meta( $attach_id, '_eg_has_gallery', true );
// Only proceed if the image is attached to any Envira galleries.
if ( ! empty( $has_gallery ) ) {
foreach ( (array) $has_gallery as $post_id ) {
// Remove the in_gallery association.
$in_gallery = get_post_meta( $post_id, '_eg_in_gallery', true );
if ( ! empty( $in_gallery ) ) {
$in_key = array_search( $attach_id, (array) $in_gallery, true );
if ( false !== $in_key && ! empty( $key ) && isset( $in_gallery[ $key ] ) ) {
unset( $in_gallery[ $key ] );
}
}
update_post_meta( $post_id, '_eg_in_gallery', $in_gallery );
// Remove the image from the gallery altogether.
$gallery_data = get_post_meta( $post_id, '_eg_gallery_data', true );
if ( ! empty( $gallery_data['gallery'] ) ) {
unset( $gallery_data['gallery'][ $attach_id ] );
}
// Update the post meta for the gallery.
update_post_meta( $post_id, '_eg_gallery_data', $gallery_data );
// Flush necessary gallery caches.
envira_flush_gallery_caches( $post_id, ( ! empty( $gallery_data['config']['slug'] ) ? $gallery_data['config']['slug'] : '' ) );
}
}
}
/**
* Trash a gallery when the gallery post type is trashed.
*
* @since 1.0.0
*
* @param integer $id The post ID being trashed.
* @return null Return early if no gallery is found.
*/
public function trash_gallery( $id ) {
$gallery = get_post( $id );
// Return early if not an Envira gallery.
if ( 'envira' !== $gallery->post_type ) {
return;
}
// Flush necessary gallery caches to ensure trashed galleries are not showing.
envira_flush_gallery_caches( $id );
// Check some gallery data exists.
$gallery_data = get_post_meta( $id, '_eg_gallery_data', true );
if ( empty( $gallery_data ) ) {
return;
}
// Set the gallery status to inactive.
$gallery_data['status'] = 'inactive';
update_post_meta( $id, '_eg_gallery_data', $gallery_data );
// Allow other addons to run routines when a Gallery is trashed.
do_action( 'envira_gallery_trash', $id, $gallery_data );
}
/**
* Untrash a gallery when the gallery post type is untrashed.
*
* @since 1.0.0
*
* @param integer $id The post ID being untrashed.
* @return null Return early if no gallery is found.
*/
public function untrash_gallery( $id ) {
$gallery = get_post( $id );
// Flush necessary gallery caches to ensure untrashed galleries are showing.
envira_flush_gallery_caches( $id );
// Return early if not an Envira gallery.
if ( 'envira' !== $gallery->post_type ) {
return;
}
// Set the gallery status to inactive.
$gallery_data = get_post_meta( $id, '_eg_gallery_data', true );
if ( empty( $gallery_data ) ) {
return;
}
if ( isset( $gallery_data['status'] ) ) {
unset( $gallery_data['status'] );
}
update_post_meta( $id, '_eg_gallery_data', $gallery_data );
// Allow other addons to run routines when a Gallery is untrashed.
do_action( 'envira_gallery_untrash', $id, $gallery_data );
}
/**
* Fired when a gallery is about to be permanently deleted from Trash
*
* Checks if the media_delete setting is enabled, and if so safely deletes
* media that isn't being used elsewhere on the site
*
* @since 1.3.6.1
*
* @param int $id Post ID.
* @return null
*/
public function delete_gallery( $id ) {
// Check if the media_delete setting is enabled.
$media_delete = envira_get_setting( 'media_delete' );
if ( '1' !== $media_delete ) {
return;
}
// Get post.
$gallery = get_post( $id );
// Flush necessary gallery caches to ensure untrashed galleries are showing.
envira_flush_gallery_caches( $id );
// Return early if not an Envira gallery.
if ( 'envira' !== $gallery->post_type ) {
return;
}
// Determine what images are inside this gallery, thanks to Envira meta-data.
$in_gallery = get_post_meta( $id, '_eg_in_gallery', true );
if ( ! is_array( $in_gallery ) ) {
return;
}
// Iterate through media, deleting - making sure to delete only images that aren't in another gallery.
foreach ( $in_gallery as $attach_id ) {
$attachment = get_post( $attach_id );
$has_gallery = get_post_meta( $attach_id, '_eg_has_gallery', true );
// If post parent is the Gallery ID, and the image isn't in another gallery, we're OK to delete the image.
if ( ( $attachment->post_parent === $id || in_array( $attach_id, $in_gallery, true ) ) && ( count( [ $has_gallery ] ) === 1 ) ) { // the "1" should mean only one gallery - the one we are deleting.
wp_delete_attachment( $attach_id );
}
}
}
/**
* I'm sure some plugins mean well, but they go a bit too far trying to reduce
* conflicts without thinking of the consequences.
*
* 1. Elementor doesn't play nice with instagram activation, settings, and envira things
*
* @since 1.0.0
*/
public function plugin_humility() {
global $wp_scripts;
if ( ! is_admin() ) {
return;
}
if ( class_exists( 'Elementor\\Admin' ) ) {
// Remove Custom Post Type Support For Various Plugins In The Admin.
remove_post_type_support( 'envira', 'elementor' );
remove_post_type_support( 'envira_albums', 'elementor' );
if ( isset( $_GET['post'] ) && ( 'envira' === get_post_type( intval( $_GET['post'] ) ) || 'envira_albums' === get_post_type( intval( $_GET['post'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_dequeue_script( 'elementor-admin-app' );
}
}
if ( is_plugin_active( 'advanced-rich-text-tools/index.php' ) ) {
if ( isset( $_GET['post'] ) && ( 'envira' === get_post_type( intval( $_GET['post'] ) ) || 'envira_albums' === get_post_type( intval( $_GET['post'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_dequeue_script( 'advanced-rich-text-tools' );
}
}
}
/**
* Specific function that clears gallery cache when a photo gets replaced by other third-party plugins.
*
* @since 1.9.3.1
*
* @param string $target_url Target URL.
* @param string $source_url Source URL.
* @param int $post_id Post ID.
*/
public function emr_clear_envira_cache( $target_url = false, $source_url = false, $post_id = false ) {
$has_gallery = get_post_meta( $post_id, '_eg_has_gallery', true );
if ( count( (array) $has_gallery ) > 0 ) :
foreach ( $has_gallery as $gallery_id ) {
envira_flush_gallery_caches( $gallery_id );
}
endif;
}
/**
* Callback for the Envira Gallery Media Settings section.
*
* @return void
*/
public function envira_gallery_media_settings_add_new_section() {
// Register a new section in the "Media" settings page.
add_settings_section(
'envira_gallery_convert_section',
__( 'Envira Gallery', 'envira-gallery' ),
[ $this, 'envira_gallery_convert_section_callback' ],
'media'
);
}
/**
* Callback for the Envira Gallery content section.
*
* @return void
*/
public function envira_gallery_convert_section_callback() {
?>
<p><?php esc_html_e( 'Convert all of your existing WP Galleries to Envira Galleries. Take advantage of advanced options like drag and drop features, themes, lightboxes, and much more.', 'envira-gallery' ); ?></p>
<?php
$convert_button = __( 'Convert to Envira Gallery', 'envira-gallery' );
?>
<p><a href="<?php echo esc_url( admin_url( 'edit.php?post_type=envira&page=envira-gallery-settings#!envira-tab-convert_to_envira' ) ); ?>" title="<?php echo esc_attr( $convert_button ); ?>" class="button button-secondary envira-media-settings-convert-btn"><?php echo esc_attr( $convert_button ); ?></a></p>
<?php
}
/**
* Renders the post types dropdown.
*
* @return void
*/
public function envira_render_post_types_dropdown() {
// Exclusions for keywords, exact names, and labels.
$exclusions = [
'keywords' => [ 'envira', 'ngg', 'nextgen', 'photocrati', 'gallery', 'elementor' ],
'exact_names' => [ 'e-landing-page' ],
'labels' => [ 'NextGEN', 'Gallery' ],
];
// Retrieve all custom post types.
$post_types = get_post_types( [ '_builtin' => false ], 'objects' );
// Filter post types based on exclusions.
$filtered_post_types = array_filter(
$post_types,
function ( $post_type ) use ( $exclusions ) {
// Exclude based on keywords in the post type name.
foreach ( $exclusions['keywords'] as $keyword ) {
if ( strpos( $post_type->name, $keyword ) !== false ) {
return false;
}
}
// Exclude based on exact post type names.
if ( in_array( $post_type->name, $exclusions['exact_names'], true ) ) {
return false;
}
// Exclude based on labels.
foreach ( $exclusions['labels'] as $label ) {
if ( strpos( $post_type->label, $label ) !== false ) {
return false;
}
}
return true; // Include post type if it passes all checks.
}
);
// Allow users to modify the post types list.
$filtered_post_types = apply_filters( 'envira_convert_post_types', $filtered_post_types );
?>
<div class="envira-posttype-dropdown-section">
<select name="envira_convert_post_types_dropdown" id="envira_convert_post_types_dropdown">
<option value=""><?php esc_html_e( 'Select Post Type for Conversion', 'envira-gallery' ); ?></option>
<option value="post"><?php esc_html_e( 'Posts', 'envira-gallery' ); ?></option>
<option value="page"><?php esc_html_e( 'Pages', 'envira-gallery' ); ?></option>
<?php
foreach ( $filtered_post_types as $post_type ) {
?>
<option value="<?php echo esc_html( $post_type->name ); ?>">
<?php echo esc_html( $post_type->label ); ?>
</option>
<?php
}
?>
</select>
<br />
<span class="envira-posttype-dropdown-error"><?php esc_html_e( 'Please select a post type for conversion.', 'envira-gallery' ); ?></span>
</div>
<?php
}
/**
* Add link target.
*/
public function admin_cdn_menu_target_blank() {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('span.cdn_new_badge').closest('li').find('a').attr('target', '_blank');
});
</script>
<?php
}
}
?>