PageRenderTime 82ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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. * Include file with the Base Controller Class
  12. */
  13. require_once ( WP_TABLE_RELOADED_ABSPATH . 'controllers/controller-base.php' );
  14. /**
  15. * Define the WP-Table Reloaded Text Domain, used to separate between plugin and core localization
  16. */
  17. define( 'WP_TABLE_RELOADED_TEXTDOMAIN', 'wp-table-reloaded' );
  18. /**
  19. * Admin Controller class, extends Base Controller Class
  20. */
  21. class WP_Table_Reloaded_Controller_Admin extends WP_Table_Reloaded_Controller_Base {
  22. /**
  23. * Nonce for security of links/forms, to prevent "CSRF"
  24. * @var string
  25. */
  26. var $nonce_base = 'wp-table-reloaded-nonce';
  27. /**
  28. * List of allowed actions that a user can perform with tables or the plugin
  29. * @var array
  30. */
  31. var $allowed_actions = array( 'list', 'add', 'edit', 'bulk_edit', 'copy', 'delete', 'import', 'export', 'options', 'uninstall', 'about', 'hide_donate_nag', 'hide_welcome_message' );
  32. // 'ajax_list', 'ajax_preview' also exist, but are handled separately
  33. /**
  34. * current action that is performed in this page load, populated in load_manage_page()
  35. * @var string
  36. */
  37. var $action = 'list';
  38. /**
  39. * List of available translations of WP-Table Reloaded, init in __construct, because of the translations
  40. * @var array
  41. */
  42. var $available_plugin_languages = array();
  43. /**
  44. * Default plugin options and their default values, fresh installs use those, updated installs update their options accordingly
  45. * @var array
  46. */
  47. var $default_options = array(
  48. 'installed_version' => '0',
  49. 'plugin_language' => 'auto',
  50. 'uninstall_upon_deactivation' => false,
  51. 'show_exit_warning' => true,
  52. 'growing_textareas' => true,
  53. 'use_datatables_on_table_list' => true,
  54. 'add_target_blank_to_links' => false,
  55. 'enable_tablesorter' => true,
  56. 'tablesorter_script' => 'datatables', // others are 'datatables-tabletools', 'tablesorter', and 'tablesorter_extended'
  57. 'use_default_css' => true,
  58. 'use_custom_css' => true,
  59. 'custom_css' => '',
  60. 'enable_search' => true,
  61. 'admin_menu_parent_page' => 'tools.php',
  62. 'user_access_plugin' => 'author', // others are 'contributor', 'editor', and 'admin'
  63. 'user_access_plugin_options' => 'author', // others are 'editor', and 'admin'
  64. 'frontend_edit_table_link' => true,
  65. 'install_time' => 0,
  66. 'show_donate_nag' => true,
  67. 'show_welcome_message' => 0, // 0 = no message, 1 = install message, 2 = update message
  68. 'update_message' => array(),
  69. 'last_id' => 0
  70. );
  71. /**
  72. * Default list of tables (empty, because there are no tables right after installation)
  73. * @var array
  74. */
  75. var $default_tables = array();
  76. /**
  77. * 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
  78. * @var object
  79. */
  80. var $helper;
  81. /**
  82. * Instance of the WP_Table_Reloaded_Export class
  83. * @var object
  84. */
  85. var $export_instance;
  86. /**
  87. * Instance of the WP_Table_Reloaded_Import class
  88. * @var object
  89. */
  90. var $import_instance;
  91. /**
  92. * Hook (i.e. name) WordPress uses for the WP-Table Reloaded page, needed for certain plugin actions and filters, populated in add_manage_page()
  93. * @var string
  94. */
  95. var $hook = '';
  96. /**
  97. * Name of the file, WP-Table Reloaded is accessible under, dependant of whether admin has moved the WP-Table Reloaded menu entry
  98. * @var string
  99. */
  100. var $page_url = '';
  101. /**
  102. * PHP4 class constructor, calls the PHP5 class constructor __construct()
  103. */
  104. function WP_Table_Reloaded_Controller_Admin() {
  105. $this->__construct();
  106. }
  107. /**
  108. * PHP5 class constructor
  109. *
  110. * Initiate Backend functionality, by checking for AJAX calls, eventually answering those or setting up the admin page
  111. */
  112. function __construct() {
  113. register_activation_hook( WP_TABLE_RELOADED__FILE__, array( &$this, 'plugin_activation_hook' ) );
  114. register_deactivation_hook( WP_TABLE_RELOADED__FILE__, array( &$this, 'plugin_deactivation_hook' ) );
  115. $this->helper = $this->create_class_instance( 'WP_Table_Reloaded_Helper', 'helper.class.php' );
  116. // load plugin options and existing tables
  117. $this->init_plugin();
  118. // WordPress 3.1 requires new update check
  119. if ( version_compare( $this->options['installed_version'], WP_TABLE_RELOADED_PLUGIN_VERSION, '<' ) )
  120. add_action( 'init', array( &$this, 'plugin_update' ) );
  121. // init variables to check whether we do valid AJAX
  122. $doing_ajax = defined( 'DOING_AJAX' ) ? DOING_AJAX : false;
  123. $valid_ajax_call = ( isset( $_GET['page'] ) && $this->page_slug == $_GET['page'] ) ? true : false;
  124. // have to check for possible "export all" request this early,
  125. // because otherwise http-headers will be sent by WP before we can send download headers
  126. if ( !$doing_ajax && $valid_ajax_call && isset( $_POST['export_all'] ) ) {
  127. // can be done in plugins_loaded, as no language support is needed
  128. add_action( 'plugins_loaded', array( &$this, 'do_action_export_all' ) );
  129. $doing_ajax = true;
  130. }
  131. // have to check for possible export file download request this early,
  132. // because otherwise http-headers will be sent by WP before we can send download headers
  133. if ( !$doing_ajax && $valid_ajax_call && isset( $_POST['download_export_file'] ) && 'true' == $_POST['download_export_file'] ) {
  134. // can be done in plugins_loaded, as no language support is needed
  135. add_action( 'plugins_loaded', array( &$this, 'do_action_export' ) );
  136. $doing_ajax = true;
  137. }
  138. // have to check for possible call by editor button to show list of tables
  139. // and possible call to show a table preview in a thickbox on "List Tables" screen
  140. if ( !$doing_ajax && $valid_ajax_call && isset( $_GET['action'] ) && ( 'ajax_list' == $_GET['action'] || 'ajax_preview' == $_GET['action'] ) ) {
  141. // can not be done earlier, because we need language support
  142. add_action( 'init', array( &$this, 'do_action_' . $_GET['action'] ) );
  143. $doing_ajax = true;
  144. }
  145. // we are not doing AJAX, so we call the main plugin handler
  146. if ( !$doing_ajax ) {
  147. add_action( 'admin_menu', array( &$this, 'add_manage_page' ) );
  148. // add JS to add button to editor on admin pages that might have an editor
  149. $pages_with_editor_button = array( 'post.php', 'post-new.php', 'page.php', 'page-new.php' );
  150. foreach ( $pages_with_editor_button as $page )
  151. add_action( 'load-' . $page, array( &$this, 'add_editor_button' ) );
  152. }
  153. // add message to list of plugins, if an update is available / add additional links on Plugins page, for both regular visits and AJAX calls
  154. add_action( 'in_plugin_update_message-' . WP_TABLE_RELOADED_BASENAME, array( &$this, 'add_plugin_update_message' ), 10, 2 );
  155. add_filter( 'plugin_row_meta', array( &$this, 'add_plugin_row_meta' ), 10, 2);
  156. }
  157. /**
  158. * Add admin page to the correct place in the admin menu, and set handler for when page is loaded or shown
  159. */
  160. function add_manage_page() {
  161. // user needs at least this capability to view WP-Table Reloaded config page
  162. // capabilities from http://codex.wordpress.org/Roles_and_Capabilities
  163. $user_group = $this->options['user_access_plugin'];
  164. $capabilities = array(
  165. 'admin' => 'manage_options',
  166. 'editor' => 'publish_pages',
  167. 'author' => 'publish_posts',
  168. 'contributor' => 'edit_posts'
  169. );
  170. $min_capability = isset( $capabilities[ $user_group ] ) ? $capabilities[ $user_group ] : 'manage_options';
  171. $min_capability = apply_filters( 'wp_table_reloaded_min_needed_capability', $min_capability ); // plugins may filter/change this though
  172. $display_name = 'WP-Table Reloaded'; // the name that is displayed in the admin menu on the left
  173. $display_name = apply_filters( 'wp_table_reloaded_plugin_display_name', $display_name ); // can be filtered to something shorter maybe
  174. $admin_menu_page = apply_filters( 'wp_table_reloaded_admin_menu_parent_page', $this->options['admin_menu_parent_page'] );
  175. // backward-compatibility for the filter
  176. if ( 'top-level' == $admin_menu_page )
  177. $admin_menu_page = 'admin.php';
  178. // 'edit-pages.php' was renamed to 'edit.php?post_type=page' in WP 3.0
  179. if ( 'edit-pages.php' == $admin_menu_page )
  180. $admin_menu_page = 'edit.php?post_type=page';
  181. if ( !in_array( $admin_menu_page, $this->possible_admin_menu_parent_pages ) )
  182. $admin_menu_page = 'tools.php';
  183. // Top-Level menu is created in different function, all others are created with the filename as a parameter
  184. if ( 'admin.php' == $admin_menu_page )
  185. $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__ ) );
  186. else
  187. $this->hook = add_submenu_page( $admin_menu_page, 'WP-Table Reloaded', $display_name, $min_capability, $this->page_slug, array( &$this, 'show_manage_page' ) );
  188. $this->page_url = $admin_menu_page;
  189. add_action( 'load-' . $this->hook, array( &$this, 'load_manage_page' ) );
  190. }
  191. /**
  192. * Function is loaded by WordPress, if WP-Table Reloaded's admin menu entry is called,
  193. * Load the scripts, stylesheets and language, all of this will be done before the page is shown by show_manage_page()
  194. */
  195. function load_manage_page() {
  196. // show admin footer message (only on pages of WP-Table Reloaded)
  197. add_filter( 'admin_footer_text', array( &$this->helper, 'add_admin_footer_text' ) );
  198. $this->init_language_support();
  199. // get and check action parameter from passed variables
  200. $default_action = 'list';
  201. $default_action = apply_filters( 'wp_table_reloaded_default_action', $default_action );
  202. $this->allowed_actions = apply_filters( 'wp_table_reloaded_allowed_actions', $this->allowed_actions );
  203. $action = ( !empty( $_REQUEST['action'] ) ) ? $_REQUEST['action'] : $default_action;
  204. // check if action is in allowed actions and if method is callable, if yes, call it
  205. if ( in_array( $action, $this->allowed_actions ) )
  206. $this->action = $action;
  207. add_thickbox();
  208. // load js and css for admin, needs to stand below thickbox script
  209. $this->add_manage_page_js(); // will add script to footer
  210. $this->add_manage_page_css(); // needs to be added to the header
  211. // done after determining the action, because needs action parameter to load correct help string
  212. add_contextual_help( $this->hook, $this->helper->get_contextual_help_string() );
  213. }
  214. /**
  215. * Function is loaded by WordPress, if WP-Table Reloaded's admin menu entry is called,
  216. * responsible for calling the appropriate action handler, output of WP admin menu and header is already done here
  217. */
  218. function show_manage_page() {
  219. $this->available_plugin_languages = array(
  220. 'ar' => __( 'Arabic', WP_TABLE_RELOADED_TEXTDOMAIN ),
  221. 'be_BY' => __( 'Belarusian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  222. 'bg_BG' => __( 'Bulgarian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  223. 'cs_CZ' => __( 'Czech', WP_TABLE_RELOADED_TEXTDOMAIN ),
  224. 'de_DE' => __( 'German', WP_TABLE_RELOADED_TEXTDOMAIN ),
  225. 'en_US' => __( 'English', WP_TABLE_RELOADED_TEXTDOMAIN ),
  226. 'es_ES' => __( 'Spanish', WP_TABLE_RELOADED_TEXTDOMAIN ),
  227. 'fi' => __( 'Finnish', WP_TABLE_RELOADED_TEXTDOMAIN ),
  228. 'fr_FR' => __( 'French', WP_TABLE_RELOADED_TEXTDOMAIN ),
  229. 'he_IL' => __( 'Hebrew', WP_TABLE_RELOADED_TEXTDOMAIN ),
  230. 'hi_IN' => __( 'Hindi', WP_TABLE_RELOADED_TEXTDOMAIN ),
  231. 'id_ID' => __( 'Indonesian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  232. 'it_IT' => __( 'Italian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  233. 'ja' => __( 'Japanese', WP_TABLE_RELOADED_TEXTDOMAIN ),
  234. 'nl_NL' => __( 'Dutch', WP_TABLE_RELOADED_TEXTDOMAIN ),
  235. 'pl_PL' => __( 'Polish', WP_TABLE_RELOADED_TEXTDOMAIN ),
  236. 'pt_BR' => __( 'Brazilian Portuguese', WP_TABLE_RELOADED_TEXTDOMAIN ),
  237. 'pt_PT' => __( 'Portuguese (Portugal)', WP_TABLE_RELOADED_TEXTDOMAIN ),
  238. 'ru_RU' => __( 'Russian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  239. 'sk_SK' => __( 'Slovak', WP_TABLE_RELOADED_TEXTDOMAIN ),
  240. 'sv_SE' => __( 'Swedish', WP_TABLE_RELOADED_TEXTDOMAIN ),
  241. 'ua_UA' => __( 'Ukrainian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  242. 'zh_CN' => __( 'Chinese (Simplified)', WP_TABLE_RELOADED_TEXTDOMAIN ),
  243. // the following are inactive because they are not up-to-date
  244. // 'ga_IR' => __( 'Irish', WP_TABLE_RELOADED_TEXTDOMAIN ),
  245. // 'sq_AL' => __( 'Albanian', WP_TABLE_RELOADED_TEXTDOMAIN ),
  246. // 'tr_TR' => __( 'Turkish', WP_TABLE_RELOADED_TEXTDOMAIN ),
  247. );
  248. asort( $this->available_plugin_languages );
  249. // do WP plugin action (before action is fired) -> can stop further plugin execution by returning true
  250. $overwrite = apply_filters( 'wp_table_reloaded_action_pre_' . $this->action, false );
  251. if ( $overwrite )
  252. return;
  253. // call appropriate action, $this->action is populated in load_manage_page
  254. if ( is_callable( array( &$this, 'do_action_' . $this->action ) ) )
  255. call_user_func( array( &$this, 'do_action_' . $this->action ) );
  256. }
  257. // ###################################################################################################################
  258. // ########################################## ######################################################
  259. // ########################################## ACTIONS ######################################################
  260. // ########################################## ######################################################
  261. // ###################################################################################################################
  262. /**
  263. * "List Tables" action handler
  264. */
  265. function do_action_list() {
  266. $messages = array(
  267. 0 => false,
  268. 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/' ),
  269. 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/' )
  270. );
  271. $message = ( isset( $messages[ $this->options['show_welcome_message'] ] ) ) ? $messages[ $this->options['show_welcome_message'] ] : false;
  272. if ( $message ) {
  273. $hide_welcome_message_url = $this->get_action_url( array( 'action' => 'hide_welcome_message' ), true );
  274. $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 ) ) );
  275. }
  276. if ( $this->may_print_donate_nag() ) {
  277. $donate_url = 'http://tobias.baethge.com/go/wp-table-reloaded/donate/message/';
  278. $donated_true_url = $this->get_action_url( array( 'action' => 'hide_donate_nag', 'user_donated' => true ), true );
  279. $donated_false_url = $this->get_action_url( array( 'action' => 'hide_donate_nag', 'user_donated' => false ), true );
  280. $this->helper->print_header_message(
  281. __( '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/>' .
  282. 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/>' .
  283. sprintf( '<a href="%s" target="_blank">%s</a>', $donate_url, __( 'Sure, no problem!', WP_TABLE_RELOADED_TEXTDOMAIN ) ) . '&nbsp;&nbsp;&middot;&nbsp;&nbsp;' .
  284. 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;' .
  285. sprintf( '<a href="%s" style="font-weight:normal;">%s</a>', $donated_false_url, __( 'No, thanks. Don\'t ask again.', WP_TABLE_RELOADED_TEXTDOMAIN ) )
  286. );
  287. }
  288. $this->load_view( 'list' );
  289. }
  290. /**
  291. * "Add new Table" action handler
  292. */
  293. function do_action_add() {
  294. if ( isset( $_POST['submit'] ) && isset( $_POST['table'] ) ) {
  295. check_admin_referer( $this->get_nonce( 'add' ) );
  296. $rows = ( 0 < $_POST['table']['rows'] ) ? $_POST['table']['rows'] : 1;
  297. $cols = ( 0 < $_POST['table']['cols'] ) ? $_POST['table']['cols'] : 1;
  298. $table = $this->default_table;
  299. $table['id'] = $this->get_new_table_id();
  300. $table['data'] = $this->helper->create_empty_table( $rows, $cols );
  301. $table['visibility']['rows'] = array_fill( 0, $rows, false );
  302. $table['visibility']['columns'] = array_fill( 0, $cols, false );
  303. $table['name'] = $_POST['table']['name'];
  304. $table['description'] = $_POST['table']['description'];
  305. $this->save_table( $table );
  306. $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; added successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ) ) );
  307. $table_id = $table['id'];
  308. $this->load_view( 'edit', compact( 'table_id' ) );
  309. } else {
  310. $this->load_view( 'add' );
  311. }
  312. }
  313. /**
  314. * "Edit Table" action handler
  315. */
  316. function do_action_edit() {
  317. if ( isset( $_POST['submit'] ) && isset( $_POST['table'] ) ) {
  318. check_admin_referer( $this->get_nonce( 'edit' ) );
  319. $subactions = array_keys( $_POST['submit'] );
  320. $subaction = $subactions[0];
  321. switch( $subaction ) {
  322. case 'update':
  323. case 'save_back':
  324. $table = $_POST['table']; // careful here to not miss any stuff!!! (options, etc.)
  325. // do we want to change the ID?
  326. $new_table_id = ( isset( $_POST['table_id'] ) ) ? $_POST['table_id'] : $table['id'] ;
  327. if ( $new_table_id != $table['id'] && is_numeric( $new_table_id ) && ( 0 < $new_table_id ) ) {
  328. if ( !$this->table_exists( $new_table_id ) ) {
  329. // delete table with old ID
  330. $old_table_id = $table['id'];
  331. $this->delete_table( $old_table_id );
  332. // set new table ID
  333. $table['id'] = $new_table_id;
  334. $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 );
  335. } else {
  336. $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 );
  337. }
  338. } else {
  339. $message = __( 'Table edited successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  340. }
  341. // save table options (checkboxes!), only checked checkboxes are submitted (then as true)
  342. $table['options']['alternating_row_colors'] = isset( $_POST['table']['options']['alternating_row_colors'] );
  343. $table['options']['row_hover'] = isset( $_POST['table']['options']['row_hover'] );
  344. $table['options']['first_row_th'] = isset( $_POST['table']['options']['first_row_th'] );
  345. $table['options']['table_footer'] = isset( $_POST['table']['options']['table_footer'] );
  346. $table['options']['print_name'] = isset( $_POST['table']['options']['print_name'] );
  347. $table['options']['print_description'] = isset( $_POST['table']['options']['print_description'] );
  348. $table['options']['cache_table_output'] = isset( $_POST['table']['options']['cache_table_output'] );
  349. $table['options']['custom_css_class'] = trim( $table['options']['custom_css_class'] ); // more complex sanitize_* functions would change spaces to hyphens...
  350. $table['options']['use_tablesorter'] = isset( $_POST['table']['options']['use_tablesorter'] );
  351. $table['options']['datatables_sort'] = isset( $_POST['table']['options']['datatables_sort'] );
  352. $table['options']['datatables_paginate'] = isset( $_POST['table']['options']['datatables_paginate'] );
  353. $table['options']['datatables_lengthchange'] = isset( $_POST['table']['options']['datatables_lengthchange'] );
  354. $table['options']['datatables_filter'] = isset( $_POST['table']['options']['datatables_filter'] );
  355. $table['options']['datatables_info'] = isset( $_POST['table']['options']['datatables_info'] );
  356. $table['options']['datatables_tabletools'] = isset( $_POST['table']['options']['datatables_tabletools'] );
  357. $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'];
  358. // $table['options']['datatables_customcommands'] is an input type=text field that is always submitted
  359. // $table['options']['print_name|description_position'] are select fields that are always submitted
  360. // save visibility settings (checkboxes!)
  361. foreach ( $table['data'] as $row_idx => $row )
  362. $table['visibility']['rows'][$row_idx] = ( isset( $_POST['table']['visibility']['rows'][$row_idx] ) && ( 'true' == $_POST['table']['visibility']['rows'][$row_idx] ) );
  363. foreach ( $table['data'][0] as $col_idx => $col )
  364. $table['visibility']['columns'][$col_idx] = ( isset( $_POST['table']['visibility']['columns'][$col_idx] ) && ( 'true' == $_POST['table']['visibility']['columns'][$col_idx] ) );
  365. if ( !empty( $table['custom_fields'] ) )
  366. uksort( $table['custom_fields'], 'strnatcasecmp' ); // sort the keys naturally
  367. $this->save_table( $table );
  368. break;
  369. case 'swap_rows':
  370. $table_id = $_POST['table']['id'];
  371. $row_id1 = ( isset( $_POST['swap']['row'][1] ) ) ? $_POST['swap']['row'][1] : -1;
  372. $row_id2 = ( isset( $_POST['swap']['row'][2] ) ) ? $_POST['swap']['row'][2] : -1;
  373. $table = $this->load_table( $table_id );
  374. $rows = count( $table['data'] );
  375. // swap rows $row_id1 and $row_id2
  376. if ( ( 1 < $rows ) && ( -1 < $row_id1 ) && ( -1 < $row_id2 ) && ( $row_id1 != $row_id2 ) ) {
  377. $temp_row = $table['data'][$row_id1];
  378. $table['data'][$row_id1] = $table['data'][$row_id2];
  379. $table['data'][$row_id2] = $temp_row;
  380. $temp_visibility = $table['visibility']['rows'][$row_id1];
  381. $table['visibility']['rows'][$row_id1] = $table['visibility']['rows'][$row_id2];
  382. $table['visibility']['rows'][$row_id2] = $temp_visibility;
  383. }
  384. $this->save_table( $table );
  385. $message = __( 'Rows swapped successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  386. break;
  387. case 'swap_cols':
  388. $table_id = $_POST['table']['id'];
  389. $col_id1 = ( isset( $_POST['swap']['col'][1] ) ) ? $_POST['swap']['col'][1] : -1;
  390. $col_id2 = ( isset( $_POST['swap']['col'][2] ) ) ? $_POST['swap']['col'][2] : -1;
  391. $table = $this->load_table( $table_id );
  392. $rows = count( $table['data'] );
  393. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  394. // swap rows $col_id1 and $col_id2
  395. if ( ( 1 < $cols ) && ( -1 < $col_id1 ) && ( -1 < $col_id2 ) && ( $col_id1 != $col_id2 ) ) {
  396. foreach ( $table['data'] as $row_idx => $row ) {
  397. $temp_col = $table['data'][$row_idx][$col_id1];
  398. $table['data'][$row_idx][$col_id1] = $table['data'][$row_idx][$col_id2];
  399. $table['data'][$row_idx][$col_id2] = $temp_col;
  400. }
  401. $temp_visibility = $table['visibility']['columns'][$col_id1];
  402. $table['visibility']['columns'][$col_id1] = $table['visibility']['columns'][$col_id2];
  403. $table['visibility']['columns'][$col_id2] = $temp_visibility;
  404. }
  405. $this->save_table( $table );
  406. $message = __( 'Columns swapped successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  407. break;
  408. case 'sort':
  409. $table_id = $_POST['table']['id'];
  410. $column = ( isset( $_POST['sort']['col'] ) ) ? $_POST['sort']['col'] : -1;
  411. $sort_order = ( isset( $_POST['sort']['order'] ) ) ? $_POST['sort']['order'] : 'ASC';
  412. $table = $this->load_table( $table_id );
  413. $rows = count( $table['data'] );
  414. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  415. // sort array for $column in $sort_order
  416. if ( ( 1 < $rows ) && ( -1 < $column ) ) {
  417. // for sorting: temporarily store row visibility in data, so that it gets sorted, too
  418. foreach ( $table['data'] as $row_idx => $row )
  419. array_splice( $table['data'][$row_idx], $cols, 0, $table['visibility']['rows'][$row_idx] );
  420. $array_to_sort = $table['data'];
  421. if ( isset( $table['options']['first_row_th'] ) && $table['options']['first_row_th'] )
  422. $first_row = array_shift( $array_to_sort );
  423. if ( isset( $table['options']['table_footer'] ) && $table['options']['table_footer'] )
  424. $last_row = array_pop( $array_to_sort );
  425. $sortarray = $this->create_class_instance( 'arraysort', 'arraysort.class.php' );
  426. $sortarray->input_array = $array_to_sort;
  427. $sortarray->column = $column;
  428. $sortarray->order = $sort_order;
  429. $sortarray->sort();
  430. $sorted_array = $sortarray->sorted_array;
  431. if ( isset( $table['options']['first_row_th'] ) && $table['options']['first_row_th'] )
  432. array_unshift( $sorted_array, $first_row );
  433. if ( isset( $table['options']['table_footer'] ) && $table['options']['table_footer'] )
  434. array_push( $sorted_array, $last_row );
  435. $table['data'] = $sorted_array;
  436. // then restore row visibility from sorted data and remove temporary column
  437. foreach ( $table['data'] as $row_idx => $row ) {
  438. $table['visibility']['rows'][$row_idx] = $table['data'][$row_idx][$cols];
  439. array_splice( $table['data'][$row_idx], $cols, 1 );
  440. }
  441. }
  442. $this->save_table( $table );
  443. $message = __( 'Table sorted successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  444. break;
  445. case 'move_row':
  446. $table_id = $_POST['table']['id'];
  447. $row_id1 = ( isset( $_POST['move']['row'][1] ) ) ? $_POST['move']['row'][1] : -1;
  448. $row_id2 = ( isset( $_POST['move']['row'][2] ) ) ? $_POST['move']['row'][2] : -1;
  449. $move_where = ( isset( $_POST['move']['row']['where'] ) ) ? $_POST['move']['row']['where'] : 'before';
  450. if ( 'after' == $move_where )
  451. $row_id2 = $row_id2 + 1; // move after is the same as move before the next row
  452. $table = $this->load_table( $table_id );
  453. $rows = count( $table['data'] );
  454. // move row $row_id1 before/after $row_id2
  455. if ( ( 1 < $rows ) && ( -1 < $row_id1 ) && ( -1 < $row_id2 ) && ( $row_id1 != $row_id2 ) ) {
  456. if ( $row_id2 > $row_id1 )
  457. $row_id2 = $row_id2 - 1; // if target higher than source, source element is removed, so target index smaller by one
  458. $temp_row = array( $table['data'][$row_id1] );
  459. unset( $table['data'][$row_id1] );
  460. array_splice( $table['data'], $row_id2, 0, $temp_row );
  461. $temp_visibility = $table['visibility']['rows'][$row_id1];
  462. unset( $table['visibility']['rows'][$row_id1] );
  463. array_splice( $table['visibility']['rows'], $row_id2, 0, $temp_visibility );
  464. }
  465. $this->save_table( $table );
  466. $message = __( 'Row moved successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  467. break;
  468. case 'move_col':
  469. $table_id = $_POST['table']['id'];
  470. $col_id1 = ( isset( $_POST['move']['col'][1] ) ) ? $_POST['move']['col'][1] : -1;
  471. $col_id2 = ( isset( $_POST['move']['col'][2] ) ) ? $_POST['move']['col'][2] : -1;
  472. $move_where = ( isset( $_POST['move']['col']['where'] ) ) ? $_POST['move']['col']['where'] : 'before';
  473. if ( 'after' == $move_where )
  474. $col_id2 = $col_id2 + 1; // move after is the same as move before the next row
  475. $table = $this->load_table( $table_id );
  476. $rows = count( $table['data'] );
  477. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  478. // move col $col_id1 before/after $col_id2
  479. if ( ( 1 < $cols ) && ( -1 < $col_id1 ) && ( -1 < $col_id2 ) && ( $col_id1 != $col_id2 ) ) {
  480. if ( $col_id2 > $col_id1 )
  481. $col_id2 = $col_id2 - 1; // if target higher than source, source element is removed, so target index smaller by one
  482. foreach ( $table['data'] as $row_idx => $row ) {
  483. $temp_col = $table['data'][$row_idx][$col_id1];
  484. unset( $table['data'][$row_idx][$col_id1] );
  485. array_splice( $table['data'][$row_idx], $col_id2, 0, $temp_col );
  486. }
  487. $temp_visibility = $table['visibility']['columns'][$col_id1];
  488. unset( $table['visibility']['columns'][$col_id1] );
  489. array_splice( $table['visibility']['columns'], $col_id2, 0, $temp_visibility );
  490. }
  491. $this->save_table( $table );
  492. $message = __( 'Column moved successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  493. break;
  494. case 'delete_rows':
  495. $table_id = $_POST['table']['id'];
  496. $delete_rows = ( isset( $_POST['table_select']['rows'] ) ) ? $_POST['table_select']['rows'] : array();
  497. $table = $this->load_table( $table_id );
  498. $rows = count( $table['data'] );
  499. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  500. $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
  501. if ( ( 1 < $rows ) && ( 0 < count( $delete_rows ) ) && ( count( $delete_rows ) < $rows ) ) {
  502. // remove rows and re-index
  503. foreach ( $delete_rows as $row_idx => $value) {
  504. unset( $table['data'][$row_idx] );
  505. unset( $table['visibility']['rows'][$row_idx] );
  506. }
  507. $table['data'] = array_merge( $table['data'] );
  508. $table['visibility']['rows'] = array_merge( $table['visibility']['rows'] );
  509. $message = _n( 'Row deleted successfully.', 'Rows deleted successfully.', count( $delete_rows ), WP_TABLE_RELOADED_TEXTDOMAIN );
  510. }
  511. $this->save_table( $table );
  512. break;
  513. case 'delete_cols':
  514. $table_id = $_POST['table']['id'];
  515. $delete_columns = ( isset( $_POST['table_select']['columns'] ) ) ? $_POST['table_select']['columns'] : array();
  516. $table = $this->load_table( $table_id );
  517. $rows = count( $table['data'] );
  518. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  519. $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
  520. if ( ( 1 < $cols ) && ( 0 < count( $delete_columns ) ) && ( count( $delete_columns ) < $cols ) ) {
  521. foreach ( $table['data'] as $row_idx => $row ) {
  522. // remove columns and re-index
  523. foreach ( $delete_columns as $col_idx => $value) {
  524. unset( $table['data'][$row_idx][$col_idx] );
  525. }
  526. $table['data'][$row_idx] = array_merge( $table['data'][$row_idx] );
  527. }
  528. foreach ( $delete_columns as $col_idx => $value) {
  529. unset( $table['visibility']['columns'][$col_idx] );
  530. }
  531. $table['visibility']['columns'] = array_merge( $table['visibility']['columns'] );
  532. $message = _n( 'Column deleted successfully.', 'Columns deleted successfully.', count( $delete_columns ), WP_TABLE_RELOADED_TEXTDOMAIN );
  533. }
  534. $this->save_table( $table );
  535. break;
  536. case 'insert_rows': // insert row before each selected row
  537. $table_id = $_POST['table']['id'];
  538. $insert_rows = ( isset( $_POST['table_select']['rows'] ) ) ? $_POST['table_select']['rows'] : array();
  539. $table = $this->load_table( $table_id );
  540. $rows = count( $table['data'] );
  541. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  542. // insert rows and re-index
  543. $row_change = 0; // row_change is growing parameter, needed because indices change
  544. $new_row = array( array_fill( 0, $cols, '' ) );
  545. foreach ( $insert_rows as $row_idx => $value) {
  546. $row_id = $row_idx + $row_change;
  547. // init new empty row (with all columns) and insert it before row with key $row_id
  548. array_splice( $table['data'], $row_id, 0, $new_row );
  549. array_splice( $table['visibility']['rows'], $row_id, 0, false );
  550. $row_change++;
  551. }
  552. $this->save_table( $table );
  553. $message = _n( 'Row inserted successfully.', 'Rows inserted successfully.', count( $insert_rows ), WP_TABLE_RELOADED_TEXTDOMAIN );
  554. break;
  555. case 'insert_cols': // insert column before each selected column
  556. $table_id = $_POST['table']['id'];
  557. $insert_columns = ( isset( $_POST['table_select']['columns'] ) ) ? $_POST['table_select']['columns'] : array();
  558. $table = $this->load_table( $table_id );
  559. $rows = count( $table['data'] );
  560. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  561. // insert cols and re-index
  562. $new_col = '';
  563. foreach ( $table['data'] as $row_idx => $row ) {
  564. $col_change = 0; // col_change is growing parameter, needed because indices change
  565. foreach ( $insert_columns as $col_idx => $value) {
  566. $col_id = $col_idx + $col_change;
  567. array_splice( $table['data'][$row_idx], $col_id, 0, $new_col );
  568. $col_change++;
  569. }
  570. }
  571. $col_change = 0; // col_change is growing parameter, needed because indices change
  572. foreach ( $insert_columns as $col_idx => $value) {
  573. $col_id = $col_idx + $col_change;
  574. array_splice( $table['visibility']['columns'], $col_id, 0, false );
  575. $col_change++;
  576. }
  577. $this->save_table( $table );
  578. $message = _n( 'Column inserted successfully.', 'Columns inserted successfully.', count( $insert_columns ), WP_TABLE_RELOADED_TEXTDOMAIN );
  579. break;
  580. case 'append_rows':
  581. $table_id = $_POST['table']['id'];
  582. $number = ( isset( $_POST['insert']['row']['number'] ) && ( 0 < $_POST['insert']['row']['number'] ) ) ? $_POST['insert']['row']['number'] : 1;
  583. $row_id = $_POST['insert']['row']['id'];
  584. $table = $this->load_table( $table_id );
  585. $rows = count( $table['data'] );
  586. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  587. // init new empty row (with all columns) and insert it before row with key $row_id
  588. $new_rows = $this->helper->create_empty_table( $number, $cols, '' );
  589. $new_rows_visibility = array_fill( 0, $number, false );
  590. array_splice( $table['data'], $row_id, 0, $new_rows );
  591. array_splice( $table['visibility']['rows'], $row_id, 0, $new_rows_visibility );
  592. $this->save_table( $table );
  593. $message = _n( 'Row added successfully.', 'Rows added successfully.', $number, WP_TABLE_RELOADED_TEXTDOMAIN );
  594. break;
  595. case 'append_cols':
  596. $table_id = $_POST['table']['id'];
  597. $number = ( isset( $_POST['insert']['col']['number'] ) && ( 0 < $_POST['insert']['col']['number'] ) ) ? $_POST['insert']['col']['number'] : 1;
  598. $col_id = $_POST['insert']['col']['id'];
  599. $table = $this->load_table( $table_id );
  600. // init new empty row (with all columns) and insert it before row with key $col_id
  601. $new_cols = array_fill( 0, $number, '' );
  602. $new_cols_visibility = array_fill( 0, $number, false );
  603. foreach ( $table['data'] as $row_idx => $row )
  604. array_splice( $table['data'][$row_idx], $col_id, 0, $new_cols );
  605. array_splice( $table['visibility']['columns'], $col_id, 0, $new_cols_visibility );
  606. $this->save_table( $table );
  607. $message = _n( 'Column added successfully.', 'Columns added successfully.', $number, WP_TABLE_RELOADED_TEXTDOMAIN );
  608. break;
  609. case 'insert_cf':
  610. $table_id = $_POST['table']['id'];
  611. $table = $this->load_table( $table_id );
  612. $name = ( isset( $_POST['insert']['custom_field'] ) ) ? $_POST['insert']['custom_field'] : '';
  613. if ( empty( $name ) ) {
  614. $message = __( 'Could not add Custom Data Field, because you did not enter a name.', WP_TABLE_RELOADED_TEXTDOMAIN );
  615. break;
  616. }
  617. $reserved_names = array( 'name', 'description', 'last_modified', 'last_editor' );
  618. if ( in_array( $name, $reserved_names ) ) {
  619. $message = __( 'Could not add Custom Data Field, because the name you entered is reserved for other table data.', WP_TABLE_RELOADED_TEXTDOMAIN );
  620. break;
  621. }
  622. // Name can only contain lowercase letters, numbers, _ and - (like permalink slugs)
  623. $clean_name = sanitize_title_with_dashes( $name );
  624. if ( $name != $clean_name ) {
  625. $message = __( 'Could not add Custom Data Field, because the name contained illegal characters.', WP_TABLE_RELOADED_TEXTDOMAIN );
  626. break;
  627. }
  628. if ( isset( $table['custom_fields'][$name] ) ) {
  629. $message = __( 'Could not add Custom Data Field, because a Field with that name already exists.', WP_TABLE_RELOADED_TEXTDOMAIN );
  630. break;
  631. }
  632. $table['custom_fields'][$name] = '';
  633. uksort( $table['custom_fields'], 'strnatcasecmp' ); // sort the keys naturally
  634. $this->save_table( $table );
  635. $message = __( 'Custom Data Field added successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  636. break;
  637. default:
  638. $this->do_action_list();
  639. return;
  640. }
  641. $this->helper->print_header_message( $message );
  642. if ( 'save_back' == $subaction ) {
  643. $this->do_action_list();
  644. } else {
  645. $table_id = $table['id'];
  646. $this->load_view( 'edit', compact( 'table_id' ) );
  647. }
  648. } elseif ( isset( $_GET['table_id'] ) && $this->table_exists( $_GET['table_id'] ) ) {
  649. $table_id = $_GET['table_id'];
  650. $this->load_view( 'edit', compact( 'table_id' ) );
  651. } else {
  652. $this->do_action_list();
  653. }
  654. }
  655. /**
  656. * "Bulk Edit" action handler
  657. */
  658. function do_action_bulk_edit() {
  659. if ( isset( $_POST['submit'] ) ) {
  660. check_admin_referer( $this->get_nonce( 'bulk_edit' ) );
  661. if ( isset( $_POST['tables'] ) ) {
  662. $subactions = array_keys( $_POST['submit'] );
  663. $subaction = $subactions[0];
  664. switch( $subaction ) {
  665. case 'copy': // see do_action_copy for explanations
  666. foreach ( $_POST['tables'] as $table_id ) {
  667. $table_to_copy = $this->load_table( $table_id );
  668. $new_table = $table_to_copy;
  669. $new_table['id'] = $this->get_new_table_id();
  670. $new_table['name'] = __( 'Copy of', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . $table_to_copy['name'];
  671. unset( $table_to_copy );
  672. $this->save_table( $new_table );
  673. }
  674. $message = _n( 'Table copied successfully.', 'Tables copied successfully.', count( $_POST['tables'] ), WP_TABLE_RELOADED_TEXTDOMAIN );
  675. break;
  676. case 'delete': // see do_action_delete for explanations
  677. foreach ( $_POST['tables'] as $table_id ) {
  678. $this->delete_table( $table_id );
  679. }
  680. $message = _n( 'Table deleted successfully.', 'Tables deleted successfully.', count( $_POST['tables'] ), WP_TABLE_RELOADED_TEXTDOMAIN );
  681. break;
  682. case 'wp_table_import': // see do_action_import for explanations
  683. $this->import_instance = $this->create_class_instance( 'WP_Table_Reloaded_Import', 'import.class.php' );
  684. $this->import_instance->import_format = 'wp_table';
  685. foreach ( $_POST['tables'] as $table_id ) {
  686. $this->import_instance->wp_table_id = $table_id;
  687. $this->import_instance->import_table();
  688. $imported_table = $this->import_instance->imported_table;
  689. $table = array_merge( $this->default_table, $imported_table );
  690. $rows = count( $table['data'] );
  691. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  692. $rows = ( 0 < $rows ) ? $rows : 1;
  693. $cols = ( 0 < $cols ) ? $cols : 1;
  694. $table['visibility']['rows'] = array_fill( 0, $rows, false );
  695. $table['visibility']['columns'] = array_fill( 0, $cols, false );
  696. $table['id'] = $this->get_new_table_id();
  697. $this->save_table( $table );
  698. }
  699. $message = _n( 'Table imported successfully.', 'Tables imported successfully.', count( $_POST['tables'] ), WP_TABLE_RELOADED_TEXTDOMAIN );
  700. break;
  701. default:
  702. break;
  703. }
  704. } else {
  705. $message = __( 'You did not select any tables!', WP_TABLE_RELOADED_TEXTDOMAIN );
  706. }
  707. $this->helper->print_header_message( $message );
  708. }
  709. $this->do_action_list();
  710. }
  711. /**
  712. * "Copy" action handler
  713. */
  714. function do_action_copy() {
  715. if ( isset( $_GET['table_id'] ) ) {
  716. check_admin_referer( $this->get_nonce( 'copy' ) );
  717. $table_to_copy = $this->load_table( $_GET['table_id'] );
  718. $new_table = $table_to_copy;
  719. $new_table['id'] = $this->get_new_table_id();
  720. $new_table['name'] = __( 'Copy of', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . $table_to_copy['name'];
  721. unset( $table_to_copy );
  722. $this->save_table( $new_table );
  723. $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; copied successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $new_table['name'] ) ) );
  724. }
  725. $this->do_action_list();
  726. }
  727. /**
  728. * "Delete" action handler, for tables and custom fields
  729. */
  730. function do_action_delete() {
  731. if ( isset( $_GET['table_id'] ) && isset( $_GET['item'] ) ) {
  732. check_admin_referer( $this->get_nonce( 'delete', $_GET['item'] ) );
  733. $table_id = $_GET['table_id'];
  734. $table = $this->load_table( $table_id );
  735. switch( $_GET['item'] ) {
  736. case 'table':
  737. $this->delete_table( $table_id );
  738. $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; deleted successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ) ) );
  739. $this->do_action_list();
  740. break;
  741. case 'custom_field':
  742. $name = ( isset( $_GET['element_id'] ) ) ? $_GET['element_id'] : '';
  743. if ( !empty( $name ) && isset( $table['custom_fields'][$name] ) ) {
  744. unset( $table['custom_fields'][$name] );
  745. $this->save_table( $table );
  746. $message = __( 'Custom Data Field deleted successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  747. } else {
  748. $message = __( 'Custom Data Field could not be deleted.', WP_TABLE_RELOADED_TEXTDOMAIN );
  749. }
  750. $this->helper->print_header_message( $message );
  751. $this->load_view( 'edit', compact( 'table_id' ) );
  752. break;
  753. default:
  754. $this->helper->print_header_message( __( 'Delete failed.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  755. $this->do_action_list();
  756. }
  757. } else {
  758. $this->do_action_list();
  759. }
  760. }
  761. /**
  762. * "Import" action handler, for single tables and a Dump File
  763. */
  764. function do_action_import() {
  765. $this->import_instance = $this->create_class_instance( 'WP_Table_Reloaded_Import', 'import.class.php' );
  766. if ( isset( $_POST['submit'] ) && isset( $_POST['import_from'] ) ) {
  767. check_admin_referer( $this->get_nonce( 'import' ) );
  768. $import_error = false;
  769. switch( $_POST['import_from'] ) {
  770. case 'file-upload':
  771. if ( !empty( $_FILES['import_file']['tmp_name'] ) ) {
  772. $this->import_instance->tempname = $_FILES['import_file']['tmp_name'];
  773. $this->import_instance->filename = $_FILES['import_file']['name'];
  774. $this->import_instance->mimetype = $_FILES['import_file']['type'];
  775. $this->import_instance->import_from = 'file-upload';
  776. $unlink_file = true;
  777. } else {
  778. $import_error = true;
  779. }
  780. break;
  781. case 'server':
  782. if ( !empty( $_POST['import_server'] ) ) {
  783. $this->import_instance->tempname = $_POST['import_server'];
  784. $this->import_instance->filename = __( 'Imported Table', WP_TABLE_RELOADED_TEXTDOMAIN );
  785. $this->import_instance->mimetype = sprintf( __( 'from %s', WP_TABLE_RELOADED_TEXTDOMAIN ), $_POST['import_server'] );
  786. $this->import_instance->import_from = 'server';
  787. } else {
  788. $import_error = true;
  789. }
  790. break;
  791. case 'form-field':
  792. if ( !empty( $_POST['import_data'] ) ) {
  793. $this->import_instance->tempname = '';
  794. $this->import_instance->filename = __( 'Imported Table', WP_TABLE_RELOADED_TEXTDOMAIN );
  795. $this->import_instance->mimetype = __( 'via form', WP_TABLE_RELOADED_TEXTDOMAIN );
  796. $this->import_instance->import_from = 'form-field';
  797. $this->import_instance->import_data = stripslashes( $_POST['import_data'] );
  798. } else {
  799. $import_error = true;
  800. }
  801. break;
  802. case 'url':
  803. if ( !empty( $_POST['import_url'] ) ) {
  804. $this->import_instance->tempname = '';
  805. $this->import_instance->filename = __( 'Imported Table', WP_TABLE_RELOADED_TEXTDOMAIN );
  806. $this->import_instance->mimetype = sprintf( __( 'from %s', WP_TABLE_RELOADED_TEXTDOMAIN ), $_POST['import_url'] );
  807. $this->import_instance->import_from = 'url';
  808. $url = esc_url( $_POST['import_url'] );
  809. $temp_data = wp_remote_fopen( $url );
  810. $this->import_instance->import_data = ( false !== $temp_data ) ? $temp_data : '';
  811. } else {
  812. $import_error = true;
  813. }
  814. break;
  815. default:
  816. // no valid import source
  817. $import_error = true;
  818. }
  819. if ( $import_error ) {
  820. // no valid data submitted
  821. $this->helper->print_header_message( __( 'Table could not be imported.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  822. $this->load_view( 'import' );
  823. return;
  824. }
  825. // do import with the config set above
  826. $this->import_instance->import_format = $_POST['import_format'];
  827. $this->import_instance->import_table();
  828. $error = $this->import_instance->error;
  829. $imported_table = $this->import_instance->imported_table;
  830. if ( isset( $unlink_file) && $unlink_file )
  831. $this->import_instance->unlink_uploaded_file();
  832. if ( isset( $_POST['import_addreplace'] ) && isset( $_POST['import_addreplace_table'] ) && ( 'replace' == $_POST['import_addreplace'] ) && $this->table_exists( $_POST['import_addreplace_table'] ) ) {
  833. $table = $this->load_table( $_POST['import_addreplace_table'] );
  834. $table['data'] = $imported_table['data'];
  835. $success_message = sprintf( __( 'Table %s (%s) replaced successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ), $this->helper->safe_output( $table['id'] ) );
  836. } else {
  837. $table = array_merge( $this->default_table, $imported_table );
  838. $table['id'] = $this->get_new_table_id();
  839. $success_message = _n( 'Table imported successfully.', 'Tables imported successfully.', 1, WP_TABLE_RELOADED_TEXTDOMAIN );
  840. }
  841. unset( $imported_table );
  842. foreach ( $table['data'] as $row_idx => $row )
  843. $table['visibility']['rows'][$row_idx] = isset( $table['visibility']['rows'][$row_idx] ) ? $table['visibility']['rows'][$row_idx] : false;
  844. foreach ( $table['data'][0] as $col_idx => $col )
  845. $table['visibility']['columns'][$col_idx] = isset( $table['visibility']['columns'][$col_idx] ) ? $table['visibility']['columns'][$col_idx] : false;
  846. if ( !$error ) {
  847. $this->save_table( $table );
  848. $this->helper->print_header_message( $success_message );
  849. $table_id = $table['id'];
  850. $this->load_view( 'edit', compact( 'table_id' ) );
  851. } else {
  852. $this->helper->print_header_message( __( 'Table could not be imported.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  853. $this->load_view( 'import' );
  854. }
  855. } elseif ( isset( $_GET['import_format'] ) && 'wp_table' == $_GET['import_format'] && isset( $_GET['wp_table_id'] ) ) {
  856. check_admin_referer( $this->get_nonce( 'import' ) );
  857. $this->import_instance->import_format = 'wp_table';
  858. $this->import_instance->wp_table_id = $_GET['wp_table_id'];
  859. $this->import_instance->import_table();
  860. $imported_table = $this->import_instance->imported_table;
  861. $table = array_merge( $this->default_table, $imported_table );
  862. $rows = count( $table['data'] );
  863. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  864. $table['visibility']['rows'] = array_fill( 0, $rows, false );
  865. $table['visibility']['columns'] = array_fill( 0, $cols, false );
  866. $table['id'] = $this->get_new_table_id();
  867. $this->save_table( $table );
  868. $this->helper->print_header_message( _n( 'Table imported successfully.', 'Tables imported successfully.', 1, WP_TABLE_RELOADED_TEXTDOMAIN ) );
  869. $table_id = $table['id'];
  870. $this->load_view( 'edit', compact( 'table_id' ) );
  871. } elseif ( isset( $_POST['import_wp_table_reloaded_dump_file'] ) ) {
  872. check_admin_referer( $this->get_nonce( 'import_dump' ), $this->get_nonce( 'import_dump' ) );
  873. // check if user is admin
  874. if ( !current_user_can( 'manage_options' ) ) {
  875. $this->helper->print_header_message( __( 'You do not have sufficient rights to perform this action.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  876. $this->load_view( 'options' );
  877. return;
  878. }
  879. // check if file was uploaded
  880. if ( empty( $_FILES['dump_file']['tmp_name'] ) ) {
  881. $this->helper->print_header_message( __( 'You did not upload a WP-Table Reloaded dump file.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  882. $this->load_view( 'options' );
  883. return;
  884. }
  885. // read data from file and rewrite string to array
  886. $import_data = file_get_contents( $_FILES['dump_file']['tmp_name'] );
  887. $import = unserialize( $import_data );
  888. // check if import dump is not empty
  889. if ( empty( $import ) ) {
  890. $this->helper->print_header_message( __( 'The uploaded dump file is empty. Please upload a valid dump file.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  891. $this->load_view( 'options' );
  892. return;
  893. }
  894. // NEED TO ADD SOME MORE CHECKS HERE, IF IMPORT IS VALID AND COMPLETE!
  895. // remove all existing data
  896. foreach ( $this->tables as $id => $tableoptionname )
  897. delete_option( $tableoptionname );
  898. delete_option( $this->optionname['tables'] );
  899. delete_option( $this->optionname['options'] );
  900. // import and save options
  901. $this->options = $import['options'];
  902. $this->update_options();
  903. // import and save table overview
  904. $this->tables = $import['table_info'];
  905. $this->update_tables();
  906. // import each table
  907. foreach ( $this->tables as $table_id => $tableoptionname ) {
  908. $dump_table = $import['tables'][ $table_id ];
  909. update_option( $tableoptionname, $dump_table );
  910. }
  911. // check if plugin update is necessary, compared to imported data
  912. if ( version_compare( $this->options['installed_version'], WP_TABLE_RELOADED_PLUGIN_VERSION, '<' ) )
  913. $this->plugin_update();
  914. $this->helper->print_header_message( __( 'All Tables, Settings and Options were successfully imported.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  915. $this->do_action_list();
  916. } else {
  917. $this->load_view( 'import' );
  918. }
  919. }
  920. /**
  921. * "Export" action handler, for single tables
  922. */
  923. function do_action_export() {
  924. $this->export_instance = $this->create_class_instance( 'WP_Table_Reloaded_Export', 'export.class.php' );
  925. if ( isset( $_POST['submit'] ) && isset( $_POST['table_id'] ) && isset( $_POST['export_format'] ) ) {
  926. check_admin_referer( $this->get_nonce( 'export' ) );
  927. $table_to_export = $this->load_table( $_POST['table_id'] );
  928. $this->export_instance->table_to_export = $table_to_export;
  929. $this->export_instance->export_format = $_POST['export_format'];
  930. $this->export_instance->delimiter = $_POST['delimiter'];
  931. $this->export_instance->export_table();
  932. $exported_table = $this->export_instance->exported_table;
  933. if ( isset( $_POST['download_export_file'] ) && 'true' == $_POST['download_export_file'] ) {
  934. $filename = $table_to_export['id'] . '-' . $table_to_export['name'] . '-' . date( 'Y-m-d' ) . '.' . $_POST['export_format'];
  935. $this->helper->prepare_download( $filename, strlen( $exported_table ), 'text/' . $_POST['export_format'] );
  936. echo $exported_table;
  937. exit;
  938. } else {
  939. $this->helper->print_header_message( sprintf( __( 'Table &quot;%s&quot; exported successfully.', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table_to_export['name'] ) ) );
  940. $table_id = $_POST['table_id'];
  941. $output = $exported_table;
  942. $this->load_view( 'export', compact( 'table_id', 'output' ) );
  943. }
  944. } else {
  945. $table_id = isset( $_REQUEST['table_id'] ) ? $_REQUEST['table_id'] : 0;
  946. $this->load_view( 'export', compact( 'table_id' ) );
  947. }
  948. }
  949. /**
  950. * "Export" action handler, for Dump Files, stores all plugin data, like tables, options, etc. in a single array,
  951. * serializes it and offers the resulting string for download in a Dump File
  952. */
  953. function do_action_export_all() {
  954. if ( isset( $_POST['export_all'] ) ) {
  955. check_admin_referer( $this->get_nonce( 'export_all' ), $this->get_nonce( 'export_all' ) );
  956. $export = array();
  957. $export['table_info'] = $this->tables;
  958. foreach ( $this->tables as $table_id => $tableoptionname ) {
  959. $dump_table = $this->load_table( $table_id );
  960. $export['tables'][ $table_id ] = $dump_table;
  961. }
  962. $export['options'] = $this->options;
  963. $export_dump = serialize( $export );
  964. $filename = 'wp-table-reloaded-export-' . date( 'Y-m-d' ) . '.dump';
  965. $this->helper->prepare_download( $filename, strlen( $export_dump ), 'text/data' );
  966. echo $export_dump;
  967. exit;
  968. }
  969. }
  970. /**
  971. * "Plugin Options" action handler
  972. */
  973. function do_action_options() {
  974. if ( isset( $_POST['submit'] ) && isset( $_POST['options'] ) ) {
  975. check_admin_referer( $this->get_nonce( 'options' ), $this->get_nonce( 'options' ) );
  976. // check if user can access Plugin Options
  977. if ( !$this->user_has_access( 'plugin-options' ) ) {
  978. $this->helper->print_header_message( __( 'You do not have sufficient rights to access the Plugin Options.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  979. $this->load_view( 'options' );
  980. return;
  981. }
  982. $new_options = $_POST['options'];
  983. // checkboxes: option value is defined by whether option isset (e.g. was checked) or not
  984. $this->options['show_exit_warning'] = isset( $new_options['show_exit_warning'] );
  985. $this->options['growing_textareas'] = isset( $new_options['growing_textareas'] );
  986. $this->options['use_datatables_on_table_list'] = isset( $new_options['use_datatables_on_table_list'] );
  987. $this->options['enable_tablesorter'] = isset( $new_options['enable_tablesorter'] );
  988. $this->options['use_default_css'] = isset( $new_options['use_default_css'] );
  989. $this->options['use_custom_css'] = isset( $new_options['use_custom_css'] );
  990. $this->options['add_target_blank_to_links'] = isset( $new_options['add_target_blank_to_links'] );
  991. // drop down: only set when not disabled (by JavaScript)
  992. if ( isset( $new_options['tablesorter_script'] ) )
  993. $this->options['tablesorter_script'] = $new_options['tablesorter_script'];
  994. // only save these settings, if user is administrator, as they are admin options
  995. if ( current_user_can( 'manage_options' ) ) {
  996. $this->options['uninstall_upon_deactivation'] = isset( $new_options['uninstall_upon_deactivation'] );
  997. $this->options['enable_search'] = isset( $new_options['enable_search'] );
  998. $this->options['frontend_edit_table_link'] = isset( $new_options['frontend_edit_table_link'] );
  999. // plugin language
  1000. if ( isset( $this->available_plugin_languages[ $new_options['plugin_language'] ] ) )
  1001. $this->options['plugin_language'] = $new_options['plugin_language'];
  1002. else
  1003. $this->options['plugin_language'] = 'auto';
  1004. // admin menu parent page
  1005. $admin_menu_parent_page_changed = ( $this->options['admin_menu_parent_page'] != $new_options['admin_menu_parent_page'] );
  1006. if ( in_array( $new_options['admin_menu_parent_page'], $this->possible_admin_menu_parent_pages ) )
  1007. $this->options['admin_menu_parent_page'] = $new_options['admin_menu_parent_page'];
  1008. else
  1009. $this->options['admin_menu_parent_page'] = 'tools.php';
  1010. // update $this->page_url, so that next page load will work
  1011. $this->page_url = $this->options['admin_menu_parent_page'] ;
  1012. // user access to plugin
  1013. if ( in_array( $new_options['user_access_plugin'], array( 'admin', 'editor', 'author', 'contributor' ) ) )
  1014. $this->options['user_access_plugin'] = $new_options['user_access_plugin'];
  1015. else
  1016. $this->options['user_access_plugin'] = 'admin'; // better set it high, if something is wrong
  1017. // user access to plugin options
  1018. if ( in_array( $new_options['user_access_plugin_options'], array( 'admin', 'editor', 'author' ) ) )
  1019. $this->options['user_access_plugin_options'] = $new_options['user_access_plugin_options'];
  1020. else
  1021. $this->options['user_access_plugin_options'] = 'admin'; // better set it high, if something is wrong
  1022. }
  1023. // clean up CSS style input (if user enclosed it into <style...></style>
  1024. if ( isset( $new_options['custom_css'] ) ) {
  1025. if ( 1 == preg_match( '/<style.*?>(.*?)<\/style>/is', stripslashes( $new_options['custom_css'] ), $matches ) )
  1026. $new_options['custom_css'] = $matches[1]; // if found, take match as style to save
  1027. $this->options['custom_css'] = $new_options['custom_css'];
  1028. }
  1029. $this->update_options();
  1030. $message = __( 'Options saved successfully.', WP_TABLE_RELOADED_TEXTDOMAIN );
  1031. if ( $admin_menu_parent_page_changed ) {
  1032. $url = $this->get_action_url( array( 'action' => 'options' ), false );
  1033. $message .= ' ' . sprintf( __( '<a href="%s">Click here to Proceed.</a>', WP_TABLE_RELOADED_TEXTDOMAIN ), $url );
  1034. }
  1035. $this->helper->print_header_message( $message );
  1036. }
  1037. $this->load_view( 'options' );
  1038. }
  1039. /**
  1040. * "Plugin Uninstall" action handler, checks if an admin is performing it, sets uninstall to true and deactivates the plugin
  1041. * (which executes the plugin_deactivation_hook() which then deletes all options from the DB
  1042. */
  1043. function do_action_uninstall() {
  1044. check_admin_referer( $this->get_nonce( 'uninstall' ) );
  1045. if ( !current_user_can( 'manage_options' ) ) {
  1046. $this->helper->print_header_message( __( 'You do not have sufficient rights to perform this action.', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  1047. $this->load_view( 'options' );
  1048. return;
  1049. }
  1050. // everything shall be deleted (manual uninstall)
  1051. $this->options['uninstall_upon_deactivation'] = true;
  1052. $this->update_options();
  1053. $plugin = WP_TABLE_RELOADED_BASENAME;
  1054. deactivate_plugins( $plugin );
  1055. if ( false !== get_option( 'recently_activated', false ) )
  1056. update_option( 'recently_activated', array( $plugin => time() ) + (array)get_option( 'recently_activated' ) );
  1057. $this->load_view( 'uninstall', array(), false );
  1058. }
  1059. /**
  1060. * "About" action handler, only calls the view
  1061. */
  1062. function do_action_about() {
  1063. $this->load_view( 'about' );
  1064. }
  1065. /**
  1066. * "AJAX List of Tables" action handler
  1067. */
  1068. function do_action_ajax_list() {
  1069. check_admin_referer( $this->get_nonce( 'ajax_list' ) );
  1070. $this->init_language_support();
  1071. $this->load_view( 'ajax_list', array(), false );
  1072. exit; // necessary to stop page building here!
  1073. }
  1074. /**
  1075. * "AJAX Table Preview" action handler
  1076. */
  1077. function do_action_ajax_preview() {
  1078. check_admin_referer( $this->get_nonce( 'ajax_preview' ) );
  1079. $this->init_language_support();
  1080. $table_id = ( isset( $_GET['table_id'] ) && 0 < (int)$_GET['table_id'] ) ? (int)$_GET['table_id'] : 0;
  1081. if ( $this->table_exists( $table_id ) ) {
  1082. $this->load_view( 'ajax_preview', compact( 'table_id' ), false );
  1083. } else {
  1084. ?>
  1085. <div style="clear:both;"><p style="width:97%;"><?php _e( 'There is no table with this ID!', WP_TABLE_RELOADED_TEXTDOMAIN ); ?></p></div>
  1086. <?php
  1087. }
  1088. exit; // necessary to stop page building here!
  1089. }
  1090. /**
  1091. * "Hide welcome message" action handler (which either is a plugin update or a plugin install message)
  1092. */
  1093. function do_action_hide_welcome_message() {
  1094. check_admin_referer( $this->get_nonce( 'hide_welcome_message' ) );
  1095. $this->options['show_welcome_message'] = 0;
  1096. $this->update_options();
  1097. $this->do_action_list();
  1098. }
  1099. /**
  1100. * "Hide Donate Message" action handler
  1101. */
  1102. function do_action_hide_donate_nag() {
  1103. check_admin_referer( $this->get_nonce( 'hide_donate_nag' ) );
  1104. $this->options['show_donate_nag'] = false;
  1105. $this->update_options();
  1106. if ( isset( $_GET['user_donated'] ) && $_GET['user_donated'] ) {
  1107. $this->helper->print_header_message( __( 'Thank you very much! Your donation is highly appreciated. You just contributed to the further development of WP-Table Reloaded!', WP_TABLE_RELOADED_TEXTDOMAIN ) );
  1108. } else {
  1109. $this->helper->print_header_message( sprintf( __( 'No problem! I still hope you enjoy the benefits that WP-Table Reloaded brings to you. If you should want to change your mind, you\'ll always find the &quot;%s&quot; button on the <a href="%s">WP-Table Reloaded website</a>.', WP_TABLE_RELOADED_TEXTDOMAIN ), __( 'Donate', WP_TABLE_RELOADED_TEXTDOMAIN ), 'http://tobias.baethge.com/go/wp-table-reloaded/website/' ) );
  1110. }
  1111. $this->do_action_list();
  1112. }
  1113. // ###################################################################################################################
  1114. // ########################################## ####################################################
  1115. // ########################################## Page Generation ####################################################
  1116. // ########################################## ####################################################
  1117. // ###################################################################################################################
  1118. /**
  1119. * Load a view from another file, which contains HTML for the $name view to render and output
  1120. *
  1121. * @param string $name Name of the view to load
  1122. * @param array $params (optional) Parameters/PHP variables that shall be available to the view, extracted to single variables at the beginning
  1123. * @param bool $print_submenu_navigation (optional) Whether to print the submenu navigation under the page headline
  1124. */
  1125. function load_view( $name, $params = array(), $print_submenu_navigation = true ) {
  1126. extract( $params );
  1127. $headlines = array(
  1128. 'list' => __( 'List of Tables', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' &lsaquo; ' . __( 'WP-Table Reloaded', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1129. 'add' => __( 'Add new Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1130. 'import' => __( 'Import a Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1131. 'export' => __( 'Export a Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1132. 'options' => __( 'Plugin Options', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' &lsaquo; ' . __( 'WP-Table Reloaded', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1133. 'about' => __( 'About WP-Table Reloaded', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1134. 'uninstall' => __( 'WP-Table Reloaded', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1135. 'ajax_list' => __( 'List of Tables', WP_TABLE_RELOADED_TEXTDOMAIN )
  1136. );
  1137. // these views also need the complete table, besides the parameters
  1138. if ( in_array( $name, array( 'edit', 'ajax_preview' ) ) ) {
  1139. $table = $this->load_table( $table_id );
  1140. $headlines['edit'] = sprintf( __( 'Edit Table &quot;%s&quot; (ID %s)', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ), $this->helper->safe_output( $table['id'] ) );
  1141. $headlines['ajax_preview'] = sprintf( __( 'Preview of Table &quot;%s&quot; (ID %s)', WP_TABLE_RELOADED_TEXTDOMAIN ), $this->helper->safe_output( $table['name'] ), $this->helper->safe_output( $table['id'] ) );
  1142. }
  1143. $headline = isset( $headlines[ $name ] ) ? $headlines[ $name ] : '';
  1144. $this->helper->print_page_header( $headline );
  1145. if ( $print_submenu_navigation )
  1146. $this->print_submenu_navigation( $name );
  1147. include ( WP_TABLE_RELOADED_ABSPATH . "views/view-{$name}.php" );
  1148. $this->helper->print_page_footer();
  1149. }
  1150. /**
  1151. * Render and output the submenu navigation with links to the possible actions, highlighting the current one,
  1152. * separated into table actions (List, Add, Import, Export) and plugin actions (Options, About)
  1153. *
  1154. * @param string $the_action Action that is being processed in this page load
  1155. */
  1156. function print_submenu_navigation( $the_action ) {
  1157. $table_actions = array(
  1158. 'list' => __( 'List Tables', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1159. 'add' => __( 'Add new Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1160. 'import' => __( 'Import a Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1161. 'export' => __( 'Export a Table', WP_TABLE_RELOADED_TEXTDOMAIN )
  1162. );
  1163. $table_actions = apply_filters( 'wp_table_reloaded_backend_table_actions', $table_actions );
  1164. $_table_actions = array_keys( $table_actions );
  1165. $last_table_action = array_pop( $_table_actions );
  1166. $plugin_actions = array(
  1167. 'options' => __( 'Plugin Options', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1168. 'about' => __( 'About the plugin', WP_TABLE_RELOADED_TEXTDOMAIN )
  1169. );
  1170. $plugin_actions = apply_filters( 'wp_table_reloaded_backend_plugin_actions', $plugin_actions );
  1171. $_plugin_actions = array_keys( $plugin_actions );
  1172. $last_plugin_action = array_pop( $_plugin_actions );
  1173. ?>
  1174. <ul class="subsubsub">
  1175. <?php
  1176. foreach ( $table_actions as $action => $name ) {
  1177. $action_url = $this->get_action_url( array( 'action' => $action ), false );
  1178. $class = ( $action == $the_action ) ? 'class="current" ' : '';
  1179. $bar = ( $last_table_action != $action ) ? ' | ' : '';
  1180. echo "<li><a {$class}href=\"{$action_url}\">{$name}</a>{$bar}</li>";
  1181. }
  1182. echo '<li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>';
  1183. foreach ( $plugin_actions as $action => $name ) {
  1184. $action_url = $this->get_action_url( array( 'action' => $action ), false );
  1185. $class = ( $action == $the_action ) ? 'class="current" ' : '';
  1186. $bar = ( $last_plugin_action != $action ) ? ' | ' : '';
  1187. echo "<li><a {$class}href=\"{$action_url}\">{$name}</a>{$bar}</li>";
  1188. }
  1189. ?>
  1190. </ul>
  1191. <br class="clear" />
  1192. <?php
  1193. }
  1194. /**
  1195. * Decide whether a donate message can be shown on the "List Tables" screen, depending on passed days since installation and whether it was shown before
  1196. *
  1197. * @return bool Whether the donate message can be shown on the "List Tables" screen
  1198. */
  1199. function may_print_donate_nag() {
  1200. if ( !$this->options['show_donate_nag'] )
  1201. return false;
  1202. // how long has the plugin been installed?
  1203. $secs = time() - $this->options['install_time'];
  1204. $days = floor( $secs / (60*60*24) );
  1205. return ( $days >= 30 ) ? true : false;
  1206. }
  1207. // ###################################################################################################################
  1208. // ######################################### ####################################################
  1209. // ######################################### Options Functions ####################################################
  1210. // ######################################### ####################################################
  1211. // ###################################################################################################################
  1212. /**
  1213. * Determine an ID that is not yet used and can be used for a new table, by checking what the last ID was
  1214. *
  1215. * @return int New and unused table ID
  1216. */
  1217. function get_new_table_id() {
  1218. // need to check new ID candidate, because a higher one might be in use, if a table ID was manually changed
  1219. do {
  1220. $this->options['last_id'] = $this->options['last_id'] + 1;
  1221. } while ( $this->table_exists( $this->options['last_id'] ) );
  1222. $this->update_options();
  1223. return $this->options['last_id'];
  1224. }
  1225. /**
  1226. * Save current set of Plugin Options to the DB (as an option in the WP options DB table),
  1227. */
  1228. function update_options() {
  1229. // possibility to overwrite option updating (i.e. to update them in own DB table)
  1230. $options_updated = apply_filters( 'wp_table_reloaded_update_options', false, $this->options );
  1231. if ( $options_updated )
  1232. return;
  1233. update_option( $this->optionname['options'], $this->options );
  1234. }
  1235. /**
  1236. * Save current List of Tables to the DB (as an option in the WP options DB table),
  1237. */
  1238. function update_tables() {
  1239. ksort( $this->tables, SORT_NUMERIC ); // sort for table IDs, as one with a small ID might have been appended
  1240. // possibility to overwrite tables updating (i.e. to update them in own DB table)
  1241. $tables_updated = apply_filters( 'wp_table_reloaded_update_tables', false, $this->tables );
  1242. if ( $tables_updated )
  1243. return;
  1244. update_option( $this->optionname['tables'], $this->tables );
  1245. }
  1246. /**
  1247. * Save the given $table to its option in the DB and also update the List of Tables
  1248. *
  1249. * @param array $table Table to store, including data, options, custom fields, visibility settings, ...
  1250. */
  1251. function save_table( $table ) {
  1252. if ( 0 < $table['id'] ) {
  1253. // update last changes data
  1254. $table['last_modified'] = current_time( 'mysql' );
  1255. $user = wp_get_current_user();
  1256. $table['last_editor_id'] = $user->ID;
  1257. // possibility to overwrite table saving (i.e. to store it in own DB table)
  1258. $table_saved = apply_filters( 'wp_table_reloaded_save_table', false, $table );
  1259. if ( $table_saved )
  1260. return;
  1261. $table = apply_filters( 'wp_table_reloaded_pre_save_table', $table );
  1262. $table = apply_filters( 'wp_table_reloaded_pre_save_table_id-' . $table['id'], $table );
  1263. // delete the transient that caches the table output
  1264. $cache_name = "wp_table_reloaded_table_output_{$table['id']}";
  1265. delete_transient( $cache_name );
  1266. $this->tables[ $table['id'] ] = ( isset( $this->tables[ $table['id'] ] ) ) ? $this->tables[ $table['id'] ] : $this->optionname['table'] . '_' . $table['id'];
  1267. update_option( $this->tables[ $table['id'] ], $table );
  1268. $this->update_tables();
  1269. }
  1270. }
  1271. /**
  1272. * Delete the table with the given $table_id from the DB and also remove it from the List of Tables
  1273. *
  1274. * @param int $table_id ID of the table to delete
  1275. */
  1276. function delete_table( $table_id ) {
  1277. // possibility to overwrite table deleting (i.e. to delete it in own DB table)
  1278. $table_deleted = apply_filters( 'wp_table_reloaded_delete_table', false, $table_id );
  1279. if ( !$table_deleted ) {
  1280. $this->tables[ $table_id ] = ( isset( $this->tables[ $table_id ] ) ) ? $this->tables[ $table_id ] : $this->optionname['table'] . '_' . $table_id;
  1281. delete_option( $this->tables[ $table_id ] );
  1282. }
  1283. unset( $this->tables[ $table_id ] );
  1284. $this->update_tables();
  1285. }
  1286. // ###################################################################################################################
  1287. // ######################################### ####################################################
  1288. // ######################################### URL Support ####################################################
  1289. // ######################################### ####################################################
  1290. // ###################################################################################################################
  1291. /**
  1292. * Generate the complete nonce string, from the nonce base, the action and an item, e.g. wp-table-reloaded-nonce_delete_table
  1293. *
  1294. * @param string $action Action for which the nonce is needed
  1295. * @param string $item (optional) Item for which the action will be performed, like "table" or "custom_field"
  1296. * @return string The complete nonce string
  1297. */
  1298. function get_nonce( $action, $item = false ) {
  1299. return ( false !== $item ) ? $this->nonce_base . '_' . $action . '_' . $item : $this->nonce_base . '_' . $action;
  1300. }
  1301. /**
  1302. * Generate the action URL, to be used as a link within the plugin (e.g. in the submenu navigation or List of Tables)
  1303. *
  1304. * @param array $params (optional) Parameters to form the Query String of the URL
  1305. * @param bool $add_nonce (optional) Whether the URL shall be nonced by WordPress
  1306. * @return string The action URL
  1307. */
  1308. function get_action_url( $params = array(), $add_nonce = false ) {
  1309. $default_params = array(
  1310. 'page' => $this->page_slug,
  1311. 'action' => false,
  1312. 'item' => false
  1313. );
  1314. $url_params = array_merge( $default_params, $params );
  1315. $action_url = add_query_arg( $url_params, admin_url( $this->page_url ) );
  1316. if ( $add_nonce )
  1317. $action_url = wp_nonce_url( $action_url, $this->get_nonce( $url_params['action'], $url_params['item'] ) );
  1318. $action_url = esc_url( $action_url );
  1319. return $action_url;
  1320. }
  1321. // ###################################################################################################################
  1322. // ####################################### ###################################################
  1323. // ####################################### Plugin Management ###################################################
  1324. // ####################################### ###################################################
  1325. // ###################################################################################################################
  1326. /**
  1327. * Load plugin options and list of tables from DB options, if not available: install plugin first
  1328. */
  1329. function init_plugin() {
  1330. $this->options = $this->load_options();
  1331. $this->tables = $this->load_tables();
  1332. if ( false === $this->options || false === $this->tables )
  1333. $this->plugin_install();
  1334. }
  1335. /**
  1336. * Commands to be executed, when plugin is activated by WordPress, performs check, if plugin is freshly installed or updated
  1337. */
  1338. function plugin_activation_hook() {
  1339. $this->options = $this->load_options();
  1340. if ( false !== $this->options && isset( $this->options['installed_version'] ) ) {
  1341. // check if update needed, or just re-activated the latest version of it
  1342. if ( version_compare( $this->options['installed_version'], WP_TABLE_RELOADED_PLUGIN_VERSION, '<' ) ) {
  1343. $this->plugin_update();
  1344. } else {
  1345. // just reactivating, but latest version of plugin installed
  1346. }
  1347. } else {
  1348. // plugin has never been installed before
  1349. $this->plugin_install();
  1350. }
  1351. }
  1352. /**
  1353. * Commands to be executed, when plugin is deactivated by WordPress, removes all options and tables, if corresponding admin option is set
  1354. */
  1355. function plugin_deactivation_hook() {
  1356. $this->options = $this->load_options();
  1357. $this->tables = $this->load_tables();
  1358. if ( false !== $this->options && isset( $this->options['uninstall_upon_deactivation'] ) && $this->options['uninstall_upon_deactivation'] ) {
  1359. // delete all options and tables
  1360. foreach ( $this->tables as $id => $tableoptionname )
  1361. delete_option( $tableoptionname );
  1362. delete_option( $this->optionname['tables'] );
  1363. delete_option( $this->optionname['options'] );
  1364. }
  1365. }
  1366. /**
  1367. * Install the plugin by setting up the options and tables
  1368. */
  1369. function plugin_install() {
  1370. $this->options = $this->default_options;
  1371. $this->options['installed_version'] = WP_TABLE_RELOADED_PLUGIN_VERSION;
  1372. $this->options['install_time'] = time();
  1373. $this->options['custom_css'] = ''; // we could add initial CSS here, for demonstration
  1374. $this->options['show_welcome_message'] = 1; // 1 = install message
  1375. $this->update_options();
  1376. $this->tables = $this->default_tables;
  1377. $this->update_tables();
  1378. }
  1379. /**
  1380. * Update the plugin, add new values to the plugin and table options and remove deprecated ones
  1381. */
  1382. function plugin_update() {
  1383. // update general plugin options
  1384. // 1. step: by adding/overwriting existing options
  1385. $this->options = $this->load_options();
  1386. // do nothing, if installed version is up-to-date
  1387. if ( ! version_compare( $this->options['installed_version'], WP_TABLE_RELOADED_PLUGIN_VERSION, '<' ) )
  1388. return;
  1389. $new_options = array();
  1390. // 1b. step: update new default options before possibly adding them
  1391. $this->default_options['install_time'] = time();
  1392. // 2a. step: add/delete new/deprecated options by overwriting new ones with existing ones, if there are any
  1393. foreach ( $this->default_options as $key => $value )
  1394. $new_options[ $key ] = ( isset( $this->options[ $key ] ) ) ? $this->options[ $key ] : $this->default_options[ $key ] ;
  1395. // 2b., take care of CSS
  1396. $new_options['use_custom_css'] = ( !isset( $this->options['use_custom_css'] ) && isset( $this->options['use_global_css'] ) ) ? $this->options['use_global_css'] : $this->options['use_custom_css'];
  1397. // 2c., take care of Tablesorter script, comparison to 1.4.9 equaly means smaller than anything like 1.5
  1398. if ( version_compare( $this->options['installed_version'] , '1.4.9', '<' ) )
  1399. $new_options['tablesorter_script'] = ( isset( $this->options['use_tablesorter_extended'] ) && $this->options['use_tablesorter_extended'] ) ? 'tablesorter_extended' : 'tablesorter';
  1400. // 2d., 'edit-pages.php' was renamed to 'edit.php?post_type=page' in WP 3.0
  1401. if ( 'edit-pages.php' == $this->options['admin_menu_parent_page'] )
  1402. $new_options['admin_menu_parent_page'] = 'edit.php?post_type=page';
  1403. // 2e., 'top-level' was renamed to 'admin.php' (internally)
  1404. if ( 'top-level' == $this->options['admin_menu_parent_page'] )
  1405. $new_options['admin_menu_parent_page'] = 'admin.php';
  1406. // 3. step: update installed version number, empty update message cache, set welcome message
  1407. $new_options['installed_version'] = WP_TABLE_RELOADED_PLUGIN_VERSION;
  1408. $new_options['update_message'] = array();
  1409. $new_options['show_welcome_message'] = 2; // 2 = update message
  1410. // 4. step: save the new options
  1411. $this->options = $new_options;
  1412. $this->update_options();
  1413. // update individual tables and their options
  1414. $this->tables = $this->load_tables();
  1415. foreach ( $this->tables as $id => $tableoptionname ) {
  1416. $table = $this->load_table( $id );
  1417. $temp_table = $this->default_table;
  1418. // if table doesn't have visibility information, add them
  1419. $rows = count( $table['data'] );
  1420. $cols = (0 < $rows) ? count( $table['data'][0] ) : 0;
  1421. $temp_table['visibility']['rows'] = array_fill( 0, $rows, false );
  1422. $temp_table['visibility']['columns'] = array_fill( 0, $cols, false );
  1423. foreach ( $temp_table as $key => $value )
  1424. $new_table[ $key ] = ( isset( $table[ $key ] ) ) ? $table[ $key ] : $temp_table[ $key ] ;
  1425. foreach ( $temp_table['options'] as $key => $value )
  1426. $new_table['options'][ $key ] = ( isset( $table['options'][ $key ] ) ) ? $table['options'][ $key ] : $temp_table['options'][ $key ] ;
  1427. $this->save_table( $new_table );
  1428. }
  1429. }
  1430. /**
  1431. * Find out whether the current user has enough rights to access $screen, currently only used for Plugin Options ($screen = 'plugin-options')
  1432. *
  1433. * @param string $screen Screen/View that shall be checked for access, currently only used in the filter
  1434. * @return bool Whether the user has access to $screen
  1435. */
  1436. function user_has_access( $screen ) {
  1437. // capabilities from http://codex.wordpress.org/Roles_and_Capabilities
  1438. $user_group = $this->options['user_access_plugin_options'];
  1439. $capabilities = array(
  1440. 'admin' => 'manage_options',
  1441. 'editor' => 'publish_pages',
  1442. 'author' => 'publish_posts'
  1443. );
  1444. $needed_cap = isset( $capabilities[ $user_group ] ) ? $capabilities[ $user_group ] : 'manage_options';
  1445. $has_access = current_user_can( $needed_cap );
  1446. $has_access = apply_filters( 'wp_table_reloaded_user_access_' . $screen, $has_access, $this->options['user_access_plugin_options'] );
  1447. return $has_access;
  1448. }
  1449. /**
  1450. * Get the plugin update message from the remote server, if there is an update available
  1451. *
  1452. * @param array $current Information about the currently installed version (provided by WP)
  1453. * @param object $new Information about the available plugin version (provided by WP)
  1454. * @return string Plugin Update Message
  1455. */
  1456. function get_plugin_update_message( $current, $new ) {
  1457. if ( empty( $this->options['update_message'][ $new->new_version ] ) ) {
  1458. $message = $this->helper->retrieve_plugin_update_message( $current['Version'], $new->new_version );
  1459. $this->options['update_message'][ $new->new_version ] = $message;
  1460. $this->update_options();
  1461. }
  1462. return $this->options['update_message'][ $new->new_version ];
  1463. }
  1464. /**
  1465. * Print the plugin update message in the Plugins list (right in the row), if there's an update available,
  1466. * wrapper for get_plugin_update_message()
  1467. *
  1468. * @param array $current Information about the currently installed version (provided by WP)
  1469. * @param object $new Information about the available plugin version (provided by WP)
  1470. */
  1471. function add_plugin_update_message( $current, $new ) {
  1472. $message = $this->get_plugin_update_message( $current, $new );
  1473. if ( !empty( $message ) )
  1474. echo '<br />' . $this->helper->safe_output( $message );
  1475. }
  1476. /**
  1477. * Add more links to plugin's entry on Plugins page
  1478. *
  1479. * @param array $links List of links to print on the Plugins page
  1480. * @param string $file Name of the plugin
  1481. * @return array Extended list of links to print on the Plugins page
  1482. */
  1483. function add_plugin_row_meta( $links, $file ) {
  1484. if ( WP_TABLE_RELOADED_BASENAME != $file )
  1485. return $links;
  1486. $links[] = '<a href="' . $this->get_action_url() . '" title="' . __( 'WP-Table Reloaded Plugin Page', WP_TABLE_RELOADED_TEXTDOMAIN ) . '">' . __( 'Plugin Page', WP_TABLE_RELOADED_TEXTDOMAIN ) . '</a>';
  1487. $links[] = '<a href="http://tobias.baethge.com/go/wp-table-reloaded/faq/" title="' . __( 'Frequently Asked Questions', WP_TABLE_RELOADED_TEXTDOMAIN ) . '">' . __( 'FAQ', WP_TABLE_RELOADED_TEXTDOMAIN ) . '</a>';
  1488. $links[] = '<a href="http://tobias.baethge.com/go/wp-table-reloaded/support/" title="' . __( 'Support', WP_TABLE_RELOADED_TEXTDOMAIN ) . '">' . __( 'Support', WP_TABLE_RELOADED_TEXTDOMAIN ) . '</a>';
  1489. $links[] = '<a href="http://tobias.baethge.com/go/wp-table-reloaded/documentation/" title="' . __( 'Plugin Documentation', WP_TABLE_RELOADED_TEXTDOMAIN ) . '">' . __( 'Documentation', WP_TABLE_RELOADED_TEXTDOMAIN ) . '</a>';
  1490. $links[] = '<a href="http://tobias.baethge.com/go/wp-table-reloaded/donate/" title="' . __( 'Support WP-Table Reloaded with your donation!', WP_TABLE_RELOADED_TEXTDOMAIN ) . '"><strong>' . __( 'Donate', WP_TABLE_RELOADED_TEXTDOMAIN ) . '</strong></a>';
  1491. return $links;
  1492. }
  1493. /**
  1494. * Initialize i18n support, load plugin's textdomain, to retrieve correct translations
  1495. */
  1496. function init_language_support() {
  1497. add_filter( 'locale', array( &$this, 'get_plugin_locale' ) ); // allow changing the plugin language
  1498. $language_directory = basename( dirname( WP_TABLE_RELOADED__FILE__ ) ) . '/languages';
  1499. load_plugin_textdomain( WP_TABLE_RELOADED_TEXTDOMAIN, false, $language_directory );
  1500. remove_filter( 'locale', array( &$this, 'get_plugin_locale' ) );
  1501. }
  1502. /**
  1503. * Retrieve the locale the plugin shall be shown in, applied as a filter in get_locale()
  1504. */
  1505. function get_plugin_locale( $locale ) {
  1506. if ( isset( $_POST['options']['plugin_language'] ) ) {
  1507. if ( 'auto' != $_POST['options']['plugin_language'] )
  1508. return $_POST['options']['plugin_language'];
  1509. else
  1510. return $locale;
  1511. }
  1512. $locale = ( !empty( $this->options['plugin_language'] ) && 'auto' != $this->options['plugin_language'] ) ? $this->options['plugin_language'] : $locale;
  1513. return $locale;
  1514. }
  1515. /**
  1516. * Enqueue plugin's JavaScript functions for the backend, by registering, translating (text strings and options) and printing the JS file
  1517. */
  1518. function add_manage_page_js() {
  1519. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '.dev' : '';
  1520. $jsfile = "admin/admin-script{$suffix}.js";
  1521. $js_url = plugins_url( $jsfile, WP_TABLE_RELOADED__FILE__ );
  1522. wp_enqueue_script( 'wp-table-reloaded-admin-js', $js_url, array( 'jquery', 'thickbox' ), $this->options['installed_version'], true );
  1523. wp_localize_script( 'wp-table-reloaded-admin-js', 'WP_Table_Reloaded_Admin', array(
  1524. 'str_UninstallCheckboxActivation' => __( 'Do you really want to activate this? You should only do that right before uninstallation!', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1525. 'str_DataManipulationLinkInsertURL' => __( 'URL of link to insert', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1526. 'str_DataManipulationLinkInsertText' => __( 'Text of link', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1527. 'str_DataManipulationLinkInsertExplain' => __( 'To insert the following HTML code for a link into a cell, just click the cell after closing this dialog.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1528. 'str_DataManipulationImageInsertThickbox' => __( 'To insert an image, click &quot;OK&quot; and then click into the cell into which you want to insert the image.', WP_TABLE_RELOADED_TEXTDOMAIN ) . "\n" . __( 'The Media Library will open, from which you can select the desired image or insert the image URL.', WP_TABLE_RELOADED_TEXTDOMAIN ) . "\n" . sprintf( __( 'Click the &quot;%s&quot; button to insert the image.', WP_TABLE_RELOADED_TEXTDOMAIN ), esc_js( __( 'Insert into Post', 'default' ) ) ),
  1529. 'str_DataManipulationAddColspan' => __( 'To combine cells within a row, click into the cell to the right of the cell that has the content the combined cells shall have.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1530. 'str_DataManipulationAddRowspan' => __( 'To combine cells within a column, click into the cell below the cell that has the content the combined cells shall have.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1531. 'str_BulkCopyTablesLink' => __( 'Do you want to copy the selected tables?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1532. 'str_BulkDeleteTablesLink' => __( 'The selected tables and all content will be erased. Do you really want to delete them?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1533. 'str_BulkImportwpTableTablesLink' => __( 'Do you really want to import the selected tables from the wp-Table plugin?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1534. 'str_CopyTableLink' => __( 'Do you want to copy this table?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1535. 'str_DeleteTableLink' => __( 'The complete table and all content will be erased. Do you really want to delete it?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1536. 'str_DeleteRowsConfirm' => __( 'Do you really want to delete the selected rows?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1537. 'str_DeleteColsConfirm' => __( 'Do you really want to delete the selected columns?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1538. 'str_DeleteRowsFailedNoSelection' => __( 'You have not selected any rows.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1539. 'str_DeleteColsFailedNoSelection' => __( 'You have not selected any columns.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1540. 'str_DeleteRowsFailedNotAll' => __( 'You can not delete all rows of the table at once!', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1541. 'str_DeleteColsFailedNotAll' => __( 'You can not delete all columns of the table at once!', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1542. 'str_UnHideRowsNoSelection' => __( 'You have not selected any rows.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1543. 'str_UnHideColsNoSelection' => __( 'You have not selected any columns.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1544. 'str_InsertRowsNoSelection' => __( 'You have not selected any rows.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1545. 'str_InsertColsNoSelection' => __( 'You have not selected any columns.', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1546. 'str_ImportwpTableLink' => __( 'Do you really want to import this table from the wp-Table plugin?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1547. 'str_UninstallPluginLink_1' => __( 'Do you really want to uninstall the plugin and delete ALL data?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1548. 'str_UninstallPluginLink_2' => __( 'Are you really sure?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1549. 'str_ChangeTableID' => __( 'Do you really want to change the ID of the table?', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1550. 'str_CFShortcodeMessage' => __( 'To show this Custom Data Field, use this shortcode:', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1551. 'str_TableShortcodeMessage' => __( 'To show this table, use this shortcode:', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1552. 'str_ImportDumpFile' => __( 'Warning: You will lose all current Tables and Settings! You should create a backup first. Be warned!', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1553. 'str_saveAlert' => __( 'You have made changes to the content of this table and not yet saved them.', WP_TABLE_RELOADED_TEXTDOMAIN ) . ' ' . sprintf( __( 'You should first click &quot;%s&quot; or they will be lost if you navigate away from this page.', WP_TABLE_RELOADED_TEXTDOMAIN ), __( 'Update Changes', WP_TABLE_RELOADED_TEXTDOMAIN ) ),
  1554. 'option_show_exit_warning' => $this->options['show_exit_warning'],
  1555. 'option_growing_textareas' => $this->options['growing_textareas'],
  1556. 'option_add_target_blank_to_links' => $this->options['add_target_blank_to_links'],
  1557. 'option_tablesorter_enabled' => $this->options['enable_tablesorter'],
  1558. 'option_datatables_active' => $this->options['enable_tablesorter'] && ( 'datatables' == $this->options['tablesorter_script'] || 'datatables-tabletools' == $this->options['tablesorter_script'] ),
  1559. 'option_tabletools_active' => $this->options['enable_tablesorter'] && ( 'datatables-tabletools' == $this->options['tablesorter_script'] ),
  1560. 'l10n_print_after' => 'try{convertEntities(WP_Table_Reloaded_Admin);}catch(e){};'
  1561. ) );
  1562. }
  1563. /**
  1564. * Enqueue plugin's CSS Stylesheet for the backend
  1565. */
  1566. function add_manage_page_css() {
  1567. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '.dev' : '';
  1568. $cssfile = "admin/admin-style{$suffix}.css";
  1569. wp_enqueue_style( 'wp-table-reloaded-admin-css', plugins_url( $cssfile, WP_TABLE_RELOADED__FILE__ ), array(), $this->options['installed_version'] );
  1570. // RTL languages support
  1571. if ( is_rtl() ) {
  1572. $cssfile = "admin/admin-style.rtl.css";
  1573. wp_enqueue_style( 'wp-table-reloaded-admin-rtl-css', plugins_url( $cssfile, WP_TABLE_RELOADED__FILE__ ), array(), $this->options['installed_version'] );
  1574. }
  1575. }
  1576. /**
  1577. * Add button "Table" to HTML editor on "Edit Post" and "Edit Pages" pages of WordPress, but only if there is at least one table available
  1578. */
  1579. function add_editor_button() {
  1580. if ( 0 == count( $this->tables ) )
  1581. return;
  1582. $this->init_language_support();
  1583. add_thickbox(); // we need thickbox to show the list
  1584. $params = array(
  1585. 'page' => $this->page_slug,
  1586. 'action' => 'ajax_list'
  1587. );
  1588. $ajax_url = add_query_arg( $params, admin_url( $this->page_url ) );
  1589. $ajax_url = wp_nonce_url( $ajax_url, $this->get_nonce( $params['action'], false ) );
  1590. $ajax_url = esc_url( $ajax_url );
  1591. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '.dev' : '';
  1592. $jsfile = "admin/admin-editor-buttons-script{$suffix}.js";
  1593. // HTML editor integration
  1594. wp_register_script( 'wp-table-reloaded-editor-button-js', plugins_url( $jsfile, WP_TABLE_RELOADED__FILE__ ), array( 'jquery', 'thickbox', 'media-upload', 'quicktags' ), $this->options['installed_version'], true );
  1595. wp_localize_script( 'wp-table-reloaded-editor-button-js', 'WP_Table_Reloaded_Editor_Button', array(
  1596. 'str_EditorButtonCaption' => __( 'Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1597. 'str_EditorButtonTitle' => __( 'Insert a Table', WP_TABLE_RELOADED_TEXTDOMAIN ),
  1598. 'str_EditorButtonAjaxURL' => $ajax_url,
  1599. 'l10n_print_after' => 'try{convertEntities(WP_Table_Reloaded_Editor_Button);}catch(e){};'
  1600. ) );
  1601. // TinyMCE integration
  1602. if ( user_can_richedit() ) {
  1603. add_filter( 'mce_external_plugins', array( &$this, 'add_tinymce_plugin' ) );
  1604. add_filter( 'mce_buttons', array( &$this, 'add_tinymce_button' ) );
  1605. }
  1606. add_action( 'admin_print_footer_scripts', array( &$this, '_print_editor_button' ), 100 );
  1607. }
  1608. function _print_editor_button() {
  1609. wp_print_scripts( 'wp-table-reloaded-editor-button-js' );
  1610. }
  1611. /**
  1612. * Add "Table" button and separator to the TinyMCE toolbar
  1613. *
  1614. * @param array $buttons Current set of buttons in the TinyMCE toolbar
  1615. * @return array Current set of buttons in the TinyMCE toolbar, including "Table" button
  1616. */
  1617. function add_tinymce_button( $buttons ) {
  1618. $buttons[] = '|';
  1619. $buttons[] = 'table';
  1620. return $buttons;
  1621. }
  1622. /**
  1623. * Register "Table" button plugin to TinyMCE
  1624. *
  1625. * @param array $plugins Current set of registered TinyMCE plugins
  1626. * @return array Current set of registered TinyMCE plugins, including "Table" button plugin
  1627. */
  1628. function add_tinymce_plugin( $plugins ) {
  1629. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '.dev' : '';
  1630. $jsfile = "admin/admin-tinymce-buttons-script{$suffix}.js";
  1631. $plugins['table'] = plugins_url( $jsfile, WP_TABLE_RELOADED__FILE__ );
  1632. return $plugins;
  1633. }
  1634. /**
  1635. * Load DataTables library and print JavaScript commands for sorting and AJAX functions on the "List Tables" screen
  1636. */
  1637. function output_tablesorter_js() {
  1638. $datatables = '';
  1639. // filter to false, to prevent using DataTables in the List of Tables (seems to cause problems with IE 7)
  1640. $use_datatables = $this->options['use_datatables_on_table_list'];
  1641. $use_datatables = apply_filters( 'wp_table_reloaded_admin_use_datatables', $use_datatables );
  1642. // sorting doesn't make sense, if there is only one table in the list
  1643. if ( $use_datatables && 1 < count( $this->tables ) ) {
  1644. $datatables_url = plugins_url( 'js/jquery.datatables.min.js', WP_TABLE_RELOADED__FILE__ );
  1645. wp_register_script( 'wp-table-reloaded-datatables-js', $datatables_url, array( 'wp-table-reloaded-admin-js' ), $this->options['installed_version'] );
  1646. wp_print_scripts( 'wp-table-reloaded-datatables-js' );
  1647. $sProcessing = __( 'Please wait...', WP_TABLE_RELOADED_TEXTDOMAIN );
  1648. $sLengthMenu = __( 'Show _MENU_ Tables', WP_TABLE_RELOADED_TEXTDOMAIN );
  1649. $sLengthMenu_All = __( 'All', WP_TABLE_RELOADED_TEXTDOMAIN );
  1650. $sZeroRecords = __( 'No tables were found.', WP_TABLE_RELOADED_TEXTDOMAIN );
  1651. $sInfo = __( '_START_ to _END_ of _TOTAL_ Tables', WP_TABLE_RELOADED_TEXTDOMAIN );
  1652. $sInfoFiltered = __( '(filtered from _MAX_ Tables)', WP_TABLE_RELOADED_TEXTDOMAIN );
  1653. $sSearch = __( 'Filter:', WP_TABLE_RELOADED_TEXTDOMAIN );
  1654. $sFirst = __( 'First', WP_TABLE_RELOADED_TEXTDOMAIN );
  1655. $sPrevious = __( 'Back', WP_TABLE_RELOADED_TEXTDOMAIN );
  1656. $sNext = __( 'Next', WP_TABLE_RELOADED_TEXTDOMAIN );
  1657. $sLast = __( 'Last', WP_TABLE_RELOADED_TEXTDOMAIN );
  1658. $pagination = '';
  1659. if ( 11 > count( $this->tables ) )
  1660. $pagination = '"bPaginate": false, "bLengthChange": false,';
  1661. $datatables = <<<DATATABLES
  1662. \nvar tablelist = $('#wp-table-reloaded-list').dataTable({
  1663. "bSortClasses": false,
  1664. {$pagination}
  1665. "aLengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "{$sLengthMenu_All}"]],
  1666. "aaSorting": [],
  1667. "bProcessing": true,
  1668. "sPaginationType": "full_numbers",
  1669. "asStripClasses": ['alternate',''],
  1670. "aoColumns": [
  1671. { "sWidth": "24px", "bSortable": false, "bSearchable": false },
  1672. { "sType": "numeric" },
  1673. { "bVisible": false, "bSearchable": true, "sType": "string" },
  1674. { "bSearchable": false, "iDataSort": 2 },
  1675. { "sType": "string" },
  1676. { "bSortable": false }
  1677. ],
  1678. "oLanguage": {
  1679. "sProcessing": "{$sProcessing}",
  1680. "sLengthMenu": "{$sLengthMenu}",
  1681. "sZeroRecords": "{$sZeroRecords}",
  1682. "sInfo": "{$sInfo}",
  1683. "sInfoFiltered": "{$sInfoFiltered}",
  1684. "sSearch": "{$sSearch}",
  1685. "oPaginate": {
  1686. "sFirst": "{$sFirst}",
  1687. "sPrevious": "{$sPrevious}",
  1688. "sNext": "{$sNext}",
  1689. "sLast": "{$sLast}"
  1690. }
  1691. }
  1692. })
  1693. .find('.sorting').append('&nbsp;<span>&nbsp;&nbsp;&nbsp;</span>');\n
  1694. DATATABLES;
  1695. }
  1696. $datatables = apply_filters( 'wp_table_reloaded_admin_datatables_js', $datatables );
  1697. echo <<<JSSCRIPT
  1698. <script type="text/javascript">
  1699. /* <![CDATA[ */
  1700. jQuery(document).ready(function($){
  1701. {$datatables}
  1702. });
  1703. /* ]]> */
  1704. </script>
  1705. JSSCRIPT;
  1706. }
  1707. } // class WP_Table_Reloaded_Controller_Admin
  1708. ?>