PageRenderTime 675ms CodeModel.GetById 15ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 524ms

/plugins/wp-table-reloaded/controllers/controller-admin.php

https://github.com/petergibbons/OpenCounterWP
PHP | 1922 lines | 1484 code | 130 blank | 308 comment | 100 complexity | 66101b57c9f2d3a43f2dec20f7d79e9b MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1<?php
  2/**
  3 * Admin Controller for WP-Table Reloaded with functions for the backend
  4 *
  5 * @package WP-Table Reloaded
  6 * @subpackage Admin Controller
  7 * @author Tobias B&auml;thge
  8 * @since 1.6
  9 */
 10
 11/**
 12 * Include file with the Base Controller Class
 13 */
 14require_once ( WP_TABLE_RELOADED_ABSPATH . 'controllers/controller-base.php' );
 15
 16/**
 17 * Define the WP-Table Reloaded Text Domain, used to separate between plugin and core localization
 18 */
 19define( 'WP_TABLE_RELOADED_TEXTDOMAIN', 'wp-table-reloaded' );
 20
 21/**
 22 * Admin Controller class, extends Base Controller Class
 23 */
 24class WP_Table_Reloaded_Controller_Admin extends WP_Table_Reloaded_Controller_Base {
 25
 26    /**
 27     * Nonce for security of links/forms, to prevent "CSRF"
 28     * @var string
 29     */
 30    var $nonce_base = 'wp-table-reloaded-nonce';
 31
 32    /**
 33     * List of allowed actions that a user can perform with tables or the plugin
 34     * @var array
 35     */
 36    var $allowed_actions = array( 'list', 'add', 'edit', 'bulk_edit', 'copy', 'delete', 'import', 'export', 'options', 'uninstall', 'about', 'hide_donate_nag', 'hide_welcome_message' );
 37                            // 'ajax_list', 'ajax_preview' also exist, but are handled separately
 38
 39    /**
 40     * current action that is performed in this page load, populated in load_manage_page()
 41     * @var string
 42     */
 43    var $action = 'list';
 44
 45    /**
 46     * List of available translations of WP-Table Reloaded, init in __construct, because of the translations
 47     * @var array
 48     */
 49    var $available_plugin_languages = array();
 50
 51    /**
 52     * Default plugin options and their default values, fresh installs use those, updated installs update their options accordingly
 53     * @var array
 54     */
 55    var $default_options = array(
 56        'installed_version' => '0',
 57        'plugin_language' => 'auto',
 58        'uninstall_upon_deactivation' => false,
 59        'show_exit_warning' => true,
 60        'growing_textareas' => true,
 61        'use_datatables_on_table_list' => true,
 62        'add_target_blank_to_links' => false,
 63        'enable_tablesorter' => true,
 64        'tablesorter_script' => 'datatables', // others are 'datatables-tabletools', 'tablesorter', and 'tablesorter_extended'
 65        'use_default_css' => true,
 66        'use_custom_css' => true,
 67        'custom_css' => '',
 68        'enable_search' => true,
 69        'admin_menu_parent_page' => 'tools.php',
 70        'user_access_plugin' => 'author', // others are 'contributor', 'editor', and 'admin'
 71        'user_access_plugin_options' => 'author', // others are 'editor', and 'admin'
 72        'frontend_edit_table_link' => true,
 73        'install_time' => 0,
 74        'show_donate_nag' => true,
 75        'show_welcome_message' => 0, // 0 = no message, 1 = install message, 2 = update message
 76        'update_message' => array(),
 77        'last_id' => 0
 78    );
 79    
 80    /**
 81     * Default list of tables (empty, because there are no tables right after installation)
 82     * @var array
 83     */
 84    var $default_tables = array();
 85
 86    /**
 87     * Instance of the WP_Table_Reloaded_Helper class, which has additional functions for frontend and backend, stored in separate file for better overview and maintenance
 88     * @var object
 89     */
 90    var $helper;
 91
 92    /**
 93     * Instance of the WP_Table_Reloaded_Export class
 94     * @var object
 95     */
 96    var $export_instance;
 97    
 98    /**
 99     * Instance of the WP_Table_Reloaded_Import class
100     * @var object
101     */
102    var $import_instance;
103
104    /**
105     * Hook (i.e. name) WordPress uses for the WP-Table Reloaded page, needed for certain plugin actions and filters, populated in add_manage_page()
106     * @var string
107     */
108    var $hook = '';
109
110    /**
111     * Name of the file, WP-Table Reloaded is accessible under, dependant of whether admin has moved the WP-Table Reloaded menu entry
112     * @var string
113     */
114    var $page_url = '';
115
116    /**
117     * PHP4 class constructor, calls the PHP5 class constructor __construct()
118     */
119    function WP_Table_Reloaded_Controller_Admin() {
120        $this->__construct();
121    }
122
123    /**
124     * PHP5 class constructor
125     *
126     * Initiate Backend functionality, by checking for AJAX calls, eventually answering those or setting up the admin page
127     */
128    function __construct() {
129        register_activation_hook( WP_TABLE_RELOADED__FILE__, array( &$this, 'plugin_activation_hook' ) );
130        register_deactivation_hook( WP_TABLE_RELOADED__FILE__, array( &$this, 'plugin_deactivation_hook' ) );
131
132        $this->helper = $this->create_class_instance( 'WP_Table_Reloaded_Helper', 'helper.class.php' );
133
134        // load plugin options and existing tables
135        $this->init_plugin();
136
137        // WordPress 3.1 requires new update check
138        if ( version_compare( $this->options['installed_version'], WP_TABLE_RELOADED_PLUGIN_VERSION, '<' ) )
139            add_action( 'init', array( &$this, 'plugin_update' ) );
140
141        // init variables to check whether we do valid AJAX
142        $doing_ajax = defined( 'DOING_AJAX' ) ? DOING_AJAX : false;
143        $valid_ajax_call = ( isset( $_GET['page'] ) && $this->page_slug == $_GET['page'] ) ? true : false;
144
145        // have to check for possible "export all" request this early,
146        // because otherwise http-headers will be sent by WP before we can send download headers
147        if ( !$doing_ajax && $valid_ajax_call && isset( $_POST['export_all'] ) ) {
148            // can be done in plugins_loaded, as no language support is needed
149            add_action( 'plugins_loaded', array( &$this, 'do_action_export_all' ) );
150            $doing_ajax = true;
151        }
152        // have to check for possible export file download request this early,
153        // because otherwise http-headers will be sent by WP before we can send download headers
154        if ( !$doing_ajax && $valid_ajax_call && isset( $_POST['download_export_file'] ) && 'true' == $_POST['download_export_file'] ) {
155            // can be done in plugins_loaded, as no language support is needed
156            add_action( 'plugins_loaded', array( &$this, 'do_action_export' ) );
157            $doing_ajax = true;
158        }
159        // have to check for possible call by editor button to show list of tables
160        // and possible call to show a table preview in a thickbox on "List Tables" screen
161        if ( !$doing_ajax && $valid_ajax_call && isset( $_GET['action'] ) && ( 'ajax_list' == $_GET['action'] || 'ajax_preview' == $_GET['action'] ) ) {
162            // can not be done earlier, because we need language support
163            add_action( 'init', array( &$this, 'do_action_' . $_GET['action'] ) );
164            $doing_ajax = true;
165        }
166
167        // we are not doing AJAX, so we call the main plugin handler
168        if ( !$doing_ajax ) {
169            add_action( 'admin_menu', array( &$this, 'add_manage_page' ) );
170
171            // add JS to add button to editor on admin pages that might have an editor
172            $pages_with_editor_button = array( 'post.php', 'post-new.php', 'page.php', 'page-new.php' );
173            foreach ( $pages_with_editor_button as $page )
174                add_action( 'load-' . $page, array( &$this, 'add_editor_button' ) );
175        }
176
177        // add message to list of plugins, if an update is available / add additional links on Plugins page, for both regular visits and AJAX calls
178        add_action( 'in_plugin_update_message-' . WP_TABLE_RELOADED_BASENAME, array( &$this, 'add_plugin_update_message' ), 10, 2 );
179        add_filter( 'plugin_row_meta', array( &$this, 'add_plugin_row_meta' ), 10, 2);
180    }
181
182    /**
183     * Add admin page to the correct place in the admin menu, and set handler for when page is loaded or shown
184     */
185    function add_manage_page() {
186        // user needs at least this capability to view WP-Table Reloaded config page
187        // capabilities from http://codex.wordpress.org/Roles_and_Capabilities
188        $user_group = $this->options['user_access_plugin'];
189        $capabilities = array(
190            'admin' => 'manage_options',
191            'editor' => 'publish_pages',
192            'author' => 'publish_posts',
193            'contributor' => 'edit_posts'
194        );
195        $min_capability = isset( $capabilities[ $user_group ] ) ? $capabilities[ $user_group ] : 'manage_options';
196        $min_capability = apply_filters( 'wp_table_reloaded_min_needed_capability', $min_capability ); // plugins may filter/change this though
197        
198        $display_name = 'WP-Table Reloaded'; // the name that is displayed in the admin menu on the left
199        $display_name = apply_filters( 'wp_table_reloaded_plugin_display_name', $display_name ); // can be filtered to something shorter maybe
200
201        $admin_menu_page = apply_filters( 'wp_table_reloaded_admin_menu_parent_page', $this->options['admin_menu_parent_page'] );
202        // backward-compatibility for the filter
203        if ( 'top-level' == $admin_menu_page )
204            $admin_menu_page = 'admin.php';
205        // 'edit-pages.php' was renamed to 'edit.php?post_type=page' in WP 3.0
206        if ( 'edit-pages.php' == $admin_menu_page )
207            $admin_menu_page = 'edit.php?post_type=page';
208        if ( !in_array( $admin_menu_page, $this->possible_admin_menu_parent_pages ) )
209            $admin_menu_page = 'tools.php';
210
211        // Top-Level menu is created in different function, all others are created with the filename as a parameter
212        if ( 'admin.php' == $admin_menu_page )
213            $this->hook = add_menu_page( 'WP-Table Reloaded', $display_name, $min_capability, $this->page_slug, array( &$this, 'show_manage_page' ), plugins_url( 'admin/plugin-icon-small.png', WP_TABLE_RELOADED__FILE__ ) );
214        else
215            $this->hook = add_submenu_page( $admin_menu_page, 'WP-Table Reloaded', $display_name, $min_capability, $this->page_slug, array( &$this, 'show_manage_page' ) );
216        $this->page_url = $admin_menu_page;
217
218        add_action( 'load-' . $this->hook, array( &$this, 'load_manage_page' ) );
219    }
220
221    /**
222     * Function is loaded by WordPress, if WP-Table Reloaded's admin menu entry is called,
223     * Load the scripts, stylesheets and language, all of this will be done before the page is shown by show_manage_page()
224     */
225    function load_manage_page() {
226        // show admin footer message (only on pages of WP-Table Reloaded)
227		add_filter( 'admin_footer_text', array( &$this->helper, 'add_admin_footer_text' ) );
228
229        $this->init_language_support();
230
231        // get and check action parameter from passed variables
232        $default_action = 'list';
233        $default_action = apply_filters( 'wp_table_reloaded_default_action', $default_action );
234        $this->allowed_actions = apply_filters( 'wp_table_reloaded_allowed_actions', $this->allowed_actions );
235        $action = ( !empty( $_REQUEST['action'] ) ) ? $_REQUEST['action'] : $default_action;
236        // check if action is in allowed actions and if method is callable, if yes, call it
237        if ( in_array( $action, $this->allowed_actions ) )
238            $this->action = $action;
239
240        add_thickbox();
241        // load js and css for admin, needs to stand below thickbox script
242        $this->add_manage_page_js(); // will add script to footer
243        $this->add_manage_page_css(); // needs to be added to the header
244
245        // done after determining the action, because needs action parameter to load correct help string
246        add_contextual_help( $this->hook, $this->helper->get_contextual_help_string() );
247    }
248
249    /**
250     * Function is loaded by WordPress, if WP-Table Reloaded's admin menu entry is called,
251     * responsible for calling the appropriate action handler, output of WP admin menu and header is already done here
252     */
253    function show_manage_page() {
254        $this->available_plugin_languages = array(
255            'ar'    => __( 'Arabic', WP_TABLE_RELOADED_TEXTDOMAIN ),
256            'be_BY' => __( 'Belarusian', WP_TABLE_RELOADED_TEXTDOMAIN ),
257			'bg_BG' => __( 'Bulgarian', WP_TABLE_RELOADED_TEXTDOMAIN ),
258            'cs_CZ' => __( 'Czech', WP_TABLE_RELOADED_TEXTDOMAIN ),
259            'de_DE' => __( 'German', WP_TABLE_RELOADED_TEXTDOMAIN ),
260            'en_US' => __( 'English', WP_TABLE_RELOADED_TEXTDOMAIN ),
261            'es_ES' => __( 'Spanish', WP_TABLE_RELOADED_TEXTDOMAIN ),
262            'fi'    => __( 'Finnish', WP_TABLE_RELOADED_TEXTDOMAIN ),
263            'fr_FR' => __( 'French', WP_TABLE_RELOADED_TEXTDOMAIN ),
264            'he_IL' => __( 'Hebrew', WP_TABLE_RELOADED_TEXTDOMAIN ),
265            'hi_IN' => __( 'Hindi', WP_TABLE_RELOADED_TEXTDOMAIN ),
266            'id_ID' => __( 'Indonesian', WP_TABLE_RELOADED_TEXTDOMAIN ),
267            'it_IT' => __( 'Italian', WP_TABLE_RELOADED_TEXTDOMAIN ),
268            'ja'    => __( 'Japanese', WP_TABLE_RELOADED_TEXTDOMAIN ),
269            'nl_NL' => __( 'Dutch', WP_TABLE_RELOADED_TEXTDOMAIN ),
270            'pl_PL' => __( 'Polish', WP_TABLE_RELOADED_TEXTDOMAIN ),
271            'pt_BR' => __( 'Brazilian Portuguese', WP_TABLE_RELOADED_TEXTDOMAIN ),
272            'pt_PT' => __( 'Portuguese (Portugal)', WP_TABLE_RELOADED_TEXTDOMAIN ),
273            'ru_RU' => __( 'Russian', WP_TABLE_RELOADED_TEXTDOMAIN ),
274            'sk_SK' => __( 'Slovak', WP_TABLE_RELOADED_TEXTDOMAIN ),
275            'sv_SE' => __( 'Swedish', WP_TABLE_RELOADED_TEXTDOMAIN ),
276            'ua_UA' => __( 'Ukrainian', WP_TABLE_RELOADED_TEXTDOMAIN ),
277            'zh_CN' => __( 'Chinese (Simplified)', WP_TABLE_RELOADED_TEXTDOMAIN ),
278            // the following are inactive because they are not up-to-date
279            // 'ga_IR' => __( 'Irish', WP_TABLE_RELOADED_TEXTDOMAIN ),
280            // 'sq_AL' => __( 'Albanian', WP_TABLE_RELOADED_TEXTDOMAIN ),
281            // 'tr_TR' => __( 'Turkish', WP_TABLE_RELOADED_TEXTDOMAIN ),
282        );
283        asort( $this->available_plugin_languages );
284
285        // do WP plugin action (before action is fired) -> can stop further plugin execution by returning true
286        $overwrite = apply_filters( 'wp_table_reloaded_action_pre_' . $this->action, false );
287        if ( $overwrite )
288            return;
289    
290        // call appropriate action, $this->action is populated in load_manage_page
291        if ( is_callable( array( &$this, 'do_action_' . $this->action ) ) )
292            call_user_func( array( &$this, 'do_action_' . $this->action ) );
293    }
294    
295    // ###################################################################################################################
296    // ##########################################                   ######################################################
297    // ##########################################      ACTIONS      ######################################################
298    // ##########################################                   ######################################################
299    // ###################################################################################################################
300
301    /**
302     * "List Tables" action handler
303     */
304    function do_action_list() {
305        $messages = array(
306            0 => false,
307            1 => sprintf( __( 'Welcome to WP-Table Reloaded %s. If you encounter any questions or problems, please refer to the <a href="%s">FAQ</a>, the <a href="%s">documentation</a>, and the <a href="%s">support</a> section.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->options['installed_version'], 'http://tobias.baethge.com/go/wp-table-reloaded/faq/', 'http://tobias.baethge.com/go/wp-table-reloaded/documentation/', 'http://tobias.baethge.com/go/wp-table-reloaded/support/' ),
308            2 => sprintf( __( 'Thank you for upgrading to WP-Table Reloaded %s.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->options['installed_version'] ) . ' ' . __( 'This version includes several bugfixes and a few enhancements.', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . sprintf( __( 'Please read the <a href="%s">release announcement</a> for more information.', WP_TABLE_RELOADED_TEXTDOMAIN ), "http://tobias.baethge.com/go/wp-table-reloaded/release-announcement/{$this->options['installed_version']}/" ) . '<br/>' . sprintf( __( 'If you like the new features and enhancements, I would appreciate a small <a href="%s">donation</a>. Thank you.', WP_TABLE_RELOADED_TEXTDOMAIN ), 'http://tobias.baethge.com/go/wp-table-reloaded/donate/' )
309        );
310        $message = ( isset( $messages[ $this->options['show_welcome_message'] ] ) ) ? $messages[ $this->options['show_welcome_message'] ] : false;
311        if ( $message ) {
312            $hide_welcome_message_url = $this->get_action_url( array( 'action' => 'hide_welcome_message' ), true );
313            $this->helper->print_header_message( $message . '<br/><br/>' . sprintf( '<a href="%s" style="font-weight:normal;">%s</a>', $hide_welcome_message_url, __( 'Hide this message', WP_TABLE_RELOADED_TEXTDOMAIN ) ) );
314        }
315
316        if ( $this->may_print_donate_nag() ) {
317            $donate_url = 'http://tobias.baethge.com/go/wp-table-reloaded/donate/message/';
318            $donated_true_url = $this->get_action_url( array( 'action' => 'hide_donate_nag', 'user_donated' => true ), true );
319            $donated_false_url = $this->get_action_url( array( 'action' => 'hide_donate_nag', 'user_donated' => false ), true );
320            $this->helper->print_header_message(
321                __( 'Thanks for using this plugin! You\'ve installed WP-Table Reloaded over a month ago.', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . sprintf( _n( 'If it works and you are satisfied with the results of managing your %s table, isn\'t it worth at least one dollar or euro?', 'If it works and you are satisfied with the results of managing your %s tables, isn\'t it worth at least one dollar or euro?', count( $this->tables ), WP_TABLE_RELOADED_TEXTDOMAIN ), count( $this->tables ) ) . '<br/><br/>' .
322                sprintf( __( '<a href="%s">Donations</a> help me to continue support and development of this <i>free</i> software - things for which I spend countless hours of my free time! Thank you!', WP_TABLE_RELOADED_TEXTDOMAIN ), $donate_url ) . '<br/><br/>' .
323                sprintf( '<a href="%s" target="_blank">%s</a>', $donate_url, __( 'Sure, no problem!', WP_TABLE_RELOADED_TEXTDOMAIN ) ) . '&nbsp;&nbsp;&middot;&nbsp;&nbsp;' .
324                sprintf( '<a href="%s" style="font-weight:normal;">%s</a>', $donated_true_url, __( 'I already donated.', WP_TABLE_RELOADED_TEXTDOMAIN ) ) . '&nbsp;&nbsp;&middot;&nbsp;&nbsp;' .
325                sprintf( '<a href="%s" style="font-weight:normal;">%s</a>', $donated_false_url, __( 'No, thanks. Don\'t ask again.', WP_TABLE_RELOADED_TEXTDOMAIN ) )
326            );
327        }
328
329        $this->load_view( 'list' );
330    }
331    
332    /**
333     * "Add new Table" action handler
334     */
335    function do_action_add() {
336        if ( isset( $_POST['submit'] ) && isset( $_POST['table'] ) ) {
337            check_admin_referer( $this->get_nonce( 'add' ) );
338
339            $rows = ( 0 < $_POST['table']['rows'] ) ? $_POST['table']['rows'] : 1;
340            $cols = ( 0 < $_POST['table']['cols'] ) ? $_POST['table']['cols'] : 1;
341
342            $table = $this->default_table;
343
344            $table['id'] = $this->get_new_table_id();
345            $table['data'] = $this->helper->create_empty_table( $rows, $cols );
346            $table['visibility']['rows'] = array_fill( 0, $rows, false );
347            $table['visibility']['columns'] = array_fill( 0, $cols, false );
348            $table['name'] = $_POST['table']['name'];
349            $table['description'] = $_POST['table']['description'];
350
351            $this->save_table( $table );
352
353            $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; added successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ) ) );
354            $table_id = $table['id'];
355            $this->load_view( 'edit', compact( 'table_id' ) );
356        } else {
357            $this->load_view( 'add' );
358        }
359    }
360
361    /**
362     * "Edit Table" action handler
363     */
364    function do_action_edit() {
365        if ( isset( $_POST['submit'] ) && isset( $_POST['table'] ) ) {
366            check_admin_referer( $this->get_nonce( 'edit' ) );
367            
368            $subactions = array_keys( $_POST['submit'] );
369            $subaction = $subactions[0];
370            
371            switch( $subaction ) {
372            case 'update':
373            case 'save_back':
374                $table = $_POST['table'];   // careful here to not miss any stuff!!! (options, etc.)
375                // do we want to change the ID?
376                $new_table_id = ( isset( $_POST['table_id'] ) ) ? $_POST['table_id'] : $table['id'] ;
377                if ( $new_table_id != $table['id'] && is_numeric( $new_table_id ) && ( 0 < $new_table_id ) ) {
378                    if ( !$this->table_exists( $new_table_id ) ) {
379                        // delete table with old ID
380                        $old_table_id = $table['id'];
381                        $this->delete_table( $old_table_id );
382                        // set new table ID
383                        $table['id'] = $new_table_id;
384                        $message = sprintf( __( "Table edited successfully. This Table now has the ID %s. You'll need to adjust existing shortcodes accordingly.", WP_TABLE_RELOADED_TEXTDOMAIN ), $new_table_id );
385                    } else {
386                        $message = sprintf( __( 'The ID could not be changed from %s to %s, because there already is a Table with that ID.', WP_TABLE_RELOADED_TEXTDOMAIN ), $table['id'], $new_table_id );
387                    }
388                } else {
389                    $message = __( 'Table edited successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
390                }
391                // save table options (checkboxes!), only checked checkboxes are submitted (then as true)
392                $table['options']['alternating_row_colors'] = isset( $_POST['table']['options']['alternating_row_colors'] );
393                $table['options']['row_hover'] = isset( $_POST['table']['options']['row_hover'] );
394                $table['options']['first_row_th'] = isset( $_POST['table']['options']['first_row_th'] );
395                $table['options']['table_footer'] = isset( $_POST['table']['options']['table_footer'] );
396                $table['options']['print_name'] = isset( $_POST['table']['options']['print_name'] );
397                $table['options']['print_description'] = isset( $_POST['table']['options']['print_description'] );
398                $table['options']['cache_table_output'] = isset( $_POST['table']['options']['cache_table_output'] );
399                $table['options']['custom_css_class'] = trim( $table['options']['custom_css_class'] ); // more complex sanitize_* functions would change spaces to hyphens...
400                $table['options']['use_tablesorter'] = isset( $_POST['table']['options']['use_tablesorter'] );
401                $table['options']['datatables_sort'] = isset( $_POST['table']['options']['datatables_sort'] );
402                $table['options']['datatables_paginate'] = isset( $_POST['table']['options']['datatables_paginate'] );
403                $table['options']['datatables_lengthchange'] = isset( $_POST['table']['options']['datatables_lengthchange'] );
404                $table['options']['datatables_filter'] = isset( $_POST['table']['options']['datatables_filter'] );
405                $table['options']['datatables_info'] = isset( $_POST['table']['options']['datatables_info'] );
406                $table['options']['datatables_tabletools'] = isset( $_POST['table']['options']['datatables_tabletools'] );
407                $table['options']['datatables_paginate_entries'] = ( is_numeric( $table['options']['datatables_paginate_entries'] ) ) ? absint( $table['options']['datatables_paginate_entries'] ) : $this->default_table['options']['datatables_paginate_entries'];
408                // $table['options']['datatables_customcommands'] is an input type=text field that is always submitted
409                // $table['options']['print_name|description_position'] are select fields that are always submitted
410
411                // save visibility settings (checkboxes!)
412                foreach ( $table['data'] as $row_idx => $row )
413                    $table['visibility']['rows'][$row_idx] = ( isset( $_POST['table']['visibility']['rows'][$row_idx] ) && ( 'true' == $_POST['table']['visibility']['rows'][$row_idx] ) );
414                foreach ( $table['data'][0] as $col_idx => $col )
415                    $table['visibility']['columns'][$col_idx] = ( isset( $_POST['table']['visibility']['columns'][$col_idx] ) && ( 'true' == $_POST['table']['visibility']['columns'][$col_idx] ) );
416
417                if ( !empty( $table['custom_fields'] ) )
418                    uksort( $table['custom_fields'], 'strnatcasecmp' ); // sort the keys naturally
419
420                $this->save_table( $table );
421                break;
422            case 'swap_rows':
423                $table_id = $_POST['table']['id'];
424                $row_id1 = ( isset( $_POST['swap']['row'][1] ) ) ? $_POST['swap']['row'][1] : -1;
425                $row_id2 = ( isset( $_POST['swap']['row'][2] ) ) ? $_POST['swap']['row'][2] : -1;
426                $table = $this->load_table( $table_id );
427                $rows = count( $table['data'] );
428                // swap rows $row_id1 and $row_id2
429                if ( ( 1 < $rows ) && ( -1 < $row_id1 ) && ( -1 < $row_id2 ) && ( $row_id1 != $row_id2 ) ) {
430                    $temp_row = $table['data'][$row_id1];
431                    $table['data'][$row_id1] = $table['data'][$row_id2];
432                    $table['data'][$row_id2] = $temp_row;
433                    $temp_visibility = $table['visibility']['rows'][$row_id1];
434                    $table['visibility']['rows'][$row_id1] = $table['visibility']['rows'][$row_id2];
435                    $table['visibility']['rows'][$row_id2] = $temp_visibility;
436                }
437                $this->save_table( $table );
438                $message = __( 'Rows swapped successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
439                break;
440            case 'swap_cols':
441                $table_id = $_POST['table']['id'];
442                $col_id1 = ( isset( $_POST['swap']['col'][1] ) ) ? $_POST['swap']['col'][1] : -1;
443                $col_id2 = ( isset( $_POST['swap']['col'][2] ) ) ? $_POST['swap']['col'][2] : -1;
444                $table = $this->load_table( $table_id );
445                $rows = count( $table['data'] );
446                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
447                // swap rows $col_id1 and $col_id2
448                if ( ( 1 < $cols ) && ( -1 < $col_id1 ) && ( -1 < $col_id2 ) && ( $col_id1 != $col_id2 ) ) {
449                    foreach ( $table['data'] as $row_idx => $row ) {
450                        $temp_col = $table['data'][$row_idx][$col_id1];
451                        $table['data'][$row_idx][$col_id1] = $table['data'][$row_idx][$col_id2];
452                        $table['data'][$row_idx][$col_id2] = $temp_col;
453                    }
454                    $temp_visibility = $table['visibility']['columns'][$col_id1];
455                    $table['visibility']['columns'][$col_id1] = $table['visibility']['columns'][$col_id2];
456                    $table['visibility']['columns'][$col_id2] = $temp_visibility;
457                }
458                $this->save_table( $table );
459                $message = __( 'Columns swapped successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
460                break;
461            case 'sort':
462                $table_id = $_POST['table']['id'];
463                $column = ( isset( $_POST['sort']['col'] ) ) ? $_POST['sort']['col'] : -1;
464                $sort_order = ( isset( $_POST['sort']['order'] ) ) ? $_POST['sort']['order'] : 'ASC';
465                $table = $this->load_table( $table_id );
466                $rows = count( $table['data'] );
467                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
468                // sort array for $column in $sort_order
469                if ( ( 1 < $rows ) && ( -1 < $column ) ) {
470                    // for sorting: temporarily store row visibility in data, so that it gets sorted, too
471                    foreach ( $table['data'] as $row_idx => $row )
472                        array_splice( $table['data'][$row_idx], $cols, 0, $table['visibility']['rows'][$row_idx] );
473
474                    $array_to_sort = $table['data'];
475                    if ( isset( $table['options']['first_row_th'] ) && $table['options']['first_row_th'] )
476                        $first_row = array_shift( $array_to_sort );
477                    if ( isset( $table['options']['table_footer'] ) && $table['options']['table_footer'] )
478                        $last_row = array_pop( $array_to_sort );
479
480                    $sortarray = $this->create_class_instance( 'arraysort', 'arraysort.class.php' );
481                    $sortarray->input_array = $array_to_sort;
482                    $sortarray->column = $column;
483                    $sortarray->order = $sort_order;
484                    $sortarray->sort();
485                    $sorted_array = $sortarray->sorted_array;
486
487                    if ( isset( $table['options']['first_row_th'] ) && $table['options']['first_row_th'] )
488                        array_unshift( $sorted_array, $first_row );
489                    if ( isset( $table['options']['table_footer'] ) && $table['options']['table_footer'] )
490                        array_push( $sorted_array, $last_row );
491                    $table['data'] = $sorted_array;
492
493                    // then restore row visibility from sorted data and remove temporary column
494                    foreach ( $table['data'] as $row_idx => $row ) {
495                        $table['visibility']['rows'][$row_idx] = $table['data'][$row_idx][$cols];
496                        array_splice( $table['data'][$row_idx], $cols, 1 );
497                    }
498                }
499                $this->save_table( $table );
500                $message = __( 'Table sorted successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
501                break;
502            case 'move_row':
503                $table_id = $_POST['table']['id'];
504                $row_id1 = ( isset( $_POST['move']['row'][1] ) ) ? $_POST['move']['row'][1] : -1;
505                $row_id2 = ( isset( $_POST['move']['row'][2] ) ) ? $_POST['move']['row'][2] : -1;
506                $move_where = ( isset( $_POST['move']['row']['where'] ) ) ? $_POST['move']['row']['where'] : 'before';
507                if ( 'after' == $move_where )
508                    $row_id2 = $row_id2 + 1; // move after is the same as move before the next row
509                $table = $this->load_table( $table_id );
510                $rows = count( $table['data'] );
511                // move row $row_id1 before/after $row_id2
512                if ( ( 1 < $rows ) && ( -1 < $row_id1 ) && ( -1 < $row_id2 ) && ( $row_id1 != $row_id2 ) ) {
513                    if ( $row_id2 > $row_id1 )
514                        $row_id2 = $row_id2 - 1; // if target higher than source, source element is removed, so target index smaller by one
515                    $temp_row = array( $table['data'][$row_id1] );
516                    unset( $table['data'][$row_id1] );
517                    array_splice( $table['data'], $row_id2, 0, $temp_row );
518                    $temp_visibility = $table['visibility']['rows'][$row_id1];
519                    unset( $table['visibility']['rows'][$row_id1] );
520                    array_splice( $table['visibility']['rows'], $row_id2, 0, $temp_visibility );
521                }
522                $this->save_table( $table );
523                $message = __( 'Row moved successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
524                break;
525            case 'move_col':
526                $table_id = $_POST['table']['id'];
527                $col_id1 = ( isset( $_POST['move']['col'][1] ) ) ? $_POST['move']['col'][1] : -1;
528                $col_id2 = ( isset( $_POST['move']['col'][2] ) ) ? $_POST['move']['col'][2] : -1;
529                $move_where = ( isset( $_POST['move']['col']['where'] ) ) ? $_POST['move']['col']['where'] : 'before';
530                if ( 'after' == $move_where )
531                    $col_id2 = $col_id2 + 1; // move after is the same as move before the next row
532                $table = $this->load_table( $table_id );
533                $rows = count( $table['data'] );
534                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
535                // move col $col_id1 before/after $col_id2
536                if ( ( 1 < $cols ) && ( -1 < $col_id1 ) && ( -1 < $col_id2 ) && ( $col_id1 != $col_id2 ) ) {
537                    if ( $col_id2 > $col_id1 )
538                        $col_id2 = $col_id2 - 1; // if target higher than source, source element is removed, so target index smaller by one
539                    foreach ( $table['data'] as $row_idx => $row ) {
540                        $temp_col = $table['data'][$row_idx][$col_id1];
541                        unset( $table['data'][$row_idx][$col_id1] );
542                        array_splice( $table['data'][$row_idx], $col_id2, 0, $temp_col );
543
544                    }
545                    $temp_visibility = $table['visibility']['columns'][$col_id1];
546                    unset( $table['visibility']['columns'][$col_id1] );
547                    array_splice( $table['visibility']['columns'], $col_id2, 0, $temp_visibility );
548                }
549                $this->save_table( $table );
550                $message = __( 'Column moved successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
551                break;
552            case 'delete_rows':
553                $table_id = $_POST['table']['id'];
554                $delete_rows = ( isset( $_POST['table_select']['rows'] ) ) ? $_POST['table_select']['rows'] : array();
555                $table = $this->load_table( $table_id );
556                $rows = count( $table['data'] );
557                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
558                $message = _n( 'Row could not be deleted.', 'Rows could not be deleted.', count( $delete_rows ), WP_TABLE_RELOADED_TEXTDOMAIN ); // only used if deletion fails below
559                if ( ( 1 < $rows ) && ( 0 < count( $delete_rows ) ) && ( count( $delete_rows ) < $rows ) ) {
560                    // remove rows and re-index
561                    foreach ( $delete_rows as $row_idx => $value) {
562                        unset( $table['data'][$row_idx] );
563                        unset( $table['visibility']['rows'][$row_idx] );
564                    }
565                    $table['data'] = array_merge( $table['data'] );
566                    $table['visibility']['rows'] = array_merge( $table['visibility']['rows'] );
567                    $message = _n( 'Row deleted successfully.', 'Rows deleted successfully.', count( $delete_rows ), WP_TABLE_RELOADED_TEXTDOMAIN );
568                }
569                $this->save_table( $table );
570                break;
571            case 'delete_cols':
572                $table_id = $_POST['table']['id'];
573                $delete_columns = ( isset( $_POST['table_select']['columns'] ) ) ? $_POST['table_select']['columns'] : array();
574                $table = $this->load_table( $table_id );
575                $rows = count( $table['data'] );
576                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
577                $message = _n( 'Column could not be deleted.', 'Columns could not be deleted.', count( $delete_columns ), WP_TABLE_RELOADED_TEXTDOMAIN ); // only used if deletion fails below
578                if ( ( 1 < $cols ) && ( 0 < count( $delete_columns ) ) && ( count( $delete_columns ) < $cols ) ) {
579                    foreach ( $table['data'] as $row_idx => $row ) {
580                        // remove columns and re-index
581                        foreach ( $delete_columns as $col_idx => $value) {
582                            unset( $table['data'][$row_idx][$col_idx] );
583                        }
584                        $table['data'][$row_idx] = array_merge( $table['data'][$row_idx] );
585                    }
586                    foreach ( $delete_columns as $col_idx => $value) {
587                        unset( $table['visibility']['columns'][$col_idx] );
588                    }
589                    $table['visibility']['columns'] = array_merge( $table['visibility']['columns'] );
590                    $message = _n( 'Column deleted successfully.', 'Columns deleted successfully.', count( $delete_columns ), WP_TABLE_RELOADED_TEXTDOMAIN );
591                }
592                $this->save_table( $table );
593                break;
594            case 'insert_rows': // insert row before each selected row
595                $table_id = $_POST['table']['id'];
596                $insert_rows = ( isset( $_POST['table_select']['rows'] ) ) ? $_POST['table_select']['rows'] : array();
597                $table = $this->load_table( $table_id );
598                $rows = count( $table['data'] );
599                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
600
601                // insert rows and re-index
602                $row_change = 0; // row_change is growing parameter, needed because indices change
603                $new_row = array( array_fill( 0, $cols, '' ) );
604                foreach ( $insert_rows as $row_idx => $value) {
605                    $row_id = $row_idx + $row_change;
606                    // init new empty row (with all columns) and insert it before row with key $row_id
607                    array_splice( $table['data'], $row_id, 0, $new_row );
608                    array_splice( $table['visibility']['rows'], $row_id, 0, false );
609                    $row_change++;
610                }
611                
612                $this->save_table( $table );
613                $message = _n( 'Row inserted successfully.', 'Rows inserted successfully.', count( $insert_rows ), WP_TABLE_RELOADED_TEXTDOMAIN );
614                break;
615            case 'insert_cols': // insert column before each selected column
616                $table_id = $_POST['table']['id'];
617                $insert_columns = ( isset( $_POST['table_select']['columns'] ) ) ? $_POST['table_select']['columns'] : array();
618                $table = $this->load_table( $table_id );
619                $rows = count( $table['data'] );
620                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
621
622                // insert cols and re-index
623                $new_col = '';
624                foreach ( $table['data'] as $row_idx => $row ) {
625                    $col_change = 0; // col_change is growing parameter, needed because indices change
626                    foreach ( $insert_columns as $col_idx => $value) {
627                        $col_id = $col_idx + $col_change;
628                        array_splice( $table['data'][$row_idx], $col_id, 0, $new_col );
629                        $col_change++;
630                    }
631                }
632                $col_change = 0; // col_change is growing parameter, needed because indices change
633                foreach ( $insert_columns as $col_idx => $value) {
634                    $col_id = $col_idx + $col_change;
635                    array_splice( $table['visibility']['columns'], $col_id, 0, false );
636                    $col_change++;
637                }
638
639                $this->save_table( $table );
640                $message = _n( 'Column inserted successfully.', 'Columns inserted successfully.', count( $insert_columns ), WP_TABLE_RELOADED_TEXTDOMAIN );
641                break;
642            case 'append_rows':
643                $table_id = $_POST['table']['id'];
644                $number = ( isset( $_POST['insert']['row']['number'] ) && ( 0 < $_POST['insert']['row']['number'] ) ) ? $_POST['insert']['row']['number'] : 1;
645                $row_id = $_POST['insert']['row']['id'];
646                $table = $this->load_table( $table_id );
647                $rows = count( $table['data'] );
648                $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
649                // init new empty row (with all columns) and insert it before row with key $row_id
650                $new_rows = $this->helper->create_empty_table( $number, $cols, '' );
651                $new_rows_visibility = array_fill( 0, $number, false );
652                array_splice( $table['data'], $row_id, 0, $new_rows );
653                array_splice( $table['visibility']['rows'], $row_id, 0, $new_rows_visibility );
654                $this->save_table( $table );
655                $message = _n( 'Row added successfully.', 'Rows added successfully.', $number, WP_TABLE_RELOADED_TEXTDOMAIN );
656                break;
657            case 'append_cols':
658                $table_id = $_POST['table']['id'];
659                $number = ( isset( $_POST['insert']['col']['number'] ) && ( 0 < $_POST['insert']['col']['number'] ) ) ? $_POST['insert']['col']['number'] : 1;
660                $col_id = $_POST['insert']['col']['id'];
661                $table = $this->load_table( $table_id );
662                // init new empty row (with all columns) and insert it before row with key $col_id
663                $new_cols = array_fill( 0, $number, '' );
664                $new_cols_visibility = array_fill( 0, $number, false );
665                foreach ( $table['data'] as $row_idx => $row )
666                    array_splice( $table['data'][$row_idx], $col_id, 0, $new_cols );
667                array_splice( $table['visibility']['columns'], $col_id, 0, $new_cols_visibility );
668                $this->save_table( $table );
669                $message = _n( 'Column added successfully.', 'Columns added successfully.', $number, WP_TABLE_RELOADED_TEXTDOMAIN );
670                break;
671            case 'insert_cf':
672                $table_id = $_POST['table']['id'];
673                $table = $this->load_table( $table_id );
674                $name = ( isset( $_POST['insert']['custom_field'] ) ) ? $_POST['insert']['custom_field'] : '';
675                if ( empty( $name ) ) {
676                    $message = __( 'Could not add Custom Data Field, because you did not enter a name.', WP_TABLE_RELOADED_TEXTDOMAIN );
677                    break;
678                }
679                $reserved_names = array( 'name', 'description', 'last_modified', 'last_editor' );
680                if ( in_array( $name, $reserved_names ) ) {
681                    $message = __( 'Could not add Custom Data Field, because the name you entered is reserved for other table data.', WP_TABLE_RELOADED_TEXTDOMAIN );
682                    break;
683                }
684                // Name can only contain lowercase letters, numbers, _ and - (like permalink slugs)
685                $clean_name = sanitize_title_with_dashes( $name );
686                if ( $name != $clean_name ) {
687                    $message = __( 'Could not add Custom Data Field, because the name contained illegal characters.', WP_TABLE_RELOADED_TEXTDOMAIN );
688                    break;
689                }
690                if ( isset( $table['custom_fields'][$name] ) ) {
691                    $message = __( 'Could not add Custom Data Field, because a Field with that name already exists.', WP_TABLE_RELOADED_TEXTDOMAIN );
692                    break;
693                }
694                $table['custom_fields'][$name] = '';
695                uksort( $table['custom_fields'], 'strnatcasecmp' ); // sort the keys naturally
696                $this->save_table( $table );
697                $message = __( 'Custom Data Field added successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
698                break;
699            default:
700                $this->do_action_list();
701                return;
702            }
703
704            $this->helper->print_header_message( $message );
705            if ( 'save_back' == $subaction ) {
706                $this->do_action_list();
707            } else {
708                $table_id = $table['id'];
709                $this->load_view( 'edit', compact( 'table_id' ) );
710            }
711        } elseif ( isset( $_GET['table_id'] ) && $this->table_exists( $_GET['table_id'] ) ) {
712            $table_id = $_GET['table_id'];
713            $this->load_view( 'edit', compact( 'table_id' ) );
714        } else {
715            $this->do_action_list();
716        }
717    }
718
719    /**
720     * "Bulk Edit" action handler
721     */
722    function do_action_bulk_edit() {
723        if ( isset( $_POST['submit'] ) ) {
724            check_admin_referer( $this->get_nonce( 'bulk_edit' ) );
725
726            if ( isset( $_POST['tables'] ) ) {
727
728                $subactions = array_keys( $_POST['submit'] );
729                $subaction = $subactions[0];
730
731                switch( $subaction ) {
732                case 'copy': // see do_action_copy for explanations
733                    foreach ( $_POST['tables'] as $table_id ) {
734                        $table_to_copy = $this->load_table( $table_id );
735                        $new_table = $table_to_copy;
736                        $new_table['id'] = $this->get_new_table_id();
737                        $new_table['name'] = __( 'Copy of', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . $table_to_copy['name'];
738                        unset( $table_to_copy );
739                        $this->save_table( $new_table );
740                    }
741                    $message = _n( 'Table copied successfully.', 'Tables copied successfully.', count( $_POST['tables'] ), WP_TABLE_RELOADED_TEXTDOMAIN );
742                    break;
743                case 'delete': // see do_action_delete for explanations
744                    foreach ( $_POST['tables'] as $table_id ) {
745                        $this->delete_table( $table_id );
746                    }
747                    $message = _n( 'Table deleted successfully.', 'Tables deleted successfully.', count( $_POST['tables'] ), WP_TABLE_RELOADED_TEXTDOMAIN );
748                    break;
749                case 'wp_table_import': // see do_action_import for explanations
750                    $this->import_instance = $this->create_class_instance( 'WP_Table_Reloaded_Import', 'import.class.php' );
751                    $this->import_instance->import_format = 'wp_table';
752                    foreach ( $_POST['tables'] as $table_id ) {
753                        $this->import_instance->wp_table_id = $table_id;
754                        $this->import_instance->import_table();
755                        $imported_table = $this->import_instance->imported_table;
756                        $table = array_merge( $this->default_table, $imported_table );
757                        
758                        $rows = count( $table['data'] );
759                        $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
760                        $rows = ( 0 < $rows ) ? $rows : 1;
761                        $cols = ( 0 < $cols ) ? $cols : 1;
762                        $table['visibility']['rows'] = array_fill( 0, $rows, false );
763                        $table['visibility']['columns'] = array_fill( 0, $cols, false );
764                        
765                        $table['id'] = $this->get_new_table_id();
766                        $this->save_table( $table );
767                    }
768                    $message = _n( 'Table imported successfully.', 'Tables imported successfully.', count( $_POST['tables'] ), WP_TABLE_RELOADED_TEXTDOMAIN );
769                    break;
770                default:
771                    break;
772                }
773
774            } else {
775                $message = __( 'You did not select any tables!', WP_TABLE_RELOADED_TEXTDOMAIN );
776            }
777            $this->helper->print_header_message( $message );
778        }
779        $this->do_action_list();
780    }
781
782    /**
783     * "Copy" action handler
784     */
785    function do_action_copy() {
786        if ( isset( $_GET['table_id'] ) ) {
787            check_admin_referer( $this->get_nonce( 'copy' ) );
788
789            $table_to_copy = $this->load_table( $_GET['table_id'] );
790
791            $new_table = $table_to_copy;
792            $new_table['id'] = $this->get_new_table_id();
793            $new_table['name'] = __( 'Copy of', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . $table_to_copy['name'];
794            unset( $table_to_copy );
795
796            $this->save_table( $new_table );
797
798            $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; copied successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $new_table['name'] ) ) );
799        }
800        $this->do_action_list();
801    }
802
803    /**
804     * "Delete" action handler, for tables and custom fields
805     */
806    function do_action_delete() {
807        if ( isset( $_GET['table_id'] ) && isset( $_GET['item'] ) ) {
808            check_admin_referer( $this->get_nonce( 'delete', $_GET['item'] ) );
809
810            $table_id = $_GET['table_id'];
811            $table = $this->load_table( $table_id );
812
813            switch( $_GET['item'] ) {
814            case 'table':
815                $this->delete_table( $table_id );
816                $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; deleted successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ) ) );
817                $this->do_action_list();
818                break;
819            case 'custom_field':
820                $name = ( isset( $_GET['element_id'] ) ) ? $_GET['element_id'] : '';
821                if ( !empty( $name ) && isset( $table['custom_fields'][$name] ) ) {
822                    unset( $table['custom_fields'][$name] );
823                    $this->save_table( $table );
824                    $message = __( 'Custom Data Field deleted successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
825                } else {
826                    $message = __( 'Custom Data Field could not be deleted.', WP_TABLE_RELOADED_TEXTDOMAIN );
827                }
828                $this->helper->print_header_message( $message );
829                $this->load_view( 'edit', compact( 'table_id' ) );
830                break;
831            default:
832                $this->helper->print_header_message( __( 'Delete failed.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
833                $this->do_action_list();
834            }
835        } else {
836            $this->do_action_list();
837        }
838    }
839
840    /**
841     * "Import" action handler, for single tables and a Dump File
842     */
843    function do_action_import() {
844        $this->import_instance = $this->create_class_instance( 'WP_Table_Reloaded_Import', 'import.class.php' );
845        if ( isset( $_POST['submit'] ) && isset( $_POST['import_from'] ) ) {
846            check_admin_referer( $this->get_nonce( 'import' ) );
847
848            $import_error = false;
849            switch( $_POST['import_from'] ) {
850            case 'file-upload':
851      

Large files files are truncated, but you can click here to view the full file