<?php
// TOOLSET TO MANAGE LOCAL IMAGES UPLOAD ANF FOLDERS DIVISION


class gg_local_images {
	
    public $suggested_path  = '';
    public $suggested_url   = '';
    
    public $folder_path 	= false; // gg upload folder path  
	public $folder_url 		= false; // gg upload folder url
	public $fs_method; // (string) which method to use to manage files? autofilled by WP by default (direct | ssh2 | ftpext | ftpsockets)
	public $fs; // avoid recalling $wp_filesystem var each time
    
	protected $is_ready = false; // (bool) flag to know whether the class is ready to operate
    public $folder_error = false; // (bool) flag to know whether problem is with container folder 
	public $last_uploaded_filename; // (string) utility attribute to know last uploaded filename
	
	
	/* 
	 * Setup main vars and filesystem method to be used  
	 * is possible to define a custom filesystem metehod (for debugging purposes: 'direct', 'ssh2', 'ftpext', or 'ftpsockets'.)
	 */
	public function __construct($fs_method = false) {
		
        // setup suggested paths
        $upload_dirs    = wp_upload_dir();
		$this->suggested_path = $upload_dirs['basedir'] .'/gg_albums';	
        $this->suggested_url  = $upload_dirs['baseurl'] .'/gg_albums';
        
        
		// setup folder vars
		$this->folder_path 	= untrailingslashit( get_option('gg_albums_basepath', $this->suggested_path) );
		$this->folder_url 	= untrailingslashit( get_option('gg_albums_baseurl', $this->suggested_url) );
		
        
		// filesystem method - if no option is set, use the WP function	
		if($fs_method && !defined('FS_METHOD')) {
			define('FS_METHOD', $fs_method);
		}
		
		if(!function_exists('get_filesystem_method')) {
			require_once(ABSPATH .'wp-admin/includes/file.php');	
		}
		$this->fs_method = get_filesystem_method();
	}
	

	
	
