PageRenderTime 64ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/log-deprecated-notices/log-deprecated-notices.php

https://gitlab.com/f3z0/tc-site
PHP | 839 lines | 575 code | 63 blank | 201 comment | 69 complexity | 38c12a973c916f8ab8a934247979212c MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, AGPL-1.0, Apache-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * @package Deprecated_Log
  4. */
  5. /*
  6. * Plugin Name: Log Deprecated Notices
  7. * Plugin URI: http://wordpress.org/extend/plugins/log-deprecated-notices/
  8. * Description: Logs the usage of deprecated files, functions, hooks, and function arguments, offers the alternative if available, and identifies where the deprecated functionality is being used. WP_DEBUG not required (but its general use is strongly recommended).
  9. * Version: 0.2
  10. * Author: Andrew Nacin
  11. * Author URI: http://nacin.com/
  12. * License: GPLv2 or later
  13. */
  14. if ( ! class_exists( 'Deprecated_Log' ) ) :
  15. /**
  16. * Base class.
  17. *
  18. * @package Deprecated_Log
  19. *
  20. * @todo Plugin ID. Also, notice on plugins page next to said plugin.
  21. * @todo Support closures as hook callbacks.
  22. */
  23. class Deprecated_Log {
  24. /**
  25. * Instance.
  26. *
  27. * @var object
  28. */
  29. static $instance;
  30. /**
  31. * DB version.
  32. *
  33. * @var int
  34. */
  35. const db_version = 4;
  36. /**
  37. * Options.
  38. *
  39. * @var array
  40. */
  41. var $options = array();
  42. /**
  43. * Option name in DB.
  44. *
  45. * @var string
  46. */
  47. const option_name = 'log_deprecated_notices';
  48. /**
  49. * Custom post type.
  50. *
  51. * @var string
  52. */
  53. const pt = 'deprecated_log';
  54. /**
  55. * Logging queue, to be inserted on shutdown.
  56. *
  57. * @var array
  58. */
  59. var $queued_posts = array();
  60. /**
  61. * Constructor. Adds hooks.
  62. */
  63. function Deprecated_Log() {
  64. self::$instance = $this;
  65. // Bail without 3.0.
  66. if ( ! function_exists( '__return_false' ) )
  67. return;
  68. // Registers the uninstall hook.
  69. register_activation_hook( __FILE__, array( &$this, 'on_activation' ) );
  70. // Registers post type.
  71. add_action( 'init', array( &$this, 'action_init' ) );
  72. // Log on shutdown.
  73. add_action( 'shutdown', array( &$this, 'shutdown' ) );
  74. // For testing, so the queries are picked up by the Debug Bar:
  75. // add_action( 'admin_footer', array( &$this, 'shutdown' ), 999 );
  76. // Silence E_NOTICE for deprecated usage.
  77. if ( WP_DEBUG ) {
  78. foreach ( array( 'deprecated_function', 'deprecated_file', 'deprecated_argument', 'doing_it_wrong', 'deprecated_hook' ) as $item )
  79. add_action( "{$item}_trigger_error", '__return_false' );
  80. }
  81. // Log deprecated notices.
  82. add_action( 'deprecated_function_run', array( &$this, 'log_function' ), 10, 3 );
  83. add_action( 'deprecated_file_included', array( &$this, 'log_file' ), 10, 4 );
  84. add_action( 'deprecated_argument_run', array( &$this, 'log_argument' ), 10, 4 );
  85. add_action( 'doing_it_wrong_run', array( &$this, 'log_wrong' ), 10, 3 );
  86. add_action( 'deprecated_hook_used', array( &$this, 'log_hook' ), 10, 4 );
  87. if ( ! is_admin() )
  88. return;
  89. $this->options = get_option( self::option_name );
  90. // Textdomain and upgrade routine.
  91. add_action( 'admin_init', array( &$this, 'action_admin_init' ) );
  92. // Basic CSS.
  93. add_action( 'admin_print_styles', array( &$this, 'action_admin_print_styles' ), 20 );
  94. // Column handling.
  95. add_action( 'manage_posts_custom_column', array( &$this, 'action_manage_posts_custom_column' ), 10, 2 );
  96. // Column headers.
  97. add_filter( 'manage_' . self::pt . '_posts_columns', array( &$this, 'filter_manage_post_type_posts_columns' ) );
  98. // Filters and 'Clear Log'.
  99. add_action( 'restrict_manage_posts', array( &$this, 'action_restrict_manage_posts' ) );
  100. // Modify Bulk Actions.
  101. add_action( 'bulk_actions-edit-' . self::pt, array( &$this, 'filter_bulk_actions' ) );
  102. // Basic JS (changes Bulk Actions options).
  103. add_action( 'admin_footer-edit.php', array( &$this, 'action_admin_footer_edit_php' ) );
  104. // Add/Edit permissions handling, make 'Clear Log' work, and other actions.
  105. foreach ( array( 'edit.php', 'post.php', 'post-new.php' ) as $item )
  106. add_action( "load-{$item}", array( &$this, 'action_load_edit_php' ) );
  107. // Handle special edit.php filters.
  108. add_filter( 'request', array( &$this, 'filter_request' ) );
  109. // Don't have the 'New Post' favorites action.
  110. add_filter( 'favorite_actions', array( &$this, 'favorite_actions' ) );
  111. }
  112. /**
  113. * Attached to admin_init. Loads the textdomain and the upgrade routine.
  114. */
  115. function action_admin_init() {
  116. if ( false === $this->options || ! isset( $this->options['db_version'] ) || $this->options['db_version'] < self::db_version ) {
  117. if ( ! is_array( $this->options ) )
  118. $this->options = array();
  119. $current_db_version = isset( $this->options['db_version'] ) ? $this->options['db_version'] : 0;
  120. $this->upgrade( $current_db_version );
  121. $this->options['db_version'] = self::db_version;
  122. update_option( self::option_name, $this->options );
  123. }
  124. load_plugin_textdomain('log-deprecated', null, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
  125. }
  126. /**
  127. * Upgrade routine.
  128. */
  129. function upgrade( $current_db_version ) {
  130. global $wpdb;
  131. // Change post type name and the meta key prefix, also set up a new option.
  132. if ( $current_db_version < 1 ) {
  133. $wpdb->update( $wpdb->posts, array( 'post_type' => 'deprecated_log' ), array( 'post_type' => 'nacin_deprecated' ) );
  134. $wpdb->update( $wpdb->postmeta, array( 'meta_key' => '_deprecated_log_meta' ), array( 'meta_key' => '_nacin_deprecated_meta' ) );
  135. $this->options['last_viewed'] = current_time( 'mysql' );
  136. }
  137. // We're gonna go with publish as the default now and also leverage Trash.
  138. if ( $current_db_version < 2 )
  139. $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'post_type' => 'deprecated_log' ) );
  140. // Split the original meta key into individual keys.
  141. if ( $current_db_version < 4 ) {
  142. $meta_rows = $wpdb->get_results( "SELECT * FROM $wpdb->postmeta WHERE meta_key = '_deprecated_log_meta'" );
  143. foreach ( $meta_rows as $meta_row ) {
  144. $meta = maybe_unserialize( $meta_row->meta_value );
  145. foreach ( array_keys( $meta ) as $key ) {
  146. add_post_meta( $meta_row->post_id, '_deprecated_log_' . $key, $meta[ $key ], true );
  147. }
  148. }
  149. // Remove bad data caused by an undefined index.
  150. $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_deprecated_log_'" );
  151. }
  152. }
  153. /**
  154. * Attached to deprecated_function_run action.
  155. */
  156. function log_function( $function, $replacement, $version ) {
  157. $backtrace = debug_backtrace();
  158. $deprecated = $function . '()';
  159. $hook = null;
  160. $bt = 4;
  161. // Check if we're a hook callback.
  162. if ( ! isset( $backtrace[4]['file'] ) && 'call_user_func_array' == $backtrace[5]['function'] ) {
  163. $hook = $backtrace[6]['args'][0];
  164. $bt = 6;
  165. }
  166. $in_file = $this->strip_abspath( $backtrace[ $bt ]['file'] );
  167. $on_line = $backtrace[ $bt ]['line'];
  168. $this->log( 'function', compact( 'deprecated', 'replacement', 'version', 'hook', 'in_file', 'on_line' ) );
  169. }
  170. /**
  171. * Attached to deprecated_hook_used action.
  172. */
  173. function log_hook( $hook, $replacement, $version, $message ) {
  174. global $wp_filter;
  175. $backtrace = debug_backtrace();
  176. /*
  177. echo '<pre>';
  178. var_dump( $GLOBALS['wp_filter'][ $hook ] );
  179. $this->queued_posts = array();
  180. var_dump( $backtrace );
  181. echo '</pre>';
  182. //*/
  183. $callbacks_attached = array();
  184. foreach ( $wp_filter[ $hook ] as $priority => $callbacks ) {
  185. foreach ( $callbacks as $callback ) {
  186. $callbacks_attached[] = $this->callback_to_string( $callback['function'] );
  187. }
  188. }
  189. // For actions fired within a function.
  190. $in_file = $this->strip_abspath( $backtrace[3]['file'] );
  191. $on_line = $backtrace[3]['line'] + 1; // _deprecated_file() is one line before do_action()
  192. $deprecated = $hook;
  193. foreach ( $callbacks_attached as $callback )
  194. $this->log( 'hook', compact( 'deprecated', 'replacement', 'version', 'in_file', 'on_line', 'callback' ) );
  195. }
  196. /**
  197. * Returns a string representation of a callback.
  198. */
  199. function callback_to_string( $callback ) {
  200. if ( is_array( $callback ) ) {
  201. if ( is_object( $callback[0] ) )
  202. return get_class( $callback[0] ) . '::' . $callback[1];
  203. else
  204. return $callback[0] . '::' . $callback[1];
  205. }
  206. return $callback;
  207. }
  208. /**
  209. * Attached to doing_it_wrong_run action.
  210. *
  211. * @todo Support hook callbacks, I guess.
  212. */
  213. function log_wrong( $function, $message, $version ) {
  214. $backtrace = debug_backtrace();
  215. $deprecated = $function . '()';
  216. $in_file = $this->strip_abspath( $backtrace[ 4 ]['file'] );
  217. $on_line = $backtrace[ 4 ]['line'];
  218. $this->log( 'wrong', compact( 'deprecated', 'message', 'version', 'in_file', 'on_line' ) );
  219. }
  220. /**
  221. * Attached to deprecated_argument_run action.
  222. */
  223. function log_argument( $function, $message, $version ) {
  224. $backtrace = debug_backtrace();
  225. $deprecated = $function . '()';
  226. $menu = $in_file = $on_line = null;
  227. // @todo [core] Introduce _deprecated_message() or something.
  228. switch ( $function ) {
  229. case 'options.php' :
  230. $deprecated = __( 'Unregistered Setting', 'log-deprecated' );
  231. $this->log( 'functionality', compact( 'deprecated', 'message', 'version' ) );
  232. return;
  233. case 'has_cap' :
  234. if ( 0 === strpos( $backtrace[7]['function'], 'add_' ) && '_page' == substr( $backtrace[7]['function'], -5 ) ) {
  235. $bt = 7;
  236. if ( 0 === strpos( $backtrace[8]['function'], 'add_' ) && '_page' == substr( $backtrace[8]['function'], -5 ) )
  237. $bt = 8;
  238. $in_file = $this->strip_abspath( $backtrace[ $bt ]['file'] );
  239. $on_line = $backtrace[ $bt ]['line'];
  240. $deprecated = $backtrace[ $bt ]['function'] . '()';
  241. } elseif ( '_wp_menu_output' == $backtrace[7]['function'] ) {
  242. $deprecated = 'current_user_can()';
  243. $menu = true;
  244. } else {
  245. $in_file = $this->strip_abspath( $backtrace[6]['file'] );
  246. $on_line = $backtrace[6]['line'];
  247. $deprecated = 'current_user_can()';
  248. }
  249. break;
  250. case 'get_plugin_data' :
  251. $in_file = $this->strip_abspath( $backtrace[4]['args'][0] );
  252. break;
  253. case 'define()' :
  254. case 'define' :
  255. if ( 'ms_subdomain_constants' == $backtrace[4]['function'] ) {
  256. $deprecated = 'VHOST';
  257. $this->log( 'constant', compact( 'deprecated', 'message', 'menu', 'version' ) );
  258. return;
  259. }
  260. // Fall through.
  261. default :
  262. $in_file = $this->strip_abspath( $backtrace[4]['file'] );
  263. $on_line = $backtrace[4]['line'];
  264. break;
  265. }
  266. $this->log( 'argument', compact( 'deprecated', 'message', 'menu', 'version', 'in_file', 'on_line' ) );
  267. }
  268. /**
  269. * Attached to deprecated_file_included action.
  270. */
  271. function log_file( $file, $replacement, $version, $message ) {
  272. $backtrace = debug_backtrace();
  273. $deprecated = $this->strip_abspath( $backtrace[3]['file'] );
  274. $in_file = $this->strip_abspath( $backtrace[4]['file'] );
  275. $on_line = $backtrace[4]['line'];
  276. $this->log( 'file', compact( 'deprecated', 'replacement', 'message', 'version', 'in_file', 'on_line' ) );
  277. }
  278. /**
  279. * Strip ABSPATH from an absolute filepath. Also, Windows is lame.
  280. */
  281. function strip_abspath( $path ) {
  282. return ltrim( str_replace( array( untrailingslashit( ABSPATH ), '\\' ), array( '', '/' ), $path ), '/' );
  283. }
  284. /**
  285. * Used to log deprecated usage.
  286. *
  287. * @todo Logging what I end up displaying is probably a bad idea.
  288. */
  289. function log( $type, $args ) {
  290. global $wpdb;
  291. extract( $args, EXTR_SKIP );
  292. switch ( $type ) {
  293. case 'functionality' :
  294. $deprecated = sprintf( __( 'Functionality: %s', 'log-deprecated' ), $deprecated );
  295. break;
  296. case 'constant' :
  297. $deprecated = sprintf( __( 'Constant: %s', 'log-deprecated' ), $deprecated );
  298. break;
  299. case 'function' :
  300. $deprecated = sprintf( __( 'Function: %s', 'log-deprecated' ), $deprecated );
  301. break;
  302. case 'file' :
  303. $deprecated = sprintf( __( 'File: %s', 'log-deprecated' ), $deprecated );
  304. break;
  305. case 'argument' :
  306. $deprecated = sprintf( __( 'Argument in %s', 'log-deprecated' ), $deprecated );
  307. break;
  308. case 'wrong' :
  309. $deprecated = sprintf( __( 'Incorrect Use of %s', 'log-deprecated' ), $deprecated );
  310. break;
  311. case 'hook' :
  312. $deprecated = sprintf( __( 'Hook: %s', 'log-deprecated' ), $deprecated );
  313. break;
  314. }
  315. $content = '';
  316. if ( ! empty( $replacement ) )
  317. // translators: %s is name of function.
  318. $content = sprintf( __( 'Use %s instead.', 'log-deprecated' ), $replacement );
  319. if ( ! empty( $message ) )
  320. $content .= ( strlen( $content ) ? ' ' : '' ) . (string) $message;
  321. if ( empty( $content ) )
  322. $content = __( 'No alternative available.', 'log-deprecated' );
  323. if ( 'wrong' == $type )
  324. $content .= "\n" . sprintf( __( 'This message was added in version %s.', 'log-deprecated' ), $version );
  325. else
  326. $content .= "\n" . sprintf( __( 'Deprecated in version %s.', 'log-deprecated' ), $version );
  327. if ( 'hook' == $type ) {
  328. $excerpt = sprintf( __( 'The callback %3$s() is attached to this hook, which is fired in %1$s on line %2$d.' ), $in_file, $on_line, $callback );
  329. } elseif ( ! empty( $hook ) ) {
  330. $excerpt = sprintf( __( 'Attached to the %1$s hook, fired in %2$s on line %3$d.', 'log-deprecated' ), $hook, $in_file, $on_line );
  331. } elseif ( ! empty( $menu ) ) {
  332. $excerpt = __( 'An admin menu page is using user levels instead of capabilities. There is likely a related log item with specifics.', 'log-deprecated' );
  333. } elseif ( ! empty( $on_line ) ) {
  334. $excerpt = sprintf( __( 'Used in %1$s on line %2$d.', 'log-deprecated' ), $in_file, $on_line );
  335. } elseif ( ! empty( $in_file ) ) {
  336. // translators: %s is file name.
  337. $excerpt = sprintf( __( 'Used in %s.', 'log-deprecated' ), $in_file );
  338. } else {
  339. $excerpt = '';
  340. }
  341. $post_name = md5( $type . implode( $args ) );
  342. $meta_pairs = array(
  343. '_deprecated_log_meta' => array_merge( array( 'type' => $type ), $args ),
  344. '_deprecated_log_type' => $type,
  345. );
  346. foreach ( array_keys( $args ) as $meta_key ) {
  347. $meta_pairs[ '_deprecated_log_' . $meta_key ] = $args[ $meta_key ];
  348. }
  349. $post_data = array(
  350. 'post_date' => current_time( 'mysql' ),
  351. 'post_excerpt' => $excerpt,
  352. 'post_type' => self::pt,
  353. 'post_status' => 'publish',
  354. 'post_title' => $deprecated,
  355. 'post_content' => $content . "\n<!--more-->\n" . $excerpt, // searches
  356. 'post_name' => $post_name,
  357. );
  358. $this->queued_post( $post_name, $post_data, $meta_pairs );
  359. }
  360. /**
  361. * Queues a post for final submission to the database.
  362. *
  363. * @param string $post_name
  364. * @param array $post_data
  365. * @param array $meta_pairs
  366. */
  367. function queued_post( $post_name, $post_data, $meta_pairs ) {
  368. if ( isset( $this->queued_posts[ $post_name ] ) ) {
  369. $this->queued_posts[ $post_name ]->count++;
  370. } else {
  371. $this->queued_posts[ $post_name ] = (object) array(
  372. 'post' => $post_data,
  373. 'count' => 1,
  374. 'meta' => $meta_pairs,
  375. );
  376. }
  377. }
  378. /**
  379. * Logs all queued posts and counts on shutdown.
  380. */
  381. function shutdown() {
  382. global $wpdb;
  383. $existing = (array) $wpdb->get_results( $wpdb->prepare( "SELECT post_name, ID, comment_count FROM $wpdb->posts WHERE post_type = %s", self::pt ), OBJECT_K );
  384. foreach ( $this->queued_posts as $post_name => $queued_post ) {
  385. if ( isset( $existing[ $post_name ] ) ) {
  386. $new_count = $existing[ $post_name ]->comment_count + $queued_post->count;
  387. $wpdb->update(
  388. $wpdb->posts,
  389. array( 'comment_count' => $new_count, 'post_date' => current_time( 'mysql' ) ),
  390. array( 'ID' => $existing[ $post_name ]->ID )
  391. );
  392. } else {
  393. $post_id = wp_insert_post( $queued_post->post );
  394. foreach ( $queued_post->meta as $meta_key => $meta_value )
  395. update_post_meta( $post_id, $meta_key, $meta_value );
  396. $wpdb->update( $wpdb->posts, array( 'comment_count' => 1 ), array( 'ID' => $post_id ) );
  397. }
  398. }
  399. }
  400. /**
  401. * Attached to manage_posts_custom_column action.
  402. *
  403. * @todo [core] Custom post types should be able to disable/enable trash or change
  404. * the length of EMPTY_TRASH_DAYS. Thinking empty_trash_days() and is_trash_enabled()
  405. * based on the constant originally, and each can be drilled down by post type.
  406. */
  407. function action_manage_posts_custom_column( $col, $post_id ) {
  408. global $wp_list_table;
  409. switch ( $col ) {
  410. case 'deprecated_title' :
  411. $post = get_post( $post_id );
  412. $post_type_object = get_post_type_object( $post->post_type );
  413. echo '<strong>' . esc_html( $post->post_title ) . '</strong>';
  414. echo '<br/>' . esc_html( $post->post_excerpt );
  415. echo '<div class="row-actions">';
  416. if ( EMPTY_TRASH_DAYS && $wp_list_table->is_trash )
  417. echo "<span class='untrash'><a title='" . esc_attr__( 'Unmute', 'log-deprecated' ) . "' href='" . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post_id ) ), 'untrash-' . $post->post_type . '_' . $post_id ) . "'>" . __( 'Unmute', 'log-deprecated' ) . '</a></span> | ';
  418. elseif ( EMPTY_TRASH_DAYS )
  419. echo "<span class='mute'><a class='submitdelete' title='" . esc_attr__( 'Mute', 'log-deprecated' ) . "' href='" . get_delete_post_link($post->ID) . "'>" . __( 'Mute', 'log-deprecated' ) . '</a></span> | ';
  420. echo '<span class="delete"><a class="submitdelete" title="' . esc_attr__( 'Delete', 'log-deprecated' ) . '" href="' . get_delete_post_link( $post_id, '', true ) . '">' . __( 'Delete', 'log-deprecated' ) . '</a></span></div>';
  421. break;
  422. case 'deprecated_count' :
  423. $post = get_post( $post_id );
  424. $count = $post->comment_count ? $post->comment_count : 1; // Caching. Don't want 0
  425. echo number_format_i18n( $count );
  426. break;
  427. case 'deprecated_modified' :
  428. echo get_the_date( __('Y/m/d g:i:s A', 'log-deprecated' ) );
  429. break;
  430. case 'deprecated_version' :
  431. $meta = get_post_meta( $post_id, '_deprecated_log_meta', true );
  432. echo $meta['version'];
  433. break;
  434. case 'deprecated_alternative':
  435. $post = get_post( $post_id );
  436. echo nl2br( preg_replace( '/<!--more(.*?)?-->(.*)/s', '', $post->post_content ) );
  437. break;
  438. }
  439. }
  440. /**
  441. * Attached to manage_{post_type}_posts_columns filter.
  442. *
  443. * @todo Is a separate version column desirable?
  444. */
  445. function filter_manage_post_type_posts_columns( $cols ) {
  446. $cols = array(
  447. 'cb' => '<input type="checkbox" />',
  448. 'deprecated_title' => __( 'Deprecated Call', 'log-deprecated' ),
  449. 'deprecated_version' => __( 'Version', 'log-deprecated' ),
  450. 'deprecated_alternative' => __( 'Alternative', 'log-deprecated' ),
  451. 'deprecated_count' => __( 'Count', 'log-deprecated' ),
  452. 'deprecated_modified' => __( 'Last Used', 'log-deprecated' ),
  453. );
  454. unset( $cols['deprecated_version'] ); // We're not using it, but get it translated.
  455. return $cols;
  456. }
  457. /**
  458. * Prints basic CSS.
  459. *
  460. * Hides Add New button, sets some column widths.
  461. */
  462. function action_admin_print_styles() {
  463. global $current_screen;
  464. if ( 'edit-' . self::pt != $current_screen->id )
  465. return;
  466. ?>
  467. <style type="text/css">
  468. .add-new-h2, .view-switch, body.no-js .tablenav select[name^=action], body.no-js #doaction, body.no-js #doaction2 { display: none }
  469. .widefat .column-deprecated_modified, .widefat .column-deprecated_version { width: 10%; }
  470. .widefat .column-deprecated_count { width: 10%; text-align: right }
  471. .widefat .column-deprecated_cb { padding: 0; width: 2.2em }
  472. </style>
  473. <?php
  474. }
  475. /**
  476. * Basic JS -- changes Bulk Action options.
  477. */
  478. function action_admin_footer_edit_php() {
  479. global $current_screen;
  480. if ( 'edit-' . self::pt != $current_screen->id )
  481. return;
  482. ?>
  483. <script type="text/javascript">
  484. //<![CDATA[
  485. jQuery(document).ready( function($) {
  486. var s = $('div.actions select[name^=action]');
  487. s.find('option[value=delete]').remove();
  488. s.find('option[value=trash]').text('<?php echo addslashes( __( 'Mute', 'log-deprecated' ) ); ?>');
  489. s.find('option[value=untrash]').text('<?php echo addslashes( __( 'Unmute', 'log-deprecated' ) ); ?>');
  490. s.append('<option value="delete"><?php echo addslashes( __( 'Delete', 'log-deprecated' ) ); ?></option>');
  491. });
  492. //]]>
  493. </script>
  494. <?php
  495. }
  496. /**
  497. * Modifies bulk actions.
  498. *
  499. * We're allowed to use this filter in 3.1, but only to remove.
  500. */
  501. function filter_bulk_actions( $actions ) {
  502. unset( $actions['edit'] );
  503. return $actions;
  504. }
  505. /**
  506. * Modifies 'Empty Trash' to 'Clear Log'.
  507. */
  508. function filter_gettext_bulk_actions( $translation, $text ) {
  509. switch ( $text ) {
  510. case 'Empty Trash' :
  511. global $wp_list_table, $is_trash;
  512. if ( isset( $wp_list_table ) )
  513. $wp_list_table->is_trash = $this->_is_trash;
  514. else
  515. $is_trash = $this->_is_trash;
  516. return __( 'Clear Log', 'log-deprecated' );
  517. case 'Move to Trash' :
  518. return __( 'Mute', 'log-deprecated' );
  519. case 'Restore' :
  520. return __( 'Unmute', 'log-deprecated' );
  521. case 'Delete Permanently' :
  522. return __( 'Delete', 'log-deprecated' );
  523. }
  524. return $translation;
  525. }
  526. /**
  527. * Post filters.
  528. *
  529. * Also, a cheap hack to show a 'Clear Log' button.
  530. * Somehow, there is not a decent hook anywhere on edit.php (but there is for edit-comments.php).
  531. */
  532. function action_restrict_manage_posts() {
  533. global $wpdb, $typenow, $wp_list_table, $is_trash;
  534. if ( self::pt != $typenow )
  535. return;
  536. if ( isset( $wp_list_table ) ) {
  537. $this->_is_trash = $wp_list_table->is_trash;
  538. $wp_list_table->is_trash = true;
  539. } else {
  540. $this->_is_trash = $is_trash;
  541. $is_trash = true;
  542. }
  543. add_filter( 'gettext', array( &$this, 'filter_gettext_bulk_actions' ), 10, 2 );
  544. $types = array(
  545. 'constant' => __( 'Constant', 'log-deprecated' ),
  546. 'function' => __( 'Function', 'log-deprecated' ),
  547. 'argument' => __( 'Argument', 'log-deprecated' ),
  548. 'functionality' => __( 'Functionality', 'log-deprecated' ),
  549. 'file' => __( 'File', 'log-deprecated' ),
  550. 'wrong' => __( 'Incorrect Usage', 'log-deprecated' ),
  551. 'hook' => __( 'Hook', 'log-deprecated' ),
  552. );
  553. $types_used = $wpdb->get_col( "SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = '_deprecated_log_type'" );
  554. if ( count( $types_used ) > 1 ) {
  555. echo '<select name="deprecated_type">';
  556. echo '<option value="">' . esc_html__( 'Show all types', 'log-deprecated' ) . '</option>';
  557. foreach ( $types_used as $type ) {
  558. $selected = ! empty( $_GET['deprecated_type'] ) ? selected( $_GET['deprecated_type'], $type, false ) : '';
  559. echo '<option' . $selected . ' value="' . esc_attr( $type ) . '">' . esc_html( $types[ $type ] ) . '</option>';
  560. }
  561. echo '</select>';
  562. }
  563. $versions = $wpdb->get_col( "SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = '_deprecated_log_version'" );
  564. if ( count( $versions ) > 1 ) {
  565. echo '<select name="deprecated_version">';
  566. echo '<option value="">' . esc_html__( 'Since', 'log-deprecated' ) . '</option>';
  567. usort( $versions, 'version_compare' );
  568. foreach ( array_reverse( $versions ) as $version ) {
  569. $selected = ! empty( $_GET['deprecated_version'] ) ? selected( $_GET['deprecated_version'], $version, false ) : '';
  570. echo '<option' . $selected . ' value="' . esc_attr( $version ) . '">' . esc_html( '&#8804; ' . $version ) . '</option>';
  571. }
  572. echo '</select> ';
  573. }
  574. return; // @disable
  575. $files = $wpdb->get_col( "SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = '_deprecated_log_in_file'" );
  576. if ( count( $files > 1 ) ) {
  577. echo '<select name="deprecated_file">';
  578. echo '<option value="">' . esc_html__( 'Show all files', 'log-deprecated' ) . '</option>';
  579. foreach ( array_filter( $files ) as $file ) {
  580. $selected = '';
  581. if ( ! empty( $_GET['deprecated_file'] ) )
  582. $selected = selected( stripslashes( $_GET['deprecated_file'] ), $file, false );
  583. echo '<option' . $selected . ' value="' . esc_attr( $file ) . '">' . esc_html( $file ) . '</option>';
  584. }
  585. echo '</select>';
  586. }
  587. }
  588. /**
  589. * Filters the request based on additional post filters.
  590. *
  591. * Adds posts_where and posts_join filters as necessary, if more than one meta key/value pair is queried.
  592. *
  593. * @todo use the new meta_query in 3.1.
  594. */
  595. function filter_request( $qv ) {
  596. if ( ! is_admin() )
  597. return;
  598. $pairs = array();
  599. if ( ! empty( $_GET['deprecated_file'] ) )
  600. $pairs[] = array( '_deprecated_log_in_file', stripslashes( $_GET['deprecated_file'] ), '=' );
  601. if ( ! empty( $_GET['deprecated_type'] ) )
  602. $pairs[] = array( '_deprecated_log_type', $_GET['deprecated_type'], '=' );
  603. if ( ! empty( $_GET['deprecated_version'] ) )
  604. $pairs[] = array( '_deprecated_log_version', ( $_GET['deprecated_version'] + 0 ), '<=' );
  605. if ( ! empty( $pairs ) ) {
  606. $pair = array_shift( $pairs );
  607. list( $qv['meta_key'], $qv['meta_value'], $qv['meta_compare'] ) = $pair;
  608. if ( ! empty( $pairs ) ) {
  609. add_filter( 'posts_where', array( &$this, 'filter_posts_where' ), 10, 2 );
  610. add_filter( 'posts_join', array( &$this, 'filter_posts_join' ), 10, 2 );
  611. $this->_additional_filters = $pairs;
  612. }
  613. }
  614. return $qv;
  615. }
  616. /**
  617. * Handles additional meta key/value filters in the WHERE clause.
  618. */
  619. function filter_posts_where( $where, $object ) {
  620. global $wpdb;
  621. foreach ( $this->_additional_filters as $pair )
  622. $where .= $wpdb->prepare(" AND postmeta{$pair[0]}.meta_key = %s AND postmeta{$pair[0]}.meta_value {$pair[2]} %s ", $pair[0], $pair[1] );
  623. return $where;
  624. }
  625. /**
  626. * Creates additional joins to handle additional meta key/value filters.
  627. */
  628. function filter_posts_join( $join, $object ) {
  629. global $wpdb;
  630. foreach ( $this->_additional_filters as $pair )
  631. $join .= " LEFT JOIN $wpdb->postmeta AS postmeta{$pair[0]} ON ($wpdb->posts.ID = postmeta{$pair[0]}.post_id) ";
  632. return $join;
  633. }
  634. /**
  635. * Changes status filter and updated strings.
  636. *
  637. * 'All' and 'Trash' becomes 'Log' and 'Muted'.
  638. * 'Item moved/deleted/restored' are modified as well.
  639. */
  640. function filter_ngettext( $translation, $single, $plural, $number ) {
  641. switch ( $single ) {
  642. case 'All <span class="count">(%s)</span>' :
  643. return _n( 'Log <span class="count">(%s)</span>', 'Log <span class="count">(%s)</span>', $number, 'log-deprecated' );
  644. case 'Trash <span class="count">(%s)</span>' :
  645. return _n( 'Muted <span class="count">(%s)</span>', 'Muted <span class="count">(%s)</span>', $number, 'log-deprecated' );
  646. case 'Item moved to the Trash.' :
  647. return _n( 'Entry muted.', 'Entries muted.', $number, 'log-deprecated' );
  648. case 'Item permanently deleted.' :
  649. return _n( 'Entry deleted.', 'Entries deleted.', $number, 'log-deprecated' );
  650. case 'Item restored from the Trash.' :
  651. return _n( 'Entry unmuted.', 'Entries unmuted.', $number, 'log-deprecated' );
  652. }
  653. return $translation;
  654. }
  655. /**
  656. * Cheap hacks when we're in the post type UI.
  657. *
  658. * First, it locks out post.php and post-new.php, since our permissions
  659. * don't cover that.
  660. *
  661. * Then it gets our 'Clear Log' button to work on the 'All' page, as it
  662. * uses the 'Empty Trash' functionality and requires a post status to work
  663. * off of, not the 'all' status. Don't try this at home, folks.
  664. *
  665. * We're using 'All' because we can't remove that, but by directly modifying
  666. * the show_in_admin_status_list property of the publish post status (also in
  667. * this function), we can hide that one.
  668. *
  669. * This function also sets the last_viewed option, for the unread menu bubble.
  670. *
  671. * This function adds a filter to _n() and _nx() to change the text of status links.
  672. *
  673. * @todo [core] Filter on $status_links in edit.php
  674. * @todo [core] Custom post stati should have granular properties per post type.
  675. */
  676. function action_load_edit_php() {
  677. $screen = get_current_screen();
  678. if ( self::pt == $screen->id && ( $screen->action == 'add' || $_GET['action'] == 'edit' ) )
  679. wp_die( __( 'Invalid post type.', 'log-deprecated' ) );
  680. if ( self::pt != $screen->post_type )
  681. return;
  682. if ( ( empty( $_GET['post_status'] ) || 'all' == $_GET['post_status'] )
  683. && ( isset( $_GET['delete_all'] ) || isset( $_GET['delete_all2'] ) )
  684. )
  685. $_GET['post_status'] = $_REQUEST['post_status'] = 'publish';
  686. $this->options['last_viewed'] = current_time('mysql');
  687. update_option( self::option_name, $this->options );
  688. global $wp_post_statuses;
  689. // You didn't see this.
  690. $wp_post_statuses['publish']->show_in_admin_status_list = false;
  691. foreach ( array( 'ngettext', 'ngettext_with_context' ) as $filter )
  692. add_filter( $filter, array( &$this, 'filter_ngettext' ), 10, 4 );
  693. add_filter( 'gettext', array( &$this, 'filter_gettext_bulk_actions' ), 10, 2 );
  694. }
  695. /**
  696. * Registers the custom post type.
  697. */
  698. function action_init() {
  699. global $typenow, $wpdb;
  700. $labels = array(
  701. 'name' => __( 'Deprecated Calls', 'log-deprecated' ),
  702. 'singular_name' => __( 'Deprecated Call', 'log-deprecated' ),
  703. // add_new, add_new_item, edit_item, new_item, view_item
  704. 'search_items' => __( 'Search Logs', 'log-deprecated' ),
  705. 'not_found' => __( 'Nothing in the log! Your plugins are oh so fine.', 'log-deprecated' ),
  706. 'not_found_in_trash' => __( 'Nothing muted.', 'log-deprecated' ),
  707. );
  708. if ( is_admin()
  709. && ( ! isset( $_REQUEST['post_type'] ) || self::pt != $_REQUEST['post_type'] )
  710. && $this->options && ! empty( $this->options['last_viewed'] ) )
  711. {
  712. $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = %s AND post_date > %s AND post_status = %s", self::pt, $this->options['last_viewed'], 'publish' ) );
  713. if ( $count )
  714. $labels['menu_name'] = sprintf( __( 'Deprecated Calls %s', 'log-deprecated' ), "<span class='update-plugins count-$count'><span class='update-count'>" . number_format_i18n( $count ) . '</span></span>' );
  715. }
  716. $args = array(
  717. 'labels' => $labels,
  718. 'show_in_menu' => 'tools.php',
  719. 'show_ui' => true,
  720. 'public' => false,
  721. 'capabilities' => array(
  722. 'edit_post' => 'activate_plugins',
  723. 'edit_posts' => 'activate_plugins',
  724. 'edit_others_posts' => 'activate_plugins',
  725. 'publish_posts' => 'do_not_allow',
  726. 'read_post' => 'activate_plugins',
  727. 'read_private_posts' => 'do_not_allow',
  728. 'delete_post' => 'activate_plugins',
  729. ),
  730. 'rewrite' => false,
  731. 'query_var' => false,
  732. );
  733. register_post_type( self::pt, $args );
  734. }
  735. /**
  736. * Runs on activation. Simply registers the uninstall routine.
  737. */
  738. function on_activation() {
  739. register_uninstall_hook( __FILE__, array( 'Deprecated_Log', 'on_uninstall' ) );
  740. }
  741. /**
  742. * Runs on uninstall. Removes all log data.
  743. */
  744. function on_uninstall() {
  745. global $wpdb;
  746. $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE post_type = %s", self::pt ) );
  747. $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key LIKE '\_deprecated\_log\_%'" );
  748. delete_option( self::option_name );
  749. }
  750. /**
  751. * Don't have the 'New Posts' favorite action.
  752. *
  753. * @since 0.2
  754. */
  755. function favorite_actions( $actions ) {
  756. if ( isset( $actions['post-new.php?post_type=deprecated_log'] ) )
  757. unset( $actions['post-new.php?post_type=deprecated_log'] );
  758. return $actions;
  759. }
  760. }
  761. /** Initialize. */
  762. new Deprecated_Log;
  763. endif;