Title: liammitchell's Replies | WordPress.org

---

# liammitchell

  [  ](https://wordpress.org/support/users/liammitchell/)

 *   [Profile](https://wordpress.org/support/users/liammitchell/)
 *   [Topics Started](https://wordpress.org/support/users/liammitchell/topics/)
 *   [Replies Created](https://wordpress.org/support/users/liammitchell/replies/)
 *   [Reviews Written](https://wordpress.org/support/users/liammitchell/reviews/)
 *   [Topics Replied To](https://wordpress.org/support/users/liammitchell/replied-to/)
 *   [Engagements](https://wordpress.org/support/users/liammitchell/engagements/)
 *   [Favorites](https://wordpress.org/support/users/liammitchell/favorites/)

 Search replies:

## Forum Replies Created

Viewing 8 replies - 1 through 8 (of 8 total)

 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[TablePress - Tables in WordPress made easy] Updating an existing table programmatically](https://wordpress.org/support/topic/updating-an-existing-table-programmatically/)
 *  [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [5 years, 11 months ago](https://wordpress.org/support/topic/updating-an-existing-table-programmatically/#post-13095424)
 * Sorry, it ran fine from admin but not via the cron schedule.
 * [@tobiasbg](https://wordpress.org/support/users/tobiasbg/) , I’ll certainly check
   the prepare method thank you 🙂 I guess I just have to build the array of 1’s
   for each row I want to show (all in this case). At the beginning of this I was
   not aware that we could hide rows but ran into it as you can tell from my other
   post, which now I can see is because I didn’t update the visibility array when
   updating automatically! Doh.
 * Maybe something like this would work in my case to show all rows and columns.
   Untested…
    I assume that at least the header row was set up via the tablepress
   admin and I retain it each import.
 * `$table['visibility'] = array( 'rows' => array_fill(0, count($table['data']),
   1), 'columns' => array_fill(0, count($table['data'][0]), 1) );`
 * TablePress::$model_table->table_exists was silently failing (no errors in debug.
   log or error log so weird)
 * I found in another extension the Table Auto Import that we can load specific 
   classes/models, this makes sense to me now as the TablePress likely does not 
   run functions/load extra things when running on cron as it does not run in cron
   normally without other extensions and would not want to create unnecessary complexity
   for a WP site should it not be needed to load.
 * [https://tablepress.org/extensions/table-auto-import/](https://tablepress.org/extensions/table-auto-import/)
 * I had to get the table model like this.
    `$model_table = TablePress::load_model('
   table' );`
 * Redacted code sample:
 *     ```
       <?php
   
       /*
       Plugin Name: VT Datatables
       Plugin URI:
       Description: Uses Remote Service Intergration to get a remote database handle.
       Runs hard-coded querys against remote DB updating hard-coded TablePress table ids.
       Version: 1.0
       Author: Liam Mitchell
       Author URI: http://github.com/LiamKarlMitchell
       License: MIT
       */
   
       // On plugin activation schedule our daily datatable update.
       register_activation_hook( __FILE__, 'vt_create_daily_datatable_schedule' );
       function vt_create_daily_datatable_schedule(){
         // Use wp_next_scheduled to check if the event is already scheduled
         $timestamp = wp_next_scheduled( 'vt_create_daily_datatable' );
   
         // If $timestamp == false schedule daily datatables since it hasn't been done previously
         if( $timestamp == false ){
           //Schedule the event for right now, then to repeat daily using the hook 'vt_create_daily_datatable'
           wp_schedule_event( time(), 'daily', 'vt_update_daily_datatables_hook' );
         }
       }
   
       register_deactivation_hook( __FILE__, 'vt_remove_daily_datatable_schedule' );
       function vt_remove_daily_datatable_schedule(){
           wp_clear_scheduled_hook( 'vt_update_daily_datatables_hook' );
       }
   
       add_action('admin_menu', 'vt_register_submenu_page');
   
       function vt_register_submenu_page() {
           add_submenu_page('remote-server-integration/remote-server-integration-admin-page.php', 'Datatables', 'Datatables', 'manage_options', __FILE__.'/datatables', 'vt_datatables_admin_page');
       }
   
       function vt_datatables_admin_page() {
           // TODO: Add nonce to form?
           // https://codex.wordpress.org/WordPress_Nonces
           ?>
           <p>Data tables are hard-coded into the vt.php.<br>Queries are run against the remote database for a service daily and the appropriate data tables have their data updated.</p>
           <form action="<?php echo admin_url('admin-post.php'); ?>" method="post">
               <input type="hidden" name="action" value="vt_update_daily_datatables">
               <input type="submit" value="Update datatables.">
           </form>
           <?php
       }
   
       add_action( 'admin_post_vt_update_daily_datatables', 'vt_update_daily_datatables_from_admin' );
   
       function vt_load_table( $table_id ) {
           vt_log(__FUNCTION__." load table running $table_id");
           vt_log(__FUNCTION__." load table check if exists $table_id");
           try {
               $model_table = TablePress::load_model( 'table' );
               if (!$model_table->table_exists($table_id)) {
                   vt_log(__FUNCTION__ . " table does not exist.");
                   return false;
               }
           } catch (Exception $e) {
               vt_log(__FUNCTION__." exception ".$e->getMessage());
               return false;
           }
           // Load the table.
           vt_log(__FUNCTION__." load table $table_id");
           $table = $model_table->load( $table_id, true, true );
           // Skip tables that could not be loaded or that are corrupted.
           if ( is_wp_error( $table ) || ( isset( $table['is_corrupted'] ) && $table['is_corrupted'] ) ) {
               vt_log(__FUNCTION__." table could not load.");
   
               vt_log(__FUNCTION__." load table wp_error ".$table->get_error_message());
               return false;
           }
           vt_log(__FUNCTION__." table loaded.");
           return $table;
       }
   
       function vt_save_table( $table ) {
           vt_log(__FUNCTION__." saving table.");
           $model_table = TablePress::load_model( 'table' );
           $result = $model_table->save($table);
           if (is_wp_error($result)) {
               // TODO: Log error?
               vt_log(__FUNCTION__." error saving table.");
               vt_log(__FUNCTION__." save table wp_error ".$result->get_error_message());
           }
       }
   
       function vt_update_daily_datatables_from_admin() {
           define( 'WP_DEBUG' , true);
           define( 'WP_DEBUG_DISPLAY' , true);
           define('WP_DISABLE_FATAL_ERROR_HANDLER',true);
           ini_set('display_errors', 1);
           ini_set('display_startup_errors', 1);
           error_reporting(E_ALL);
   
           // TODO: NONCE? https://premium.wpmudev.org/blog/handling-form-submissions/
           vt_update_daily_datatables();
   
           die('done');
           wp_safe_redirect(
               esc_url_raw(
                   add_query_arg( 'tablesupdated', 'success', admin_url('admin.php?page=vt%2Fvt.php%2Fdatatables') )
               )
           );
           exit();
       }
   
       // Note: This is just a log function made to get some output out of the extension whilst I was debugging the problem.
       function vt_log($message) {
           file_put_contents(WP_CONTENT_DIR . '/vt-debug.txt', current_time('Y-m-d h:m:s').' '.$message."\n", FILE_APPEND);
       }
   
       add_action('vt_update_daily_datatables_hook', 'vt_update_daily_datatables');
       function vt_update_daily_datatables(){
   
       //    /**
       //     * This will log all errors notices and warnings to a file called debug.log in
       //     * wp-content (if Apache does not have write permission, you may need to create
       //     * the file first and set the appropriate permissions (i.e. use 666) )
       //     */
       //    define('WP_DEBUG', true);
       //    define('WP_DEBUG_LOG', true);
       //    define('WP_DEBUG_DISPLAY', true);
       //    @ini_set('display_errors',0);
       // Note: THIS DID NOT WORK!
   
           //wp_mail( 'PutYourEmailHereForDebugPurposes', 'Testing Cron', 'Cron is running' );
           vt_log(__FUNCTION__." running.");
           try {
               // Level Ranking
               vt_level_table_update();
           } catch (Exception $e) {
               vt_log($e->getMessage());
           }
           //wp_mail( 'PutYourEmailHereForDebugPurposes', 'Testing Cron', 'Cron finished' );
       }
   
       function vt_level_table_update() {
           vt_log(__FUNCTION__." running.");
           global $rsi;
           // TODO: If user is admin on admin page show log to page?
           vt_log(__FUNCTION__." loading table.");
           $table = vt_load_table(1);
           if ($table === false) {
               // Error loading table.
               vt_log(__FUNCTION__." error loading table.");
               return;
           }
           vt_log(__FUNCTION__." connecting to db.");
           // Get connection to Database.
           $db = $rsi->getDBForService('vsky2');
           if ($db === false) {
               // Error connecting to DB?
               vt_log(__FUNCTION__." error connecting to remote db.");
               return;
           }
           // var_dump($db->get_results("SELECT VERSION();"));
           // Note: ROW_NUMBER() OVER does not work in this version of Maria DB we need 10.2 or higher we are on 10.1.38-MariaDB
           // https://stackoverflow.com/questions/57765698/problem-using-row-number-function-in-mariadb
           $rows = $db->get_results("SELECT A B C FROM SomeTable", ARRAY_N);
   
           // If an array was returned and no errors occurred, return the result set
           if (is_array($rows) && empty($db->last_error)) {
               $rowcount = count($rows);
               // Put rank number on manually as row_number method does not work in our version of MariaDB.
               for ($i=0; $i < $rowcount; $i++) {
                   $rows[$i][0] = strval($i + 1);
               }
   
               // Make a new array containing the table headers and the rows.
               $table['data'] = array_merge([$table['data'][0]], $rows);
               vt_log(__FUNCTION__." saving table.");
               // Save table.
               vt_save_table($table);
           } else {
               vt_log(__FUNCTION__." DB Error: ".$db->last_error);
           }
       }
       ```
   
 * Example screenshot:
    [img]https://i.imgur.com/jwrYHUj.png[/img]
 * Thanks again 🙂
    -  This reply was modified 5 years, 11 months ago by [liammitchell](https://wordpress.org/support/users/liammitchell/).
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[TablePress - Tables in WordPress made easy] tablepress_table_visibility meta very large.](https://wordpress.org/support/topic/tablepress_table_visibility-meta-very-large/)
 *  Thread Starter [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [5 years, 11 months ago](https://wordpress.org/support/topic/tablepress_table_visibility-meta-very-large/#post-13095260)
 * No problems that I can see, it just seems wasteful/odd? Maybe I’m over thinking
   it…
    Keep up the good work 🙂
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[TablePress - Tables in WordPress made easy] Updating an existing table programmatically](https://wordpress.org/support/topic/updating-an-existing-table-programmatically/)
 *  [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [5 years, 11 months ago](https://wordpress.org/support/topic/updating-an-existing-table-programmatically/#post-13073343)
 * To do this, I made a wordpress plugin with a daily schedule and an admin page
   with button to update.
 * Mine goes to a remote database and queries it, but you can easily adapt it for
   other sources.
 * Note: My admin page is a subpage of one of my other plugins admin pages.
    Also
   might have some issue with the visibility metadata I’m not updating it yet.
 *     ```
       <?php
   
       /*
       Plugin Name: Update Datatables
       Plugin URI:
       Description: Uses Remote Service Intergration to get a remote database handle.
       Runs hard-coded querys against remote DB updating hard-coded TablePress table ids.
       Version: 1.0
       Author: Liam Mitchell
       Author URI: http://github.com/LiamKarlMitchell
       License: MIT
       */
   
       // On plugin activation schedule our daily datatable update.
       register_activation_hook( __FILE__, 'vt_create_daily_datatable_schedule' );
       function vt_create_daily_datatable_schedule(){
         // Use wp_next_scheduled to check if the event is already scheduled
         $timestamp = wp_next_scheduled( 'vt_create_daily_datatable' );
   
         // If $timestamp == false schedule daily datatables since it hasn't been done previously
         if( $timestamp == false ){
           //Schedule the event for right now, then to repeat daily using the hook 'vt_create_daily_datatable'
           wp_schedule_event( time(), 'daily', 'vt_update_daily_datatables_hook' );
         }
       }
   
       register_deactivation_hook( __FILE__, 'vt_remove_daily_datatable_schedule' );
       function vt_remove_daily_datatable_schedule(){
           wp_clear_scheduled_hook( 'vt_update_daily_datatables_hook' );
       }
   
       add_action('admin_menu', 'vt_register_submenu_page');
   
       function vt_register_submenu_page() {
           add_submenu_page('remote-server-integration/remote-server-integration-admin-page.php', 'Datatables', 'Datatables', 'manage_options', __FILE__.'/datatables', 'vt_datatables_admin_page');
       }
   
       function vt_datatables_admin_page() {
           // TODO: Add nonce to form?
           // https://codex.wordpress.org/WordPress_Nonces
           ?>
           <p>Data tables are hard-coded into the update-my-datatables.php.<br>Queries are run against the remote database for a service daily and the appropriate data tables have their data updated.</p>
           <form action="<?php echo admin_url('admin-post.php'); ?>" method="post">
               <input type="hidden" name="action" value="vt_update_daily_datatables">
               <input type="submit" value="Update datatables.">
           </form>
           <?php
       }
   
       add_action( 'admin_post_vt_update_daily_datatables', 'vt_update_daily_datatables_from_admin' );
   
       function vt_load_table( $table_id ) {
           if ( ! TablePress::$model_table->table_exists( $table_id ) ) {
               return false;
           }
           // Load the table.
           $table = TablePress::$model_table->load( $table_id, true, true );
           // Skip tables that could not be loaded or that are corrupted.
           if ( is_wp_error( $table ) || ( isset( $table['is_corrupted'] ) && $table['is_corrupted'] ) ) {
               return false;
           }
           return $table;
       }
   
       function vt_save_table( $table ) {
           $result = TablePress::$model_table->save($table);
           if (is_wp_error($result)) {
               // TODO: Log error?
           }
       }
   
       function vt_update_daily_datatables_from_admin() {
           define( 'WP_DEBUG' , true);
           define( 'WP_DEBUG_DISPLAY' , true);
           define('WP_DISABLE_FATAL_ERROR_HANDLER',true);
           ini_set('display_errors', 1);
           ini_set('display_startup_errors', 1);
           error_reporting(E_ALL);
   
           // TODO: NONCE? https://premium.wpmudev.org/blog/handling-form-submissions/
           vt_update_daily_datatables();
   
           die('done');
           wp_safe_redirect(
               esc_url_raw(
                   add_query_arg( 'tablesupdated', 'success', admin_url('admin.php?page=update-my-datatables%2Fupdate-my-datatables.php%2Fdatatables') )
               )
           );
           exit();
       }
   
       add_action('vt_update_daily_datatables_hook', 'vt_update_daily_datatables');
       function vt_update_daily_datatables(){
           vt_table_update();
       }
   
       function vt_table_update() {
           global $rsi;
           // TODO: If user is admin on admin page show log to page?
           $table = vt_load_table(1);
           if ($table === false) {
               // Error loading table.
               return;
           }
           // Get connection to Database.
           $db = $rsi->getDBForService('another_remote_mysql_database');
           if ($db === false) {
               // Error connecting to DB?
               return;
           }
           // var_dump($db->get_results("SELECT columns from table etc.", ARRAY_N);
   
           // If an array was returned and no errors occurred, return the result set
           if (is_array($rows) && empty($db->last_error)) {
               $rowcount = count($rows);
               // Put rank number on manually as row_number method does not work in our version of MariaDB.
               for ($i=0; $i < $rowcount; $i++) {
                   $rows[$i][0] = strval($i + 1);
               }
   
               // Make a new array containing the table headers and the rows.
               $table['data'] = array_merge([$table['data'][0]], $rows);
               // Save table.
               vt_save_table($table);
           }
       //    else {
       //        // echo $db->last_error;
       //    }
       }
       ```
   
    -  This reply was modified 5 years, 11 months ago by [liammitchell](https://wordpress.org/support/users/liammitchell/).
    -  This reply was modified 5 years, 11 months ago by [liammitchell](https://wordpress.org/support/users/liammitchell/).
    -  This reply was modified 5 years, 11 months ago by [liammitchell](https://wordpress.org/support/users/liammitchell/).
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[Wallet for WooCommerce] decrease wallet wallet balance on button click](https://wordpress.org/support/topic/decrease-wallet-wallet-balance-on-button-click/)
 *  [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [6 years, 6 months ago](https://wordpress.org/support/topic/decrease-wallet-wallet-balance-on-button-click/#post-12233382)
 * Yes, you can call the debit method via an ajax request for example.
 * class Woo_Wallet_Wallet
 *     ```
       global $woo_wallet;
       $woo_wallet->debit($user_id, amount, 'Description here');
       ```
   
 * Or maybe it was $woo_wallet->wallet->debit … I forget.
 * Also useful.
    get_wallet_balance($user_id) You can leave user_id blank to get
   for the logged in user.
 * See
    [https://github.com/malsubrata/woo-wallet/blob/master/includes/class-woo-wallet-wallet.php](https://github.com/malsubrata/woo-wallet/blob/master/includes/class-woo-wallet-wallet.php)
 * There is a filter in recode_transaction for woo_wallet_disallow_negative_transaction
   to say if going negative balance is okay or not.
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[Two Factor Authentication] Adding to Buddy Press profile and Woocommerce account settings.](https://wordpress.org/support/topic/adding-to-buddy-press-profile-and-woocommerce-account-settings/)
 *  Thread Starter [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [7 years ago](https://wordpress.org/support/topic/adding-to-buddy-press-profile-and-woocommerce-account-settings/#post-11584542)
 * Hi [@ted_smith](https://wordpress.org/support/users/ted_smith/),
 * If I remember right I put it in my sites plugin functions.php
    But could probably
   go in wordpress theme functions.php as well.
 * I like to make a plugin for site specific codes rather than to mess with the 
   theme (which could change).
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[Wallet for WooCommerce] Allow custom step increments on topup amount and integer only option.](https://wordpress.org/support/topic/allow-custom-step-increments-on-topup-amount-and-integer-only-option/)
 *  Thread Starter [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [7 years ago](https://wordpress.org/support/topic/allow-custom-step-increments-on-topup-amount-and-integer-only-option/#post-11584515)
 * Oh brilliant thank you. (Woocomerce template system is nice).
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[Wallet for WooCommerce] How to ensure only one spend at a time?](https://wordpress.org/support/topic/how-to-ensure-only-one-spend-at-a-time/)
 *  Thread Starter [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [7 years ago](https://wordpress.org/support/topic/how-to-ensure-only-one-spend-at-a-time/#post-11584489)
 * Thanks, [@subratamal](https://wordpress.org/support/users/subratamal/)
 * There is a wpdesk/wp-mutex package that may be of interest.
    [https://packagist.org/packages/wpdesk/wp-mutex](https://packagist.org/packages/wpdesk/wp-mutex)
   [https://gitlab.com/wpdesk/wp-mutex/tree/master](https://gitlab.com/wpdesk/wp-mutex/tree/master)
 * May be other considerations when using with multisite installations.
 * You could acquire a lock name based on the user id.
 * Their one makes use of MySQL Locking Functions.
    [https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html](https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html)
 * Possibly MySQL 5.7+.
 *   Forum: [Plugins](https://wordpress.org/support/forum/plugins-and-hacks/)
    In
   reply to: [[Wallet for WooCommerce] How to ensure only one spend at a time?](https://wordpress.org/support/topic/how-to-ensure-only-one-spend-at-a-time/)
 *  Thread Starter [liammitchell](https://wordpress.org/support/users/liammitchell/)
 * (@liammitchell)
 * [7 years ago](https://wordpress.org/support/topic/how-to-ensure-only-one-spend-at-a-time/#post-11582369)
 * So from my understanding debit/credit operation is not an atomic/blocking operation.
 * If two sessions for the same user (e.g. different browser windows) do it at the
   same time, the result could be surprising.
 * [https://github.com/malsubrata/woo-wallet/blob/master/includes/class-woo-wallet-wallet.php#L189-L197](https://github.com/malsubrata/woo-wallet/blob/master/includes/class-woo-wallet-wallet.php#L189-L197)
 * Assume at least two threads are handling requests.
    Thread 1 and Thread 2 for
   same/similar request both get past the Balance check end up on line 193. User
   has $100 wanting to spend $100. Thread 1 and Thread 2 would both insert a record
   to the transactions table at this point. $200 is effectively “spent” each having
   a transaction id, but the balance is still calculated at $previousBalance -= 
   $amount;
 * Is this a Double spend problem?
 * I’m probably just going to install an extension to limit to 1 session per WordPress
   user as a precaution.
    [https://www.wpsecurityauditlog.com/support-documentation/managing-multiple-same-wordpress-user-sessions/](https://www.wpsecurityauditlog.com/support-documentation/managing-multiple-same-wordpress-user-sessions/)
 * I could be over-thinking this and worrying about nothing, but it could be possible
   have not reproduced yet.
 * Look at it another way, multiple bot accounts constantly trading amounts between
   themselves with multiple sessions in an attempt to duplicate amounts, at some
   point would it screw out?
 * Yes, I think assuming the webserver has multiple threads/workers.
    But there 
   may be something else that I have over-looked as I have not gone through every
   code just yet, that just stood out to me as a potential concern.

Viewing 8 replies - 1 through 8 (of 8 total)