	/* 
	 * Initializes WP filesystem and prepare/check the files folder
	 * @return (bool|string) true if system is ready to opereate or the error message
	 */
	public function is_ready() {
		if($this->is_ready) {
			return true;	
		}
		 
		// if method isn't direct, get credentials
		$fs_creds = ($this->fs_method != 'direct') ? get_option('gg_filesys_creds', array()) : false;			
		
		// try connecting
		if(!WP_Filesystem($fs_creds)) {
			return esc_html__('WP Filesystem connection failed', 'gg_ml');	
		}
		$this->fs = $GLOBALS['wp_filesystem'];
		
        
		// if path value is the suggested one - try creating the folder
		if(!$this->fs->exists($this->folder_path)) {
            
            if($this->folder_path == $this->suggested_path) {
                $this->fs->mkdir($this->suggested_path);
                
                if(!$this->fs->exists($this->folder_path)) {
                    $this->folder_error = true;
                    return esc_html__("Cannot create folder", 'gg_ml');	        
                }
            }
            
            $this->folder_error = true;
            return esc_html__("Main folder not found", 'gg_ml');	
		}
		
		
		// try creating a demo file
        $test_file_path = $this->folder_path .'/test.txt'; 

        if(!$this->fs->exists($test_file_path)) {
            if(!$this->fs->put_contents($test_file_path, 'test')) {
                return esc_html__('Cannot create files', 'gg_ml');	
            }

            $this->fs->delete($test_file_path, false, 'f');
        }
		
		$this->is_ready = true;
		return true;
	}
	
	
     
    
    /* 
	 * Echoes WP filesystem setup form
	 */
	public function wp_filesys_setup_form() {
        $defaults = array(
            'hostname' 		=> '',
            'username' 		=> '',
            'password' 		=> '',
            'public_key' 	=> '',
            'private_key'	=> '',
            'connection_type' => ''
        );
        $stored_creds = get_option('gg_filesys_creds', array());
        $fs_creds = array_merge($defaults, $stored_creds);	

        // preload stored data into the form
        foreach($fs_creds as $key => $val) {
            $_POST[$key] = $val;	
        }
        $_POST['password'] = ''; // never print psw


        // trick to display forced data
        $_POST['_fs_nonce'] = wp_create_nonce('filesystem-credentials'); 


        $txt = (strpos($this->fs_method, 'ssh') !== 'false') ? esc_html__( 'Please enter your FTP or SSH credentials to proceed.' ) : esc_html__( 'Please enter your FTP credentials to proceed.' );
        echo '
        <div class="error gg_fs_setup_error">
            <p>'. esc_html__('Looks like your server has got some restrictions over files management', 'gg_ml') .'.<br/>
            '. $txt .'</p>
        </div>

        <div class="gg_fs_creds_wrap">';
            request_filesystem_credentials('', $this->fs_method);

        echo '
        </div>
        ';

        ?>
        <script type="text/javascript">
        (function($) { 
            "use strict";

            $(document).ready(function() {
                let is_acting = false;

                // handle WP FILESYS credential submit
                $(document).on('click', '#upgrade', function(e) {
                    e.preventDefault();
                    var $form = $('.gg_fs_creds_wrap form');

                    if(is_acting ) {
                        return false;
                    }

                    // each field must be filled
                    var is_ok = true;
                    $form.find('input').each(function() {
                        if(!$(this).val()) {
                            is_ok = false;	
                        }
                    });

                    if(!$('#ftp').is(':checked') && !$('#ftps').is(':checked')) {
                        is_ok = false;		
                    }

                    if(!is_ok) {
                        lc_wp_popup_message('error', "<?php esc_html_e('Each field must be filled', 'gg_ml') ?>");	
                        return false;	
                    }


                    // proceed
                    is_acting = true;
                    $form.fadeTo(200, 0.7);

                    // submit
                    var data = 'action=gg_save_wp_filesys_data&nonce=<?php echo wp_create_nonce('lcwp_ajax') ?>&' + $form.serialize();
                    $.post(ajaxurl, data, function(response) {
                        if($.trim(response) == 'success') {
                            lc_wp_popup_message('success', "<?php esc_html_e('Connection performed successfully!', 'gg_ml') ?>");

                            setTimeout(function() {
                                window.location.reload();
                            }, 1850);
                        }
                        else {
                            is_acting = false;
                            $form.fadeTo(200, 1);

                            lc_wp_popup_message('error', response);	
                        }
                    })
                    .fail(function(e) {
                        console.error(e);
                        is_acting = false;
                        $form.fadeTo(200, 1);

                        lc_wp_popup_message('error', "<?php esc_html_e('Connection error', 'gg_ml') ?>");	
                    });	
                });

            });


        })(jQuery);
        </script>
        <?php
    }
	
    
    
	
    /* 
	 * Echoes basepath setup form
	 */
	public function basepaths_setup_form() {
        
        if(!current_user_can("manage_options")) {
            echo '
            <div id="gg_lim_paths_wrap">
                <div class="gg_lim_notice">
                    <p>'. esc_html__("Only administrators can setup album paths", 'gg_ml') .'</p>
                </div>
            </div>';    
            
            return false;
        }
        
        ?>
        <form>
            <table class="widefat" id="gg_lim_paths_wrap">
                <tr>
                    <td>
                        <label><span class="dashicons dashicons-editor-help" title="<?php esc_attr_e("server path to a DEDICATED existing folder", 'gg_ml') ?>"></span><?php esc_html_e('Albums basepath', 'gg_ml') ?></label>
                    </td>
                    <td>
                        <input type="text" name="gg_albums_basepath" value="<?php echo esc_attr($this->folder_path) ?>" autocomplete="off" />
                        <p><?php esc_html_e("Suggested one", 'gg_ml') .': <em>'. $this->suggested_path .'</em>' ?></p>
                    </td>
                </tr>
                <tr>
                    <td>
                        <label><span class="dashicons dashicons-editor-help" title="<?php esc_attr_e("URL related to the basepath", 'gg_ml') ?>"></span><?php esc_html_e('Albums baseurl', 'gg_ml') ?></label>    
                    </td>    
                    <td>    
                        <input type="text" name="gg_albums_baseurl" value="<?php echo esc_attr($this->folder_url) ?>" autocomplete="off" />
                        <p><?php esc_html_e("Suggested one", 'gg_ml') .': <em>'. $this->suggested_url .'</em>' ?></p>
                    </td>
                </tr>
                <?php if(strpos($this->folder_path, 'plugins/global-gallery') !== false) : ?>
                <tr>
                    <td colspan="2">
                        <strong class="gg_lim_cfm_error"><?php esc_html_e("WARNING: using the plugin folder is discouraged, albums will be lost uninstalling it!", 'gg_ml') ?></strong>
                    </td>
                </tr>
                <?php endif; ?>
                <tr>
                    <td>
                        <input type="button" name="gg_save_albums_bp" value="<?php esc_attr_e('Save') ?>" class="button-primary" />
                        
                        <?php if($this->folder_path != $this->suggested_path) : ?>
                            <input type="button" name="gg_use_suggested_bp" value="<?php esc_attr_e('Use suggested paths', 'gg_ml') ?>" class="button-secondary" />
                        <?php endif; ?>
                    </td>
                    <td class="gg_lim_paths_response">

                    </td>
                </tr>
            </table>
        </form>
        
        <script type="text/javascript">
        (function($) { 
            "use strict";

            $(document).ready(function() {
                let is_acting = false,
                    already_confirmed = false;
                
                
                // set suggested values
                $(document).on('click', 'input[name="gg_use_suggested_bp"]', function(e) {
                    if(is_acting) {
                        return false;
                    }
                    
                    if(confirm("<?php esc_attr_e("Override path values with suggested ones?", 'gg_ml') ?>")) {
                        const targets = ['gg_albums_basepath', 'gg_albums_baseurl'];
                        $.each(targets, function(i, target) {
                            
                            const $input    = $('input[name="'+ target +'"]'),
                                  val       = $input.parent().find('em').text();
                            
                            $input.val(val);
                        });
                        
                        $('input[name="gg_save_albums_bp"]').click();
                    }
                });
                
                
                
                // handle basepaths submit
                $(document).on('click', 'input[name="gg_save_albums_bp"]', function(e) {
                    e.preventDefault();
                    const $btn = $(this);
                    
                    if(is_acting) {
                        return false;
                    }
                    
                    const basepath  = $.trim( $('input[name="gg_albums_basepath"]').val()),
                          baseurl   = $.trim( $('input[name="gg_albums_baseurl"]').val());
                    
                    if(!basepath || !baseurl) {
                        lc_wp_popup_message('error', "<?php esc_html_e('Each field must be filled', 'gg_ml') ?>");	
                        return false;    
                    }

                    // proceed
                    is_acting = true;
                    $btn.fadeTo(200, 0.7);

                    // submit
                    let data = {
                        action      : 'gg_save_gg_album_basepaths',
                        nonce       : "<?php echo wp_create_nonce('lcwp_ajax') ?>",
                        basepath    : basepath,
                        baseurl     : baseurl,
                    };
                    
                    $.post(ajaxurl, data, function(response) {
                        if($.trim(response) == 'success') {
                            lc_wp_popup_message('success', "<?php esc_html_e('Paths successfully updated!', 'gg_ml') ?>");

                            setTimeout(function() {
                                window.location.reload();
                            }, 1850);
                        }
                        else {
                            is_acting = false;
                            $btn.fadeTo(200, 1);

                            lc_wp_popup_message('error', response);	
                        }
                    })
                    .fail(function(e) {
                        console.error(e);
                        is_acting = false;
                        $btn.fadeTo(200, 1);

                        lc_wp_popup_message('error', "<?php esc_html_e('Error updating paths', 'gg_ml') ?>");	
                    });	
                });

            });
        })(jQuery);
        </script>
        <?php
    }
    
    
        
        
        
        
	

	
	###################################################################
	#### PHYSICAL FILES MANAG #########################################
	###################################################################
	

    
    /*
     * Given a folder name checks its validity and existence   
     * @return (bool)
     */
    public function folder_exists($folder_name, $sanitize_name = false) {
        if($sanitize_name) {
            $folder_name = sanitize_title($folder_name);    
        }
        
        if(empty($folder_name)) {
            return false;
        }

        $f_path = $this->folder_path .'/'. (string)$folder_name;       
        return $this->fs->exists($f_path);
    }
    
    
    
    
    /*
     * Given a folder name creates it 
     * @return (bool)
     */
    public function create_folder($folder_name, $sanitize_name = false) {
        
        if($sanitize_name) {
            $folder_name = sanitize_title( (string)$folder_name );    
        }
        if(empty($folder_name)) {
            return false;
        }
        
        $f_path = $this->folder_path .'/'. $folder_name;  
        return $this->fs->mkdir($f_path);
    }
    
    
    
