<?php

/*
  Plugin Name: EntryWizard
  Plugin URI: http:
  Description:  Uploading by logged-in users of sets of image files and associated data. Administrators may download the images together with the data in spreadsheet form.
  Version: 1.3.29
  Author: Josie Stauffer
  Author URI:
  License: GPL2
  Text Domain: entrywizard
  Domain Path: /languages/
*/
/*  Copyright 2012-2021  Josie Stauffer  (email : )

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

defined( 'ABSPATH' ) or exit;   // show a blank page if try to access this file directly

define( 'EWZ_CURRENT_VERSION', '1.3.29' );

define( 'EWZ_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'EWZ_CUSTOM_DIR', plugin_dir_path( __FILE__ ) );
define( 'EWZ_REQUIRED_WP_VERSION', '5.0' );
define( 'EWZ_REQUIRED_PHP_VERSION', '7.0' );

/*
 * INCLUDES
 */
require_once( EWZ_PLUGIN_DIR . 'classes/ewz-setup.php' );
require_once( EWZ_PLUGIN_DIR . 'classes/ewz-webform.php' );
if ( is_admin() ) {
    require_once( EWZ_PLUGIN_DIR . '/includes/ewz-admin.php' );
}
// this is needed for admin, too, because the ajax function runs as admin
require_once( EWZ_PLUGIN_DIR . '/includes/ewz-upload.php' );
require_once( EWZ_PLUGIN_DIR . '/includes/ewz-followup.php' );

/*
 * HOOKS
 */
register_activation_hook( __FILE__,   array( 'Ewz_Setup', 'activate_or_install_ewz' ) );
// no uninstall hook, use the uninstall.php file


/**
 * ACTIONS and FILTERS
 */
/**
 * Warn admins not to update entrywizard while uploads are open.
 * See documentation for wp hook plugin_auto_update_setting_html
 *
 * @param  string  $html
 * @param  string  $plugin_file
 * @param  array<mixed>   $plugin_data
 * @return string  html string
 */
function entrywizard_auto_update_setting_html( $html, $plugin_file, $plugin_data ) {

    if ( 'entrywizard/entrywizard.php' === $plugin_file ) {
        $html .= '<div>Updates are not advised while a webform is open for uploads.</div>';
    }

    return $html;
}
add_filter( 'plugin_auto_update_setting_html', 'entrywizard_auto_update_setting_html', 10, 3 );

add_action('plugins_loaded', 'ewz_load_translations' );

add_action('plugins_loaded', 'ewz_requires_version', 5 );   // first useable hook

add_action( 'plugins_loaded', 'ewz_init_globals', 10 );

// in the admin area we add another function after this, so make sure we know its priority
add_action( 'init', 'ewz_set_dev_env', 1 );

// Needed for cron job to auto-close webform
add_action( 'init', 'Ewz_Webform::schedule_do_actions' );


add_action( 'admin_init', 'ewz_check_for_db_updates', 8 );

//add_filter( 'wp_privacy_personal_data_erasers', 'ewz_erase_entrywizard_data', 10 );

add_filter( 'wp_privacy_personal_data_exporters', 'ewz_export_entrywizard_data', 10 );

add_action( 'wp_enqueue_scripts', 'ewz_register' );    // just registers scripts and styles for front-end. Enqueue only on relevant pages.

// warn about uploaded items before a user is deleted
add_action( 'delete_user_form', array('Ewz_User', 'warn_deleting_items') ,10, 2  );  // warn before confirming deletion

// delete items when a user is deleted
add_action( 'deleted_user', 'Ewz_Item::delete_user_items' );

/*
 * SHORTCODES
 */
/**
 * Set up the EWZShortcodes menu in tinyMCE
 *
 * @param object $screen
 *
 * @return void
 */
function ewz_shortcode_menu($screen) {
    // assert( is_object( $screen ) );
    // only hook up these filters if
    //    in the admin panel,
    //    editing a post, page or custom post type ( had issues with extra stuff the profile from other plugins ),
    //    the current user has permission to edit posts and pages,
    //    the current user has some entrywizard permissions
    if ( current_user_can( 'edit_posts' ) &&
         current_user_can( 'edit_pages' ) &&
         Ewz_Permission::can_see_webform_page() &&
         $screen->post_type) {
        add_filter( 'mce_buttons_2', 'ewz_mce_button' );
        add_filter( 'mce_external_plugins', 'ewz_mce_plugin' );

        foreach( array('post.php','post-new.php') as $hook ){
            add_action( "admin_head-$hook", 'ewz_admin_head' );
        }
    }
}
// need to be this late to have access to screen
add_action( 'current_screen', 'ewz_shortcode_menu', 10 ,1 );


