WP Mail Logging - Version 1.2

Version Description

Download this release

Release Info

Developer No3x
Plugin Icon 128x128 WP Mail Logging
Version 1.2
Comparing to
See all releases

Code changes from version 1.1 to 1.2

.svnignore CHANGED
@@ -3,4 +3,5 @@ tests
3
  .svnignore
4
  .travis.yml
5
  phpunit.xml
6
- README.md
 
3
  .svnignore
4
  .travis.yml
5
  phpunit.xml
6
+ README.md
7
+ CONTRIBUTING.md
WPML_API_Example.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  /**
4
  * @author No3x
 
5
  * The Plugin provides mechanisms to extend the displayed data.
6
  * This class is not an API class. It is just an example.
7
  */
@@ -20,21 +21,21 @@ class WPML_API_Example {
20
 
21
  /**
22
  * Is called when List Table is gathering columns.
23
- *
24
  * @param array $columns Array of columns
25
  * @return array $columns Updated array of columns
26
  */
27
  public function add_column( $columns ) {
28
  return $columns = array_merge( $columns,
29
- array('test' => __( 'test', 'wml' ) )
30
- //,array('test2' => __( 'test2', 'wml' ) ) // ...
31
- );
32
  }
33
 
34
 
35
  /**
36
  * Is called when the List Table could not find the column. So we can hook in and modify the column.
37
- *
38
  * @param array $item A singular item (one full row's worth of data)
39
  * @param array $column_name The name/slug of the column to be processed
40
  * @return string Text or HTML to be placed inside the column <td>
2
 
3
  /**
4
  * @author No3x
5
+ * @since 1.0
6
  * The Plugin provides mechanisms to extend the displayed data.
7
  * This class is not an API class. It is just an example.
8
  */
21
 
22
  /**
23
  * Is called when List Table is gathering columns.
24
+ * @since 1.0
25
  * @param array $columns Array of columns
26
  * @return array $columns Updated array of columns
27
  */
28
  public function add_column( $columns ) {
29
  return $columns = array_merge( $columns,
30
+ array('test' => __( 'test', 'wml' ) )
31
+ //,array('test2' => __( 'test2', 'wml' ) ) // ...
32
+ );
33
  }
34
 
35
 
36
  /**
37
  * Is called when the List Table could not find the column. So we can hook in and modify the column.
38
+ * @since 1.0
39
  * @param array $item A singular item (one full row's worth of data)
40
  * @param array $column_name The name/slug of the column to be processed
41
  * @return string Text or HTML to be placed inside the column <td>
WPML_Email_Log_List.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if( !class_exists( 'WP_List_Table') ) {
4
+ require_once( plugin_dir_path( __FILE__ ) . 'inc/class-wp-list-table.php' );
5
+ }
6
+
7
+ /**
8
+ * @author No3x
9
+ * @since 1.0
10
+ * Renders the mails in a table list.
11
+ */
12
+ class Email_Logging_ListTable extends WP_List_Table {
13
+
14
+ /**
15
+ * Initializes the List Table
16
+ * @since 1.0
17
+ */
18
+ function __construct() {
19
+ global $status, $page, $hook_suffix;
20
+
21
+ parent::__construct( array(
22
+ 'singular' => __( 'Email', 'wml' ),//singular name of the listed records
23
+ 'plural' => __( 'Emails', 'wml' ),//plural name of the listed records
24
+ 'ajax' => false //does this table support ajax?
25
+ ) );
26
+ }
27
+
28
+ /**
29
+ * Is displayed if no item is available to render
30
+ * @since 1.0
31
+ * @see WP_List_Table::no_items()
32
+ */
33
+ function no_items() {
34
+ _e( 'No ' . $this->_args['singular'] . ' logged yet.' );
35
+ return;
36
+ }
37
+
38
+ /**
39
+ * Defines the available columns.
40
+ * @since 1.0
41
+ * @see WP_List_Table::get_columns()
42
+ */
43
+ function get_columns() {
44
+ $columns = array(
45
+ 'cb' => '<input type="checkbox" />',
46
+ 'mail_id' => __( 'ID', 'wml'),
47
+ 'timestamp' => __( 'Time', 'wml'),
48
+ 'receiver' => __( 'Receiver', 'wml'),
49
+ 'subject' => __( 'Subject', 'wml'),
50
+ 'message' => __( 'Message', 'wml'),
51
+ 'headers' => __( 'Headers', 'wml'),
52
+ 'attachments' => __( 'Attachments', 'wml'),
53
+ 'plugin_version'=> __( 'Plugin Version', 'wml')
54
+ );
55
+
56
+ // give a plugin the change to edit the columns
57
+ $columns = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS, $columns );
58
+
59
+ $reserved = array('_title', 'comment', 'media', 'name', 'title', 'username', 'blogname');
60
+
61
+ // show message for reserved column names
62
+ foreach ( $reserved as $reserved_key ) {
63
+ if( array_key_exists( $reserved_key, $columns ) ) {
64
+ echo "You should avoid $reserved_key as keyname since it is treated by WordPress specially: Your table would still work, but you won't be able to show/hide the columns. You can prefix your columns!";
65
+ break;
66
+ }
67
+ }
68
+
69
+ return $columns;
70
+ }
71
+
72
+ /**
73
+ * Define which columns are hidden
74
+ * @since 1.0
75
+ * @return Array
76
+ */
77
+ function get_hidden_columns() {
78
+ return array(
79
+ 'plugin_version',
80
+ 'mail_id'
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Prepares the items for rendering
86
+ * @since 1.0
87
+ * @see WP_List_Table::prepare_items()
88
+ */
89
+ function prepare_items() {
90
+ global $wpdb;
91
+ $tableName = WPML_Plugin::getTablename('mails');
92
+
93
+ $columns = $this->get_columns();
94
+ $hidden = $this->get_hidden_columns();
95
+ $sortable = $this->get_sortable_columns();
96
+ $this->_column_headers = array($columns, $hidden, $sortable);
97
+
98
+ $this->process_bulk_action();
99
+
100
+ $per_page = $this->get_items_per_page( 'per_page', 25 );
101
+ $current_page = $this->get_pagenum();
102
+ $total_items = $wpdb->get_var("SELECT COUNT(*) FROM `$tableName`");
103
+ $limit = $per_page*$current_page;
104
+ //TODO: make option for default order
105
+ $orderby_default = "mail_id";
106
+ $order_default = "desc";
107
+ $orderby = ( !empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : $orderby_default;
108
+ $order = ( !empty($_GET['order'] ) ) ? $_GET['order'] : $order_default;
109
+
110
+ $found_data = $wpdb->get_results("SELECT * FROM `$tableName` ORDER BY $orderby $order LIMIT $limit", ARRAY_A);
111
+
112
+ $dataset = array_slice( $found_data,( ( $current_page-1 ) * $per_page ), $per_page );
113
+
114
+ $this->set_pagination_args( array(
115
+ 'total_items' => $total_items, //WE have to calculate the total number of items
116
+ 'per_page' => $per_page //WE have to determine how many items to show on a page
117
+ ) );
118
+
119
+ $this->items = $dataset;
120
+ }
121
+
122
+ /**
123
+ * Renders the cell.
124
+ * Note: We can easily add filter for all columns if you want to / need to manipulate the content. (currently only additional column manipulation is supported)
125
+ * @since 1.0
126
+ * @param array $item
127
+ * @param string $column_name
128
+ * @return string The cell content
129
+ */
130
+ function column_default( $item, $column_name ) {
131
+ switch( $column_name ) {
132
+ case 'mail_id':
133
+ case 'timestamp':
134
+ case 'receiver':
135
+ case 'subject':
136
+ case 'message':
137
+ case 'headers':
138
+ case 'attachments':
139
+ case 'plugin_version':
140
+ return $item[ $column_name ];
141
+ default:
142
+ // if we don't know this column maybe a hook does - if no hook extracted data (string) out of the array we can avoid the output of 'Array()' (array)
143
+ return (is_array( $res = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, $item, $column_name ) ) ) ? "" : $res;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Defines available bulk actions.
149
+ * @since 1.0
150
+ * @see WP_List_Table::get_bulk_actions()
151
+ */
152
+ function get_bulk_actions() {
153
+ $actions = array(
154
+ 'delete' => 'Delete'
155
+ );
156
+ return $actions;
157
+ }
158
+
159
+ function process_bulk_action() {
160
+ global $wpdb;
161
+ $name = $this->_args['singular'];
162
+ $tableName = WPML_Plugin::getTablename('mails');
163
+
164
+ //Detect when a bulk action is being triggered...
165
+ if( 'delete' == $this->current_action() ) {
166
+ foreach($_REQUEST[$name] as $item_id) {
167
+ $wpdb->query("DELETE FROM `$tableName` WHERE mail_id = $item_id");
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Render the cb column
174
+ * @since 1.0
175
+ * @param object $item The current item
176
+ * @return string the rendered cb cell content
177
+ */
178
+ function column_cb($item) {
179
+ $name = $this->_args['singular'];
180
+ return sprintf(
181
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />', $name, $item['mail_id']
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Define the sortable columns
187
+ * @since 1.0
188
+ * @return Array
189
+ */
190
+ function get_sortable_columns() {
191
+ return array(
192
+ // column_name => array( 'display_name', true[asc] | false[desc] )
193
+ 'mail_id' => array('mail_id', false),
194
+ 'timestamp' => array('timestamp', true),
195
+ 'receiver' => array('receiver', true),
196
+ 'subject' => array('subject', true),
197
+ 'message' => array('message', true),
198
+ 'headers' => array('headers', true),
199
+ 'attachments' => array('attachments', true),
200
+ 'plugin_version' => array('plugin_version', true)
201
+ );
202
+ }
203
+ }
204
+
205
+ ?>
WPML_LifeCycle.php CHANGED
@@ -43,7 +43,7 @@ class WPML_LifeCycle extends WPML_InstallIndicator {
43
 
44
  public function uninstall() {
45
  $this->otherUninstall();
46
- if( get_option( $this->prefix('DeleteOnDeactivation') , false) == true ) {
47
  //TOOD: is multi site?
48
  $this->unInstallDatabaseTables();
49
  $this->deleteSavedOptions();
43
 
44
  public function uninstall() {
45
  $this->otherUninstall();
46
+ if( get_option( $this->prefix('DeleteOnDeactivation'), false) == 'true' ) {
47
  //TOOD: is multi site?
48
  $this->unInstallDatabaseTables();
49
  $this->deleteSavedOptions();
WPML_OptionsManager.php CHANGED
@@ -282,36 +282,18 @@ class WPML_OptionsManager {
282
 
283
 
284
  // List screen properties
285
- $variables = '<ul style="width:50%;float:left;"> <strong>Screen variables </strong>'
286
- . sprintf( '<li> Screen id : %s</li>', $screen_id )
287
- . sprintf( '<li> Screen base : %s</li>', $screen->base )
288
- . sprintf( '<li>Parent base : %s</li>', $screen->parent_base )
289
- . sprintf( '<li> Parent file : %s</li>', $screen->parent_file )
290
- . sprintf( '<li> Hook suffix : %s</li>', $hook_suffix )
291
- . '</ul>';
292
 
293
- // Append global $hook_suffix to the hook stems
294
- $hooks = array(
295
- "load-$hook_suffix",
296
- "admin_print_styles-$hook_suffix",
297
- "admin_print_scripts-$hook_suffix",
298
- "admin_head-$hook_suffix",
299
- "admin_footer-$hook_suffix"
300
- );
301
-
302
- // If add_meta_boxes or add_meta_boxes_{screen_id} is used, list these too
303
- if ( did_action( 'add_meta_boxes_' . $screen_id ) )
304
- $hooks[] = 'add_meta_boxes_' . $screen_id;
305
-
306
- if ( did_action( 'add_meta_boxes' ) )
307
- $hooks[] = 'add_meta_boxes';
308
 
309
- // Get List HTML for the hooks
310
- $hooks = '<ul style="width:50%;float:left;"> <strong>Hooks </strong> <li>';
311
-
 
312
 
313
- // Combine $variables list with $hooks list.
314
- $help_content = $variables . $hooks;
315
 
316
  /**
317
  * Content specified inline
@@ -331,7 +313,7 @@ class WPML_OptionsManager {
331
  '<p><a href = "http://wordpress.org/extend/plugins/wp-mail-logging/">' . __('Plugin Homepage/support', 'wpml') . '</a></p>' .
332
  '<p><a href = "http://no3x.de/">' . __("Plugin author's blog", 'wpml') . '</a></p>'
333
  );
334
-
335
  // Add screen options
336
  $screen->add_option(
337
  'per_page',
@@ -350,9 +332,8 @@ class WPML_OptionsManager {
350
  wp_die(__('You do not have sufficient permissions to access this page.', 'wpml'));
351
  }
352
 
353
-
354
  if (!class_exists( 'Email_Log_List_Table' ) ) {
355
- require_once dirname( __FILE__ ) . '/inc/class-email-logging-list-table.php';
356
  }
357
 
358
  ?>
282
 
283
 
284
  // List screen properties
285
+ $left = '<div style="width:50%;float:left;">'
286
+ . '<h4>About this plugin</h4>'
287
+ . '<p>This plugin is open source.</p>'
288
+ . '</div>';
 
 
 
289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
+ $right = '<div style="width:50%;float:right;">'
292
+ . '<h4>Donate</h4>'
293
+ . '<p>If you like the plugin please consider to make a donation. More information are provided on my <a href="http://no3x.de/web/donate">website</a>.</p>'
294
+ . '</div>';
295
 
296
+ $help_content = $left . $right;
 
297
 
298
  /**
299
  * Content specified inline
313
  '<p><a href = "http://wordpress.org/extend/plugins/wp-mail-logging/">' . __('Plugin Homepage/support', 'wpml') . '</a></p>' .
314
  '<p><a href = "http://no3x.de/">' . __("Plugin author's blog", 'wpml') . '</a></p>'
315
  );
316
+
317
  // Add screen options
318
  $screen->add_option(
319
  'per_page',
332
  wp_die(__('You do not have sufficient permissions to access this page.', 'wpml'));
333
  }
334
 
 
335
  if (!class_exists( 'Email_Log_List_Table' ) ) {
336
+ require_once ( plugin_dir_path( __FILE__ ) . 'WPML_Email_Log_List.php' );
337
  }
338
 
339
  ?>
WPML_Plugin.php CHANGED
@@ -8,6 +8,12 @@ class WPML_Plugin extends WPML_LifeCycle {
8
  const HOOK_LOGGING_COLUMNS = 'wpml_hook_mail_columns';
9
  const HOOK_LOGGING_COLUMNS_RENDER = 'wpml_hook_mail_columns_render';
10
 
 
 
 
 
 
 
11
  /**
12
  * See: http://plugin.michael-simpson.com/?page_id=31
13
  * @return array of option meta data.
@@ -57,11 +63,11 @@ class WPML_Plugin extends WPML_LifeCycle {
57
  */
58
  protected function installDatabaseTables() {
59
  global $wpdb;
60
- $tableName = _get_tablename('mails');
61
  $wpdb->query("CREATE TABLE IF NOT EXISTS `$tableName` (
62
  `mail_id` INT NOT NULL AUTO_INCREMENT,
63
  `timestamp` TIMESTAMP NOT NULL,
64
- `to` VARCHAR(200) NOT NULL DEFAULT '0',
65
  `subject` VARCHAR(200) NOT NULL DEFAULT '0',
66
  `message` TEXT NULL,
67
  `headers` TEXT NULL,
@@ -79,7 +85,7 @@ class WPML_Plugin extends WPML_LifeCycle {
79
  */
80
  protected function unInstallDatabaseTables() {
81
  global $wpdb;
82
- $tableName = _get_tablename('mails');
83
  $wpdb->query("DROP TABLE IF EXISTS `$tableName`");
84
  }
85
 
@@ -90,22 +96,23 @@ class WPML_Plugin extends WPML_LifeCycle {
90
  * @return void
91
  */
92
  public function upgrade() {
93
- // global $wpdb;
94
- // $upgradeOk = true;
95
- // $savedVersion = $this->getVersionSaved();
96
 
97
- // if ($this->isVersionLessThan($savedVersion, '1.0')) {
98
- // if ($this->isVersionLessThan($savedVersion, '0.2')) {
99
- // $tableName = $this->prefixTableName('mail_logging');
100
- // $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN ( `plugin_version` VARCHAR(200) NOT NULL DEFAULT '0')");
101
- // }
102
- // }
 
103
 
104
- // // Post-upgrade, set the current version in the options
105
- // $codeVersion = $this->getVersion();
106
- // if ($upgradeOk && $savedVersion != $codeVersion) {
107
- // $this->saveInstalledVersion();
108
- // }
109
  }
110
 
111
  public function addActionsAndFilters() {
@@ -142,36 +149,38 @@ class WPML_Plugin extends WPML_LifeCycle {
142
  // http://plugin.michael-simpson.com/?page_id=41
143
 
144
  }
145
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  public function log_email( $mailOriginal ) {
 
147
  $mail = $mailOriginal;
148
  global $wpdb;
149
- /*
150
- [to] => to@example.com
151
- [subject] => Test Mail
152
- [message] => Test Mail
153
-
154
- [headers] =>
155
- [attachments] => Array
156
- (
157
- )
158
- */
159
- $to = is_array($mail["to"]) ? implode(",\n", $mail['to']) : $mail['to'];
160
- $subject = $mail["subject"];
161
- $message = $mail["message"];
162
- $headers = is_array($mail["headers"]) ? implode(",\n", $mail['headers']) : $mail['headers'];
163
- $hasAttachments = (count ($mail['attachments']) > 0) ? "true" : "false";
164
 
165
- $tableName = _get_tablename('mails');
166
- $wpdb->insert($tableName, array(
167
- 'to' => $to,
168
- 'timestamp' => current_time('mysql'),
169
- 'subject' => $subject,
170
- 'message' => $message,
171
- 'headers' => $headers,
172
- 'attachments' => $hasAttachments,
173
- 'plugin_version' => $this->getVersion()
174
- ));
175
 
176
  return $mailOriginal;
177
  }
8
  const HOOK_LOGGING_COLUMNS = 'wpml_hook_mail_columns';
9
  const HOOK_LOGGING_COLUMNS_RENDER = 'wpml_hook_mail_columns_render';
10
 
11
+
12
+ public static function getTablename( $name ) {
13
+ global $wpdb;
14
+ return $wpdb->prefix . 'wpml_' . $name;
15
+ }
16
+
17
  /**
18
  * See: http://plugin.michael-simpson.com/?page_id=31
19
  * @return array of option meta data.
63
  */
64
  protected function installDatabaseTables() {
65
  global $wpdb;
66
+ $tableName = WPML_Plugin::getTablename('mails');
67
  $wpdb->query("CREATE TABLE IF NOT EXISTS `$tableName` (
68
  `mail_id` INT NOT NULL AUTO_INCREMENT,
69
  `timestamp` TIMESTAMP NOT NULL,
70
+ `receiver` VARCHAR(200) NOT NULL DEFAULT '0',
71
  `subject` VARCHAR(200) NOT NULL DEFAULT '0',
72
  `message` TEXT NULL,
73
  `headers` TEXT NULL,
85
  */
86
  protected function unInstallDatabaseTables() {
87
  global $wpdb;
88
+ $tableName = WPML_Plugin::getTablename('mails');
89
  $wpdb->query("DROP TABLE IF EXISTS `$tableName`");
90
  }
91
 
96
  * @return void
97
  */
98
  public function upgrade() {
99
+ global $wpdb;
100
+ $upgradeOk = true;
101
+ $savedVersion = $this->getVersionSaved();
102
 
103
+ if ($this->isVersionLessThan($savedVersion, '2.0')) {
104
+ if ($this->isVersionLessThan($savedVersion, '1.2')) {
105
+ $tableName = $this->prefixTableName('mail_logging');
106
+ $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN ( `plugin_version` VARCHAR(200) NOT NULL DEFAULT '0')");
107
+ $wpdb->query("ALTER TABLE `$tableName` CHANGE `to` `receiver` VARCHAR(200)");
108
+ }
109
+ }
110
 
111
+ // Post-upgrade, set the current version in the options
112
+ $codeVersion = $this->getVersion();
113
+ if ($upgradeOk && $savedVersion != $codeVersion) {
114
+ $this->saveInstalledVersion();
115
+ }
116
  }
117
 
118
  public function addActionsAndFilters() {
149
  // http://plugin.michael-simpson.com/?page_id=41
150
 
151
  }
152
+
153
+ private function extractReceiver( $receiver ) {
154
+ return is_array( $receiver ) ? implode( ',\n', $receiver ) : $receiver;
155
+ }
156
+
157
+ private function extractHeader( $headers ) {
158
+ return is_array( $headers ) ? implode( ',\n', $headers ) : $headers;
159
+ }
160
+
161
+ private function extractHasAttachments( $attachments ) {
162
+ return ( count ( $attachments ) > 0 ) ? 'true' : 'false';
163
+ }
164
+
165
+ private function extractFields( $mail ) {
166
+ return array(
167
+ 'receiver' => $this->extractReceiver( $mail['to'] ),
168
+ 'subject' => $mail['subject'],
169
+ 'message' => $mail['message'],
170
+ 'headers' => $this->extractHeader( $mail['headers'] ),
171
+ 'attachments' => $this->extractHasAttachments( $mail['attachments'] )
172
+ );
173
+ }
174
+
175
  public function log_email( $mailOriginal ) {
176
+ // make copy to avoid any changes on the original mail
177
  $mail = $mailOriginal;
178
  global $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
+ $fields = $this->extractFields( $mail );
181
+
182
+ $tableName = WPML_Plugin::getTablename('mails');
183
+ $wpdb->insert($tableName, $fields);
 
 
 
 
 
 
184
 
185
  return $mailOriginal;
186
  }
inc/class-wp-list-table.php ADDED
@@ -0,0 +1,974 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base class for displaying a list of items in an ajaxified HTML table.
4
+ *
5
+ * @package WordPress
6
+ * @subpackage List_Table
7
+ * @since 3.1.0
8
+ */
9
+
10
+ /**
11
+ * Base class for displaying a list of items in an ajaxified HTML table.
12
+ *
13
+ * @package WordPress
14
+ * @subpackage List_Table
15
+ * @since 3.1.0
16
+ * @access private
17
+ */
18
+ class WP_List_Table {
19
+
20
+ /**
21
+ * The current list of items
22
+ *
23
+ * @since 3.1.0
24
+ * @var array
25
+ * @access protected
26
+ */
27
+ var $items;
28
+
29
+ /**
30
+ * Various information about the current table
31
+ *
32
+ * @since 3.1.0
33
+ * @var array
34
+ * @access private
35
+ */
36
+ var $_args;
37
+
38
+ /**
39
+ * Various information needed for displaying the pagination
40
+ *
41
+ * @since 3.1.0
42
+ * @var array
43
+ * @access private
44
+ */
45
+ var $_pagination_args = array();
46
+
47
+ /**
48
+ * The current screen
49
+ *
50
+ * @since 3.1.0
51
+ * @var object
52
+ * @access protected
53
+ */
54
+ var $screen;
55
+
56
+ /**
57
+ * Cached bulk actions
58
+ *
59
+ * @since 3.1.0
60
+ * @var array
61
+ * @access private
62
+ */
63
+ var $_actions;
64
+
65
+ /**
66
+ * Cached pagination output
67
+ *
68
+ * @since 3.1.0
69
+ * @var string
70
+ * @access private
71
+ */
72
+ var $_pagination;
73
+
74
+ /**
75
+ * Constructor. The child class should call this constructor from its own constructor
76
+ *
77
+ * @param array $args An associative array with information about the current table
78
+ * @access protected
79
+ */
80
+ function __construct( $args = array() ) {
81
+ $args = wp_parse_args( $args, array(
82
+ 'plural' => '',
83
+ 'singular' => '',
84
+ 'ajax' => false,
85
+ 'screen' => null,
86
+ ) );
87
+
88
+ $this->screen = convert_to_screen( $args['screen'] );
89
+
90
+ add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
91
+
92
+ if ( !$args['plural'] )
93
+ $args['plural'] = $this->screen->base;
94
+
95
+ $args['plural'] = sanitize_key( $args['plural'] );
96
+ $args['singular'] = sanitize_key( $args['singular'] );
97
+
98
+ $this->_args = $args;
99
+
100
+ if ( $args['ajax'] ) {
101
+ // wp_enqueue_script( 'list-table' );
102
+ add_action( 'admin_footer', array( $this, '_js_vars' ) );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Checks the current user's permissions
108
+ * @uses wp_die()
109
+ *
110
+ * @since 3.1.0
111
+ * @access public
112
+ * @abstract
113
+ */
114
+ function ajax_user_can() {
115
+ die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
116
+ }
117
+
118
+ /**
119
+ * Prepares the list of items for displaying.
120
+ * @uses WP_List_Table::set_pagination_args()
121
+ *
122
+ * @since 3.1.0
123
+ * @access public
124
+ * @abstract
125
+ */
126
+ function prepare_items() {
127
+ die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
128
+ }
129
+
130
+ /**
131
+ * An internal method that sets all the necessary pagination arguments
132
+ *
133
+ * @param array $args An associative array with information about the pagination
134
+ * @access protected
135
+ */
136
+ function set_pagination_args( $args ) {
137
+ $args = wp_parse_args( $args, array(
138
+ 'total_items' => 0,
139
+ 'total_pages' => 0,
140
+ 'per_page' => 0,
141
+ ) );
142
+
143
+ if ( !$args['total_pages'] && $args['per_page'] > 0 )
144
+ $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
145
+
146
+ // redirect if page number is invalid and headers are not already sent
147
+ if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
148
+ wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
149
+ exit;
150
+ }
151
+
152
+ $this->_pagination_args = $args;
153
+ }
154
+
155
+ /**
156
+ * Access the pagination args
157
+ *
158
+ * @since 3.1.0
159
+ * @access public
160
+ *
161
+ * @param string $key
162
+ * @return array
163
+ */
164
+ function get_pagination_arg( $key ) {
165
+ if ( 'page' == $key )
166
+ return $this->get_pagenum();
167
+
168
+ if ( isset( $this->_pagination_args[$key] ) )
169
+ return $this->_pagination_args[$key];
170
+ }
171
+
172
+ /**
173
+ * Whether the table has items to display or not
174
+ *
175
+ * @since 3.1.0
176
+ * @access public
177
+ *
178
+ * @return bool
179
+ */
180
+ function has_items() {
181
+ return !empty( $this->items );
182
+ }
183
+
184
+ /**
185
+ * Message to be displayed when there are no items
186
+ *
187
+ * @since 3.1.0
188
+ * @access public
189
+ */
190
+ function no_items() {
191
+ _e( 'No items found.' );
192
+ }
193
+
194
+ /**
195
+ * Display the search box.
196
+ *
197
+ * @since 3.1.0
198
+ * @access public
199
+ *
200
+ * @param string $text The search button text
201
+ * @param string $input_id The search input id
202
+ */
203
+ function search_box( $text, $input_id ) {
204
+ if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
205
+ return;
206
+
207
+ $input_id = $input_id . '-search-input';
208
+
209
+ if ( ! empty( $_REQUEST['orderby'] ) )
210
+ echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
211
+ if ( ! empty( $_REQUEST['order'] ) )
212
+ echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
213
+ if ( ! empty( $_REQUEST['post_mime_type'] ) )
214
+ echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
215
+ if ( ! empty( $_REQUEST['detached'] ) )
216
+ echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
217
+ ?>
218
+ <p class="search-box">
219
+ <label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label>
220
+ <input type="search" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>" />
221
+ <?php submit_button( $text, 'button', false, false, array('id' => 'search-submit') ); ?>
222
+ </p>
223
+ <?php
224
+ }
225
+
226
+ /**
227
+ * Get an associative array ( id => link ) with the list
228
+ * of views available on this table.
229
+ *
230
+ * @since 3.1.0
231
+ * @access protected
232
+ *
233
+ * @return array
234
+ */
235
+ function get_views() {
236
+ return array();
237
+ }
238
+
239
+ /**
240
+ * Display the list of views available on this table.
241
+ *
242
+ * @since 3.1.0
243
+ * @access public
244
+ */
245
+ function views() {
246
+ $views = $this->get_views();
247
+ /**
248
+ * Filter the list of available list table views.
249
+ *
250
+ * The dynamic portion of the hook name, $this->screen->id, refers
251
+ * to the ID of the current screen, usually a string.
252
+ *
253
+ * @since 3.5.0
254
+ *
255
+ * @param array $views An array of available list table views.
256
+ */
257
+ $views = apply_filters( "views_{$this->screen->id}", $views );
258
+
259
+ if ( empty( $views ) )
260
+ return;
261
+
262
+ echo "<ul class='subsubsub'>\n";
263
+ foreach ( $views as $class => $view ) {
264
+ $views[ $class ] = "\t<li class='$class'>$view";
265
+ }
266
+ echo implode( " |</li>\n", $views ) . "</li>\n";
267
+ echo "</ul>";
268
+ }
269
+
270
+ /**
271
+ * Get an associative array ( option_name => option_title ) with the list
272
+ * of bulk actions available on this table.
273
+ *
274
+ * @since 3.1.0
275
+ * @access protected
276
+ *
277
+ * @return array
278
+ */
279
+ function get_bulk_actions() {
280
+ return array();
281
+ }
282
+
283
+ /**
284
+ * Display the bulk actions dropdown.
285
+ *
286
+ * @since 3.1.0
287
+ * @access public
288
+ */
289
+ function bulk_actions() {
290
+ if ( is_null( $this->_actions ) ) {
291
+ $no_new_actions = $this->_actions = $this->get_bulk_actions();
292
+ /**
293
+ * Filter the list table Bulk Actions drop-down.
294
+ *
295
+ * The dynamic portion of the hook name, $this->screen->id, refers
296
+ * to the ID of the current screen, usually a string.
297
+ *
298
+ * This filter can currently only be used to remove bulk actions.
299
+ *
300
+ * @since 3.5.0
301
+ *
302
+ * @param array $actions An array of the available bulk actions.
303
+ */
304
+ $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
305
+ $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
306
+ $two = '';
307
+ } else {
308
+ $two = '2';
309
+ }
310
+
311
+ if ( empty( $this->_actions ) )
312
+ return;
313
+
314
+ echo "<select name='action$two'>\n";
315
+ echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n";
316
+
317
+ foreach ( $this->_actions as $name => $title ) {
318
+ $class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
319
+
320
+ echo "\t<option value='$name'$class>$title</option>\n";
321
+ }
322
+
323
+ echo "</select>\n";
324
+
325
+ submit_button( __( 'Apply' ), 'action', false, false, array( 'id' => "doaction$two" ) );
326
+ echo "\n";
327
+ }
328
+
329
+ /**
330
+ * Get the current action selected from the bulk actions dropdown.
331
+ *
332
+ * @since 3.1.0
333
+ * @access public
334
+ *
335
+ * @return string|bool The action name or False if no action was selected
336
+ */
337
+ function current_action() {
338
+ if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
339
+ return $_REQUEST['action'];
340
+
341
+ if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
342
+ return $_REQUEST['action2'];
343
+
344
+ return false;
345
+ }
346
+
347
+ /**
348
+ * Generate row actions div
349
+ *
350
+ * @since 3.1.0
351
+ * @access protected
352
+ *
353
+ * @param array $actions The list of actions
354
+ * @param bool $always_visible Whether the actions should be always visible
355
+ * @return string
356
+ */
357
+ function row_actions( $actions, $always_visible = false ) {
358
+ $action_count = count( $actions );
359
+ $i = 0;
360
+
361
+ if ( !$action_count )
362
+ return '';
363
+
364
+ $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
365
+ foreach ( $actions as $action => $link ) {
366
+ ++$i;
367
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
368
+ $out .= "<span class='$action'>$link$sep</span>";
369
+ }
370
+ $out .= '</div>';
371
+
372
+ return $out;
373
+ }
374
+
375
+ /**
376
+ * Display a monthly dropdown for filtering items
377
+ *
378
+ * @since 3.1.0
379
+ * @access protected
380
+ */
381
+ function months_dropdown( $post_type ) {
382
+ global $wpdb, $wp_locale;
383
+
384
+ $months = $wpdb->get_results( $wpdb->prepare( "
385
+ SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
386
+ FROM $wpdb->posts
387
+ WHERE post_type = %s
388
+ ORDER BY post_date DESC
389
+ ", $post_type ) );
390
+
391
+ /**
392
+ * Filter the 'Months' drop-down results.
393
+ *
394
+ * @since 3.7.0
395
+ *
396
+ * @param object $months The months drop-down query results.
397
+ * @param string $post_type The post type.
398
+ */
399
+ $months = apply_filters( 'months_dropdown_results', $months, $post_type );
400
+
401
+ $month_count = count( $months );
402
+
403
+ if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
404
+ return;
405
+
406
+ $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
407
+ ?>
408
+ <select name='m'>
409
+ <option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates' ); ?></option>
410
+ <?php
411
+ foreach ( $months as $arc_row ) {
412
+ if ( 0 == $arc_row->year )
413
+ continue;
414
+
415
+ $month = zeroise( $arc_row->month, 2 );
416
+ $year = $arc_row->year;
417
+
418
+ printf( "<option %s value='%s'>%s</option>\n",
419
+ selected( $m, $year . $month, false ),
420
+ esc_attr( $arc_row->year . $month ),
421
+ /* translators: 1: month name, 2: 4-digit year */
422
+ sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
423
+ );
424
+ }
425
+ ?>
426
+ </select>
427
+ <?php
428
+ }
429
+
430
+ /**
431
+ * Display a view switcher
432
+ *
433
+ * @since 3.1.0
434
+ * @access protected
435
+ */
436
+ function view_switcher( $current_mode ) {
437
+ $modes = array(
438
+ 'list' => __( 'List View' ),
439
+ 'excerpt' => __( 'Excerpt View' )
440
+ );
441
+
442
+ ?>
443
+ <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
444
+ <div class="view-switch">
445
+ <?php
446
+ foreach ( $modes as $mode => $title ) {
447
+ $class = ( $current_mode == $mode ) ? 'class="current"' : '';
448
+ echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
449
+ }
450
+ ?>
451
+ </div>
452
+ <?php
453
+ }
454
+
455
+ /**
456
+ * Display a comment count bubble
457
+ *
458
+ * @since 3.1.0
459
+ * @access protected
460
+ *
461
+ * @param int $post_id
462
+ * @param int $pending_comments
463
+ */
464
+ function comments_bubble( $post_id, $pending_comments ) {
465
+ $pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
466
+
467
+ if ( $pending_comments )
468
+ echo '<strong>';
469
+
470
+ echo "<a href='" . esc_url( add_query_arg( 'p', $post_id, admin_url( 'edit-comments.php' ) ) ) . "' title='" . esc_attr( $pending_phrase ) . "' class='post-com-count'><span class='comment-count'>" . number_format_i18n( get_comments_number() ) . "</span></a>";
471
+
472
+ if ( $pending_comments )
473
+ echo '</strong>';
474
+ }
475
+
476
+ /**
477
+ * Get the current page number
478
+ *
479
+ * @since 3.1.0
480
+ * @access protected
481
+ *
482
+ * @return int
483
+ */
484
+ function get_pagenum() {
485
+ $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
486
+
487
+ if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
488
+ $pagenum = $this->_pagination_args['total_pages'];
489
+
490
+ return max( 1, $pagenum );
491
+ }
492
+
493
+ /**
494
+ * Get number of items to display on a single page
495
+ *
496
+ * @since 3.1.0
497
+ * @access protected
498
+ *
499
+ * @return int
500
+ */
501
+ function get_items_per_page( $option, $default = 20 ) {
502
+ $per_page = (int) get_user_option( $option );
503
+ if ( empty( $per_page ) || $per_page < 1 )
504
+ $per_page = $default;
505
+
506
+ /**
507
+ * Filter the number of items to be displayed on each page of the list table.
508
+ *
509
+ * The dynamic hook name, $option, refers to the per page option depending
510
+ * on the type of list table in use. Possible values may include:
511
+ * 'edit_comments_per_page', 'sites_network_per_page', 'site_themes_network_per_page',
512
+ * 'themes_netework_per_page', 'users_network_per_page', 'edit_{$post_type}', etc.
513
+ *
514
+ * @since 2.9.0
515
+ *
516
+ * @param int $per_page Number of items to be displayed. Default 20.
517
+ */
518
+ return (int) apply_filters( $option, $per_page );
519
+ }
520
+
521
+ /**
522
+ * Display the pagination.
523
+ *
524
+ * @since 3.1.0
525
+ * @access protected
526
+ */
527
+ function pagination( $which ) {
528
+ if ( empty( $this->_pagination_args ) )
529
+ return;
530
+
531
+ extract( $this->_pagination_args, EXTR_SKIP );
532
+
533
+ $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
534
+
535
+ $current = $this->get_pagenum();
536
+
537
+ $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
538
+
539
+ $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
540
+
541
+ $page_links = array();
542
+
543
+ $disable_first = $disable_last = '';
544
+ if ( $current == 1 )
545
+ $disable_first = ' disabled';
546
+ if ( $current == $total_pages )
547
+ $disable_last = ' disabled';
548
+
549
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
550
+ 'first-page' . $disable_first,
551
+ esc_attr__( 'Go to the first page' ),
552
+ esc_url( remove_query_arg( 'paged', $current_url ) ),
553
+ '&laquo;'
554
+ );
555
+
556
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
557
+ 'prev-page' . $disable_first,
558
+ esc_attr__( 'Go to the previous page' ),
559
+ esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
560
+ '&lsaquo;'
561
+ );
562
+
563
+ if ( 'bottom' == $which )
564
+ $html_current_page = $current;
565
+ else
566
+ $html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='paged' value='%s' size='%d' />",
567
+ esc_attr__( 'Current page' ),
568
+ $current,
569
+ strlen( $total_pages )
570
+ );
571
+
572
+ $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
573
+ $page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>';
574
+
575
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
576
+ 'next-page' . $disable_last,
577
+ esc_attr__( 'Go to the next page' ),
578
+ esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
579
+ '&rsaquo;'
580
+ );
581
+
582
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
583
+ 'last-page' . $disable_last,
584
+ esc_attr__( 'Go to the last page' ),
585
+ esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
586
+ '&raquo;'
587
+ );
588
+
589
+ $pagination_links_class = 'pagination-links';
590
+ if ( ! empty( $infinite_scroll ) )
591
+ $pagination_links_class = ' hide-if-js';
592
+ $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
593
+
594
+ if ( $total_pages )
595
+ $page_class = $total_pages < 2 ? ' one-page' : '';
596
+ else
597
+ $page_class = ' no-pages';
598
+
599
+ $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
600
+
601
+ echo $this->_pagination;
602
+ }
603
+
604
+ /**
605
+ * Get a list of columns. The format is:
606
+ * 'internal-name' => 'Title'
607
+ *
608
+ * @since 3.1.0
609
+ * @access protected
610
+ * @abstract
611
+ *
612
+ * @return array
613
+ */
614
+ function get_columns() {
615
+ die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
616
+ }
617
+
618
+ /**
619
+ * Get a list of sortable columns. The format is:
620
+ * 'internal-name' => 'orderby'
621
+ * or
622
+ * 'internal-name' => array( 'orderby', true )
623
+ *
624
+ * The second format will make the initial sorting order be descending
625
+ *
626
+ * @since 3.1.0
627
+ * @access protected
628
+ *
629
+ * @return array
630
+ */
631
+ function get_sortable_columns() {
632
+ return array();
633
+ }
634
+
635
+ /**
636
+ * Get a list of all, hidden and sortable columns, with filter applied
637
+ *
638
+ * @since 3.1.0
639
+ * @access protected
640
+ *
641
+ * @return array
642
+ */
643
+ function get_column_info() {
644
+ if ( isset( $this->_column_headers ) )
645
+ return $this->_column_headers;
646
+
647
+ $columns = get_column_headers( $this->screen );
648
+ $hidden = get_hidden_columns( $this->screen );
649
+
650
+ $sortable_columns = $this->get_sortable_columns();
651
+ /**
652
+ * Filter the list table sortable columns for a specific screen.
653
+ *
654
+ * The dynamic portion of the hook name, $this->screen->id, refers
655
+ * to the ID of the current screen, usually a string.
656
+ *
657
+ * @since 3.5.0
658
+ *
659
+ * @param array $sortable_columns An array of sortable columns.
660
+ */
661
+ $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
662
+
663
+ $sortable = array();
664
+ foreach ( $_sortable as $id => $data ) {
665
+ if ( empty( $data ) )
666
+ continue;
667
+
668
+ $data = (array) $data;
669
+ if ( !isset( $data[1] ) )
670
+ $data[1] = false;
671
+
672
+ $sortable[$id] = $data;
673
+ }
674
+
675
+ $this->_column_headers = array( $columns, $hidden, $sortable );
676
+
677
+ return $this->_column_headers;
678
+ }
679
+
680
+ /**
681
+ * Return number of visible columns
682
+ *
683
+ * @since 3.1.0
684
+ * @access public
685
+ *
686
+ * @return int
687
+ */
688
+ function get_column_count() {
689
+ list ( $columns, $hidden ) = $this->get_column_info();
690
+ $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
691
+ return count( $columns ) - count( $hidden );
692
+ }
693
+
694
+ /**
695
+ * Print column headers, accounting for hidden and sortable columns.
696
+ *
697
+ * @since 3.1.0
698
+ * @access protected
699
+ *
700
+ * @param bool $with_id Whether to set the id attribute or not
701
+ */
702
+ function print_column_headers( $with_id = true ) {
703
+ list( $columns, $hidden, $sortable ) = $this->get_column_info();
704
+
705
+ $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
706
+ $current_url = remove_query_arg( 'paged', $current_url );
707
+
708
+ if ( isset( $_GET['orderby'] ) )
709
+ $current_orderby = $_GET['orderby'];
710
+ else
711
+ $current_orderby = '';
712
+
713
+ if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
714
+ $current_order = 'desc';
715
+ else
716
+ $current_order = 'asc';
717
+
718
+ if ( ! empty( $columns['cb'] ) ) {
719
+ static $cb_counter = 1;
720
+ $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
721
+ . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
722
+ $cb_counter++;
723
+ }
724
+
725
+ foreach ( $columns as $column_key => $column_display_name ) {
726
+ $class = array( 'manage-column', "column-$column_key" );
727
+
728
+ $style = '';
729
+ if ( in_array( $column_key, $hidden ) )
730
+ $style = 'display:none;';
731
+
732
+ $style = ' style="' . $style . '"';
733
+
734
+ if ( 'cb' == $column_key )
735
+ $class[] = 'check-column';
736
+ elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
737
+ $class[] = 'num';
738
+
739
+ if ( isset( $sortable[$column_key] ) ) {
740
+ list( $orderby, $desc_first ) = $sortable[$column_key];
741
+
742
+ if ( $current_orderby == $orderby ) {
743
+ $order = 'asc' == $current_order ? 'desc' : 'asc';
744
+ $class[] = 'sorted';
745
+ $class[] = $current_order;
746
+ } else {
747
+ $order = $desc_first ? 'desc' : 'asc';
748
+ $class[] = 'sortable';
749
+ $class[] = $desc_first ? 'asc' : 'desc';
750
+ }
751
+
752
+ $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
753
+ }
754
+
755
+ $id = $with_id ? "id='$column_key'" : '';
756
+
757
+ if ( !empty( $class ) )
758
+ $class = "class='" . join( ' ', $class ) . "'";
759
+
760
+ echo "<th scope='col' $id $class $style>$column_display_name</th>";
761
+ }
762
+ }
763
+
764
+ /**
765
+ * Display the table
766
+ *
767
+ * @since 3.1.0
768
+ * @access public
769
+ */
770
+ function display() {
771
+ extract( $this->_args );
772
+
773
+ $this->display_tablenav( 'top' );
774
+
775
+ ?>
776
+ <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>" cellspacing="0">
777
+ <thead>
778
+ <tr>
779
+ <?php $this->print_column_headers(); ?>
780
+ </tr>
781
+ </thead>
782
+
783
+ <tfoot>
784
+ <tr>
785
+ <?php $this->print_column_headers( false ); ?>
786
+ </tr>
787
+ </tfoot>
788
+
789
+ <tbody id="the-list"<?php if ( $singular ) echo " data-wp-lists='list:$singular'"; ?>>
790
+ <?php $this->display_rows_or_placeholder(); ?>
791
+ </tbody>
792
+ </table>
793
+ <?php
794
+ $this->display_tablenav( 'bottom' );
795
+ }
796
+
797
+ /**
798
+ * Get a list of CSS classes for the <table> tag
799
+ *
800
+ * @since 3.1.0
801
+ * @access protected
802
+ *
803
+ * @return array
804
+ */
805
+ function get_table_classes() {
806
+ return array( 'widefat', 'fixed', $this->_args['plural'] );
807
+ }
808
+
809
+ /**
810
+ * Generate the table navigation above or below the table
811
+ *
812
+ * @since 3.1.0
813
+ * @access protected
814
+ */
815
+ function display_tablenav( $which ) {
816
+ if ( 'top' == $which )
817
+ wp_nonce_field( 'bulk-' . $this->_args['plural'] );
818
+ ?>
819
+ <div class="tablenav <?php echo esc_attr( $which ); ?>">
820
+
821
+ <div class="alignleft actions bulkactions">
822
+ <?php $this->bulk_actions(); ?>
823
+ </div>
824
+ <?php
825
+ $this->extra_tablenav( $which );
826
+ $this->pagination( $which );
827
+ ?>
828
+
829
+ <br class="clear" />
830
+ </div>
831
+ <?php
832
+ }
833
+
834
+ /**
835
+ * Extra controls to be displayed between bulk actions and pagination
836
+ *
837
+ * @since 3.1.0
838
+ * @access protected
839
+ */
840
+ function extra_tablenav( $which ) {}
841
+
842
+ /**
843
+ * Generate the <tbody> part of the table
844
+ *
845
+ * @since 3.1.0
846
+ * @access protected
847
+ */
848
+ function display_rows_or_placeholder() {
849
+ if ( $this->has_items() ) {
850
+ $this->display_rows();
851
+ } else {
852
+ list( $columns, $hidden ) = $this->get_column_info();
853
+ echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
854
+ $this->no_items();
855
+ echo '</td></tr>';
856
+ }
857
+ }
858
+
859
+ /**
860
+ * Generate the table rows
861
+ *
862
+ * @since 3.1.0
863
+ * @access protected
864
+ */
865
+ function display_rows() {
866
+ foreach ( $this->items as $item )
867
+ $this->single_row( $item );
868
+ }
869
+
870
+ /**
871
+ * Generates content for a single row of the table
872
+ *
873
+ * @since 3.1.0
874
+ * @access protected
875
+ *
876
+ * @param object $item The current item
877
+ */
878
+ function single_row( $item ) {
879
+ static $row_class = '';
880
+ $row_class = ( $row_class == '' ? ' class="alternate"' : '' );
881
+
882
+ echo '<tr' . $row_class . '>';
883
+ $this->single_row_columns( $item );
884
+ echo '</tr>';
885
+ }
886
+
887
+ /**
888
+ * Generates the columns for a single row of the table
889
+ *
890
+ * @since 3.1.0
891
+ * @access protected
892
+ *
893
+ * @param object $item The current item
894
+ */
895
+ function single_row_columns( $item ) {
896
+ list( $columns, $hidden ) = $this->get_column_info();
897
+
898
+ foreach ( $columns as $column_name => $column_display_name ) {
899
+ $class = "class='$column_name column-$column_name'";
900
+
901
+ $style = '';
902
+ if ( in_array( $column_name, $hidden ) )
903
+ $style = ' style="display:none;"';
904
+
905
+ $attributes = "$class$style";
906
+
907
+ if ( 'cb' == $column_name ) {
908
+ echo '<th scope="row" class="check-column">';
909
+ echo $this->column_cb( $item );
910
+ echo '</th>';
911
+ }
912
+ elseif ( method_exists( $this, 'column_' . $column_name ) ) {
913
+ echo "<td $attributes>";
914
+ echo call_user_func( array( $this, 'column_' . $column_name ), $item );
915
+ echo "</td>";
916
+ }
917
+ else {
918
+ echo "<td $attributes>";
919
+ echo $this->column_default( $item, $column_name );
920
+ echo "</td>";
921
+ }
922
+ }
923
+ }
924
+
925
+ /**
926
+ * Handle an incoming ajax request (called from admin-ajax.php)
927
+ *
928
+ * @since 3.1.0
929
+ * @access public
930
+ */
931
+ function ajax_response() {
932
+ $this->prepare_items();
933
+
934
+ extract( $this->_args );
935
+ extract( $this->_pagination_args, EXTR_SKIP );
936
+
937
+ ob_start();
938
+ if ( ! empty( $_REQUEST['no_placeholder'] ) )
939
+ $this->display_rows();
940
+ else
941
+ $this->display_rows_or_placeholder();
942
+
943
+ $rows = ob_get_clean();
944
+
945
+ $response = array( 'rows' => $rows );
946
+
947
+ if ( isset( $total_items ) )
948
+ $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
949
+
950
+ if ( isset( $total_pages ) ) {
951
+ $response['total_pages'] = $total_pages;
952
+ $response['total_pages_i18n'] = number_format_i18n( $total_pages );
953
+ }
954
+
955
+ die( json_encode( $response ) );
956
+ }
957
+
958
+ /**
959
+ * Send required variables to JavaScript land
960
+ *
961
+ * @access private
962
+ */
963
+ function _js_vars() {
964
+ $args = array(
965
+ 'class' => get_class( $this ),
966
+ 'screen' => array(
967
+ 'id' => $this->screen->id,
968
+ 'base' => $this->screen->base,
969
+ )
970
+ );
971
+
972
+ printf( "<script type='text/javascript'>list_args = %s;</script>\n", json_encode( $args ) );
973
+ }
974
+ }
readme.txt CHANGED
@@ -6,7 +6,7 @@ License: GPLv3
6
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
7
  Requires at least: 3.0
8
  Tested up to: 3.9.1
9
- Stable tag: 1.1
10
 
11
  Logs each email sent by WordPress.
12
 
@@ -14,10 +14,13 @@ Logs each email sent by WordPress.
14
 
15
  Logs each email sent by WordPress. This can be useful if you don't want to lose such mail contents. It can also be useful for debugging purposes while development.
16
 
 
 
17
  Features of the plugin include:
18
 
19
  * Zero-configuration - just install and enjoy
20
  * Complete list of sent mails
 
21
 
22
  **Follow this plugin on [GitHub](https://github.com/No3x/wp-mail-logging)**
23
 
@@ -38,6 +41,11 @@ Just install and activate wp-mail-logging. The plugin will do the work for you!
38
 
39
 
40
  == Changelog ==
 
 
 
 
 
41
  = 1.1 =
42
  - Modified readme.
43
 
6
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
7
  Requires at least: 3.0
8
  Tested up to: 3.9.1
9
+ Stable tag: 1.2
10
 
11
  Logs each email sent by WordPress.
12
 
14
 
15
  Logs each email sent by WordPress. This can be useful if you don't want to lose such mail contents. It can also be useful for debugging purposes while development.
16
 
17
+ [youtube http://www.youtube.com/watch?v=zU858JxhczA]
18
+
19
  Features of the plugin include:
20
 
21
  * Zero-configuration - just install and enjoy
22
  * Complete list of sent mails
23
+ * Developer: Filters are provided to extend the columns. *
24
 
25
  **Follow this plugin on [GitHub](https://github.com/No3x/wp-mail-logging)**
26
 
41
 
42
 
43
  == Changelog ==
44
+ = 1.2, August 12, 2014 =
45
+ - Fixed deletion of mails regardless of options (on update to 1.2 your mails will be deleted hopefully this happens for the last time)
46
+ - Improved help & stability
47
+ - Added video
48
+
49
  = 1.1 =
50
  - Modified readme.
51
 
wp-mail-logging.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: WP Mail Logging
4
  Plugin URI: http://wordpress.org/extend/plugins/wp-mail-logging/
5
- Version: 1.1
6
  Author: Christian Z&ouml;ller
7
  Author URI: http://no3x.de/
8
  Description: Logs each email sent by WordPress.
2
  /*
3
  Plugin Name: WP Mail Logging
4
  Plugin URI: http://wordpress.org/extend/plugins/wp-mail-logging/
5
+ Version: 1.2
6
  Author: Christian Z&ouml;ller
7
  Author URI: http://no3x.de/
8
  Description: Logs each email sent by WordPress.
wp-mail-logging_init.php CHANGED
@@ -20,9 +20,14 @@
20
  */
21
 
22
  function WPML_init($file) {
23
-
24
  require_once('WPML_Plugin.php');
25
  $aPlugin = new WPML_Plugin();
 
 
 
 
 
26
 
27
  // Install the plugin
28
  // NOTE: this file gets run each time you *activate* the plugin.
@@ -51,8 +56,3 @@ function WPML_init($file) {
51
  // Register the Plugin Deactivation Hook
52
  register_deactivation_hook($file, array(&$aPlugin, 'deactivate'));
53
  }
54
-
55
- function _get_tablename( $name ) {
56
- global $wpdb;
57
- return $wpdb->prefix . 'wpml_' . $name;
58
- }
20
  */
21
 
22
  function WPML_init($file) {
23
+
24
  require_once('WPML_Plugin.php');
25
  $aPlugin = new WPML_Plugin();
26
+
27
+ // For Testing make plugin available global
28
+ if( ! array_key_exists( 'WPML_Plugin', $GLOBALS ) ) {
29
+ $GLOBALS['WPML_Plugin'] = &$aPlugin;
30
+ }
31
 
32
  // Install the plugin
33
  // NOTE: this file gets run each time you *activate* the plugin.
56
  // Register the Plugin Deactivation Hook
57
  register_deactivation_hook($file, array(&$aPlugin, 'deactivate'));
58
  }