    /* 
     * validate local image uploads
     * @return (bool|string) true or the error message
     */
    public function validate_file($file) {

        // filesize	
        if(!$file['size']) {
            return esc_html__("Empty file", 'gg_ml'); 		
        }
        elseif($file['size'] > wp_max_upload_size()) {
            return esc_html__('File is too big', 'gg_ml');	
        }

        // filetype
        $filetype = wp_check_filetype_and_ext($file['tmp_name'], $file['name']);
        $allowed = array("image/jpeg", "image/gif", "image/png", "image/webp", "image/avif");
        
        if(!in_array($filetype['type'], $allowed)) {
            return esc_html__("Forbidden file type", 'gg_ml');	
        }

        return true;
    }
    
    
    
    
	/* 
	 * Upload a file registering it into the system
	 *
     * @param (string) $folder_name
	 * @param (array) $file - the $_FILES file resource
     *
	 * @return (bool|string) true on successful operation, otherwise the error message
	 */
	public function upload_file($folder_name, $file) {

		// be sure system is ok
		$is_ready = $this->is_ready();
		if($is_ready !== true) {
			return $is_ready;	
		}
		   
        // check folder validity
        if(!$this->folder_exists($folder_name)) {
            return esc_html__('Folder not found', 'gg_ml');	        
        }
        
        
		/*
		array(5) {
		  ["name"]=>
		  string(16) "athos giugno.jpg"
		  ["type"]=>
		  string(10) "image/jpeg"
		  ["tmp_name"]=>
		  string(14) "/tmp/phpRPwi5q"
		  ["size"]=>
		  int(99128)
		}
		*/
		
		// get extension
		$ext          = gg_static::stringToExt($file['name']);
        $stored_as    = sanitize_title( gg_static::stringToFilename($file['name']) );
		$destination  = $this->folder_path .'/'. $folder_name .'/'. $stored_as . $ext; 
        
		// be sure it is not duplicated
        if($this->fs->exists($destination)) {
            return esc_html__("Another file has this name", 'gg_ml');	
        }
        
		$upload = move_uploaded_file($file['tmp_name'], $destination);
		if(!$upload) {
			return esc_html__('Error uploading the file', 'gg_ml');	
		}
		
        $this->last_uploaded_filename = $stored_as . $ext;
		return true;
	}
		
		
		
		
			