/**
 * Pass the list of webforms to javascript for the shortcode menu.
 *
 * @return void
 */
function ewz_admin_head(){
    $ewzdata = array();
    $ewzdata['webforms'] = array_values(array_filter( Ewz_Webform::get_all_webforms( 'can_view_webform', true )));
     ?>
<script type='text/javascript'>
     var EWZdata =  <?php echo json_encode( $ewzdata ); ?>;
</script>
    <?php
}


/**
 * Load translation files if required
 * @return void;
 */
function ewz_load_translations(){
     load_plugin_textdomain('entrywizard', false, '/languages');
}

/**
 * Pass the button data to mce_buttons filter
 *
 * @param array<string> $buttons
 *
 * @return array<string> $buttons
 */
function ewz_mce_button( $buttons ) {
    array_push( $buttons,'ewz_shortcodes');
    return $buttons;
}

/**
 * Pass the javascript file name to the mce_external_plugins filter
 *
 * @param array<string> $plugins
 *
 * @return array<string> $plugins changed
 */
function ewz_mce_plugin( $plugins ) {
    $plugins['ewz_shortcodes'] = plugins_url() . '/entrywizard/javascript/ewz-shortcodes.js?ewzv=' . EWZ_CURRENT_VERSION;
    return $plugins;
}


if ( ! is_admin() ) {
    // adding this for all admin pages triggers warnings for some themes and plugins
    // just add it specifically for the ajax calls when they are run in ewz-admin.php
    add_shortcode( 'ewz_show_webform', 'ewz_show_webform' );
    add_shortcode( 'ewz_followup', 'ewz_followup' );
}

/**
 * Warn admins that the ewz shortcodes menu only shows in a classic block
 * @return void;
 */
function ewz_gutenberg_notice() {
    $user_id = get_current_user_id();
    if ( !get_user_meta( $user_id, 'gutenberg_notice_dismissed' ) ) {
        echo '<div class="notice"><p>If you use the new Gutenberg editor that comes with WordPress 5.0, the EntryWizard shortcode dropdowns are only visible inside a "Classic" block.</p><a href="?gutenberg-dismissed">Dismiss</a></div>';
    }
}
add_action( 'admin_notices', 'ewz_gutenberg_notice' );

/**
 * Save the fact that the notice has been read.
 * @return void;
 */
function ewz_gutenberg_notice_dismissed() {
    $user_id = get_current_user_id();
    if ( isset( $_GET['gutenberg-dismissed'] ) ){
        add_user_meta( $user_id, 'gutenberg_notice_dismissed', 'true', true );
    }
}
add_action( 'admin_init', 'ewz_gutenberg_notice_dismissed' );


/**
 * Deal with database updates since the last version
 * @return void;
 */
