/** * Print the content of the "Table Information" post meta box * * @since 1.0.0 */ public function postbox_table_information($data, $box) { ?> <table class="tablepress-postbox-table fixed"> <tbody> <tr class="bottom-border"> <th class="column-1" scope="row"><label for="table-id"><?php _e('Table ID', 'tablepress'); ?> :</label></th> <td class="column-2"> <input type="hidden" name="table[id]" id="table-id" value="<?php echo esc_attr($data['table']['id']); ?> " /> <input type="text" name="table[new_id]" id="table-new-id" value="<?php echo esc_attr($data['table']['id']); ?> " title="<?php esc_attr_e('The Table ID can only consist of letters, numbers, hyphens (-), and underscores (_).', 'tablepress'); ?> " pattern="[A-Za-z0-9-_]+" required <?php echo !current_user_can('tablepress_edit_table_id', $data['table']['id']) ? 'readonly ' : ''; ?> /> <div style="float: right; margin-right: 1%;"><label for="table-information-shortcode"><?php _e('Shortcode', 'tablepress'); ?> :</label> <input type="text" id="table-information-shortcode" class="table-shortcode" value="<?php echo esc_attr('[' . TablePress::$shortcode . " id={$data['table']['id']} /]"); ?> " readonly="readonly" /></div> </td> </tr> <tr class="top-border"> <th class="column-1" scope="row"><label for="table-name"><?php _e('Table Name', 'tablepress'); ?> :</label></th> <td class="column-2"><input type="text" name="table[name]" id="table-name" class="large-text" value="<?php echo esc_attr($data['table']['name']); ?> " /></td> </tr> <tr class="bottom-border"> <th class="column-1 top-align" scope="row"><label for="table-description"><?php _e('Description', 'tablepress'); ?> :</label></th> <td class="column-2"><textarea name="table[description]" id="table-description" class="large-text" rows="4"><?php echo esc_textarea($data['table']['description']); ?> </textarea></td> </tr> <tr class="top-border"> <th class="column-1" scope="row"><?php _e('Last Modified', 'tablepress'); ?> :</th> <td class="column-2"><?php printf(__('%1$s by %2$s', 'tablepress'), '<span id="last-modified">' . TablePress::format_datetime($data['table']['last_modified']) . '</span>', '<span id="last-editor">' . TablePress::get_user_display_name($data['table']['options']['last_editor']) . '</span>'); ?> </td> </tr> </tbody> </table> <?php }
/** * Handle Shortcode [table-info id=<ID> field=<name> /] in the_content() * * @since 1.0.0 * * @param array $shortcode_atts List of attributes that where included in the Shortcode * @return string Text that replaces the Shortcode (error message or asked-for information) */ public function shortcode_table_info($shortcode_atts) { // For empty Shortcodes like [table-info] or [table-info /], an empty string is passed, see Core #26927 $shortcode_atts = (array) $shortcode_atts; // parse Shortcode attributes, only allow those that are specified $default_shortcode_atts = array('id' => '', 'field' => '', 'format' => ''); /** * Filter the available/default attributes for the [table-info] Shortcode. * * @since 1.0.0 * * @param array $default_shortcode_atts The [table-info] Shortcode default attributes. */ $default_shortcode_atts = apply_filters('tablepress_shortcode_table_info_default_shortcode_atts', $default_shortcode_atts); $shortcode_atts = shortcode_atts($default_shortcode_atts, $shortcode_atts); // Optional third argument left out on purpose. Use filter in the next line instead. /** * Filter the attributes that were passed to the [table-info] Shortcode. * * @since 1.0.0 * * @param array $shortcode_atts The attributes passed to the [table-info] Shortcode. */ $shortcode_atts = apply_filters('tablepress_shortcode_table_info_shortcode_atts', $shortcode_atts); /** * Filter whether the output of the [table-info] Shortcode is overwritten/short-circuited. * * @since 1.0.0 * * @param bool|string $overwrite Whether the [table-info] output is overwritten. Return false for the regular content, and a string to overwrite the output. * @param array $shortcode_atts The attributes passed to the [table-info] Shortcode. */ $overwrite = apply_filters('tablepress_shortcode_table_info_overwrite', false, $shortcode_atts); if ($overwrite) { return $overwrite; } // check, if a table with the given ID exists $table_id = preg_replace('/[^a-zA-Z0-9_-]/', '', $shortcode_atts['id']); if (!TablePress::$model_table->table_exists($table_id)) { $message = "[table “{$table_id}” not found /]<br />\n"; /** This filter is documented in controllers/controller-frontend.php */ $message = apply_filters('tablepress_table_not_found_message', $message, $table_id); return $message; } // load the table $table = TablePress::$model_table->load($table_id, true, true); // Load table, with table data, options, and visibility settings if (is_wp_error($table)) { $message = "[table “{$table_id}” could not be loaded /]<br />\n"; /** This filter is documented in controllers/controller-frontend.php */ $message = apply_filters('tablepress_table_load_error_message', $message, $table_id, $table); return $message; } $field = preg_replace('/[^a-z_]/', '', strtolower($shortcode_atts['field'])); $format = preg_replace('/[^a-z]/', '', strtolower($shortcode_atts['format'])); // generate output, depending on what information (field) was asked for switch ($field) { case 'name': case 'description': $output = $table[$field]; break; case 'last_modified': switch ($format) { case 'raw': $output = $table['last_modified']; break 2; case 'human': $modified_timestamp = strtotime($table['last_modified']); $current_timestamp = current_time('timestamp'); $time_diff = $current_timestamp - $modified_timestamp; if ($time_diff >= 0 && $time_diff < DAY_IN_SECONDS) { // time difference is only shown up to one day $output = sprintf(__('%s ago', 'tablepress'), human_time_diff($modified_timestamp, $current_timestamp)); } else { $output = TablePress::format_datetime($table['last_modified'], 'mysql', '<br />'); } break 2; case 'mysql': default: $output = TablePress::format_datetime($table['last_modified'], 'mysql', ' '); break 2; } break; case 'last_editor': $output = TablePress::get_user_display_name($table['options']['last_editor']); break; case 'author': $output = TablePress::get_user_display_name($table['author']); break; case 'number_rows': $output = count($table['data']); if ('raw' != $format) { if ($table['options']['table_head']) { $output = $output - 1; } if ($table['options']['table_foot']) { $output = $output - 1; } } break; case 'number_columns': $output = count($table['data'][0]); break; default: $output = "[table-info field “{$field}” not found in table “{$table_id}” /]<br />\n"; /** * Filter the "table info field not found" message. * * @since 1.0.0 * * @param string $output The "table info field not found" message. * @param array $table The current table ID. * @param string $field The field that was not found. * @param string $format The return format for the field. */ $output = apply_filters('tablepress_table_info_not_found_message', $output, $table, $field, $format); } /** * Filter the output of the [table-info] Shortcode. * * @since 1.0.0 * * @param string $output The output of the [table-info] Shortcode. * @param array $table The current table. * @param array $shortcode_atts The attributes passed to the [table-info] Shortcode. */ $output = apply_filters('tablepress_shortcode_table_info_output', $output, $table, $shortcode_atts); return $output; }
/** * Callback to determine whether the given $item contains the search term * * @since 1.0.0 * * @param array $item Item that shall be searched * @return bool Whether the search term was found or not */ protected function _search_callback($item) { static $term; if (is_null($term)) { $term = stripslashes($_GET['s']); } $item = TablePress::$controller->model_table->load($item['id']); // load table again, with data // search from easy to hard, so that "expensive" code maybe doesn't have to run if (false !== stripos($item['id'], $term) || false !== stripos($item['name'], $term) || false !== stripos($item['description'], $term) || false !== stripos(TablePress::get_user_display_name($item['author']), $term) || false !== stripos(TablePress::get_user_display_name($item['options']['last_editor']), $term) || false !== stripos(TablePress::format_datetime($item['last_modified'], 'mysql', ' '), $term) || false !== stripos(json_encode($item['data']), $term)) { return true; } return false; }
/** * Callback to determine whether the given $item contains the search term. * * @since 1.0.0 * * @param string $item Table ID that shall be searched. * @return bool Whether the search term was found or not. */ protected function _search_callback($item) { static $term, $json_encoded_term; if (is_null($term) || is_null($json_encoded_term)) { $term = wp_unslash($_GET['s']); $json_encoded_term = substr(wp_json_encode($term), 1, -1); } static $debug; if (is_null($debug)) { // Set debug variable to allow searching in corrupted tables. $debug = isset($_GET['debug']) ? 'true' === $_GET['debug'] : WP_DEBUG; } // load table again, with data and options (for last_editor). $item = TablePress::$model_table->load($item, true, true); // Don't search corrupted tables, except when debug mode is enabled via $_GET parameter or WP_DEBUG constant. if (!$debug && isset($item['is_corrupted']) && $item['is_corrupted']) { return false; } // Search from easy to hard, so that "expensive" code maybe doesn't have to run. if (false !== stripos($item['id'], $term) || false !== stripos($item['name'], $term) || false !== stripos($item['description'], $term) || false !== stripos(TablePress::get_user_display_name($item['author']), $term) || false !== stripos(TablePress::get_user_display_name($item['options']['last_editor']), $term) || false !== stripos(TablePress::format_datetime($item['last_modified'], 'mysql', ' '), $term) || false !== stripos(wp_json_encode($item['data']), $json_encoded_term)) { return true; } return false; }
/** * Save the table after the "Save Changes" button on the "Edit" screen has been clicked. * * @since 1.0.0 */ public function ajax_action_save_table() { if (empty($_POST['tablepress']) || empty($_POST['tablepress']['id'])) { wp_die('-1'); } else { $edit_table = wp_unslash($_POST['tablepress']); } // Check if the submitted nonce matches the generated nonce we created earlier, dies -1 on failure. TablePress::check_nonce('edit', $edit_table['id'], '_ajax_nonce', true); // Ignore the request if the current user doesn't have sufficient permissions. if (!current_user_can('tablepress_edit_table', $edit_table['id'])) { wp_die('-1'); } // Default response data. $success = false; $message = 'error_save'; $error_details = ''; do { // to be able to "break;" (allows for better readable code) // Load table, without table data, but with options and visibility settings. $existing_table = TablePress::$model_table->load($edit_table['id'], false, true); if (is_wp_error($existing_table)) { // maybe somehow load a new table here? (TablePress::$model_table->get_table_template())? // Add an error code to the existing WP_Error. $existing_table->add('ajax_save_table_load', '', $edit_table['id']); $error_details = $this->get_wp_error_string($existing_table); break; } // Check and convert data that was transmitted as JSON. if (empty($edit_table['data']) || empty($edit_table['options']) || empty($edit_table['visibility'])) { // Create a new WP_Error. $empty_data_error = new WP_Error('ajax_save_table_data_empty', '', $edit_table['id']); $error_details = $this->get_wp_error_string($empty_data_error); break; } $edit_table['data'] = (array) json_decode($edit_table['data'], true); $edit_table['options'] = (array) json_decode($edit_table['options'], true); $edit_table['visibility'] = (array) json_decode($edit_table['visibility'], true); // Check consistency of new table, and then merge with existing table. $table = TablePress::$model_table->prepare_table($existing_table, $edit_table, true, true); if (is_wp_error($table)) { // Add an error code to the existing WP_Error. $table->add('ajax_save_table_prepare', '', $edit_table['id']); $error_details = $this->get_wp_error_string($table); break; } // DataTables Custom Commands can only be edit by trusted users. if (!current_user_can('unfiltered_html')) { $table['options']['datatables_custom_commands'] = $existing_table['options']['datatables_custom_commands']; } // Save updated table. $saved = TablePress::$model_table->save($table); if (is_wp_error($saved)) { // Add an error code to the existing WP_Error. $saved->add('ajax_save_table_save', '', $table['id']); $error_details = $this->get_wp_error_string($saved); break; } // At this point, the table was saved successfully, possible ID change remains. $success = true; $message = 'success_save'; // Check if ID change is desired. if ($table['id'] === $table['new_id']) { // If not, we are done. break; } // Change table ID. if (current_user_can('tablepress_edit_table_id', $table['id'])) { $id_changed = TablePress::$model_table->change_table_id($table['id'], $table['new_id']); if (!is_wp_error($id_changed)) { $message = 'success_save_success_id_change'; $table['id'] = $table['new_id']; } else { $message = 'success_save_error_id_change'; // Add an error code to the existing WP_Error. $id_changed->add('ajax_save_table_id_change', '', $table['new_id']); $error_details = $this->get_wp_error_string($id_changed); } } else { $message = 'success_save_error_id_change'; $error_details = 'table_id_could_not_be_changed: capability_check_failed'; } } while (false); // Do-while-loop through this exactly once, to be able to "break;" early. // Generate the response. // Common data for all responses. $response = array('success' => $success, 'message' => $message); if ($success) { $response['table_id'] = $table['id']; $response['new_edit_nonce'] = wp_create_nonce(TablePress::nonce('edit', $table['id'])); $response['new_preview_nonce'] = wp_create_nonce(TablePress::nonce('preview_table', $table['id'])); $response['last_modified'] = TablePress::format_datetime($table['last_modified']); $response['last_editor'] = TablePress::get_user_display_name($table['options']['last_editor']); } if (!empty($error_details)) { $response['error_details'] = esc_html($error_details); } // Buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid. $output_buffer = ob_get_clean(); if (!empty($output_buffer)) { $response['output_buffer'] = $output_buffer; } // Send the response. wp_send_json($response); }
/** * Callback to determine whether the given $item contains the search term. * * @since 1.0.0 * * @param string $item Table ID that shall be searched. * @return bool Whether the search term was found or not. */ protected function _search_callback($item) { static $term, $json_encoded_term; if (is_null($term) || is_null($json_encoded_term)) { $term = wp_unslash($_GET['s']); $json_encoded_term = substr(json_encode($term), 1, -1); } // Load table again, with table data, but without options and visibility settings. $item = TablePress::$model_table->load($item, true, false); if (isset($item['is_corrupted']) && $item['is_corrupted']) { return false; // Don't search corrupted tables } // Search from easy to hard, so that "expensive" code maybe doesn't have to run. if (false !== stripos($item['id'], $term) || false !== stripos($item['name'], $term) || false !== stripos($item['description'], $term) || false !== stripos(TablePress::get_user_display_name($item['author']), $term) || false !== stripos(TablePress::format_datetime($item['last_modified'], 'mysql', ' '), $term) || false !== stripos(json_encode($item['data']), $json_encoded_term)) { return true; } return false; }
/** * Handle Shortcode [table-info id=<ID> field=<name> /] in the_content() * * @since 1.0.0 * * @param array $atts list of attributes that where included in the Shortcode * @return string Text that replaces the Shortcode (error message or asked-for information) */ public function shortcode_table_info($shortcode_atts) { // parse Shortcode attributes, only allow those that are specified $default_shortcode_atts = array('id' => 0, 'field' => '', 'format' => ''); $default_shortcode_atts = apply_filters('tablepress_shortcode_table_info_default_shortcode_atts', $default_shortcode_atts); $shortcode_atts = shortcode_atts($default_shortcode_atts, $shortcode_atts); $shortcode_atts = apply_filters('tablepress_shortcode_table_info_shortcode_atts', $shortcode_atts); // allow a filter to determine behavior of this function, by overwriting its behavior, just need to return something other than false $overwrite = apply_filters('tablepress_shortcode_table_info_overwrite', false, $shortcode_atts); if ($overwrite) { return $overwrite; } // check, if a table with the given ID exists $table_id = preg_replace('/[^a-zA-Z0-9_-]/', '', $shortcode_atts['id']); if (!$this->model_table->table_exists($table_id)) { $message = "[table “{$table_id}” not found /]<br />\n"; $message = apply_filters('tablepress_table_not_found_message', $message, $table_id); return $message; } // load the table $table = $this->model_table->load($table_id); if (false === $table) { $message = "[table “{$table_id}” could not be loaded /]<br />\n"; $message = apply_filters('tablepress_table_load_error_message', $message, $table_id); return $message; } $field = preg_replace('/[^a-z_]/', '', strtolower($shortcode_atts['field'])); $format = preg_replace('/[^a-z]/', '', strtolower($shortcode_atts['format'])); // generate output, depending on what information (field) was asked for switch ($field) { case 'name': case 'description': $output = $table[$field]; break; case 'last_modified': switch ($format) { case 'raw': $output = $table['last_modified']; break 2; case 'mysql': $output = TablePress::format_datetime($table['last_modified'], 'mysql', ' '); break 2; case 'human': $modified_timestamp = strtotime($table['last_modified']); $current_timestamp = current_time('timestamp'); $time_diff = $current_timestamp - $modified_timestamp; if ($time_diff >= 0 && $time_diff < DAY_IN_SECONDS) { // time difference is only shown up to one day $output = sprintf(__('%s ago', 'tablepress'), human_time_diff($modified_timestamp, $current_timestamp)); } else { $output = TablePress::format_datetime($table['last_modified'], 'mysql', '<br />'); } break 2; default: $output = TablePress::format_datetime($table['last_modified'], 'mysql', ' '); } break; case 'last_editor': $output = TablePress::get_user_display_name($table['options']['last_editor']); break; case 'author': $output = TablePress::get_user_display_name($table['author']); break; default: $output = "[table-info field “{$field}” not found in table “{$table_id}” /]<br />\n"; $output = apply_filters('tablepress_table_info_not_found_message', $output, $table, $field, $format); } $output = apply_filters('tablepress_shortcode_table_info_output', $output, $table, $shortcode_atts); return $output; }
/** * Save the table after the "Save Changes" button on the "Edit" screen has been clicked * * @since 1.0.0 */ public function ajax_action_save_table() { if (empty($_POST['tablepress']) || empty($_POST['tablepress']['id'])) { wp_die('-1'); } else { $edit_table = stripslashes_deep($_POST['tablepress']); } // check to see if the submitted nonce matches with the generated nonce we created earlier, dies -1 on fail TablePress::check_nonce('edit', $edit_table['id'], '_ajax_nonce', true); // ignore the request if the current user doesn't have sufficient permissions if (!current_user_can('tablepress_edit_table', $edit_table['id'])) { wp_die('-1'); } // default response data: $success = false; $message = 'error_save'; do { // to be able to "break;" (allows for better readable code) // Load existing table from DB $existing_table = $this->model_table->load($edit_table['id']); if (false === $existing_table) { // maybe somehow load a new table here? ($this->model_table->get_table_template())? break; } // Check and convert data that was transmitted as JSON if (empty($edit_table['data']) || empty($edit_table['options']) || empty($edit_table['visibility'])) { break; } $edit_table['data'] = json_decode($edit_table['data'], true); $edit_table['options'] = json_decode($edit_table['options'], true); $edit_table['visibility'] = json_decode($edit_table['visibility'], true); // Check consistency of new table, and then merge with existing table $table = $this->model_table->prepare_table($existing_table, $edit_table, true, true); if (false === $table) { break; } // DataTables Custom Commands can only be edit by trusted users if (!current_user_can('unfiltered_html')) { $table['options']['datatables_custom_commands'] = $existing_table['options']['datatables_custom_commands']; } // Save updated table $saved = $this->model_table->save($table); if (false === $saved) { break; } // at this point, the table was saved successfully, possible ID change remains $success = true; $message = 'success_save'; // Check if ID change is desired if ($table['id'] === $table['new_id']) { // if not, we are done break; } // Change table ID if (current_user_can('tablepress_edit_table_id', $table['id'])) { $id_changed = $this->model_table->change_table_id($table['id'], $table['new_id']); } else { $id_changed = false; } if ($id_changed) { $message = 'success_save_success_id_change'; $table['id'] = $table['new_id']; } else { $message = 'success_save_error_id_change'; } } while (false); // do-while-loop through this exactly once, to be able to "break;" early // Generate the response $response = array('success' => $success, 'message' => $message); if ($success) { $response['table_id'] = $table['id']; $response['new_edit_nonce'] = wp_create_nonce(TablePress::nonce('edit', $table['id'])); $response['new_preview_nonce'] = wp_create_nonce(TablePress::nonce('preview_table', $table['id'])); $response['last_modified'] = TablePress::format_datetime($table['last_modified']); $response['last_editor'] = TablePress::get_user_display_name($table['options']['last_editor']); } // Send the response $response['output_buffer'] = ob_get_clean(); // buffer all outputs, to prevent errors/warnings being printed that make the JSON invalid wp_send_json($response); }