	/* 
	 * Deletes a folder or a single folder file
     * @param (string) $folder_name
	 * @param (string) $file_name
	 *
	 * @return (bool|string) true if operation is successful, otherwise the error message
	 */
	public function delete($folder_name, $file_name = false) {

		// be sure system is ok
		$is_ready = $this->is_ready();
		if($is_ready !== true) {
			return $is_ready;	
		}
		
        // check folder validity
        if(empty($folder_name)) {
            return esc_html__('Folder name missing', 'gg_ml');	        
        }
        if(!$this->folder_exists($folder_name)) {
            return esc_html__('Folder not found', 'gg_ml');	        
        }
        $folder_path = $this->folder_path .'/'. $folder_name;
        
        // folder deletion (and files within)
        if(!$file_name) {
            if(!$this->fs->delete($folder_path, true, 'd')) {
                return esc_html__('Cannot delete the folder', 'gg_ml');		
            }        
        }
        
        // file deletion
        else {
            if(empty($file_name)) {
                return esc_html__('File name missing', 'gg_ml');	        
            }
            
            $file_path = $folder_path .'/'. $file_name;  
            if(!$this->fs->exists($file_path)) {
                return $file_name .' - '. esc_html__("File not found", 'gg_ml');	
            }

            // physically delete file
            if(!$this->fs->delete($file_path, false, 'f')) {
                return esc_html__('Cannot delete the file', 'gg_ml');		
            }
        }
        
		return true;
	}
	
	
                                   
    
    
    
    /* 
	 * Returns folders list
	 * @return (array|string) folders array, otherwise the error message
	 */
	public function list_folders() {

		// be sure system is ok
		$is_ready = $this->is_ready();
		if($is_ready !== true) {
			return $is_ready;	
		}
		
        
        $folders = $this->fs->dirlist($this->folder_path);
        if(!is_array($folders)) {
            return esc_html__('Cannot retrieve folders', 'gg_ml');		            
        }
        
		return array_keys($folders);
	}                               
                                   
	
                                   
                                   
                                   
    /* 
	 * Returns an assocciative array (image_name => image data) of foder images 
	 * @return (array|string) files array, otherwise the error message
	 */
	public function list_files($folder_name) {

		// be sure system is ok
		$is_ready = $this->is_ready();
		if($is_ready !== true) {
			return $is_ready;	
		}
        
		// check folder validity
        if(!$this->folder_exists($folder_name)) {
            return esc_html__('Folder not found', 'gg_ml');	        
        }
        
        
        $files = $this->fs->dirlist($this->folder_path .'/'. $folder_name);
        if(!is_array($files)) {
            return esc_html__('Cannot retrieve files', 'gg_ml');		            
        }
        
        
        
        $to_return = array();
        foreach($files as $f) {
            $f_path = $this->folder_path .'/'. $folder_name .'/'. $f['name'];
            
            $ext = strtolower(gg_static::stringToExt($f['name']));
            if(in_array($ext, array('.png', '.jpg', '.jpeg', '.gif', '.webp', '.avif')) === false) {
                continue;    
            }
            
            $to_return[$f['name']] = array_merge(
                array(
                    'name'  => $f['name'], 
                    'path'  => $f_path,
                    'url'   => $this->folder_url .'/'. $folder_name .'/'. $f['name'],
                    'ext'   => str_replace('.', '', $ext),
                    'size'  => gg_static::human_filesize($f['size']),
                ),
                gg_static::maybe_get_iptc_tags($f_path)
            ); 
        }
        
        // natural sorting
        ksort($to_return, SORT_NATURAL);
		return array_values($to_return);
	}   
    
    
    
    /* Shortcut to return a single file data (see ->list_files) */
    public function get_file($folder_name, $filename) {
        $files = $this->list_files($folder_name);    
        
        $to_return = false;
        foreach($files as $f) {
            
            if($f['name'] == $filename) {
                $to_return = $f;
                break;
            }
        }
        
        return $to_return;
    }
                                   
}