function ewz_check_for_db_updates(){
    $ewz_data_version = get_option( 'ewz_data_version',  '0.9.5');
    if ( current_user_can('update_plugins' ) &&
         $ewz_data_version &&
         version_compare( $ewz_data_version, EWZ_CURRENT_VERSION,  '<' )
    ){
        // 0.9.6 added new apply_prefix column to webforms table
        // and changed 'min_img_area' to 'min_longest_dim' in fields/fdata
        if ( version_compare( $ewz_data_version, '0.9.6', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 0.9.6" );
            Ewz_Setup::activate_or_install_ewz();
            delete_option( 'ewz_db_version' );
            Ewz_Field::upgrade_change_min_area_to_dim();
        }
        // 0.9.8 added attach_prefs to webform table
        if ( version_compare( $ewz_data_version, '0.9.8', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 0.9.8" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.0.0 added upload_date to item table
        if ( version_compare( $ewz_data_version, '1.0.0', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.0.0" );
            Ewz_Setup::activate_or_install_ewz();
            Ewz_Item::upgrade_set_upload_date();
        }
        // 1.1.0 added num_items to webform table and override to layout table
        if ( version_compare( $ewz_data_version, '1.1.0', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.1.0" );
            Ewz_Setup::activate_or_install_ewz();
            Ewz_Webform::upgrade_set_num_items();
        }
        // 1.2.0 added append to fields table
        if ( version_compare( $ewz_data_version, '1.2.0', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.2.0" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.2.3 added gen_fname to webforms table
        if ( version_compare( $ewz_data_version, '1.2.3', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.2.3" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.2.4 htaccess and index file in EWZ_IMG_UPLOAD_DIR
        if ( version_compare( $ewz_data_version, '1.2.4', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.2.4" );
            Ewz_Setup::protect_uploads();
        }
        // 1.2.10 added webform_order to webform table, layout_order to layout table
        if ( version_compare( $ewz_data_version, '1.2.10', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.2.10" );
            Ewz_Setup::activate_or_install_ewz();
            Ewz_Webform::upgrade_set_webform_order();
            Ewz_Layout::upgrade_set_layout_order();
        }
        // 1.2.18 allowed for longer prefixes
        if ( version_compare( $ewz_data_version, '1.2.18', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.2.18" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.2.21 added "notes" column to layout table
        if ( version_compare( $ewz_data_version, '1.2.21', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.2.21" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.3.0 added "all_items" column to field table
        if ( version_compare( $ewz_data_version, '1.3.0', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.3.0" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.3.5 added wform key to item table, changed some lengths
        if ( version_compare( $ewz_data_version, '1.3.5', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.3.5" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.3.6 fixed incorrect "unsigned" for ss_column
        if ( version_compare( $ewz_data_version, '1.3.6', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.3.6" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.3.7 added 'hidden' column to ewz_webform
        if ( version_compare( $ewz_data_version, '1.3.7', '<' ) ){
            error_log( "EWZ: updating $ewz_data_version to 1.3.7" );
            Ewz_Setup::activate_or_install_ewz();
        }
        // 1.3.8 fix not deleting permissions when deleting forms and layouts
        if ( version_compare( $ewz_data_version, '1.3.8', '<' )  ){
            error_log( "EWZ: updating $ewz_data_version to 1.3.8" );
            Ewz_Permission::upgrade_delete_outdated_permissions();
        }
        // 1.3.16 convert tables to utf8
        if ( version_compare( $ewz_data_version, '1.3.16', '<' )  ){
            error_log( "EWZ: updating $ewz_data_version to 1.3.16" );
            Ewz_Setup::update_to_utf8();
        }
    }
    update_option( 'ewz_data_version', EWZ_CURRENT_VERSION );
}

/**
  *  Register the scripts and styles for the front end
  *
  * "wp_enqueue_scripts is the proper hook to use when enqueuing items that are meant to appear on the front end.
  *  Despite the name, it is used for enqueuing both scripts and styles."
  * @return void;
  *
  **/
function ewz_register() {
      wp_register_style( 'jquery-ui-dialog', includes_url() . "/css/jquery-ui-dialog.css" );

    // Note dependencies here, to force loading in right order.  'ewz-style' only is queued in the shortcode
    // It and its dependencies then get loaded in the footer
    wp_register_style( 'ewz-jq-ui-style', plugins_url( 'styles/ewz-jquery-ui.css', __FILE__ ), array('jquery-ui-dialog'), EWZ_CURRENT_VERSION );
    wp_register_style( 'ewz-style', plugins_url( 'styles/entrywizard.css', __FILE__ ),  array('jquery-ui-dialog','ewz-jq-ui-style'),  EWZ_CURRENT_VERSION );

    wp_register_script('ewz-ucommon',
                       plugins_url( 'javascript/ewz-user-common.js', __FILE__ ),
                       array( 'jquery', 'jquery-ui-core','jquery-ui-widget', 'jquery-ui-dialog', 'jquery-ui-position'),
                       EWZ_CURRENT_VERSION,
                       false
                      );
    // only register the shortcode scripts here, enqueue them within the shortcode so they are only
    // loaded when required

    wp_register_script('ewz-exif',
                       plugins_url( 'javascript/exif-js.js', __FILE__ ),
                       array( 'jquery' ),
                       EWZ_CURRENT_VERSION,
                       true      // in footer, so $ewzG has been defined
                      );
    wp_register_script('ewz-upload',
                       plugins_url( 'javascript/ewz-upload.js', __FILE__ ),
                       array( 'jquery', 'ewz-ucommon', 'ewz-exif' ),
                       EWZ_CURRENT_VERSION,
                       true      // in footer, so $ewzG has been defined
                      );
    wp_register_script('ewz-followup',
                       plugins_url( 'javascript/ewz-followup.js', __FILE__ ),
                       array( 'jquery', 'ewz-ucommon'  ),
                       EWZ_CURRENT_VERSION,
                       true      // in footer, so $ewzG has been defined
                      );
 }

/**
 * DEBUGGING OUTPUT
 * @return void;
 **/
function ewz_set_dev_env(){
    if ( is_file( plugin_dir_path( __FILE__ ). 'DEVE_ENV' )   // only true in development environment
        && ! ( isset( $_POST['action'] ) && ( 'heartbeat' == $_POST['action'] )  )
      ){
        ini_set( 'xdebug.overload_var_dump', '0' );
        /*
         * LOGGING
         */
        define( 'EWZ_DBG', true );
        $is_admin = is_admin() ? 'ADMIN' : '';
        //  stop logging of pwd
        $p = $_POST;
        unset($p['pwd']);
        if( !( defined( 'DOING_CRON' ) && DOING_CRON ) ){
            $ref='none';
            if(array_key_exists('HTTP_REFERER', $_SERVER ) ){
                $ref = $_SERVER['HTTP_REFERER'];
            }
            error_log( "EWZ: ~~~~~~~~ Starting entrywizard.php  $is_admin ~~~~~~~ \n"
                       . 'URI:' . $_SERVER['REQUEST_URI'] . "\n"
                       . 'GET:   ' . var_export( $_GET, true )
                       . 'POST:  ' . var_export( $p, true )
                       . 'FILES: ' . var_export( $_FILES, true )
                       . 'REFERRER: ' . $ref
                      );
        }
        /*
         * TESTING
         */
        if( is_file( plugin_dir_path( __FILE__ ). 'GD_ENV' ) ){
            add_filter( 'wp_image_editors', 'ewz_qa_image_editor' );
        }
    }

    $maxnumitems = ini_get( 'max_file_uploads' );
    if ( ! $maxnumitems ){
        $maxnumitems = 50;
    }
    $maxfilesize = ini_get( 'upload_max_filesize' );
    if ( ! $maxfilesize ){
        $maxfilesize = '5M';
    }
    $postmaxsize = ini_get( 'post_max_size' );
    if ( ! $postmaxsize ){
        $postmaxsize = '100M';
    }

    define( 'EWZ_MAX_NUMITEMS', (int) $maxnumitems );
    define( 'EWZ_MAX_TOTAL_MB', ewz_to_mb( $postmaxsize ) );      // default max total post size in MB
    define( 'EWZ_MAX_SIZE_MB',  ewz_to_mb( $maxfilesize ) );      // default max upload image size in MB
}

/**
 * For testing only -- allows for testing with either image editor
 * @param array<string>  $editors;
 * @return array<string>
 */
function ewz_qa_image_editor( $editors ) {
    return array('WP_Image_Editor_GD', 'WP_Image_Editor_Imagick');
}

/**
 * Set up global constants
 * @return void;
 */
function ewz_init_globals(){
    global $wpdb;

    define( 'EWZ_PREFIX', $wpdb->prefix );

    define( 'EWZ_LAYOUT_TABLE',  EWZ_PREFIX . 'ewz_layout' );
    define( 'EWZ_FIELD_TABLE',   EWZ_PREFIX . 'ewz_field' );
    define( 'EWZ_WEBFORM_TABLE', EWZ_PREFIX . 'ewz_webform' );
    define( 'EWZ_ITEM_TABLE',    EWZ_PREFIX . 'ewz_item' );

    $uploads_dir = wp_upload_dir();
    define( 'EWZ_IMG_UPLOAD_DIR', $uploads_dir['basedir'] . '/ewz_img_uploads' );
    define( 'EWZ_IMG_UPLOAD_URL', $uploads_dir['baseurl'] . '/ewz_img_uploads' );
    define( 'EWZ_IMG_UPLOAD_SUBDIR', 'ewz_img_uploads' );


    define( 'EWZ_DEFAULT_DIM',  1280 );           // default max image dimension in pixels
    define( 'EWZ_DEFAULT_MIN_LONGEST',  100 );    // default minimum longest image dimension in pixels
    define( 'EWZ_MAX_STRING_LEN', 50 );           // default max length of text input field
    define( 'EWZ_MAX_FIELD_WIDTH', 10 );          // default max field width of text input field

    define( 'EWZ_FILE_DOWNLOAD_TIME', 30 );       // number of seconds to add to max time allowance for every 25 files
    define( 'UPLOAD_ERR_EMPTY',   5 );            // error if uploaded file is empty -- see includes/ewz-upload.php

    define( 'EWZ_WP_USER_TABLE',  EWZ_PREFIX . 'users' );

    define('EWZ_MAX_VIZ', 5000 );
}

/**
 * Transform a string like 100G or 50K to megabytes
 *
 * Works for an integer followed by 'm', 'mb', 'g','gb', 'k', 'kb'
 * or the same in upper case
 *
 * @param   string    $sizestring
 * @return  integer
 */
function ewz_to_mb( $sizestring )
{
    // assert( is_string( $sizestring ) );
    // assert( preg_match( '/^(\d+)[mMgGkK]?[bB]?\s*$/', $sizestring ) );

    $val = strtolower( trim( $sizestring ) );
    $mat = array();
    preg_match( '/^(\d+)(\D+)?/', $val, $mat );
    // assert(  count( $mat ) > 1 );

    $num  = $mat[1];

    if( isset( $mat[2] ) ){
        $code = $mat[2];
    } else {
        return ( $num / 1048576 );
    }

    if ( preg_match( '/mb?/', $code ) ) {
        return ( int )$num;
    }
    if ( preg_match( '/gb?/', $code ) ) {
        return ( int )($num * 1024);
    }
    if ( preg_match( '/kb?/', $code ) ) {
        return ( int )($num / 1024);
    }
    return 0;
}

/**
 *
 * Warn if required versions of WP and PHP not available
 * @return void
 *
 */
function ewz_requires_version(){
   $reqvers = ewz_required_versions_warning();
   if ( $reqvers ) {
       add_action( 'admin_notices', 'ewz_admin_notice' );
   }
}

/**
 * Return a string like "WordPress version XXX and PHP version yyy"
 * if the required versions of WP and PHP are not available.
 * Also log to error log.
 *
 * @return string
 **/
function ewz_required_versions_warning(){
   global $wp_version;
   $reqvers = '';

   // version_compare returns -1 if left is lower than right, 0 if they are equal, 1 if right is lower than left
   if ( version_compare( EWZ_REQUIRED_WP_VERSION, $wp_version ) == 1 ) {
      $reqvers = 'EntryWizard may not work with WordPress version less than ' . EWZ_REQUIRED_WP_VERSION;
      error_log("EWZ: wordpress version not supported");
   }
   if ( version_compare( EWZ_REQUIRED_PHP_VERSION, PHP_VERSION ) == 1 ) {
       error_log("EWZ: php version not supported");
      if( $reqvers ){
            $reqvers .= " or with PHP version less than " . EWZ_REQUIRED_PHP_VERSION;
      } else {
          $reqvers .= 'EntryWizard may not work with PHP version less than ' . EWZ_REQUIRED_PHP_VERSION;
      }
   }
   return $reqvers;
}

/**
 * Warn if the required versions of WP and PHP are not available
 * @return void
 *
 **/
function ewz_admin_notice() {
    $msg = ewz_required_versions_warning();
    if( $msg ){
        print "<div class='error'><h2>EntryWizard Warning</h2> <strong>$msg </strong></div>";
    }
}

/** **************************************
 * Utility to output data structures in debug.log
 *
 * For debugging purposes
 * @param int $ignore
 * @return void
 *
 **/
function ewztrace( $ignore = 2 )
{
    // assert( is_int( $ignore ) );

    if ( version_compare( phpversion(),  '5.3.6' ) >= 0 ){
        foreach ( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) as $k => $vv ) {
            if ($k < $ignore) {
                continue;
            }
            if( isset( $vv['file'] ) ){
                if( strpos( $vv['file'], 'entrywizard' ) !== false  || strpos( $vv['file'], 'ewz' ) !== false ){
                    $shrtfile = str_replace( get_home_path() . '/wp-content/plugins/', '', $vv['file'] );
                    error_log( 'EWZ:#' . ($k - $ignore) . ' ' . $shrtfile. '(' . $vv['line'] . '): ' .
                               (isset($vv['class']) ? $vv['class'] . '->' : '') . $vv['function']  );
                }
            }
        }
    }
}

if( !function_exists( 'ewz_serialize' ) ){
    /**
     * @param array<mixed> $data
     * @param int $maxlen
     * @return string
     */
    function ewz_serialize( $data, $maxlen ){
        // assert( !is_int($data) );
        // assert( is_int($maxlen) );
        $value = serialize( $data );
        if( strlen( $value ) > $maxlen ){
            throw new EWZ_Exception( 'Max data length exceeded' );
        }
        return $value;
    }
}

/**
 * Debug function to pretty-print a variable in the error log.
 *
 * @param string  $str
 * @param mixed   $in_obj
 *
 * @return void
 */
function ewzdbg( $str, $in_obj='DUMMY' )
{
    if ( defined( 'EWZ_DBG' ) && EWZ_DBG ){    /** @phpstan-ignore-line */
        // $trace = false;
        //  if( $trace ){
        //   ewztrace();
        //}
        if ( $in_obj !== 'DUMMY' ) {
            ob_start();
            var_dump( $in_obj );
            $dump = preg_replace('/=>\s*\n/', ' => ', ob_get_contents());
            ob_end_clean();
            error_log(  "EWZ_DBG object $str: " . $dump );
            return;
        } else {
            error_log( 'EWZ_DBG variable: ' .  $str );
            return;
        }
    }
}
