WP Mail Logging - Version 1.8.0

Version Description

  • New: Error status from mail implementation is logged
  • New: Resend mail
  • New: Added translation files
  • New: Added translation for de_DE and zh_CN
  • Fix: raw mode of message renders attachments as text
  • Fix: fallback to raw mode for json mode if mail is containing html
  • Tweak: Pretty print json

=

Download this release

Release Info

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

Code changes from version 1.7.0 to 1.8.0

WPML_API_Example.php CHANGED
@@ -13,73 +13,73 @@ if ( ! defined( 'ABSPATH' ) ) exit;
13
  * If you consider writing a plugin please contact me for better hook support/documentation.
14
  */
15
  class WPML_API_Example {
16
-
17
- // require_once('WPML_API_Example.php');
18
- // $aAPI = new WPML_API_Example();
19
 
20
- public function addActionsAndFilters() {
21
- // In this example we are going to add a column 'test' in add_column.
22
- add_filter( WPML_Plugin::HOOK_LOGGING_COLUMNS, array( &$this, 'add_column' ) );
23
- add_filter( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, array( &$this, 'render_column' ), 10, 2 );
24
- // Change the supported formats of modal e.g. dashed:
25
- add_filter( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, array( &$this, 'add_supported_format') );
26
- // Change content of format dashed HOOK_LOGGING_FORMAT_CONTENT_{$your_format} e.g. dashed:
27
- add_filter( WPML_Plugin::HOOK_LOGGING_FORMAT_CONTENT . '_dashed', array( &$this, 'supported_format_dashed') );
28
- }
29
 
30
- /**
31
- * Is called when List Table is gathering columns.
32
- * @since 1.0
33
- * @param array $columns Array of columns.
34
- * @return array $columns Updated array of columns.
35
- */
36
- public function add_column( $columns ) {
37
- return $columns = array_merge( $columns,
38
- array( 'test' => __( 'test', 'wpml' ) )
39
- //,array('test2' => __( 'test2', 'wpml' ) ) // ...
40
- );
41
- }
42
 
43
- /**
44
- * Is called when the List Table could not find the column. So we can hook in and modify the column.
45
- * @since 1.0
46
- * @param array $item A singular item (one full row's worth of data).
47
- * @param array $column_name The name/slug of the column to be processed.
48
- * @return string Text or HTML to be placed inside the column <td>
49
- */
50
- public function render_column( $item, $column_name ) {
51
- switch ( $column_name ) {
52
- case 'test':
53
- return 'display relevant data. item contains all information you need about the row. You can process the data and add the result to this column. You can access it like this: $item[$column_name]';
54
- default:
55
- return '';
56
- }
57
- }
58
 
59
- /**
60
- * Is called when supported formats are collected. You can add a format here then you can provide a content function.
61
- * @since 1.6.0
62
- * @param array $formats supported formats
63
- * @return array supported formats + your additional formats
64
- * @see WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS
65
- */
66
- public function add_supported_format( $formats ) {
67
- $formats[] = 'dashed';
68
- return $formats;
69
- }
 
 
 
 
70
 
71
- /**
72
- * This function is called for each of your additional formats. Change the content of the modal here.
73
- * For example I add some dashes.
74
- * @since 1.6.0
75
- * @param $mail
76
- * @return string
77
- * @see WPML_Plugin::HOOK_LOGGING_FORMAT_CONTENT
78
- */
79
- public function supported_format_dashed( $mail ) {
80
- $dashedAppend = '';
81
- foreach( $mail as $property => $value )
82
- $dashedAppend .= str_replace(' ', '-', $value);
83
- return $dashedAppend;
84
- }
85
- }
 
 
 
 
 
 
 
 
 
 
 
 
13
  * If you consider writing a plugin please contact me for better hook support/documentation.
14
  */
15
  class WPML_API_Example {
 
 
 
16
 
17
+ // require_once('WPML_API_Example.php');
18
+ // $aAPI = new WPML_API_Example();
 
 
 
 
 
 
 
19
 
20
+ public function addActionsAndFilters() {
21
+ // In this example we are going to add a column 'test' in add_column.
22
+ add_filter( WPML_Plugin::HOOK_LOGGING_COLUMNS, array( &$this, 'add_column' ) );
23
+ add_filter( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, array( &$this, 'render_column' ), 10, 2 );
24
+ // Change the supported formats of modal e.g. dashed:
25
+ add_filter( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, array( &$this, 'add_supported_format') );
26
+ // Change content of format dashed HOOK_LOGGING_FORMAT_CONTENT_{$your_format} e.g. dashed:
27
+ add_filter( WPML_Plugin::HOOK_LOGGING_FORMAT_CONTENT . '_dashed', array( &$this, 'supported_format_dashed') );
28
+ }
 
 
 
29
 
30
+ /**
31
+ * Is called when List Table is gathering columns.
32
+ * @since 1.0
33
+ * @param array $columns Array of columns.
34
+ * @return array $columns Updated array of columns.
35
+ */
36
+ public function add_column( $columns ) {
37
+ return $columns = array_merge( $columns,
38
+ array( 'test' => 'test' )
39
+ //,array('test2' => 'wp-mail-logging' ) // ...
40
+ );
41
+ }
 
 
 
42
 
43
+ /**
44
+ * Is called when the List Table could not find the column. So we can hook in and modify the column.
45
+ * @since 1.0
46
+ * @param array $item A singular item (one full row's worth of data).
47
+ * @param array $column_name The name/slug of the column to be processed.
48
+ * @return string Text or HTML to be placed inside the column <td>
49
+ */
50
+ public function render_column( $item, $column_name ) {
51
+ switch ( $column_name ) {
52
+ case 'test':
53
+ return 'display relevant data. item contains all information you need about the row. You can process the data and add the result to this column. You can access it like this: $item[$column_name]';
54
+ default:
55
+ return '';
56
+ }
57
+ }
58
 
59
+ /**
60
+ * Is called when supported formats are collected. You can add a format here then you can provide a content function.
61
+ * @since 1.6.0
62
+ * @param array $formats supported formats
63
+ * @return array supported formats + your additional formats
64
+ * @see WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS
65
+ */
66
+ public function add_supported_format( $formats ) {
67
+ $formats[] = 'dashed';
68
+ return $formats;
69
+ }
70
+
71
+ /**
72
+ * This function is called for each of your additional formats. Change the content of the modal here.
73
+ * For example I add some dashes.
74
+ * @since 1.6.0
75
+ * @param $mail
76
+ * @return string
77
+ * @see WPML_Plugin::HOOK_LOGGING_FORMAT_CONTENT
78
+ */
79
+ public function supported_format_dashed( $mail ) {
80
+ $dashedAppend = '';
81
+ foreach( $mail as $property => $value )
82
+ $dashedAppend .= str_replace(' ', '-', $value);
83
+ return $dashedAppend;
84
+ }
85
+ }
WPML_DI_Container.php CHANGED
@@ -10,15 +10,15 @@ use No3x\WPML\Pimple\Container;
10
 
11
  class WPML_DI_Container extends Container {
12
 
13
- public function addActionsAndFilters() {
14
- foreach ( $this->keys() as $key ) {
15
- $content = $this[ $key ];
16
- if ( is_object( $content ) ) {
17
- $reflection = new \ReflectionClass( $content );
18
- if ( $reflection->hasMethod( 'addActionsAndFilters' ) ) {
19
- $content->addActionsAndFilters();
20
- }
21
- }
22
- }
23
- }
24
- }
10
 
11
  class WPML_DI_Container extends Container {
12
 
13
+ public function addActionsAndFilters() {
14
+ foreach ( $this->keys() as $key ) {
15
+ $content = $this[ $key ];
16
+ if ( is_object( $content ) ) {
17
+ $reflection = new \ReflectionClass( $content );
18
+ if ( $reflection->hasMethod( 'addActionsAndFilters' ) ) {
19
+ $content->addActionsAndFilters();
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
WPML_Email_Log_List.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace No3x\WPML;
4
 
5
  use No3x\WPML\Model\WPML_Mail as Mail;
 
6
 
7
  // Exit if accessed directly.
8
  if ( ! defined( 'ABSPATH' ) ) exit;
@@ -11,7 +12,7 @@ require_once( ABSPATH . 'wp-admin/includes/screen.php' );
11
  require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
12
 
13
  if ( ! class_exists( 'WP_List_Table' ) ) {
14
- require_once( plugin_dir_path( __FILE__ ) . 'inc/class-wp-list-table.php' );
15
  }
16
 
17
  /**
@@ -21,61 +22,62 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
21
  */
22
  class WPML_Email_Log_List extends \WP_List_Table {
23
 
24
- const NONCE_LIST_TABLE = 'wpml-list_table';
25
- private $supported_formats = array();
26
- /**
27
- * Initializes the List Table
28
- * @since 1.0
29
- */
30
- function __construct( $supported_formats = array() ) {
31
- $this->supported_formats = $supported_formats;
32
- }
33
-
34
- function addActionsAndFilters() {
35
- add_action( 'admin_init', array( $this, 'init') );
36
- add_filter( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, function() {
37
- return $this->supported_formats;
38
- } );
39
- add_action( 'wp_ajax_wpml_email_get', __CLASS__ . '::ajax_wpml_email_get' );
40
- }
41
-
42
- function init() {
43
- global $status, $page, $hook_suffix;
44
-
45
- parent::__construct( array(
46
- 'singular' => 'email', // singular name of the listed records
47
- 'plural' => 'emails', // plural name of the listed records
48
- 'ajax' => false, // does this table support ajax?
49
- ) );
50
- }
51
-
52
- /**
53
- * Is displayed if no item is available to render
54
- * @since 1.0
55
- * @see WP_List_Table::no_items()
56
- */
57
- function no_items() {
58
- _e( 'No email found.', 'wpml' );
59
- return;
60
- }
61
-
62
- /**
63
- * Defines the available columns.
64
- * @since 1.0
65
- * @see WP_List_Table::get_columns()
66
- */
67
- function get_columns() {
68
- $columns = array(
69
- 'cb' => '<input type="checkbox" />',
70
- 'mail_id' => __( 'ID', 'wpml' ),
71
- 'timestamp' => __( 'Time', 'wpml' ),
72
- 'receiver' => __( 'Receiver', 'wpml' ),
73
- 'subject' => __( 'Subject', 'wpml' ),
74
- 'message' => __( 'Message', 'wpml' ),
75
- 'headers' => __( 'Headers', 'wpml' ),
76
- 'attachments' => __( 'Attachments', 'wpml' ),
77
- 'plugin_version' => __( 'Plugin Version', 'wpml' ),
78
- );
 
79
 
80
  /* @var $instance WPML_Plugin */
81
  $instance = WPML_Init::getInstance()->getService( 'plugin' );
@@ -85,382 +87,423 @@ class WPML_Email_Log_List extends \WP_List_Table {
85
  $posAfterTimestamp = array_search('timestamp', array_keys($columns) ) + 1;
86
  $columns = array_merge(
87
  array_slice( $columns, 0, $posAfterTimestamp),
88
- [ 'host' => __( 'Host', 'wpml' ) ],
89
  array_slice( $columns, $posAfterTimestamp )
90
  );
91
  }
92
 
93
- // Give a plugin the chance to edit the columns.
94
- $columns = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS, $columns );
95
-
96
- $reserved = array( '_title', 'comment', 'media', 'name', 'title', 'username', 'blogname' );
97
-
98
- // Show message for reserved column names.
99
- foreach ( $reserved as $reserved_key ) {
100
- if ( array_key_exists( $reserved_key, $columns ) ) {
101
- 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!";
102
- break;
103
- }
104
- }
105
- return $columns;
106
- }
107
-
108
- /**
109
- * Define which columns are hidden
110
- * @since 1.0
111
- * @return array
112
- */
113
- function get_hidden_columns() {
114
- return array(
115
- 'plugin_version',
116
- 'mail_id',
117
- );
118
- }
119
-
120
- /**
121
- * Sanitize orderby parameter.
122
- * @s
123
- * @return string sanitized orderby parameter
124
- */
125
- private function sanitize_orderby() {
126
- return WPML_Utils::sanitize_expected_value( ( !empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : null, $this->get_sortable_columns(), 'mail_id');
127
- }
128
-
129
- /**
130
- * Sanitize order parameter.
131
- * @return string sanitized order parameter
132
- */
133
- private function sanitize_order() {
134
- return WPML_Utils::sanitize_expected_value( ( !empty( $_GET['order'] ) ) ? $_GET['order'] : null, array('desc', 'asc'), 'desc');
135
- }
136
-
137
- /**
138
- * Prepares the items for rendering
139
- * @since 1.0
140
- * @param string|boolean $search string you want to search for. Default false.
141
- * @see WP_List_Table::prepare_items()
142
- */
143
- function prepare_items( $search = false ) {
144
- $orderby = $this->sanitize_orderby();
145
- $order = $this->sanitize_order();
146
-
147
- $columns = $this->get_columns();
148
- $hidden = $this->get_hidden_columns();
149
- $sortable = $this->get_sortable_columns();
150
- $this->_column_headers = array( $columns, $hidden, $sortable );
151
-
152
- $this->process_bulk_action();
153
-
154
- $per_page = $this->get_items_per_page( 'per_page', 25 );
155
- $current_page = $this->get_pagenum();
156
- $offset = ( $current_page - 1 ) * $per_page;
157
-
158
- $total_items = Mail::query()
159
- ->search( $search )
160
- ->find( true );
161
-
162
- $mails = Mail::query()
163
- ->search( $search )
164
- ->sort_by( $orderby )
165
- ->order( $order )
166
- ->limit( $per_page )
167
- ->offset( $offset )
168
- ->find();
169
-
170
- foreach ( $mails as $mail ) {
171
- /* @var $mail Mail */
172
- $this->items[] = $mail->to_array();
173
- }
174
-
175
- $this->set_pagination_args( array(
176
- 'total_items' => $total_items, // The total number of items.
177
- 'per_page' => $per_page, // Number of items per page.
178
- ) );
179
- }
180
-
181
- /**
182
- * Renders the cell.
183
- * 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)
184
- * @since 1.0
185
- * @param array $item The current item.
186
- * @param string $column_name The current column name.
187
- * @return string The cell content
188
- */
189
- function column_default( $item, $column_name ) {
190
- switch ( $column_name ) {
191
- case 'mail_id':
192
- case 'timestamp':
193
- case 'host':
194
- case 'subject':
195
- case 'message':
196
- case 'headers':
197
- case 'attachments':
198
- case 'plugin_version':
199
- case 'receiver':
200
- return $item[ $column_name ];
201
- default:
202
- // 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).
203
- return ( is_array( $res = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, $item, $column_name ) ) ) ? '' : $res;
204
- }
205
- }
206
-
207
- /**
208
- * Sanitize message to remove unsafe html.
209
- * @since 1.5.1
210
- * @param string $message unsafe message.
211
- * @return string safe message.
212
- */
213
- function sanitize_message( $message ) {
214
- $allowed_tags = wp_kses_allowed_html( 'post' );
215
- $allowed_tags['a']['data-message'] = true;
216
- $allowed_tags['style'][''] = true;
217
- return wp_kses( $message, $allowed_tags );
218
- }
219
-
220
- /**
221
- * Renders the message column.
222
- * @since 1.3
223
- * @param array $item The current item.
224
- * @return void|string
225
- */
226
- function column_message( $item ) {
227
- if ( empty( $item['message'] ) ) {
228
- return '';
229
- }
230
- //$content = $this->sanitize_message( $this->render_mail_html( $item ) );
231
- $content = $item['mail_id'];
232
- $message = '<a class="wp-mail-logging-view-message button button-secondary" href="#" data-mail-id="' . esc_attr( $content ) . '">View</a>';
233
- return $message;
234
- }
235
-
236
- /**
237
- * Renders the timestamp column.
238
- * @since 1.5.0
239
- * @param array $item The current item.
240
- * @return void|string
241
- */
242
- function column_timestamp( $item ) {
243
- return date_i18n( apply_filters( 'wpml_get_date_time_format', '' ), strtotime( $item['timestamp'] ) );
244
- }
245
-
246
- /**
247
- * Renders the attachment column in compbat mode for mails prior 1.6.0.
248
- * @since 1.6.0
249
- * @param array $item The current item.
250
- * @return string The attachment column.
251
- */
252
- function column_attachments_compat_152( $item ) {
253
- $attachment_append = '';
254
- $attachments = explode( ',\n', $item['attachments'] );
255
- $attachments = is_array( $attachments ) ? $attachments : array( $attachments );
256
- foreach ( $attachments as $attachment ) {
257
- // $attachment can be an empty string ''.
258
- if ( ! empty( $attachment ) ) {
259
- $filename = basename( $attachment );
260
- $attachment_path = WP_CONTENT_DIR . $attachment;
261
- $attachment_url = WP_CONTENT_URL . $attachment;
262
- if ( is_file( $attachment_path ) ) {
263
- $attachment_append .= '<a href="' . $attachment_url . '" title="' . $filename . '">' . WPML_Utils::generate_attachment_icon( $attachment_path ) . '</a> ';
264
- } else {
265
- $message = sprintf( __( 'Attachment %s is not present', 'wpml' ), $filename );
266
- $attachment_append .= '<i class="fa fa-times" title="' . $message . '"></i>';
267
- }
268
- }
269
- }
270
- return $attachment_append;
271
- }
272
-
273
- /**
274
- * Renders the attachment column.
275
- * @since 1.3
276
- * @param array $item The current item.
277
- * @return string The attachment column.
278
- */
279
- function column_attachments( $item ) {
280
-
281
- if ( version_compare( trim( $item ['plugin_version'] ), '1.6.0', '<' ) ) {
282
- return $this->column_attachments_compat_152( $item );
283
- }
284
-
285
- $attachment_append = '';
286
- $attachments = explode( ',\n', $item['attachments'] );
287
- $attachments = is_array( $attachments ) ? $attachments : array( $attachments );
288
- foreach ( $attachments as $attachment ) {
289
- // $attachment can be an empty string ''.
290
- if ( ! empty( $attachment ) ) {
291
- $filename = basename( $attachment );
292
- $basename = '/uploads';
293
- $attachment_path = WP_CONTENT_DIR . $basename . $attachment;
294
- $attachment_url = WP_CONTENT_URL . $basename . $attachment;
295
-
296
- if ( is_file( $attachment_path ) ) {
297
- $attachment_append .= '<a href="' . $attachment_url . '" title="' . $filename . '">' . WPML_Utils::generate_attachment_icon( $attachment_path ) . '</a> ';
298
- } else {
299
- $message = sprintf( __( 'Attachment %s is not present', 'wpml' ), $filename );
300
- $attachment_append .= '<i class="fa fa-times" title="' . $message . '"></i>';
301
- }
302
- }
303
- }
304
- return $attachment_append;
305
- }
306
-
307
- /**
308
- * Renders all components of the mail.
309
- * @since 1.3
310
- * @param array $item The current item.
311
- * @return string The mail as html
312
- */
313
- function render_mail( $item ) {
314
- $mailAppend = '';
315
- foreach ( $item as $key => $value ) {
316
- if ( array_key_exists( $key, $this->get_columns() ) && ! in_array( $key, $this->get_hidden_columns() ) ) {
317
- $display = $this->get_columns();
318
- $column_name = $key;
319
- $title = "<span class=\"title\">{$display[$key]}: </span>";
320
- $content = '';
321
- if ( 'message' !== $column_name && method_exists( $this, 'column_' . $column_name ) ) {
322
- $content .= call_user_func( array( $this, 'column_' . $column_name ), $item );
323
- } else {
324
- $content .= $this->column_default( $item, $column_name );
325
- }
326
- $mailAppend .= $title . htmlentities( $content );
327
- }
328
- }
329
-
330
- return $mailAppend;
331
- }
332
-
333
- /**
334
- * Renders all components of the mail.
335
- * @since 1.6.0
336
- * @param array $item The current item.
337
- * @return string The mail as html
338
- */
339
- function render_mail_html( $item ) {
340
- $mailAppend = '';
341
- foreach ( $item as $key => $value ) {
342
- if ( array_key_exists( $key, $this->get_columns() ) && ! in_array( $key, $this->get_hidden_columns() ) ) {
343
- $display = $this->get_columns();
344
- $column_name = $key;
345
- $mailAppend .= "<span class=\"title\">{$display[$key]}: </span>";
346
- if ( 'message' !== $column_name && method_exists( $this, 'column_' . $column_name ) ) {
347
- $mailAppend .= call_user_func( array( $this, 'column_' . $column_name ), $item );
348
- } else {
349
- $mailAppend .= $this->column_default( $item, $column_name );
350
- }
351
- }
352
- }
353
- return $mailAppend;
354
- }
355
- /**
356
- * Defines available bulk actions.
357
- * @since 1.0
358
- * @see WP_List_Table::get_bulk_actions()
359
- */
360
- function get_bulk_actions() {
361
- $actions = array(
362
- 'delete' => 'Delete',
363
- );
364
- return $actions;
365
- }
366
-
367
- /**
368
- * Processes bulk actions.
369
- * @since 1.0
370
- */
371
- function process_bulk_action() {
372
- if ( false === $this->current_action() ) {
373
- return;
374
- }
375
-
376
- if ( check_admin_referer( WPML_Email_Log_List::NONCE_LIST_TABLE, WPML_Email_Log_List::NONCE_LIST_TABLE . '_nonce' ) ) {
377
- $name = $this->_args['singular'];
378
-
379
- // Detect when a bulk action is being triggered.
380
- if ( 'delete' === $this->current_action() ) {
381
- foreach ( $_REQUEST[$name] as $item_id ) {
382
- $mail = Mail::find_one( $item_id );
383
- if ( false !== $mail ) {
384
- $mail->delete();
385
- }
386
- }
387
- }
388
- }
389
- }
390
-
391
- /**
392
- * Render the cb column
393
- * @since 1.0
394
- * @param array $item The current item.
395
- * @return string the rendered cb cell content
396
- */
397
- function column_cb($item) {
398
- $name = $this->_args['singular'];
399
- return sprintf(
400
- '<input type="checkbox" name="%1$s[]" value="%2$s" />', $name, $item['mail_id']
401
- );
402
- }
403
-
404
- /**
405
- * Define the sortable columns
406
- * @since 1.0
407
- * @return array
408
- */
409
- function get_sortable_columns() {
410
- return array(
411
- // Description: column_name => array( 'display_name', true[asc] | false[desc] ).
412
- 'mail_id' => array( 'mail_id', false ),
413
- 'timestamp' => array( 'timestamp', true ),
414
- 'host' => array( 'host', true ),
415
- 'receiver' => array( 'receiver', true ),
416
- 'subject' => array( 'subject', true ),
417
- 'message' => array( 'message', true ),
418
- 'headers' => array( 'headers', true ),
419
- 'attachments' => array( 'attachments', true ),
420
- 'plugin_version'=> array( 'plugin_version', true ),
421
- );
422
- }
423
-
424
- /**
425
- * Ajax function to retrieve rendered mail in certain format.
426
- * @since 1.6.0
427
- */
428
- public static function ajax_wpml_email_get() {
429
- $formats = is_array( $additional = apply_filters( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, array() ) ) ? $additional : array();
430
-
431
- check_ajax_referer( 'wpml-modal-show', 'ajax_nonce', true );
432
-
433
- if( ! isset( $_POST['id'] ) )
434
- wp_die( "huh?" );
435
- $id = intval( $_POST['id'] );
436
-
437
- $format_requested = isset( $_POST['format'] ) ? $_POST['format'] : 'html';
438
- if ( ! in_array( $format_requested, $formats ) ) {
439
- echo "Unsupported Format. Using html as fallback.";
440
- $format_requested = WPML_Utils::sanitize_expected_value($format_requested, $formats, 'html');
441
- }
442
- $mail = Mail::find_one( $id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  /* @var $instance WPML_Email_Log_List */
444
- $instance = WPML_Init::getInstance()->getService( 'emailLogList' );
445
- $mailAppend = '';
446
- switch( $format_requested ) {
447
- case 'html': {
448
- $mailAppend .= $instance->render_mail_html( $mail->to_array() );
449
- break;
450
- }
451
- case 'raw': {
452
- $mailAppend .= $instance->render_mail( $mail->to_array() );
453
- break;
454
- }
455
- case 'json': {
456
- $mailAppend .= json_encode( $mail->to_array() );
457
- break;
458
- }
459
- default:
460
- $mailAppend .= apply_filters( WPML_Plugin::HOOK_LOGGING_FORMAT_CONTENT . "_{$format_requested}", $mail->to_array() );
461
- break;
462
- }
463
- echo $mailAppend;
464
- wp_die(); // this is required to terminate immediately and return a proper response
 
 
 
 
 
 
465
  }
466
  }
3
  namespace No3x\WPML;
4
 
5
  use No3x\WPML\Model\WPML_Mail as Mail;
6
+ use No3x\WPML\Model\WPML_Mail;
7
 
8
  // Exit if accessed directly.
9
  if ( ! defined( 'ABSPATH' ) ) exit;
12
  require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
13
 
14
  if ( ! class_exists( 'WP_List_Table' ) ) {
15
+ require_once( plugin_dir_path( __FILE__ ) . 'inc/class-wp-list-table.php' );
16
  }
17
 
18
  /**
22
  */
23
  class WPML_Email_Log_List extends \WP_List_Table {
24
 
25
+ const NONCE_LIST_TABLE = 'wpml-list_table';
26
+ private $supported_formats = array();
27
+ /**
28
+ * Initializes the List Table
29
+ * @since 1.0
30
+ */
31
+ function __construct( $supported_formats = array() ) {
32
+ $this->supported_formats = $supported_formats;
33
+ }
34
+
35
+ function addActionsAndFilters() {
36
+ add_action( 'admin_init', array( $this, 'init') );
37
+ add_filter( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, function() {
38
+ return $this->supported_formats;
39
+ } );
40
+ add_action( 'wp_ajax_wpml_email_get', __CLASS__ . '::ajax_wpml_email_get' );
41
+ }
42
+
43
+ function init() {
44
+ global $status, $page, $hook_suffix;
45
+
46
+ parent::__construct( array(
47
+ 'singular' => 'email', // singular name of the listed records
48
+ 'plural' => 'emails', // plural name of the listed records
49
+ 'ajax' => false, // does this table support ajax?
50
+ ) );
51
+ }
52
+
53
+ /**
54
+ * Is displayed if no item is available to render
55
+ * @since 1.0
56
+ * @see WP_List_Table::no_items()
57
+ */
58
+ function no_items() {
59
+ _e( 'No email found.', 'wp-mail-logging' );
60
+ return;
61
+ }
62
+
63
+ /**
64
+ * Defines the available columns.
65
+ * @since 1.0
66
+ * @see WP_List_Table::get_columns()
67
+ */
68
+ function get_columns() {
69
+ $columns = array(
70
+ 'cb' => '<input type="checkbox" />',
71
+ 'mail_id' => __( 'ID', 'wp-mail-logging' ),
72
+ 'timestamp' => __( 'Time', 'wp-mail-logging' ),
73
+ 'receiver' => __( 'Receiver', 'wp-mail-logging' ),
74
+ 'subject' => __( 'Subject', 'wp-mail-logging' ),
75
+ 'message' => __( 'Message', 'wp-mail-logging' ),
76
+ 'headers' => __( 'Headers', 'wp-mail-logging' ),
77
+ 'attachments' => __( 'Attachments', 'wp-mail-logging' ),
78
+ 'error' => __( 'Error', 'wp-mail-logging' ),
79
+ 'plugin_version' => __( 'Plugin Version', 'wp-mail-logging' ),
80
+ );
81
 
82
  /* @var $instance WPML_Plugin */
83
  $instance = WPML_Init::getInstance()->getService( 'plugin' );
87
  $posAfterTimestamp = array_search('timestamp', array_keys($columns) ) + 1;
88
  $columns = array_merge(
89
  array_slice( $columns, 0, $posAfterTimestamp),
90
+ [ 'host' => __( 'Host', 'wp-mail-logging' ) ],
91
  array_slice( $columns, $posAfterTimestamp )
92
  );
93
  }
94
 
95
+ // Give a plugin the chance to edit the columns.
96
+ $columns = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS, $columns );
97
+
98
+ $reserved = array( '_title', 'comment', 'media', 'name', 'title', 'username', 'blogname' );
99
+
100
+ // Show message for reserved column names.
101
+ foreach ( $reserved as $reserved_key ) {
102
+ if ( array_key_exists( $reserved_key, $columns ) ) {
103
+ 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!";
104
+ break;
105
+ }
106
+ }
107
+ return $columns;
108
+ }
109
+
110
+ /**
111
+ * Define which columns are hidden
112
+ * @since 1.0
113
+ * @return array
114
+ */
115
+ function get_hidden_columns() {
116
+ return array(
117
+ 'plugin_version',
118
+ 'mail_id',
119
+ );
120
+ }
121
+
122
+ /**
123
+ * Sanitize orderby parameter.
124
+ * @s
125
+ * @return string sanitized orderby parameter
126
+ */
127
+ private function sanitize_orderby() {
128
+ return WPML_Utils::sanitize_expected_value( ( !empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : null, $this->get_sortable_columns(), 'mail_id');
129
+ }
130
+
131
+ /**
132
+ * Sanitize order parameter.
133
+ * @return string sanitized order parameter
134
+ */
135
+ private function sanitize_order() {
136
+ return WPML_Utils::sanitize_expected_value( ( !empty( $_GET['order'] ) ) ? $_GET['order'] : null, array('desc', 'asc'), 'desc');
137
+ }
138
+
139
+ /**
140
+ * Prepares the items for rendering
141
+ * @since 1.0
142
+ * @param string|boolean $search string you want to search for. Default false.
143
+ * @see WP_List_Table::prepare_items()
144
+ */
145
+ function prepare_items( $search = false ) {
146
+ $orderby = $this->sanitize_orderby();
147
+ $order = $this->sanitize_order();
148
+
149
+ $columns = $this->get_columns();
150
+ $hidden = $this->get_hidden_columns();
151
+ $sortable = $this->get_sortable_columns();
152
+ $this->_column_headers = array( $columns, $hidden, $sortable );
153
+
154
+ $this->process_bulk_action();
155
+
156
+ $per_page = $this->get_items_per_page( 'per_page', 25 );
157
+ $current_page = $this->get_pagenum();
158
+ $offset = ( $current_page - 1 ) * $per_page;
159
+
160
+ $total_items = Mail::query()
161
+ ->search( $search )
162
+ ->find( true );
163
+
164
+ $mails = Mail::query()
165
+ ->search( $search )
166
+ ->sort_by( $orderby )
167
+ ->order( $order )
168
+ ->limit( $per_page )
169
+ ->offset( $offset )
170
+ ->find();
171
+
172
+ foreach ( $mails as $mail ) {
173
+ /* @var $mail Mail */
174
+ $this->items[] = $mail->to_array();
175
+ }
176
+
177
+ $this->set_pagination_args( array(
178
+ 'total_items' => $total_items, // The total number of items.
179
+ 'per_page' => $per_page, // Number of items per page.
180
+ ) );
181
+ }
182
+
183
+ /**
184
+ * Renders the cell.
185
+ * 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)
186
+ * @since 1.0
187
+ * @param array $item The current item.
188
+ * @param string $column_name The current column name.
189
+ * @return string The cell content
190
+ */
191
+ function column_default( $item, $column_name ) {
192
+ switch ( $column_name ) {
193
+ case 'mail_id':
194
+ case 'timestamp':
195
+ case 'host':
196
+ case 'subject':
197
+ case 'message':
198
+ case 'headers':
199
+ case 'attachments':
200
+ case 'error':
201
+ case 'plugin_version':
202
+ case 'receiver':
203
+ return $item[ $column_name ];
204
+ default:
205
+ // 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).
206
+ return ( is_array( $res = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, $item, $column_name ) ) ) ? '' : $res;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Sanitize message to remove unsafe html.
212
+ * @since 1.5.1
213
+ * @param string $message unsafe message.
214
+ * @return string safe message.
215
+ */
216
+ function sanitize_message( $message ) {
217
+ $allowed_tags = wp_kses_allowed_html( 'post' );
218
+ $allowed_tags['a']['data-message'] = true;
219
+ $allowed_tags['style'][''] = true;
220
+ return wp_kses( $message, $allowed_tags );
221
+ }
222
+
223
+ /**
224
+ * Renders the message column.
225
+ * @since 1.3
226
+ * @param array $item The current item.
227
+ * @return string
228
+ */
229
+ function column_message( $item ) {
230
+ if ( empty( $item['message'] ) ) {
231
+ return '';
232
+ }
233
+ $content = $item['mail_id'];
234
+ $message = '<a class="wp-mail-logging-view-message button button-secondary" href="#" data-mail-id="' . esc_attr( $content ) . '">View</a>';
235
+ return $message;
236
+ }
237
+
238
+ /**
239
+ * Renders the timestamp column.
240
+ * @since 1.5.0
241
+ * @param array $item The current item.
242
+ * @return string
243
+ */
244
+ function column_timestamp( $item ) {
245
+ return date_i18n( apply_filters( 'wpml_get_date_time_format', '' ), strtotime( $item['timestamp'] ) );
246
+ }
247
+
248
+ /**
249
+ * Renders the attachment column in compbat mode for mails prior 1.6.0.
250
+ * @since 1.6.0
251
+ * @param array $item The current item.
252
+ * @return string The attachment column.
253
+ */
254
+ function column_attachments_compat_152( $item ) {
255
+ $attachment_append = '';
256
+ $attachments = explode( ',\n', $item['attachments'] );
257
+ $attachments = is_array( $attachments ) ? $attachments : array( $attachments );
258
+ foreach ( $attachments as $attachment ) {
259
+ // $attachment can be an empty string ''.
260
+ if ( ! empty( $attachment ) ) {
261
+ $filename = basename( $attachment );
262
+ $attachment_path = WP_CONTENT_DIR . $attachment;
263
+ $attachment_url = WP_CONTENT_URL . $attachment;
264
+ if ( is_file( $attachment_path ) ) {
265
+ $attachment_append .= '<a href="' . $attachment_url . '" title="' . $filename . '">' . WPML_Utils::generate_attachment_icon( $attachment_path ) . '</a> ';
266
+ } else {
267
+ $message = sprintf( __( 'Attachment %s is not present', 'wp-mail-logging' ), $filename );
268
+ $attachment_append .= '<i class="fa fa-times" title="' . $message . '"></i>';
269
+ }
270
+ }
271
+ }
272
+ return $attachment_append;
273
+ }
274
+
275
+ /**
276
+ * Renders the attachment column.
277
+ * @since 1.3
278
+ * @param array $item The current item.
279
+ * @return string The attachment column.
280
+ */
281
+ function column_attachments( $item ) {
282
+
283
+ if ( version_compare( trim( $item ['plugin_version'] ), '1.6.0', '<' ) ) {
284
+ return $this->column_attachments_compat_152( $item );
285
+ }
286
+
287
+ $attachment_append = '';
288
+ $attachments = explode( ',\n', $item['attachments'] );
289
+ $attachments = is_array( $attachments ) ? $attachments : array( $attachments );
290
+ foreach ( $attachments as $attachment ) {
291
+ // $attachment can be an empty string ''.
292
+ if ( ! empty( $attachment ) ) {
293
+ $filename = basename( $attachment );
294
+ $basename = '/uploads';
295
+ $attachment_path = WP_CONTENT_DIR . $basename . $attachment;
296
+ $attachment_url = WP_CONTENT_URL . $basename . $attachment;
297
+
298
+ if ( is_file( $attachment_path ) ) {
299
+ $attachment_append .= '<a href="' . $attachment_url . '" title="' . $filename . '">' . WPML_Utils::generate_attachment_icon( $attachment_path ) . '</a> ';
300
+ } else {
301
+ $message = sprintf( __( 'Attachment %s is not present', 'wp-mail-logging' ), $filename );
302
+ $attachment_append .= '<i class="fa fa-times" title="' . $message . '"></i>';
303
+ }
304
+ }
305
+ }
306
+ return $attachment_append;
307
+ }
308
+
309
+ /**
310
+ * Renders the error column.
311
+ * @since 1.8.0
312
+ * @param $item
313
+ * @return string
314
+ */
315
+ function column_error($item ) {
316
+ $error = $item['error'];
317
+ if( empty($error)) return "";
318
+ $errorMessage = is_array($error) ? join(',', $error) : $error;
319
+ return "<i class='fa fa-exclamation-circle' title='{$errorMessage}' aria-hidden='true'></i>";
320
+ }
321
+
322
+ /**
323
+ * Renders all components of the mail.
324
+ * @since 1.3
325
+ * @param array $item The current item.
326
+ * @return string The mail as html
327
+ */
328
+ function render_mail( $item ) {
329
+ $mailAppend = '';
330
+ foreach ( $item as $key => $value ) {
331
+ if ( array_key_exists( $key, $this->get_columns() ) && ! in_array( $key, $this->get_hidden_columns() ) ) {
332
+ $display = $this->get_columns();
333
+ $column_name = $key;
334
+ $title = "<span class=\"title\">{$display[$key]}: </span>";
335
+ $content = '';
336
+ if ( 'message' !== $column_name && method_exists( $this, 'column_' . $column_name ) ) {
337
+ if( 'error' === $column_name || 'attachments' === $column_name ) {
338
+ // don't render with icons and stuff, just plain
339
+ $content .= is_array($item[$column_name]) ? join("\n", $item[$column_name]) : $item[$column_name];
340
+ } else {
341
+ $content .= call_user_func( array( $this, 'column_' . $column_name ), $item );
342
+ }
343
+ } else {
344
+ $content .= $this->column_default( $item, $column_name );
345
+ }
346
+ $mailAppend .= $title . htmlentities( $content );
347
+ }
348
+ }
349
+
350
+ return $mailAppend;
351
+ }
352
+
353
+ /**
354
+ * Renders all components of the mail.
355
+ * @since 1.6.0
356
+ * @param array $item The current item.
357
+ * @return string The mail as html
358
+ */
359
+ function render_mail_html( $item ) {
360
+ $mailAppend = '';
361
+ foreach ( $item as $key => $value ) {
362
+ if ( array_key_exists( $key, $this->get_columns() ) && ! in_array( $key, $this->get_hidden_columns() ) ) {
363
+ $display = $this->get_columns();
364
+ $column_name = $key;
365
+ $mailAppend .= "<span class=\"title\">{$display[$key]}: </span>";
366
+ if ( 'message' !== $column_name && method_exists( $this, 'column_' . $column_name ) ) {
367
+ $mailAppend .= call_user_func( array( $this, 'column_' . $column_name ), $item );
368
+ } else {
369
+ $mailAppend .= $this->column_default( $item, $column_name );
370
+ }
371
+ }
372
+ }
373
+ return $mailAppend;
374
+ }
375
+ /**
376
+ * Defines available bulk actions.
377
+ * @since 1.0
378
+ * @see WP_List_Table::get_bulk_actions()
379
+ */
380
+ function get_bulk_actions() {
381
+ $actions = array(
382
+ 'delete' => 'Delete',
383
+ 'resend' => 'Resend'
384
+ );
385
+ return $actions;
386
+ }
387
+
388
+ /**
389
+ * Processes bulk actions.
390
+ * @since 1.0
391
+ */
392
+ function process_bulk_action() {
393
+ if ( false === $this->current_action() ) {
394
+ return;
395
+ }
396
+
397
+ if ( check_admin_referer( WPML_Email_Log_List::NONCE_LIST_TABLE, WPML_Email_Log_List::NONCE_LIST_TABLE . '_nonce' ) ) {
398
+ $name = $this->_args['singular'];
399
+
400
+ // Detect when a bulk action is being triggered.
401
+ if ( 'delete' === $this->current_action() ) {
402
+ foreach ( $_REQUEST[$name] as $item_id ) {
403
+ $mail = Mail::find_one( $item_id );
404
+ if ( false !== $mail ) {
405
+ $mail->delete();
406
+ }
407
+ }
408
+ } else if ( 'resend' == $this->current_action() ) {
409
+ foreach ( $_REQUEST[$name] as $item_id ) {
410
+ $mail = Mail::find_one( $item_id );
411
+ if ( false !== $mail ) {
412
+ $this->resend_email( $mail );
413
+ }
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Send logged email via wp_mail
421
+ * @param WPML_Mail $mail the email object to resend
422
+ * @since 1.8.0
423
+ */
424
+ function resend_email( $mail ) {
425
+ wp_mail( $mail->get_receiver(), $mail->get_subject(), $mail->get_message(), $mail->get_headers(), $mail->get_attachments() ) ;
426
+ }
427
+
428
+ /**
429
+ * Render the cb column
430
+ * @since 1.0
431
+ * @param array $item The current item.
432
+ * @return string the rendered cb cell content
433
+ */
434
+ function column_cb($item) {
435
+ $name = $this->_args['singular'];
436
+ return sprintf(
437
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />', $name, $item['mail_id']
438
+ );
439
+ }
440
+
441
+ /**
442
+ * Define the sortable columns
443
+ * @since 1.0
444
+ * @return array
445
+ */
446
+ function get_sortable_columns() {
447
+ return array(
448
+ // Description: column_name => array( 'display_name', true[asc] | false[desc] ).
449
+ 'mail_id' => array( 'mail_id', false ),
450
+ 'timestamp' => array( 'timestamp', true ),
451
+ 'host' => array( 'host', true ),
452
+ 'receiver' => array( 'receiver', true ),
453
+ 'subject' => array( 'subject', true ),
454
+ 'message' => array( 'message', true ),
455
+ 'headers' => array( 'headers', true ),
456
+ 'attachments' => array( 'attachments', true ),
457
+ 'plugin_version'=> array( 'plugin_version', true ),
458
+ );
459
+ }
460
+
461
+ /**
462
+ * Ajax function to retrieve rendered mail in certain format.
463
+ * @since 1.6.0
464
+ */
465
+ public static function ajax_wpml_email_get() {
466
+ $formats = is_array( $additional = apply_filters( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, array() ) ) ? $additional : array();
467
+
468
+ check_ajax_referer( 'wpml-modal-show', 'ajax_nonce', true );
469
+
470
+ if( ! isset( $_POST['id'] ) )
471
+ wp_die( "huh?" );
472
+ $id = intval( $_POST['id'] );
473
+
474
+ $format_requested = isset( $_POST['format'] ) ? $_POST['format'] : 'html';
475
+ if ( ! in_array( $format_requested, $formats ) ) {
476
+ echo "Unsupported Format. Using html as fallback.";
477
+ $format_requested = WPML_Utils::sanitize_expected_value($format_requested, $formats, 'html');
478
+ }
479
+ $mail = Mail::find_one( $id );
480
  /* @var $instance WPML_Email_Log_List */
481
+ $instance = WPML_Init::getInstance()->getService( 'emailLogList' );
482
+ $mailAppend = '';
483
+ switch( $format_requested ) {
484
+ case 'html': {
485
+ $mailAppend .= $instance->render_mail_html( $mail->to_array() );
486
+ break;
487
+ }
488
+ case 'raw': {
489
+ $mailAppend .= $instance->render_mail( $mail->to_array() );
490
+ break;
491
+ }
492
+ case 'json': {
493
+ if( stristr( str_replace(' ', '', $mail->get_headers()), "Content-Type:text/html")) {
494
+ // Fallback to raw in case it is a html mail
495
+ $mailAppend .= sprintf("<span class='info'>%s</span>", __("Fallback to raw format because html is not convertible to json.", 'wp-mail-logging' ) );
496
+ $mailAppend .= $instance->render_mail( $mail->to_array() );
497
+ } else {
498
+ $mailAppend .= "<pre>" . json_encode( $mail->to_array(), JSON_PRETTY_PRINT ) . "</pre>";
499
+ }
500
+ break;
501
+ }
502
+ default:
503
+ $mailAppend .= apply_filters( WPML_Plugin::HOOK_LOGGING_FORMAT_CONTENT . "_{$format_requested}", $mail->to_array() );
504
+ break;
505
+ }
506
+ echo $mailAppend;
507
+ wp_die(); // this is required to terminate immediately and return a proper response
508
  }
509
  }
WPML_Init.php CHANGED
@@ -28,125 +28,125 @@ if ( ! defined( 'ABSPATH' ) ) exit;
28
 
29
  class WPML_Init {
30
 
31
- /**
32
- * @var Singleton The reference to *Singleton* instance of this class
33
- */
34
- private static $instance;
35
- /**
36
- * @var WPML_DI_Container The DI Container
37
- */
38
- private $container;
39
-
40
- /**
41
- * Returns the *Singleton* instance of this class.
42
- *
43
- * @return WPML_Init The *Singleton* instance.
44
- */
45
- public static function getInstance() {
46
- if (null === static::$instance) {
47
- static::$instance = new static();
48
- }
49
-
50
- return static::$instance;
51
- }
52
-
53
- /**
54
- * Protected constructor to prevent creating a new instance of the
55
- * *Singleton* via the `new` operator from outside of this class.
56
- */
57
- protected function __construct() {
58
- $this->container = new WPML_DI_Container();
59
- }
60
-
61
- public function getClosure() {
62
- return function ($prop) {
63
- return $this->$prop;
64
- };
65
- }
66
-
67
- public function init( $file ) {
68
-
69
- $this->container['plugin'] = function ($c) {
70
- return new WPML_Plugin();
71
- };
72
- $this->container['plugin-meta'] = function ($c) {
73
- /* @var $plugin WPML_Plugin */
74
- $plugin = $c['plugin'];
75
- return array(
76
- 'path' => realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR,
77
- 'uri' => plugin_dir_url( __FILE__ ),
78
- 'display_name' => $plugin->getPluginDisplayName(),
79
- 'slug' => $plugin->getPluginSlug(),
80
- 'main_file' => $plugin->getMainPluginFileName(),
81
- 'description' => $plugin->getPluginHeaderValue( 'Description' ),
82
- 'version' => $plugin->getVersion(),
83
- 'version_installed' => $plugin->getVersionSaved(),
84
- 'author_name' => $plugin->getPluginHeaderValue( 'Author' ),
85
- 'author_uri' => $plugin->getPluginHeaderValue( 'Author URI' ),
86
- 'wp_uri' => $plugin->getPluginHeaderValue( 'Plugin URI' ),
87
- 'support_uri' => $plugin->getPluginHeaderValue( 'Support URI' ),
88
- 'license' => $plugin->getPluginHeaderValue( 'License' ),
89
- );
90
- };
91
- $this->container['emailLogList-supported-formats'] = function ($c) {
92
- return array(
93
- 'html',
94
- 'raw',
95
- 'json'
96
- );
97
- };
98
- $this->container['emailLogList'] = function ($c) {
99
- return new WPML_Email_Log_List( $c['emailLogList-supported-formats'] );
100
- };
101
- $this->container['redux'] = function ($c) {
102
- return new WPML_Redux_Framework_config( $c['plugin-meta'] );
103
- };
104
- $this->container['logRotation'] = function ($c) {
105
- return new WPML_LogRotation( $c['plugin-meta'] );
106
- };
107
- $this->container['api'] = function ($c) {
108
- // Uncomment for an API Example
109
- // return new WPML_API_Example();
110
- };
111
- $this->container->addActionsAndFilters();
112
-
113
- add_filter( 'wpml_get_di_container', function() {
114
- return $this->container;
115
- } );
116
-
117
- add_filter( 'wpml_get_di_service', function( $service ) {
118
- return $this->getService( $service );
119
- } );
120
-
121
- /*
122
- * Install the plugin
123
- * NOTE: this file gets run each time you *activate* the plugin.
124
- * So in WP when you "install" the plugin, all that does it dump its files in the plugin-templates directory
125
- * but it does not call any of its code.
126
- * So here, the plugin tracks whether or not it has run its install operation, and we ensure it is run only once
127
- * on the first activation
128
- */
129
- if ( ! $this->container['plugin']->isInstalled() ) {
130
- $this->container['plugin']->install();
131
- } else {
132
- // Perform any version-upgrade activities prior to activation (e.g. database changes).
133
- $this->container['plugin']->upgrade();
134
- }
135
-
136
- if ( ! $file ) {
137
- $file = __FILE__;
138
- }
139
- // Register the Plugin Activation Hook.
140
- register_activation_hook( $file, array( &$this->container['plugin'], 'activate' ) );
141
-
142
- // Register the Plugin Deactivation Hook.
143
- register_deactivation_hook( $file, array( &$this->container['plugin'], 'deactivate' ) );
144
- }
145
-
146
- public function getService( $key ) {
147
- if( in_array( $key, $this->container->keys() ) ) {
148
- return $this->container[ $key ];
149
- }
150
- throw new \Exception("Service '{$key}' is not registered");
151
- }
152
  }
28
 
29
  class WPML_Init {
30
 
31
+ /**
32
+ * @var Singleton The reference to *Singleton* instance of this class
33
+ */
34
+ private static $instance;
35
+ /**
36
+ * @var WPML_DI_Container The DI Container
37
+ */
38
+ private $container;
39
+
40
+ /**
41
+ * Returns the *Singleton* instance of this class.
42
+ *
43
+ * @return WPML_Init The *Singleton* instance.
44
+ */
45
+ public static function getInstance() {
46
+ if (null === static::$instance) {
47
+ static::$instance = new static();
48
+ }
49
+
50
+ return static::$instance;
51
+ }
52
+
53
+ /**
54
+ * Protected constructor to prevent creating a new instance of the
55
+ * *Singleton* via the `new` operator from outside of this class.
56
+ */
57
+ protected function __construct() {
58
+ $this->container = new WPML_DI_Container();
59
+ }
60
+
61
+ public function getClosure() {
62
+ return function ($prop) {
63
+ return $this->$prop;
64
+ };
65
+ }
66
+
67
+ public function init( $file ) {
68
+
69
+ $this->container['plugin'] = function ($c) {
70
+ return new WPML_Plugin();
71
+ };
72
+ $this->container['plugin-meta'] = function ($c) {
73
+ /* @var $plugin WPML_Plugin */
74
+ $plugin = $c['plugin'];
75
+ return array(
76
+ 'path' => realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR,
77
+ 'uri' => plugin_dir_url( __FILE__ ),
78
+ 'display_name' => $plugin->getPluginDisplayName(),
79
+ 'slug' => $plugin->getPluginSlug(),
80
+ 'main_file' => $plugin->getMainPluginFileName(),
81
+ 'description' => $plugin->getPluginHeaderValue( 'Description' ),
82
+ 'version' => $plugin->getVersion(),
83
+ 'version_installed' => $plugin->getVersionSaved(),
84
+ 'author_name' => $plugin->getPluginHeaderValue( 'Author' ),
85
+ 'author_uri' => $plugin->getPluginHeaderValue( 'Author URI' ),
86
+ 'wp_uri' => $plugin->getPluginHeaderValue( 'Plugin URI' ),
87
+ 'support_uri' => $plugin->getPluginHeaderValue( 'Support URI' ),
88
+ 'license' => $plugin->getPluginHeaderValue( 'License' ),
89
+ );
90
+ };
91
+ $this->container['emailLogList-supported-formats'] = function ($c) {
92
+ return array(
93
+ 'html',
94
+ 'raw',
95
+ 'json'
96
+ );
97
+ };
98
+ $this->container['emailLogList'] = function ($c) {
99
+ return new WPML_Email_Log_List( $c['emailLogList-supported-formats'] );
100
+ };
101
+ $this->container['redux'] = function ($c) {
102
+ return new WPML_Redux_Framework_config( $c['plugin-meta'] );
103
+ };
104
+ $this->container['logRotation'] = function ($c) {
105
+ return new WPML_LogRotation( $c['plugin-meta'] );
106
+ };
107
+ $this->container['api'] = function ($c) {
108
+ // Uncomment for an API Example
109
+ // return new WPML_API_Example();
110
+ };
111
+ $this->container->addActionsAndFilters();
112
+
113
+ add_filter( 'wpml_get_di_container', function() {
114
+ return $this->container;
115
+ } );
116
+
117
+ add_filter( 'wpml_get_di_service', function( $service ) {
118
+ return $this->getService( $service );
119
+ } );
120
+
121
+ /*
122
+ * Install the plugin
123
+ * NOTE: this file gets run each time you *activate* the plugin.
124
+ * So in WP when you "install" the plugin, all that does it dump its files in the plugin-templates directory
125
+ * but it does not call any of its code.
126
+ * So here, the plugin tracks whether or not it has run its install operation, and we ensure it is run only once
127
+ * on the first activation
128
+ */
129
+ if ( ! $this->container['plugin']->isInstalled() ) {
130
+ $this->container['plugin']->install();
131
+ } else {
132
+ // Perform any version-upgrade activities prior to activation (e.g. database changes).
133
+ $this->container['plugin']->upgrade();
134
+ }
135
+
136
+ if ( ! $file ) {
137
+ $file = __FILE__;
138
+ }
139
+ // Register the Plugin Activation Hook.
140
+ register_activation_hook( $file, array( &$this->container['plugin'], 'activate' ) );
141
+
142
+ // Register the Plugin Deactivation Hook.
143
+ register_deactivation_hook( $file, array( &$this->container['plugin'], 'deactivate' ) );
144
+ }
145
+
146
+ public function getService( $key ) {
147
+ if( in_array( $key, $this->container->keys() ) ) {
148
+ return $this->container[ $key ];
149
+ }
150
+ throw new \Exception("Service '{$key}' is not registered");
151
+ }
152
  }
WPML_InstallIndicator.php CHANGED
@@ -26,161 +26,146 @@ if ( ! defined( 'ABSPATH' ) ) exit;
26
 
27
  class WPML_InstallIndicator extends WPML_OptionsManager {
28
 
29
- const optionInstalled = '_installed';
30
- const optionVersion = '_version';
31
-
32
- /**
33
- * Checks if the plugin is installed.
34
- * @return bool indicating if the plugin is installed already
35
- */
36
- public function isInstalled() {
37
- return $this->getOption( self::optionInstalled ) == true;
38
- }
39
-
40
- /**
41
- * Note in DB that the plugin is installed
42
- * @return null
43
- */
44
- protected function markAsInstalled() {
45
- return $this->updateOption( self::optionInstalled, true );
46
- }
47
-
48
- /**
49
- * Note in DB that the plugin is uninstalled
50
- * @return bool returned form delete_option.
51
- * true implies the plugin was installed at the time of this call,
52
- * false implies it was not.
53
- */
54
- protected function markAsUnInstalled() {
55
- return $this->deleteOption( self::optionInstalled );
56
- }
57
-
58
- /**
59
- * Set a version string in the options. This is useful if you install upgrade and
60
- * need to check if an older version was installed to see if you need to do certain
61
- * upgrade housekeeping (e.g. changes to DB schema).
62
- * @return null
63
- */
64
- protected function getVersionSaved() {
65
- return $this->getOption( self::optionVersion );
66
- }
67
-
68
- /**
69
- * Set a version string in the options.
70
- * need to check if
71
- * @param string $version string best practice: use a dot-delimited string like '1.2.3' so version strings can be easily
72
- * compared using version_compare (http://php.net/manual/en/function.version-compare.php)
73
- * @return null
74
- */
75
- protected function setVersionSaved( $version ) {
76
- return $this->updateOption( self::optionVersion, $version );
77
- }
78
-
79
- /**
80
- * @return string name of the main plugin file that has the header section with
81
- * "Plugin Name", "Version", "Description", "Text Domain", etc.
82
- */
83
- protected function getMainPluginFileName() {
84
- return basename( dirname( __FILE__ ) ) . 'php';
85
- }
86
-
87
- /**
88
- * Get a value for input key in the header section of main plugin file.
89
- * E.g. "Plugin Name", "Version", "Description", "Text Domain", etc.
90
- * @param $key string plugin header key
91
- * @return string if found, otherwise null
92
- */
93
- public function getPluginHeaderValue($key) {
94
- // Read the string from the comment header of the main plugin file.
95
- $data = file_get_contents( $this->getPluginDir() . DIRECTORY_SEPARATOR . $this->getMainPluginFileName() );
96
- $match = array();
97
- preg_match( '/' . $key . ':\s*(.*)/', $data, $match );
98
- if ( count( $match ) >= 1 ) {
99
- return $match[1];
100
- }
101
- return null;
102
- }
103
-
104
- /**
105
- * If your subclass of this class lives in a different directory,
106
- * override this method with the exact same code. Since __FILE__ will
107
- * be different, you will then get the right dir returned.
108
- * @return string
109
- */
110
- protected function getPluginDir() {
111
- return dirname( __FILE__ );
112
- }
113
-
114
- /**
115
- * Version of this code.
116
- * Best practice: define version strings to be easily compared using version_compare()
117
- * (http://php.net/manual/en/function.version-compare.php)
118
- * NOTE: You should manually make this match the SVN tag for your main plugin file 'Version' release and 'Stable tag' in readme.txt
119
- * @return string
120
- */
121
- public function getVersion() {
122
- return $this->getPluginHeaderValue( 'Version' );
123
- }
124
-
125
- /**
126
- * Useful when checking for upgrades, can tell if the currently installed version is earlier than the
127
- * newly installed code. This case indicates that an upgrade has been installed and this is the first time it
128
- * has been activated, so any upgrade actions should be taken.
129
- * @return bool true if the version saved in the options is earlier than the version declared in getVersion().
130
- * true indicates that new code is installed and this is the first time it is activated, so upgrade actions
131
- * should be taken. Assumes that version string comparable by version_compare, examples: '1', '1.1', '1.1.1', '2.0', etc.
132
- */
133
- public function isInstalledCodeAnUpgrade() {
134
- return $this->isSavedVersionLessThan( $this->getVersion() );
135
- }
136
-
137
- /**
138
- * Used to see if the installed code is an earlier version than the input version
139
- * @param string $aVersion version string.
140
- * @return bool true if the saved version is earlier (by natural order) than the input version
141
- */
142
- public function isSavedVersionLessThan( $aVersion ) {
143
- return $this->isVersionLessThan( $this->getVersionSaved(), $aVersion );
144
- }
145
-
146
- /**
147
- * Used to see if the installed code is the same or earlier than the input version.
148
- * Useful when checking for an upgrade. If you haven't specified the number of the newer version yet,
149
- * but the last version (installed) was 2.3 (for example) you could check if
150
- * For example, $this->isSavedVersionLessThanEqual('2.3') == true indicates that the saved version is not upgraded
151
- * past 2.3 yet and therefore you would perform some appropriate upgrade action.
152
- * @param string $aVersion version string.
153
- * @return bool true if the saved version is earlier (by natural order) than the input version
154
- */
155
- public function isSavedVersionLessThanEqual( $aVersion ) {
156
- return $this->isVersionLessThanEqual( $this->getVersionSaved(), $aVersion );
157
- }
158
-
159
- /**
160
- * @param string $version1 version string such as '1', '1.1', '1.1.1', '2.0', etc.
161
- * @param string $version2 version string such as '1', '1.1', '1.1.1', '2.0', etc.
162
- * @return bool true if version_compare of $versions1 and $version2 shows $version1 as the same or earlier
163
- */
164
- public function isVersionLessThanEqual( $version1, $version2 ) {
165
- return ( version_compare( $version1, $version2 ) <= 0 );
166
- }
167
-
168
- /**
169
- * @param string $version1 version string such as '1', '1.1', '1.1.1', '2.0', etc.
170
- * @param string $version2 version string such as '1', '1.1', '1.1.1', '2.0', etc.
171
- * @return bool true if version_compare of $versions1 and $version2 shows $version1 as earlier
172
- */
173
- public function isVersionLessThan( $version1, $version2 ) {
174
- return ( version_compare( $version1, $version2 ) < 0 );
175
- }
176
-
177
- /**
178
- * Record the installed version to options.
179
- * This helps track was version is installed so when an upgrade is installed, it should call this when finished
180
- * upgrading to record the new current version
181
- * @return void
182
- */
183
- protected function saveInstalledVersion() {
184
- $this->setVersionSaved( $this->getVersion() );
185
- }
186
  }
26
 
27
  class WPML_InstallIndicator extends WPML_OptionsManager {
28
 
29
+ const optionInstalled = '_installed';
30
+ const optionVersion = '_version';
31
+
32
+ /**
33
+ * Checks if the plugin is installed.
34
+ * @return bool indicating if the plugin is installed already
35
+ */
36
+ public function isInstalled() {
37
+ global $wpdb;
38
+ $mails = $this->getTablename('mails');
39
+ $query = $wpdb->query("SHOW TABLES LIKE \"$mails\"");
40
+ return (bool) $query;
41
+ }
42
+
43
+ /**
44
+ * Set a version string in the options. This is useful if you install upgrade and
45
+ * need to check if an older version was installed to see if you need to do certain
46
+ * upgrade housekeeping (e.g. changes to DB schema).
47
+ * @return null
48
+ */
49
+ protected function getVersionSaved() {
50
+ return $this->getOption( self::optionVersion );
51
+ }
52
+
53
+ /**
54
+ * Set a version string in the options.
55
+ * need to check if
56
+ * @param string $version string best practice: use a dot-delimited string like '1.2.3' so version strings can be easily
57
+ * compared using version_compare (http://php.net/manual/en/function.version-compare.php)
58
+ * @return null
59
+ */
60
+ protected function setVersionSaved( $version ) {
61
+ return $this->updateOption( self::optionVersion, $version );
62
+ }
63
+
64
+ /**
65
+ * @return string name of the main plugin file that has the header section with
66
+ * "Plugin Name", "Version", "Description", "Text Domain", etc.
67
+ */
68
+ protected function getMainPluginFileName() {
69
+ return basename( dirname( __FILE__ ) ) . 'php';
70
+ }
71
+
72
+ /**
73
+ * Get a value for input key in the header section of main plugin file.
74
+ * E.g. "Plugin Name", "Version", "Description", "Text Domain", etc.
75
+ * @param $key string plugin header key
76
+ * @return string if found, otherwise null
77
+ */
78
+ public function getPluginHeaderValue($key) {
79
+ // Read the string from the comment header of the main plugin file.
80
+ $data = file_get_contents( $this->getPluginDir() . DIRECTORY_SEPARATOR . $this->getMainPluginFileName() );
81
+ $match = array();
82
+ preg_match( '/' . $key . ':\s*(.*)/', $data, $match );
83
+ if ( count( $match ) >= 1 ) {
84
+ return $match[1];
85
+ }
86
+ return null;
87
+ }
88
+
89
+ /**
90
+ * If your subclass of this class lives in a different directory,
91
+ * override this method with the exact same code. Since __FILE__ will
92
+ * be different, you will then get the right dir returned.
93
+ * @return string
94
+ */
95
+ protected function getPluginDir() {
96
+ return dirname( __FILE__ );
97
+ }
98
+
99
+ /**
100
+ * Version of this code.
101
+ * Best practice: define version strings to be easily compared using version_compare()
102
+ * (http://php.net/manual/en/function.version-compare.php)
103
+ * NOTE: You should manually make this match the SVN tag for your main plugin file 'Version' release and 'Stable tag' in readme.txt
104
+ * @return string
105
+ */
106
+ public function getVersion() {
107
+ return $this->getPluginHeaderValue( 'Version' );
108
+ }
109
+
110
+ /**
111
+ * Useful when checking for upgrades, can tell if the currently installed version is earlier than the
112
+ * newly installed code. This case indicates that an upgrade has been installed and this is the first time it
113
+ * has been activated, so any upgrade actions should be taken.
114
+ * @return bool true if the version saved in the options is earlier than the version declared in getVersion().
115
+ * true indicates that new code is installed and this is the first time it is activated, so upgrade actions
116
+ * should be taken. Assumes that version string comparable by version_compare, examples: '1', '1.1', '1.1.1', '2.0', etc.
117
+ */
118
+ public function isInstalledCodeAnUpgrade() {
119
+ return $this->isSavedVersionLessThan( $this->getVersion() );
120
+ }
121
+
122
+ /**
123
+ * Used to see if the installed code is an earlier version than the input version
124
+ * @param string $aVersion version string.
125
+ * @return bool true if the saved version is earlier (by natural order) than the input version
126
+ */
127
+ public function isSavedVersionLessThan( $aVersion ) {
128
+ return $this->isVersionLessThan( $this->getVersionSaved(), $aVersion );
129
+ }
130
+
131
+ /**
132
+ * Used to see if the installed code is the same or earlier than the input version.
133
+ * Useful when checking for an upgrade. If you haven't specified the number of the newer version yet,
134
+ * but the last version (installed) was 2.3 (for example) you could check if
135
+ * For example, $this->isSavedVersionLessThanEqual('2.3') == true indicates that the saved version is not upgraded
136
+ * past 2.3 yet and therefore you would perform some appropriate upgrade action.
137
+ * @param string $aVersion version string.
138
+ * @return bool true if the saved version is earlier (by natural order) than the input version
139
+ */
140
+ public function isSavedVersionLessThanEqual( $aVersion ) {
141
+ return $this->isVersionLessThanEqual( $this->getVersionSaved(), $aVersion );
142
+ }
143
+
144
+ /**
145
+ * @param string $version1 version string such as '1', '1.1', '1.1.1', '2.0', etc.
146
+ * @param string $version2 version string such as '1', '1.1', '1.1.1', '2.0', etc.
147
+ * @return bool true if version_compare of $versions1 and $version2 shows $version1 as the same or earlier
148
+ */
149
+ public function isVersionLessThanEqual( $version1, $version2 ) {
150
+ return ( version_compare( $version1, $version2 ) <= 0 );
151
+ }
152
+
153
+ /**
154
+ * @param string $version1 version string such as '1', '1.1', '1.1.1', '2.0', etc.
155
+ * @param string $version2 version string such as '1', '1.1', '1.1.1', '2.0', etc.
156
+ * @return bool true if version_compare of $versions1 and $version2 shows $version1 as earlier
157
+ */
158
+ public function isVersionLessThan( $version1, $version2 ) {
159
+ return ( version_compare( $version1, $version2 ) < 0 );
160
+ }
161
+
162
+ /**
163
+ * Record the installed version to options.
164
+ * This helps track was version is installed so when an upgrade is installed, it should call this when finished
165
+ * upgrading to record the new current version
166
+ * @return void
167
+ */
168
+ protected function saveInstalledVersion() {
169
+ $this->setVersionSaved( $this->getVersion() );
170
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  }
WPML_LifeCycle.php CHANGED
@@ -26,189 +26,186 @@ if ( ! defined( 'ABSPATH' ) ) exit;
26
 
27
  class WPML_LifeCycle extends WPML_InstallIndicator {
28
 
29
- public function install() {
30
-
31
- // Initialize Plugin Options
32
- $this->initOptions();
33
-
34
- // Initialize DB Tables used by the plugin
35
- $this->installDatabaseTables();
36
-
37
- // Other Plugin initialization - for the plugin writer to override as needed
38
- $this->otherInstall();
39
-
40
- // Record the installed version
41
- $this->saveInstalledVersion();
42
-
43
- // To avoid running install() more then once
44
- $this->markAsInstalled();
45
- }
46
-
47
- public function uninstall() {
48
- $this->otherUninstall();
49
-
50
- if ( $this->getSetting('delete-on-deactivation', false) == true ) {
51
- //TOOD: is multi site?
52
- $this->unInstallDatabaseTables();
53
- $this->deleteSavedOptions();
54
- $this->deleteVersionOption();
55
- }
56
- $this->markAsUnInstalled();
57
- }
58
-
59
- /**
60
- * Perform any version-upgrade activities prior to activation (e.g. database changes)
61
- * @return void
62
- */
63
- public function upgrade() {
64
- }
65
-
66
- /**
67
- * See: http://plugin.michael-simpson.com/?page_id=105
68
- * @return void
69
- */
70
- public function activate() {
71
- }
72
-
73
- /**
74
- * See: http://plugin.michael-simpson.com/?page_id=105
75
- * @return void
76
- */
77
- public function deactivate() {
78
- $this->uninstall();
79
- }
80
-
81
- /**
82
- * See: http://plugin.michael-simpson.com/?page_id=31
83
- * @return void
84
- */
85
- protected function initOptions() {
86
- }
87
-
88
- public function addActionsAndFilters() {
89
- }
90
-
91
- /**
92
- * See: http://plugin.michael-simpson.com/?page_id=101
93
- * Called by install() to create any database tables if needed.
94
- * Best Practice:
95
- * (1) Prefix all table names with $wpdb->prefix
96
- * (2) make table names lower case only
97
- * @return void
98
- */
99
- protected function installDatabaseTables() {
100
- }
101
-
102
- /**
103
- * See: http://plugin.michael-simpson.com/?page_id=101
104
- * Drop plugin-created tables on uninstall.
105
- * @return void
106
- */
107
- protected function unInstallDatabaseTables() {
108
- }
109
-
110
- /**
111
- * Override to add any additional actions to be done at install time
112
- * See: http://plugin.michael-simpson.com/?page_id=33
113
- * @return void
114
- */
115
- protected function otherInstall() {
116
- }
117
-
118
- /**
119
- * Override to add any additional actions to be done at uninstall time
120
- * See: http://plugin.michael-simpson.com/?page_id=33
121
- * @return void
122
- */
123
- protected function otherUninstall() {
124
- }
125
-
126
- /**
127
- * Puts the configuration page in the Plugins menu by default.
128
- * Override to put it elsewhere or create a set of submenus
129
- * Override with an empty implementation if you don't want a configuration page
130
- * @return void
131
- */
132
- public function addSettingsSubMenuPage() {
133
- $this->addSettingsSubMenuPageToPluginsMenu();
134
- //$this->addSettingsSubMenuPageToSettingsMenu();
135
- }
136
-
137
-
138
- protected function requireExtraPluginFiles() {
139
- require_once(ABSPATH . 'wp-includes/pluggable.php');
140
- require_once(ABSPATH . 'wp-admin/includes/plugin.php');
141
- }
142
-
143
- /**
144
- * @return string Slug name for the URL to the Setting page
145
- * (i.e. the page for setting options)
146
- */
147
- protected function getSettingsSlug() {
148
- return get_class($this) . 'Settings';
149
- }
150
-
151
- protected function addSettingsSubMenuPageToPluginsMenu() {
152
- $this->requireExtraPluginFiles();
153
- $displayName = $this->getPluginDisplayName();
154
- add_submenu_page('plugins.php',
155
- $displayName,
156
- $displayName,
157
- 'manage_options',
158
- $this->getSettingsSlug(),
159
- array(&$this, 'settingsPage'));
160
- }
161
-
162
-
163
- protected function addSettingsSubMenuPageToSettingsMenu() {
164
- $this->requireExtraPluginFiles();
165
- $displayName = $this->getPluginDisplayName();
166
- add_options_page($displayName,
167
- $displayName,
168
- 'manage_options',
169
- $this->getSettingsSlug(),
170
- array(&$this, 'settingsPage'));
171
- }
172
-
173
- /**
174
- * @param $name string name of a database table
175
- * @return string input prefixed with the WordPress DB table prefix
176
- * plus the prefix for this plugin (lower-cased) to avoid table name collisions.
177
- * The plugin prefix is lower-cases as a best practice that all DB table names are lower case to
178
- * avoid issues on some platforms
179
- */
180
- protected function prefixTableName($name) {
181
- global $wpdb;
182
- return $wpdb->prefix . strtolower($this->prefix($name));
183
- }
184
-
185
-
186
- /**
187
- * Convenience function for creating AJAX URLs.
188
- *
189
- * @param $actionName string the name of the ajax action registered in a call like
190
- * add_action('wp_ajax_actionName', array(&$this, 'functionName'));
191
- * and/or
192
- * add_action('wp_ajax_nopriv_actionName', array(&$this, 'functionName'));
193
- *
194
- * If have an additional parameters to add to the Ajax call, e.g. an "id" parameter,
195
- * you could call this function and append to the returned string like:
196
- * $url = $this->getAjaxUrl('myaction&id=') . urlencode($id);
197
- * or more complex:
198
- * $url = sprintf($this->getAjaxUrl('myaction&id=%s&var2=%s&var3=%s'), urlencode($id), urlencode($var2), urlencode($var3));
199
- *
200
- * @return string URL that can be used in a web page to make an Ajax call to $this->functionName
201
- */
202
- public function getAjaxUrl($actionName) {
203
- return admin_url('admin-ajax.php') . '?action=' . $actionName;
204
- }
205
-
206
- public function registerPluginActionLinks( $actions, $plugin_file ) {
207
- if ($this->getMainPluginFileName() == basename($plugin_file)) {
208
- $settings = array('settings' => '<a href="admin.php?page=wpml_plugin_settings">' . __('Settings', 'General') . '</a>');
209
- $actions = array_merge($settings, $actions);
210
- }
211
- return $actions;
212
- }
213
 
214
  }
26
 
27
  class WPML_LifeCycle extends WPML_InstallIndicator {
28
 
29
+ public function install() {
30
+
31
+ // Initialize Plugin Options
32
+ $this->initOptions();
33
+
34
+ // Initialize DB Tables used by the plugin
35
+ $this->installDatabaseTables();
36
+
37
+ // Other Plugin initialization - for the plugin writer to override as needed
38
+ $this->otherInstall();
39
+
40
+ // Record the installed version
41
+ $this->saveInstalledVersion();
42
+
43
+ }
44
+
45
+ public function uninstall() {
46
+ $this->otherUninstall();
47
+
48
+ if ( $this->getSetting('delete-on-deactivation', false) == true ) {
49
+ //TOOD: is multi site?
50
+ $this->unInstallDatabaseTables();
51
+ $this->deleteSavedOptions();
52
+ $this->deleteVersionOption();
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Perform any version-upgrade activities prior to activation (e.g. database changes)
58
+ * @return void
59
+ */
60
+ public function upgrade() {
61
+ }
62
+
63
+ /**
64
+ * See: http://plugin.michael-simpson.com/?page_id=105
65
+ * @return void
66
+ */
67
+ public function activate() {
68
+ }
69
+
70
+ /**
71
+ * See: http://plugin.michael-simpson.com/?page_id=105
72
+ * @return void
73
+ */
74
+ public function deactivate() {
75
+ $this->uninstall();
76
+ }
77
+
78
+ /**
79
+ * See: http://plugin.michael-simpson.com/?page_id=31
80
+ * @return void
81
+ */
82
+ protected function initOptions() {
83
+ }
84
+
85
+ public function addActionsAndFilters() {
86
+ }
87
+
88
+ /**
89
+ * See: http://plugin.michael-simpson.com/?page_id=101
90
+ * Called by install() to create any database tables if needed.
91
+ * Best Practice:
92
+ * (1) Prefix all table names with $wpdb->prefix
93
+ * (2) make table names lower case only
94
+ * @return void
95
+ */
96
+ protected function installDatabaseTables() {
97
+ }
98
+
99
+ /**
100
+ * See: http://plugin.michael-simpson.com/?page_id=101
101
+ * Drop plugin-created tables on uninstall.
102
+ * @return void
103
+ */
104
+ protected function unInstallDatabaseTables() {
105
+ }
106
+
107
+ /**
108
+ * Override to add any additional actions to be done at install time
109
+ * See: http://plugin.michael-simpson.com/?page_id=33
110
+ * @return void
111
+ */
112
+ protected function otherInstall() {
113
+ }
114
+
115
+ /**
116
+ * Override to add any additional actions to be done at uninstall time
117
+ * See: http://plugin.michael-simpson.com/?page_id=33
118
+ * @return void
119
+ */
120
+ protected function otherUninstall() {
121
+ }
122
+
123
+ /**
124
+ * Puts the configuration page in the Plugins menu by default.
125
+ * Override to put it elsewhere or create a set of submenus
126
+ * Override with an empty implementation if you don't want a configuration page
127
+ * @return void
128
+ */
129
+ public function addSettingsSubMenuPage() {
130
+ $this->addSettingsSubMenuPageToPluginsMenu();
131
+ //$this->addSettingsSubMenuPageToSettingsMenu();
132
+ }
133
+
134
+
135
+ protected function requireExtraPluginFiles() {
136
+ require_once(ABSPATH . 'wp-includes/pluggable.php');
137
+ require_once(ABSPATH . 'wp-admin/includes/plugin.php');
138
+ }
139
+
140
+ /**
141
+ * @return string Slug name for the URL to the Setting page
142
+ * (i.e. the page for setting options)
143
+ */
144
+ protected function getSettingsSlug() {
145
+ return get_class($this) . 'Settings';
146
+ }
147
+
148
+ protected function addSettingsSubMenuPageToPluginsMenu() {
149
+ $this->requireExtraPluginFiles();
150
+ $displayName = $this->getPluginDisplayName();
151
+ add_submenu_page('plugins.php',
152
+ $displayName,
153
+ $displayName,
154
+ 'manage_options',
155
+ $this->getSettingsSlug(),
156
+ array(&$this, 'settingsPage'));
157
+ }
158
+
159
+
160
+ protected function addSettingsSubMenuPageToSettingsMenu() {
161
+ $this->requireExtraPluginFiles();
162
+ $displayName = $this->getPluginDisplayName();
163
+ add_options_page($displayName,
164
+ $displayName,
165
+ 'manage_options',
166
+ $this->getSettingsSlug(),
167
+ array(&$this, 'settingsPage'));
168
+ }
169
+
170
+ /**
171
+ * @param $name string name of a database table
172
+ * @return string input prefixed with the WordPress DB table prefix
173
+ * plus the prefix for this plugin (lower-cased) to avoid table name collisions.
174
+ * The plugin prefix is lower-cases as a best practice that all DB table names are lower case to
175
+ * avoid issues on some platforms
176
+ */
177
+ protected function prefixTableName($name) {
178
+ global $wpdb;
179
+ return $wpdb->prefix . strtolower($this->prefix($name));
180
+ }
181
+
182
+
183
+ /**
184
+ * Convenience function for creating AJAX URLs.
185
+ *
186
+ * @param $actionName string the name of the ajax action registered in a call like
187
+ * add_action('wp_ajax_actionName', array(&$this, 'functionName'));
188
+ * and/or
189
+ * add_action('wp_ajax_nopriv_actionName', array(&$this, 'functionName'));
190
+ *
191
+ * If have an additional parameters to add to the Ajax call, e.g. an "id" parameter,
192
+ * you could call this function and append to the returned string like:
193
+ * $url = $this->getAjaxUrl('myaction&id=') . urlencode($id);
194
+ * or more complex:
195
+ * $url = sprintf($this->getAjaxUrl('myaction&id=%s&var2=%s&var3=%s'), urlencode($id), urlencode($var2), urlencode($var3));
196
+ *
197
+ * @return string URL that can be used in a web page to make an Ajax call to $this->functionName
198
+ */
199
+ public function getAjaxUrl($actionName) {
200
+ return admin_url('admin-ajax.php') . '?action=' . $actionName;
201
+ }
202
+
203
+ public function registerPluginActionLinks( $actions, $plugin_file ) {
204
+ if ($this->getMainPluginFileName() == basename($plugin_file)) {
205
+ $settings = array('settings' => '<a href="admin.php?page=wpml_plugin_settings">' . __('Settings', 'General') . '</a>');
206
+ $actions = array_merge($settings, $actions);
207
+ }
208
+ return $actions;
209
+ }
 
 
 
210
 
211
  }
WPML_LogRotation.php CHANGED
@@ -2,8 +2,6 @@
2
 
3
  namespace No3x\WPML;
4
 
5
- use No3x\WPML\Model\WPML_Mail as Mail;
6
-
7
  // Exit if accessed directly.
8
  if ( ! defined( 'ABSPATH' ) ) exit;
9
 
@@ -14,71 +12,79 @@ if ( ! defined( 'ABSPATH' ) ) exit;
14
  */
15
  class WPML_LogRotation {
16
 
17
- const WPML_LOGROTATION_SCHEDULE_HOOK = 'wpml_log_rotation';
18
- const WPML_LOGROTATION_SCHEDULE_ACTION = 'LogRotationSchedule';
19
- private $plugin_meta;
20
-
21
- function __construct( $plugin_meta ) {
22
- $this->plugin_meta = $plugin_meta;
23
- }
24
-
25
- /**
26
- * Add actions and filters for this module.
27
- * @since 1.6.0
28
- */
29
- public function addActionsAndFilters() {
30
- add_action( 'plugins_loaded', array( $this, 'init') );
31
- add_action( self::WPML_LOGROTATION_SCHEDULE_HOOK , array( __CLASS__, self::WPML_LOGROTATION_SCHEDULE_ACTION) );
32
- register_deactivation_hook( plugin_dir_path( __FILE__ ) . $this->plugin_meta['main_file'], array( $this, 'unschedule' ) );
33
- }
34
-
35
- /**
36
- * Init this module.
37
- * @since 1.6.0
38
- */
39
- public function init() {
40
- global $wpml_settings;
41
-
42
- if ( isset( $wpml_settings ) ) {
43
- if ( $wpml_settings['log-rotation-limit-amout'] == true || $wpml_settings['log-rotation-delete-time'] == true ) {
44
- $this->schedule();
45
- } else {
46
- $this->unschedule();
47
- }
48
- }
49
- }
50
-
51
- /**
52
- * Schedules an event.
53
- * @since 1.4
54
- */
55
- function schedule() {
56
- if ( ! wp_next_scheduled( self::WPML_LOGROTATION_SCHEDULE_HOOK ) ) {
57
- wp_schedule_event( time(), 'hourly', self::WPML_LOGROTATION_SCHEDULE_HOOK );
58
- }
59
- }
60
-
61
- /**
62
- * Unschedules an event.
63
- * @since 1.4
64
- */
65
- function unschedule() {
66
- wp_clear_scheduled_hook( self::WPML_LOGROTATION_SCHEDULE_HOOK );
67
- }
68
-
69
- /**
70
- * The LogRotation supports the limitation of stored mails by amount.
71
- * @since 1.6.0
72
- */
73
- static function limitNumberOfMailsByAmount() {
74
- global $wpml_settings, $wpdb;
75
- $tableName = WPML_Plugin::getTablename( 'mails' );
76
-
77
- if ( $wpml_settings['log-rotation-limit-amout'] == true) {
78
- $keep = $wpml_settings['log-rotation-limit-amout-keep'];
79
- if ( $keep > 0 ) {
80
- $wpdb->query(
81
- "DELETE p
 
 
 
 
 
 
 
 
82
  FROM
83
  $tableName AS p
84
  JOIN
@@ -88,33 +94,38 @@ class WPML_LogRotation {
88
  LIMIT 1 OFFSET $keep
89
  ) AS lim
90
  ON p.mail_id <= lim.mail_id;"
91
- );
92
- }
93
- }
94
- }
95
-
96
- /**
97
- * The LogRotation supports the limitation of stored mails by date.
98
- * @since 1.6.0
99
- */
100
- static function limitNumberOfMailsByTime() {
101
- global $wpml_settings, $wpdb;
102
- $tableName = WPML_Plugin::getTablename( 'mails' );
103
-
104
- if ( $wpml_settings['log-rotation-delete-time'] == true) {
105
- $days = $wpml_settings['log-rotation-delete-time-days'];
106
- if ( $days > 0 ) {
107
- $wpdb->query( "DELETE FROM `$tableName` WHERE DATEDIFF( NOW(), `timestamp` ) >= $days" );
108
- }
109
- }
110
- }
111
-
112
- /**
113
- * Executes log rotation periodically.
114
- * @since 1.4
115
- */
116
- static function LogRotationSchedule() {
117
- self::limitNumberOfMailsByAmount();
118
- self::limitNumberOfMailsByTime();
119
- }
120
- }
 
 
 
 
 
2
 
3
  namespace No3x\WPML;
4
 
 
 
5
  // Exit if accessed directly.
6
  if ( ! defined( 'ABSPATH' ) ) exit;
7
 
12
  */
13
  class WPML_LogRotation {
14
 
15
+ const WPML_LOGROTATION_SCHEDULE_HOOK = 'wpml_log_rotation';
16
+ const WPML_LOGROTATION_SCHEDULE_ACTION = 'LogRotationSchedule';
17
+ private $plugin_meta;
18
+
19
+ function __construct( $plugin_meta ) {
20
+ $this->plugin_meta = $plugin_meta;
21
+ }
22
+
23
+ /**
24
+ * Add actions and filters for this module.
25
+ * @since 1.6.0
26
+ */
27
+ public function addActionsAndFilters() {
28
+ add_action( 'plugins_loaded', array( $this, 'init') );
29
+ add_action( self::WPML_LOGROTATION_SCHEDULE_HOOK , array( __CLASS__, self::WPML_LOGROTATION_SCHEDULE_ACTION) );
30
+ register_deactivation_hook( plugin_dir_path( __FILE__ ) . $this->plugin_meta['main_file'], array( $this, 'unschedule' ) );
31
+ }
32
+
33
+ /**
34
+ * Init this module.
35
+ * @since 1.6.0
36
+ */
37
+ public function init() {
38
+ global $wpml_settings;
39
+
40
+ // if plugin is installed the first time settings are not initialized properly so quit early.
41
+ if( !isset($wpml_settings) || !array_key_exists('log-rotation-limit-amout', $wpml_settings) ) {
42
+ return;
43
+ }
44
+
45
+ if ( $wpml_settings['log-rotation-limit-amout'] == true || $wpml_settings['log-rotation-delete-time'] == true ) {
46
+ $this->schedule();
47
+ } else {
48
+ $this->unschedule();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Schedules an event.
54
+ * @since 1.4
55
+ */
56
+ function schedule() {
57
+ if ( ! wp_next_scheduled( self::WPML_LOGROTATION_SCHEDULE_HOOK ) ) {
58
+ wp_schedule_event( time(), 'hourly', self::WPML_LOGROTATION_SCHEDULE_HOOK );
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Unschedules an event.
64
+ * @since 1.4
65
+ */
66
+ function unschedule() {
67
+ wp_clear_scheduled_hook( self::WPML_LOGROTATION_SCHEDULE_HOOK );
68
+ }
69
+
70
+ /**
71
+ * The LogRotation supports the limitation of stored mails by amount.
72
+ * @since 1.6.0
73
+ */
74
+ static function limitNumberOfMailsByAmount() {
75
+ global $wpml_settings, $wpdb;
76
+
77
+ if(!isset($wpml_settings)) {
78
+ return;
79
+ }
80
+
81
+ $tableName = WPML_Plugin::getTablename( 'mails' );
82
+
83
+ if ( $wpml_settings['log-rotation-limit-amout'] == true) {
84
+ $keep = $wpml_settings['log-rotation-limit-amout-keep'];
85
+ if ( $keep > 0 ) {
86
+ $wpdb->query(
87
+ "DELETE p
88
  FROM
89
  $tableName AS p
90
  JOIN
94
  LIMIT 1 OFFSET $keep
95
  ) AS lim
96
  ON p.mail_id <= lim.mail_id;"
97
+ );
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * The LogRotation supports the limitation of stored mails by date.
104
+ * @since 1.6.0
105
+ */
106
+ static function limitNumberOfMailsByTime() {
107
+ global $wpml_settings, $wpdb;
108
+
109
+ if(!isset($wpml_settings)) {
110
+ return;
111
+ }
112
+
113
+ $tableName = WPML_Plugin::getTablename( 'mails' );
114
+
115
+ if ( $wpml_settings['log-rotation-delete-time'] == true) {
116
+ $days = $wpml_settings['log-rotation-delete-time-days'];
117
+ if ( $days > 0 ) {
118
+ $wpdb->query( "DELETE FROM `$tableName` WHERE DATEDIFF( NOW(), `timestamp` ) >= $days" );
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Executes log rotation periodically.
125
+ * @since 1.4
126
+ */
127
+ static function LogRotationSchedule() {
128
+ self::limitNumberOfMailsByAmount();
129
+ self::limitNumberOfMailsByTime();
130
+ }
131
+ }
WPML_OptionsManager.php CHANGED
@@ -25,580 +25,580 @@ namespace No3x\WPML;
25
  if ( ! defined( 'ABSPATH' ) ) exit;
26
 
27
  class WPML_OptionsManager {
28
- /**
29
- * Is used to retrive a settings value
30
- * Important: This implementation understands bool for $default. (unlikely in comparision to all other settings implementation)
31
- * @since 1.4
32
- * @param string $settingName The option name to return
33
- * @param mixed $default (null) The value to return if option not set.
34
- * @return ambigous <string, mixed> the options value or $default if not found.
35
- */
36
- public function getSetting($settingName, $default = null) {
37
- global $wpml_settings;
38
-
39
- if ( array_key_exists($settingName, $wpml_settings)) {
40
- $retVal = $wpml_settings[$settingName];
41
- }
42
- if (!isset($retVal) && $default !== null) {
43
- $retVal = $default;
44
- }
45
- return $retVal;
46
- }
47
-
48
- /**
49
- * Returns the appropriate datetime format string.
50
- * @since 1.5.0
51
- * @return string datetime format string
52
- */
53
- public function getDateTimeFormatString() {
54
- // default database like format
55
- $format = 'Y-m-d G:i:s';
56
- $date_format = get_option( 'date_format' );
57
- $time_format = get_option( 'time_format' );
58
- // get option or change to user friendly format as the options maybe not set at all
59
- $date_format = empty( $date_format ) ? 'F j, Y' : $date_format;
60
- $time_format = empty( $time_format ) ? 'g:i a' : $time_format;
61
- if ( $this->getSetting( 'datetimeformat-use-wordpress', false) == true )
62
- // Overwrite with defined values or default
63
- $format = $date_format . " " . $time_format;
64
- return $format;
65
- }
66
-
67
- public function getOptionNamePrefix() {
68
- return $this->getClassnameWithoutNamespace() . '_';
69
- }
70
-
71
- /**
72
- * Define your options meta data here as an array, where each element in the array
73
- * @return array of key=>display-name and/or key=>array(display-name, choice1, choice2, ...)
74
- * key: an option name for the key (this name will be given a prefix when stored in
75
- * the database to ensure it does not conflict with other plugin options)
76
- * value: can be one of two things:
77
- * (1) string display name for displaying the name of the option to the user on a web page
78
- * (2) array where the first element is a display name (as above) and the rest of
79
- * the elements are choices of values that the user can select
80
- * e.g.
81
- * array(
82
- * 'item' => 'Item:', // key => display-name
83
- * 'rating' => array( // key => array ( display-name, choice1, choice2, ...)
84
- * 'CanDoOperationX' => array('Can do Operation X', 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber'),
85
- * 'Rating:', 'Excellent', 'Good', 'Fair', 'Poor')
86
- */
87
- public function getOptionMetaData() {
88
- return array();
89
- }
90
-
91
- /**
92
- * @return array of string name of options
93
- */
94
- public function getOptionNames() {
95
- return array_keys($this->getOptionMetaData());
96
- }
97
-
98
- /**
99
- * Override this method to initialize options to default values and save to the database with add_option
100
- * @return void
101
- */
102
- protected function initOptions() {
103
- }
104
-
105
- /**
106
- * Cleanup: remove all options from the DB
107
- * @return void
108
- */
109
- protected function deleteSavedOptions() {
110
- $optionMetaData = $this->getOptionMetaData();
111
- if (is_array($optionMetaData)) {
112
- foreach ($optionMetaData as $aOptionKey => $aOptionMeta) {
113
- $prefixedOptionName = $this->prefix($aOptionKey); // how it is stored in DB
114
- delete_option($prefixedOptionName);
115
- }
116
- }
117
- }
118
-
119
- /**
120
- * Cleanup: remove version option
121
- * @since 1.6.0
122
- * @return void
123
- */
124
- protected function deleteVersionOption() {
125
- delete_option( $this->prefix( WPML_Plugin::optionVersion ) );
126
- }
127
-
128
- /**
129
- * @return string display name of the plugin to show as a name/title in HTML.
130
- * Just returns the class name. Override this method to return something more readable
131
- */
132
- public function getPluginDisplayName() {
133
- return get_class($this);
134
- }
135
-
136
- /**
137
- * @return string slug of the plugin to use as identifier.
138
- * Just returns the class name in lowercase.
139
- */
140
- public function getPluginSlug() {
141
- return strtolower( $this->getClassnameWithoutNamespace() );
142
- }
143
-
144
- /**
145
- * Get the class name without the namespace
146
- * @return string class name without the namespace.
147
- * @link http://php.net/manual/de/function.get-class.php#114568
148
- */
149
- private function getClassnameWithoutNamespace() {
150
- $classname = get_class($this);
151
- if ($pos = strrpos( $classname, '\\')) {
152
- return substr($classname, $pos + 1);
153
- }
154
- return $classname;
155
- }
156
-
157
- /**
158
- * Get the prefixed version input $name suitable for storing in WP options
159
- * Idempotent: if $optionName is already prefixed, it is not prefixed again, it is returned without change
160
- * @param $name string option name to prefix. Defined in settings.php and set as keys of $this->optionMetaData
161
- * @return string
162
- */
163
- public function prefix($name) {
164
- $optionNamePrefix = $this->getOptionNamePrefix();
165
- if (strpos($name, $optionNamePrefix) === 0) { // 0 but not false
166
- return $name; // already prefixed
167
- }
168
- return $optionNamePrefix . $name;
169
- }
170
-
171
- /**
172
- * Remove the prefix from the input $name.
173
- * Idempotent: If no prefix found, just returns what was input.
174
- * @param $name string
175
- * @return string $optionName without the prefix.
176
- */
177
- public function &unPrefix($name) {
178
- $optionNamePrefix = $this->getOptionNamePrefix();
179
- if (strpos($name, $optionNamePrefix) === 0) {
180
- return substr($name, strlen($optionNamePrefix));
181
- }
182
- return $name;
183
- }
184
-
185
- /**
186
- * A wrapper function delegating to WP get_option() but it prefixes the input $optionName
187
- * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
188
- * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
189
- * @param $default string default value to return if the option is not set
190
- * @return string the value from delegated call to get_option(), or optional default value
191
- * if option is not set.
192
- */
193
- public function getOption($optionName, $default = null) {
194
- $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
195
- $retVal = get_option($prefixedOptionName);
196
- if (!$retVal && $default) {
197
- $retVal = $default;
198
- }
199
- return $retVal;
200
- }
201
-
202
- /**
203
- * A wrapper function delegating to WP delete_option() but it prefixes the input $optionName
204
- * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
205
- * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
206
- * @return bool from delegated call to delete_option()
207
- */
208
- public function deleteOption($optionName) {
209
- $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
210
- return delete_option($prefixedOptionName);
211
- }
212
-
213
- /**
214
- * A wrapper function delegating to WP add_option() but it prefixes the input $optionName
215
- * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
216
- * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
217
- * @param $value mixed the new value
218
- * @return null from delegated call to delete_option()
219
- */
220
- public function addOption($optionName, $value) {
221
- $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
222
- return add_option($prefixedOptionName, $value);
223
- }
224
-
225
- /**
226
- * A wrapper function delegating to WP add_option() but it prefixes the input $optionName
227
- * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
228
- * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
229
- * @param $value mixed the new value
230
- * @return null from delegated call to delete_option()
231
- */
232
- public function updateOption($optionName, $value) {
233
- $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
234
- return update_option($prefixedOptionName, $value);
235
- }
236
-
237
- /**
238
- * A Role Option is an option defined in getOptionMetaData() as a choice of WP standard roles, e.g.
239
- * 'CanDoOperationX' => array('Can do Operation X', 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber')
240
- * The idea is use an option to indicate what role level a user must minimally have in order to do some operation.
241
- * So if a Role Option 'CanDoOperationX' is set to 'Editor' then users which role 'Editor' or above should be
242
- * able to do Operation X.
243
- * Also see: canUserDoRoleOption()
244
- * @param $optionName
245
- * @return string role name
246
- */
247
- public function getRoleOption($optionName) {
248
- $roleAllowed = $this->getOption($optionName);
249
- if (!$roleAllowed || $roleAllowed == '') {
250
- $roleAllowed = 'Administrator';
251
- }
252
- return $roleAllowed;
253
- }
254
-
255
- /**
256
- * Given a WP role name (case insensitive), return a WP capability which only that role and roles above it have.
257
- * http://codex.wordpress.org/Roles_and_Capabilities
258
- * @param $roleName
259
- * @return string a WP capability or '' if unknown input role
260
- */
261
- protected function roleToCapability($roleName) {
262
- switch ( ucfirst( $roleName ) ) {
263
- case 'Super Admin':
264
- return 'manage_options';
265
- case 'Administrator':
266
- return 'manage_options';
267
- case 'Editor':
268
- return 'publish_pages';
269
- case 'Author':
270
- return 'publish_posts';
271
- case 'Contributor':
272
- return 'edit_posts';
273
- case 'Subscriber':
274
- return 'read';
275
- case 'Anyone':
276
- return 'read';
277
- }
278
- return '';
279
- }
280
-
281
- /**
282
- * @param $roleName string a standard WP role name like 'Administrator'
283
- * @return bool
284
- */
285
- public function isUserRoleEqualOrBetterThan($roleName) {
286
- if ('Anyone' == $roleName) {
287
- return true;
288
- }
289
- $capability = $this->roleToCapability($roleName);
290
- return current_user_can($capability);
291
- }
292
-
293
- /**
294
- * @param $optionName string name of a Role option (see comments in getRoleOption())
295
- * @return bool indicates if the user has adequate permissions
296
- */
297
- public function canUserDoRoleOption($optionName) {
298
- $roleAllowed = $this->getRoleOption($optionName);
299
- if ('Anyone' == $roleAllowed) {
300
- return true;
301
- }
302
- return $this->isUserRoleEqualOrBetterThan($roleAllowed);
303
- }
304
-
305
- /**
306
- * see: http://codex.wordpress.org/Creating_Options_Pages
307
- * @return void
308
- */
309
- public function createSettingsMenu() {
310
-
311
- global $wp_version;
312
- global $wp_logging_list_page;
313
-
314
- $pluginIcon = '';
315
- if ( $wp_version >= 3.8 ) $pluginIcon = 'dashicons-email-alt';
316
-
317
- $pluginNameSlug = $this->getPluginSlug();
318
- $capability = $this->getSetting( 'can-see-submission-data', 'manage_options' );
319
-
320
- //create new top-level menu
321
- $wp_logging_list_page = add_menu_page(__('WP Mail Log', 'wpml'),
322
- __('WP Mail Log', 'wpml'),
323
- $capability,
324
- $pluginNameSlug . '_log',
325
- array(&$this, 'LogMenu'),
326
- $pluginIcon
327
- );
328
-
329
- // Add Action to load assets when page is loaded
330
- add_action( 'load-' . $wp_logging_list_page, array( $this, 'load_assets' ) );
331
-
332
- add_submenu_page($pluginNameSlug . '_log',
333
- __('About', 'wpml'),
334
- __('About', 'wpml'),
335
- $capability,
336
- $pluginNameSlug . '_about',
337
- array(&$this, 'LogSubMenuAbout') );
338
-
339
- add_action( 'contextual_help', array( &$this, 'create_settings_panel' ), 10, 3 );
340
- }
341
-
342
- public function LogSubMenuAbout() {
343
- ?>
344
- <div class="wrap">
345
- <h2><?php echo $this->getPluginDisplayName(); echo ' '; _e('About', 'wpml'); ?></h2>
346
- <h3>Why use?</h3>
347
- <p>Sometimes you may ask yourself if a mail was actually sent by WordPress - with
348
- <strong>With <?php echo $this->getPluginDisplayName(); ?>, you can:</strong></p>
349
- <ul>
350
- <li>View a complete list of sent mails.</li>
351
- <li>Search for mails.</li>
352
- <li>Count on regular updates, enhancements, and troubleshooting.</li>
353
- <li>DevOP: IP of server sent the mail</li>
354
- <li>Developer: Boost your development performance by keeping track of sent mails from your WordPress site.</li>
355
- <li>Developer: Use Filters that are provided to extend the columns.</li>
356
- </ul>
357
- <h3>Contributors</h3>
358
- <p>This plugin is open source and some people helped to make it better:</p>
359
- <ul>
360
- <li>tripflex</li>
361
- <li><a href="http://www.grafixone.co.za" title="GrafixONE">Andr&eacute; Groenewald</a> (Icon)</li>
362
- </ul>
363
- <h3>Donate</h3>
364
- <p>Please consider to make a donation if you like the plugin. I spent a lot of time for support, enhancements and updates in general.</p>
365
- <a title="Donate" class="button button-primary" href="http://no3x.de/web/donate">Donate</a>
366
- </div>
367
- <?php
368
- }
369
-
370
- public function load_assets() {
371
-
372
- global $wp_logging_list_page;
373
- $screen = get_current_screen();
374
-
375
- if ( $screen->id != $wp_logging_list_page )
376
- return;
377
-
378
- // Enqueue Styles and Scripts if we're on the list page
379
- wp_enqueue_script( 'wp-logging-modal', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/js/modal.js', array( 'jquery' ), '1.0.0', true );
380
- wp_localize_script( 'wp-logging-modal', 'wpml_modal', array('ajax_nonce' => wp_create_nonce( 'wpml-modal-show' ) ) );
381
- wp_enqueue_style( 'wp-logging-modal', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/css/modal.css', array(), '1.0.0' );
382
- wp_enqueue_style( 'wp-logging-icons', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/lib/font-awesome/css/font-awesome.min.css', array(), '4.1.0' );
383
- wp_enqueue_script( 'icheck', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/lib/icheck/icheck.min.js', array(), '1.0.2' );
384
- wp_enqueue_style( 'icheck-square', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/lib/icheck/square/blue.css', array(), '1.0.2' );
385
- }
386
-
387
- /**
388
- * Add settings Panel
389
- */
390
- function create_settings_panel($contextual_help, $screen_id, $screen) {
391
-
392
- global $hook_suffix;
393
-
394
- // Just add if we are at the plugin page
395
- if ( strpos($hook_suffix, $this->getPluginSlug() . '_log' ) == false )
396
- return $contextual_help;
397
-
398
- // The add_help_tab function for screen was introduced in WordPress 3.3.
399
- if ( ! method_exists( $screen, 'add_help_tab' ) )
400
- return $contextual_help;
401
-
402
-
403
- // List screen properties
404
- $left = '<div style="width:50%;float:left;">'
405
- . '<h4>About this plugin</h4>'
406
- . '<p>This plugin is open source.</p>'
407
- . '</div>';
408
-
409
-
410
- $right = '<div style="width:50%;float:right;">'
411
- . '<h4>Donate</h4>'
412
- . '<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>'
413
- . '</div>';
414
-
415
- $help_content = $left . $right;
416
-
417
- /**
418
- * Content specified inline
419
- */
420
- $screen->add_help_tab(
421
- array(
422
- 'title' => __('About Plugin', 'wpml'),
423
- 'id' => 'about_tab',
424
- 'content' => '<p>' . __( "{$this->getPluginDisplayName()}, logs each email sent by WordPress.", 'wpml') . '</p>' . $help_content,
425
- 'callback' => false
426
- )
427
- );
428
-
429
- // Add help sidebar
430
- $screen->set_help_sidebar(
431
- '<p><strong>' . __('More information', 'wpml') . '</strong></p>' .
432
- '<p><a href = "http://wordpress.org/extend/plugins/wp-mail-logging/">' . __('Plugin Homepage/support', 'wpml') . '</a></p>' .
433
- '<p><a href = "http://no3x.de/">' . __("Plugin author's blog", 'wpml') . '</a></p>'
434
- );
435
-
436
- // Add screen options
437
- $screen->add_option(
438
- 'per_page',
439
- array(
440
- 'label' => __('Entries per page', 'wpml'),
441
- 'default' => 25,
442
- 'option' => 'per_page'
443
- )
444
- );
445
-
446
- return $contextual_help;
447
- }
448
-
449
- /**
450
- * Save Screen option
451
- * @since 1.3
452
- */
453
- function save_screen_options( $status, $option, $value ) {
454
- if ( 'per_page' == $option ) return $value;
455
- return $status;
456
- }
457
-
458
- public function LogMenu() {
459
- global $wp_version, $wpml_settings;
460
-
461
- if ( !current_user_can( $this->getSetting( 'can-see-submission-data', 'manage_options' ) ) ) {
462
- wp_die(__('You do not have sufficient permissions to access this page.', 'wpml'));
463
- }
464
-
465
- if (!class_exists( 'Email_Log_List_Table' ) ) {
466
- require_once ( plugin_dir_path( __FILE__ ) . 'WPML_Email_Log_List.php' );
467
- }
468
-
469
- ?>
470
- <div class="wrap">
471
- <h2><?php echo $this->getPluginDisplayName(); echo ' '; _e('Log', 'wpml'); ?></h2>
472
- <script>
473
- jQuery(document).ready(function($) {
474
- $('#wp-mail-logging-modal-content-header-format-switch input').iCheck({
475
- checkboxClass: 'icheckbox_square-blue',
476
- radioClass: 'iradio_square-blue',
477
- increaseArea: '20%' // optional
478
- });
479
- });
480
- </script>
481
- <div id="wp-mail-logging-modal-wrap">
482
- <div id="wp-mail-logging-modal-backdrop"></div>
483
- <div id="wp-mail-logging-modal-content-wrap">
484
- <div id="wp-mail-logging-modal-content">
485
- <div id="wp-mail-logging-modal-content-header">
486
- <a id="wp-mail-logging-modal-content-header-close" class="wp-mail-logging-modal-close" href="#" title="Close">
487
- <?php if ( $wp_version >= 3.8 ): ?>
488
- <div class="dashicons dashicons-no"></div>
489
- <?php else: ?>
490
- <span class="wp-mail-logging-modal-content-header-compat-close">X</span>
491
- <?php endif; ?>
492
- </a>
493
- <?php if ( $wp_version >= 3.8 ): ?>
494
- <div id="wp-mail-logging-modal-content-header-icon" class="dashicons dashicons-email-alt"></div>
495
- <?php endif; ?>
496
- <div id="wp-mail-logging-modal-content-header-title">
497
- <?php _e( 'Message', 'wpml' ); ?>
498
- </div>
499
- <div id="wp-mail-logging-modal-content-header-format-switch">
500
- <?php
501
- $supported_formats = apply_filters( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, array('html') );
502
- foreach( $supported_formats as $key => $format ) {
503
- $checked = checked($format, $wpml_settings['preferred-mail-format'], false);
504
- echo ' <input type="radio" name="format" ' . $checked . ' id="' . esc_attr( $format ) . '"> ' . esc_html( $format ) . '</input> ';
505
- }
506
- ?>
507
- </div>
508
- </div>
509
- <div id="wp-mail-logging-modal-content-body">
510
- <div id="wp-mail-logging-modal-content-body-content">
511
-
512
- </div>
513
- </div>
514
- <div id="wp-mail-logging-modal-content-footer">
515
- <a class="wp-mail-logging-modal-close button button-primary" href="#"><?php _e( 'Close', 'wpml' ); ?></a>
516
- </div>
517
- </div>
518
- </div>
519
- </div>
520
-
521
- <form id="email-list" method="get">
522
- <input type="hidden" name="page" value="<?php echo esc_attr( $_REQUEST['page'] ); ?>" />
523
- <?php
524
- wp_nonce_field( WPML_Email_Log_List::NONCE_LIST_TABLE, WPML_Email_Log_List::NONCE_LIST_TABLE . '_nonce' );
525
- $search = ( isset( $_REQUEST['s'] ) ) ? $_REQUEST['s'] : false;
526
- /** @var WPML_Email_Log_List $emailLogList */
527
- $emailLogList = WPML_Init::getInstance()->getService('emailLogList');
528
- $emailLogList->prepare_items( $search );
529
- $emailLogList->search_box( __( 'Search' ), 's' );
530
- $emailLogList->display();
531
- ?>
532
- </form>
533
- </div>
534
- <?php
535
- }
536
-
537
- /**
538
- * Override this method and follow its format.
539
- * The purpose of this method is to provide i18n display strings for the values of options.
540
- * For example, you may create a options with values 'true' or 'false'.
541
- * In the options page, this will show as a drop down list with these choices.
542
- * But when the the language is not English, you would like to display different strings
543
- * for 'true' and 'false' while still keeping the value of that option that is actually saved in
544
- * the DB as 'true' or 'false'.
545
- * To do this, follow the convention of defining option values in getOptionMetaData() as canonical names
546
- * (what you want them to literally be, like 'true') and then add each one to the switch statement in this
547
- * function, returning the "__()" i18n name of that string.
548
- * @param $optionValue string
549
- * @return string __($optionValue) if it is listed in this method, otherwise just returns $optionValue
550
- */
551
- protected function getOptionValueI18nString($optionValue) {
552
- switch ($optionValue) {
553
- case 'true':
554
- return __('true', 'wpml');
555
- case 'false':
556
- return __('false', 'wpml');
557
-
558
- case 'Administrator':
559
- return __('Administrator', 'wpml');
560
- case 'Editor':
561
- return __('Editor', 'wpml');
562
- case 'Author':
563
- return __('Author', 'wpml');
564
- case 'Contributor':
565
- return __('Contributor', 'wpml');
566
- case 'Subscriber':
567
- return __('Subscriber', 'wpml');
568
- case 'Anyone':
569
- return __('Anyone', 'wpml');
570
- }
571
- return $optionValue;
572
- }
573
-
574
- /**
575
- * Query MySQL DB for its version
576
- * @return string|false
577
- */
578
- protected function getMySqlVersion() {
579
- global $wpdb;
580
- $rows = $wpdb->get_results('select version() as mysqlversion');
581
- if (!empty($rows)) {
582
- return $rows[0]->mysqlversion;
583
- }
584
- return false;
585
- }
586
-
587
- /**
588
- * If you want to generate an email address like "no-reply@your-site.com" then
589
- * you can use this to get the domain name part.
590
- * E.g. 'no-reply@' . $this->getEmailDomain();
591
- * This code was stolen from the wp_mail function, where it generates a default
592
- * from "wordpress@your-site.com"
593
- * @return string domain name
594
- */
595
- public function getEmailDomain() {
596
- // Get the site domain and get rid of www.
597
- $sitename = strtolower($_SERVER['SERVER_NAME']);
598
- if (substr($sitename, 0, 4) == 'www.') {
599
- $sitename = substr($sitename, 4);
600
- }
601
- return $sitename;
602
- }
603
  }
604
 
25
  if ( ! defined( 'ABSPATH' ) ) exit;
26
 
27
  class WPML_OptionsManager {
28
+ /**
29
+ * Is used to retrive a settings value
30
+ * Important: This implementation understands bool for $default. (unlikely in comparision to all other settings implementation)
31
+ * @since 1.4
32
+ * @param string $settingName The option name to return
33
+ * @param mixed $default (null) The value to return if option not set.
34
+ * @return ambigous <string, mixed> the options value or $default if not found.
35
+ */
36
+ public function getSetting($settingName, $default = null) {
37
+ global $wpml_settings;
38
+
39
+ if ( array_key_exists($settingName, $wpml_settings)) {
40
+ $retVal = $wpml_settings[$settingName];
41
+ }
42
+ if (!isset($retVal) && $default !== null) {
43
+ $retVal = $default;
44
+ }
45
+ return $retVal;
46
+ }
47
+
48
+ /**
49
+ * Returns the appropriate datetime format string.
50
+ * @since 1.5.0
51
+ * @return string datetime format string
52
+ */
53
+ public function getDateTimeFormatString() {
54
+ // default database like format
55
+ $format = 'Y-m-d G:i:s';
56
+ $date_format = get_option( 'date_format' );
57
+ $time_format = get_option( 'time_format' );
58
+ // get option or change to user friendly format as the options maybe not set at all
59
+ $date_format = empty( $date_format ) ? 'F j, Y' : $date_format;
60
+ $time_format = empty( $time_format ) ? 'g:i a' : $time_format;
61
+ if ( $this->getSetting( 'datetimeformat-use-wordpress', false) == true )
62
+ // Overwrite with defined values or default
63
+ $format = $date_format . " " . $time_format;
64
+ return $format;
65
+ }
66
+
67
+ public function getOptionNamePrefix() {
68
+ return $this->getClassnameWithoutNamespace() . '_';
69
+ }
70
+
71
+ /**
72
+ * Define your options meta data here as an array, where each element in the array
73
+ * @return array of key=>display-name and/or key=>array(display-name, choice1, choice2, ...)
74
+ * key: an option name for the key (this name will be given a prefix when stored in
75
+ * the database to ensure it does not conflict with other plugin options)
76
+ * value: can be one of two things:
77
+ * (1) string display name for displaying the name of the option to the user on a web page
78
+ * (2) array where the first element is a display name (as above) and the rest of
79
+ * the elements are choices of values that the user can select
80
+ * e.g.
81
+ * array(
82
+ * 'item' => 'Item:', // key => display-name
83
+ * 'rating' => array( // key => array ( display-name, choice1, choice2, ...)
84
+ * 'CanDoOperationX' => array('Can do Operation X', 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber'),
85
+ * 'Rating:', 'Excellent', 'Good', 'Fair', 'Poor')
86
+ */
87
+ public function getOptionMetaData() {
88
+ return array();
89
+ }
90
+
91
+ /**
92
+ * @return array of string name of options
93
+ */
94
+ public function getOptionNames() {
95
+ return array_keys($this->getOptionMetaData());
96
+ }
97
+
98
+ /**
99
+ * Override this method to initialize options to default values and save to the database with add_option
100
+ * @return void
101
+ */
102
+ protected function initOptions() {
103
+ }
104
+
105
+ /**
106
+ * Cleanup: remove all options from the DB
107
+ * @return void
108
+ */
109
+ protected function deleteSavedOptions() {
110
+ $optionMetaData = $this->getOptionMetaData();
111
+ if (is_array($optionMetaData)) {
112
+ foreach ($optionMetaData as $aOptionKey => $aOptionMeta) {
113
+ $prefixedOptionName = $this->prefix($aOptionKey); // how it is stored in DB
114
+ delete_option($prefixedOptionName);
115
+ }
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Cleanup: remove version option
121
+ * @since 1.6.0
122
+ * @return void
123
+ */
124
+ protected function deleteVersionOption() {
125
+ delete_option( $this->prefix( WPML_Plugin::optionVersion ) );
126
+ }
127
+
128
+ /**
129
+ * @return string display name of the plugin to show as a name/title in HTML.
130
+ * Just returns the class name. Override this method to return something more readable
131
+ */
132
+ public function getPluginDisplayName() {
133
+ return get_class($this);
134
+ }
135
+
136
+ /**
137
+ * @return string slug of the plugin to use as identifier.
138
+ * Just returns the class name in lowercase.
139
+ */
140
+ public function getPluginSlug() {
141
+ return strtolower( $this->getClassnameWithoutNamespace() );
142
+ }
143
+
144
+ /**
145
+ * Get the class name without the namespace
146
+ * @return string class name without the namespace.
147
+ * @link http://php.net/manual/de/function.get-class.php#114568
148
+ */
149
+ private function getClassnameWithoutNamespace() {
150
+ $classname = get_class($this);
151
+ if ($pos = strrpos( $classname, '\\')) {
152
+ return substr($classname, $pos + 1);
153
+ }
154
+ return $classname;
155
+ }
156
+
157
+ /**
158
+ * Get the prefixed version input $name suitable for storing in WP options
159
+ * Idempotent: if $optionName is already prefixed, it is not prefixed again, it is returned without change
160
+ * @param $name string option name to prefix. Defined in settings.php and set as keys of $this->optionMetaData
161
+ * @return string
162
+ */
163
+ public function prefix($name) {
164
+ $optionNamePrefix = $this->getOptionNamePrefix();
165
+ if (strpos($name, $optionNamePrefix) === 0) { // 0 but not false
166
+ return $name; // already prefixed
167
+ }
168
+ return $optionNamePrefix . $name;
169
+ }
170
+
171
+ /**
172
+ * Remove the prefix from the input $name.
173
+ * Idempotent: If no prefix found, just returns what was input.
174
+ * @param $name string
175
+ * @return string $optionName without the prefix.
176
+ */
177
+ public function &unPrefix($name) {
178
+ $optionNamePrefix = $this->getOptionNamePrefix();
179
+ if (strpos($name, $optionNamePrefix) === 0) {
180
+ return substr($name, strlen($optionNamePrefix));
181
+ }
182
+ return $name;
183
+ }
184
+
185
+ /**
186
+ * A wrapper function delegating to WP get_option() but it prefixes the input $optionName
187
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
188
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
189
+ * @param $default string default value to return if the option is not set
190
+ * @return string the value from delegated call to get_option(), or optional default value
191
+ * if option is not set.
192
+ */
193
+ public function getOption($optionName, $default = null) {
194
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
195
+ $retVal = get_option($prefixedOptionName);
196
+ if (!$retVal && $default) {
197
+ $retVal = $default;
198
+ }
199
+ return $retVal;
200
+ }
201
+
202
+ /**
203
+ * A wrapper function delegating to WP delete_option() but it prefixes the input $optionName
204
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
205
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
206
+ * @return bool from delegated call to delete_option()
207
+ */
208
+ public function deleteOption($optionName) {
209
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
210
+ return delete_option($prefixedOptionName);
211
+ }
212
+
213
+ /**
214
+ * A wrapper function delegating to WP add_option() but it prefixes the input $optionName
215
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
216
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
217
+ * @param $value mixed the new value
218
+ * @return null from delegated call to delete_option()
219
+ */
220
+ public function addOption($optionName, $value) {
221
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
222
+ return add_option($prefixedOptionName, $value);
223
+ }
224
+
225
+ /**
226
+ * A wrapper function delegating to WP add_option() but it prefixes the input $optionName
227
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
228
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
229
+ * @param $value mixed the new value
230
+ * @return null from delegated call to delete_option()
231
+ */
232
+ public function updateOption($optionName, $value) {
233
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
234
+ return update_option($prefixedOptionName, $value);
235
+ }
236
+
237
+ /**
238
+ * A Role Option is an option defined in getOptionMetaData() as a choice of WP standard roles, e.g.
239
+ * 'CanDoOperationX' => array('Can do Operation X', 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber')
240
+ * The idea is use an option to indicate what role level a user must minimally have in order to do some operation.
241
+ * So if a Role Option 'CanDoOperationX' is set to 'Editor' then users which role 'Editor' or above should be
242
+ * able to do Operation X.
243
+ * Also see: canUserDoRoleOption()
244
+ * @param $optionName
245
+ * @return string role name
246
+ */
247
+ public function getRoleOption($optionName) {
248
+ $roleAllowed = $this->getOption($optionName);
249
+ if (!$roleAllowed || $roleAllowed == '') {
250
+ $roleAllowed = 'Administrator';
251
+ }
252
+ return $roleAllowed;
253
+ }
254
+
255
+ /**
256
+ * Given a WP role name (case insensitive), return a WP capability which only that role and roles above it have.
257
+ * http://codex.wordpress.org/Roles_and_Capabilities
258
+ * @param $roleName
259
+ * @return string a WP capability or '' if unknown input role
260
+ */
261
+ protected function roleToCapability($roleName) {
262
+ switch ( ucfirst( $roleName ) ) {
263
+ case 'Super Admin':
264
+ return 'manage_options';
265
+ case 'Administrator':
266
+ return 'manage_options';
267
+ case 'Editor':
268
+ return 'publish_pages';
269
+ case 'Author':
270
+ return 'publish_posts';
271
+ case 'Contributor':
272
+ return 'edit_posts';
273
+ case 'Subscriber':
274
+ return 'read';
275
+ case 'Anyone':
276
+ return 'read';
277
+ }
278
+ return '';
279
+ }
280
+
281
+ /**
282
+ * @param $roleName string a standard WP role name like 'Administrator'
283
+ * @return bool
284
+ */
285
+ public function isUserRoleEqualOrBetterThan($roleName) {
286
+ if ('Anyone' == $roleName) {
287
+ return true;
288
+ }
289
+ $capability = $this->roleToCapability($roleName);
290
+ return current_user_can($capability);
291
+ }
292
+
293
+ /**
294
+ * @param $optionName string name of a Role option (see comments in getRoleOption())
295
+ * @return bool indicates if the user has adequate permissions
296
+ */
297
+ public function canUserDoRoleOption($optionName) {
298
+ $roleAllowed = $this->getRoleOption($optionName);
299
+ if ('Anyone' == $roleAllowed) {
300
+ return true;
301
+ }
302
+ return $this->isUserRoleEqualOrBetterThan($roleAllowed);
303
+ }
304
+
305
+ /**
306
+ * see: http://codex.wordpress.org/Creating_Options_Pages
307
+ * @return void
308
+ */
309
+ public function createSettingsMenu() {
310
+
311
+ global $wp_version;
312
+ global $wp_logging_list_page;
313
+
314
+ $pluginIcon = '';
315
+ if ( $wp_version >= 3.8 ) $pluginIcon = 'dashicons-email-alt';
316
+
317
+ $pluginNameSlug = $this->getPluginSlug();
318
+ $capability = $this->getSetting( 'can-see-submission-data', 'manage_options' );
319
+
320
+ //create new top-level menu
321
+ $wp_logging_list_page = add_menu_page(__('WP Mail Log', 'wp-mail-logging'),
322
+ __('WP Mail Log', 'wp-mail-logging'),
323
+ $capability,
324
+ $pluginNameSlug . '_log',
325
+ array(&$this, 'LogMenu'),
326
+ $pluginIcon
327
+ );
328
+
329
+ // Add Action to load assets when page is loaded
330
+ add_action( 'load-' . $wp_logging_list_page, array( $this, 'load_assets' ) );
331
+
332
+ add_submenu_page($pluginNameSlug . '_log',
333
+ __('About', 'wp-mail-logging'),
334
+ __('About', 'wp-mail-logging'),
335
+ $capability,
336
+ $pluginNameSlug . '_about',
337
+ array(&$this, 'LogSubMenuAbout') );
338
+
339
+ add_action( 'contextual_help', array( &$this, 'create_settings_panel' ), 10, 3 );
340
+ }
341
+
342
+ public function LogSubMenuAbout() {
343
+ ?>
344
+ <div class="wrap">
345
+ <h2><?php echo $this->getPluginDisplayName(); echo ' '; _e('About', 'wp-mail-logging'); ?></h2>
346
+ <h3>Why use?</h3>
347
+ <p>Sometimes you may ask yourself if a mail was actually sent by WordPress - with
348
+ <strong>With <?php echo $this->getPluginDisplayName(); ?>, you can:</strong></p>
349
+ <ul>
350
+ <li>View a complete list of sent mails.</li>
351
+ <li>Search for mails.</li>
352
+ <li>Count on regular updates, enhancements, and troubleshooting.</li>
353
+ <li>DevOP: IP of server sent the mail</li>
354
+ <li>Developer: Boost your development performance by keeping track of sent mails from your WordPress site.</li>
355
+ <li>Developer: Use Filters that are provided to extend the columns.</li>
356
+ </ul>
357
+ <h3>Contributors</h3>
358
+ <p>This plugin is open source and some people helped to make it better:</p>
359
+ <ul>
360
+ <li>tripflex</li>
361
+ <li><a href="http://www.grafixone.co.za" title="GrafixONE">Andr&eacute; Groenewald</a> (Icon before version 1.8.0, icon after this version slightly modified)</li>
362
+ </ul>
363
+ <h3>Donate</h3>
364
+ <p>Please consider to make a donation if you like the plugin. I spent a lot of time for support, enhancements and updates in general.</p>
365
+ <a title="Donate" class="button button-primary" href="http://no3x.de/web/donate">Donate</a>
366
+ </div>
367
+ <?php
368
+ }
369
+
370
+ public function load_assets() {
371
+
372
+ global $wp_logging_list_page;
373
+ $screen = get_current_screen();
374
+
375
+ if ( $screen->id != $wp_logging_list_page )
376
+ return;
377
+
378
+ // Enqueue Styles and Scripts if we're on the list page
379
+ wp_enqueue_script( 'wp-logging-modal', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/js/modal.js', array( 'jquery' ), '1.0.0', true );
380
+ wp_localize_script( 'wp-logging-modal', 'wpml_modal', array('ajax_nonce' => wp_create_nonce( 'wpml-modal-show' ) ) );
381
+ wp_enqueue_style( 'wp-logging-modal', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/css/modal.css', array(), '1.0.0' );
382
+ wp_enqueue_style( 'wp-logging-icons', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/lib/font-awesome/css/font-awesome.min.css', array(), '4.1.0' );
383
+ wp_enqueue_script( 'icheck', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/lib/icheck/icheck.min.js', array(), '1.0.2' );
384
+ wp_enqueue_style( 'icheck-square', untrailingslashit( plugin_dir_url( __FILE__ ) ) . '/lib/icheck/square/blue.css', array(), '1.0.2' );
385
+ }
386
+
387
+ /**
388
+ * Add settings Panel
389
+ */
390
+ function create_settings_panel($contextual_help, $screen_id, $screen) {
391
+
392
+ global $hook_suffix;
393
+
394
+ // Just add if we are at the plugin page
395
+ if ( strpos($hook_suffix, $this->getPluginSlug() . '_log' ) == false )
396
+ return $contextual_help;
397
+
398
+ // The add_help_tab function for screen was introduced in WordPress 3.3.
399
+ if ( ! method_exists( $screen, 'add_help_tab' ) )
400
+ return $contextual_help;
401
+
402
+
403
+ // List screen properties
404
+ $left = '<div style="width:50%;float:left;">'
405
+ . '<h4>About this plugin</h4>'
406
+ . '<p>This plugin is open source.</p>'
407
+ . '</div>';
408
+
409
+
410
+ $right = '<div style="width:50%;float:right;">'
411
+ . '<h4>Donate</h4>'
412
+ . '<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>'
413
+ . '</div>';
414
+
415
+ $help_content = $left . $right;
416
+
417
+ /**
418
+ * Content specified inline
419
+ */
420
+ $screen->add_help_tab(
421
+ array(
422
+ 'title' => __('About Plugin', 'wp-mail-logging'),
423
+ 'id' => 'about_tab',
424
+ 'content' => '<p>' . __( "{$this->getPluginDisplayName()}, logs each email sent by WordPress.", 'wp-mail-logging') . '</p>' . $help_content,
425
+ 'callback' => false
426
+ )
427
+ );
428
+
429
+ // Add help sidebar
430
+ $screen->set_help_sidebar(
431
+ '<p><strong>' . __('More information', 'wp-mail-logging') . '</strong></p>' .
432
+ '<p><a href = "http://wordpress.org/extend/plugins/wp-mail-logging/">' . __('Plugin Homepage/support', 'wp-mail-logging') . '</a></p>' .
433
+ '<p><a href = "http://no3x.de/">' . __("Plugin author's blog", 'wp-mail-logging') . '</a></p>'
434
+ );
435
+
436
+ // Add screen options
437
+ $screen->add_option(
438
+ 'per_page',
439
+ array(
440
+ 'label' => __('Entries per page', 'wp-mail-logging'),
441
+ 'default' => 25,
442
+ 'option' => 'per_page'
443
+ )
444
+ );
445
+
446
+ return $contextual_help;
447
+ }
448
+
449
+ /**
450
+ * Save Screen option
451
+ * @since 1.3
452
+ */
453
+ function save_screen_options( $status, $option, $value ) {
454
+ if ( 'per_page' == $option ) return $value;
455
+ return $status;
456
+ }
457
+
458
+ public function LogMenu() {
459
+ global $wp_version, $wpml_settings;
460
+
461
+ if ( !current_user_can( $this->getSetting( 'can-see-submission-data', 'manage_options' ) ) ) {
462
+ wp_die(__('You do not have sufficient permissions to access this page.', 'wp-mail-logging'));
463
+ }
464
+
465
+ if (!class_exists( 'Email_Log_List_Table' ) ) {
466
+ require_once ( plugin_dir_path( __FILE__ ) . 'WPML_Email_Log_List.php' );
467
+ }
468
+
469
+ ?>
470
+ <div class="wrap">
471
+ <h2><?php echo $this->getPluginDisplayName(); echo ' '; _e('Log', 'wp-mail-logging'); ?></h2>
472
+ <script>
473
+ jQuery(document).ready(function($) {
474
+ $('#wp-mail-logging-modal-content-header-format-switch input').iCheck({
475
+ checkboxClass: 'icheckbox_square-blue',
476
+ radioClass: 'iradio_square-blue',
477
+ increaseArea: '20%' // optional
478
+ });
479
+ });
480
+ </script>
481
+ <div id="wp-mail-logging-modal-wrap">
482
+ <div id="wp-mail-logging-modal-backdrop"></div>
483
+ <div id="wp-mail-logging-modal-content-wrap">
484
+ <div id="wp-mail-logging-modal-content">
485
+ <div id="wp-mail-logging-modal-content-header">
486
+ <a id="wp-mail-logging-modal-content-header-close" class="wp-mail-logging-modal-close" href="#" title="Close">
487
+ <?php if ( $wp_version >= 3.8 ): ?>
488
+ <div class="dashicons dashicons-no"></div>
489
+ <?php else: ?>
490
+ <span class="wp-mail-logging-modal-content-header-compat-close">X</span>
491
+ <?php endif; ?>
492
+ </a>
493
+ <?php if ( $wp_version >= 3.8 ): ?>
494
+ <div id="wp-mail-logging-modal-content-header-icon" class="dashicons dashicons-email-alt"></div>
495
+ <?php endif; ?>
496
+ <div id="wp-mail-logging-modal-content-header-title">
497
+ <?php _e( 'Message', 'wp-mail-logging' ); ?>
498
+ </div>
499
+ <div id="wp-mail-logging-modal-content-header-format-switch">
500
+ <?php
501
+ $supported_formats = apply_filters( WPML_Plugin::HOOK_LOGGING_SUPPORTED_FORMATS, array('html') );
502
+ foreach( $supported_formats as $key => $format ) {
503
+ $checked = checked($format, $wpml_settings['preferred-mail-format'], false);
504
+ echo ' <input type="radio" name="format" ' . $checked . ' id="' . esc_attr( $format ) . '"> ' . esc_html( $format ) . '</input> ';
505
+ }
506
+ ?>
507
+ </div>
508
+ </div>
509
+ <div id="wp-mail-logging-modal-content-body">
510
+ <div id="wp-mail-logging-modal-content-body-content">
511
+
512
+ </div>
513
+ </div>
514
+ <div id="wp-mail-logging-modal-content-footer">
515
+ <a class="wp-mail-logging-modal-close button button-primary" href="#"><?php _e( 'Close', 'wp-mail-logging' ); ?></a>
516
+ </div>
517
+ </div>
518
+ </div>
519
+ </div>
520
+
521
+ <form id="email-list" method="get">
522
+ <input type="hidden" name="page" value="<?php echo esc_attr( $_REQUEST['page'] ); ?>" />
523
+ <?php
524
+ wp_nonce_field( WPML_Email_Log_List::NONCE_LIST_TABLE, WPML_Email_Log_List::NONCE_LIST_TABLE . '_nonce' );
525
+ $search = ( isset( $_REQUEST['s'] ) ) ? $_REQUEST['s'] : false;
526
+ /** @var WPML_Email_Log_List $emailLogList */
527
+ $emailLogList = WPML_Init::getInstance()->getService('emailLogList');
528
+ $emailLogList->prepare_items( $search );
529
+ $emailLogList->search_box( __( 'Search' ), 's' );
530
+ $emailLogList->display();
531
+ ?>
532
+ </form>
533
+ </div>
534
+ <?php
535
+ }
536
+
537
+ /**
538
+ * Override this method and follow its format.
539
+ * The purpose of this method is to provide i18n display strings for the values of options.
540
+ * For example, you may create a options with values 'true' or 'false'.
541
+ * In the options page, this will show as a drop down list with these choices.
542
+ * But when the the language is not English, you would like to display different strings
543
+ * for 'true' and 'false' while still keeping the value of that option that is actually saved in
544
+ * the DB as 'true' or 'false'.
545
+ * To do this, follow the convention of defining option values in getOptionMetaData() as canonical names
546
+ * (what you want them to literally be, like 'true') and then add each one to the switch statement in this
547
+ * function, returning the "__()" i18n name of that string.
548
+ * @param $optionValue string
549
+ * @return string __($optionValue) if it is listed in this method, otherwise just returns $optionValue
550
+ */
551
+ protected function getOptionValueI18nString($optionValue) {
552
+ switch ($optionValue) {
553
+ case 'true':
554
+ return __('true', 'wp-mail-logging');
555
+ case 'false':
556
+ return __('false', 'wp-mail-logging');
557
+
558
+ case 'Administrator':
559
+ return __('Administrator', 'wp-mail-logging');
560
+ case 'Editor':
561
+ return __('Editor', 'wp-mail-logging');
562
+ case 'Author':
563
+ return __('Author', 'wp-mail-logging');
564
+ case 'Contributor':
565
+ return __('Contributor', 'wp-mail-logging');
566
+ case 'Subscriber':
567
+ return __('Subscriber', 'wp-mail-logging');
568
+ case 'Anyone':
569
+ return __('Anyone', 'wp-mail-logging');
570
+ }
571
+ return $optionValue;
572
+ }
573
+
574
+ /**
575
+ * Query MySQL DB for its version
576
+ * @return string|false
577
+ */
578
+ protected function getMySqlVersion() {
579
+ global $wpdb;
580
+ $rows = $wpdb->get_results('select version() as mysqlversion');
581
+ if (!empty($rows)) {
582
+ return $rows[0]->mysqlversion;
583
+ }
584
+ return false;
585
+ }
586
+
587
+ /**
588
+ * If you want to generate an email address like "no-reply@your-site.com" then
589
+ * you can use this to get the domain name part.
590
+ * E.g. 'no-reply@' . $this->getEmailDomain();
591
+ * This code was stolen from the wp_mail function, where it generates a default
592
+ * from "wordpress@your-site.com"
593
+ * @return string domain name
594
+ */
595
+ public function getEmailDomain() {
596
+ // Get the site domain and get rid of www.
597
+ $sitename = strtolower($_SERVER['SERVER_NAME']);
598
+ if (substr($sitename, 0, 4) == 'www.') {
599
+ $sitename = substr($sitename, 4);
600
+ }
601
+ return $sitename;
602
+ }
603
  }
604
 
WPML_Plugin.php CHANGED
@@ -1,216 +1,257 @@
1
- <?php
2
-
3
- namespace No3x\WPML;
4
-
5
- use No3x\WPML\Model\WPML_Mail as Mail;
6
-
7
- // Exit if accessed directly.
8
- if ( ! defined( 'ABSPATH' ) ) exit;
9
-
10
- class WPML_Plugin extends WPML_LifeCycle {
11
-
12
- protected $emailLogList;
13
-
14
- const HOOK_LOGGING_COLUMNS = 'wpml_hook_mail_columns';
15
- const HOOK_LOGGING_COLUMNS_RENDER = 'wpml_hook_mail_columns_render';
16
- const HOOK_LOGGING_SUPPORTED_FORMATS = 'wpml_hook_supported_formats';
17
- const HOOK_LOGGING_FORMAT_CONTENT = 'wpml_hook_format_content';
18
-
19
- public static function getTablename( $name ) {
20
- global $wpdb;
21
- return $wpdb->prefix . 'wpml_' . $name;
22
- }
23
-
24
- public function getPluginDisplayName() {
25
- return 'WP Mail Logging';
26
- }
27
-
28
- public function getMainPluginFileName() {
29
- return 'wp-mail-logging.php';
30
- }
31
-
32
- public function getVersionSaved() {
33
- return parent::getVersionSaved();
34
- }
35
-
36
- /**
37
- * See: http://plugin.michael-simpson.com/?page_id=101
38
- * Called by install() to create any database tables if needed.
39
- * Best Practice:
40
- * (1) Prefix all table names with $wpdb->prefix
41
- * (2) make table names lower case only
42
- * @return void
43
- */
44
- protected function installDatabaseTables() {
45
- global $wpdb;
46
- $tableName = WPML_Plugin::getTablename('mails');
47
- $wpdb->query("CREATE TABLE IF NOT EXISTS `$tableName` (
48
- `mail_id` INT NOT NULL AUTO_INCREMENT,
49
- `timestamp` TIMESTAMP NOT NULL,
50
- `host` VARCHAR(200) NOT NULL DEFAULT '0',
51
- `receiver` VARCHAR(200) NOT NULL DEFAULT '0',
52
- `subject` VARCHAR(200) NOT NULL DEFAULT '0',
53
- `message` TEXT NULL,
54
- `headers` TEXT NULL,
55
- `attachments` VARCHAR(800) NOT NULL DEFAULT '0',
56
- `plugin_version` VARCHAR(200) NOT NULL DEFAULT '',
57
- PRIMARY KEY (`mail_id`)
58
- ) DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE utf8_general_ci;");
59
- }
60
-
61
-
62
- /**
63
- * See: http://plugin.michael-simpson.com/?page_id=101
64
- * Drop plugin-created tables on uninstall.
65
- * @return void
66
- */
67
- protected function unInstallDatabaseTables() {
68
- global $wpdb;
69
- $tableName = WPML_Plugin::getTablename('mails');
70
- $wpdb->query("DROP TABLE IF EXISTS `$tableName`");
71
- }
72
-
73
-
74
- /**
75
- * Perform actions when upgrading from version X to version Y
76
- * See: http://plugin.michael-simpson.com/?page_id=35
77
- * @return void
78
- */
79
- public function upgrade() {
80
- global $wpdb;
81
- $upgradeOk = true;
82
- $savedVersion = $this->getVersionSaved();
83
- $codeVersion = $this->getVersion();
84
- $tableName = $this->getTablename('mails');
85
-
86
- /* check for downgrade or beta
87
- if( $this->isVersionLessThan($codeVersion, $savedVersion)
88
- || false !== strpos($savedVersion, 'beta') ) {
89
- $upgradeOk = false;
90
- // This is only be the case if the user had a beta version installed
91
- if ( is_admin() ) {
92
- wp_die( "[{$this->getPluginDisplayName()}] You have installed version {$savedVersion} but try to install {$codeVersion}! This would require a database downgrade which is not supported! You need to install {$savedVersion} again, enable \"Cleanup\" in the settings and disable the plugin.");
93
- }
94
- }*/
95
-
96
- if ($this->isVersionLessThan($savedVersion, '2.0')) {
97
- if ($this->isVersionLessThan($savedVersion, '1.2')) {
98
- $wpdb->query("ALTER TABLE `$tableName` CHANGE COLUMN `to` `receiver` VARCHAR(200)");
99
- }
100
- if ($this->isVersionLessThan($savedVersion, '1.3')) {
101
- $wpdb->query("ALTER TABLE `$tableName` MODIFY COLUMN `attachments` VARCHAR(800) NOT NULL DEFAULT '0'");
102
- }
103
- if ($this->isVersionLessThan($savedVersion, '1.4')) {
104
- $wpdb->query("ALTER TABLE `$tableName` CHARACTER SET utf8 COLLATE utf8_general_ci;");
105
- }
106
- if ($this->isVersionLessThan($savedVersion, '1.7')) {
107
- $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN `host` VARCHAR(200) NOT NULL DEFAULT '' AFTER `timestamp`;");
108
- }
109
- }
110
-
111
- if ( !empty( $wpdb->last_error ) ) {
112
- $upgradeOk = false;
113
- if ( is_admin() ) {
114
- echo "There was at least one error while upgrading the database schema. Please report the following error: {$wpdb->last_error}";
115
- }
116
- }
117
-
118
- // Post-upgrade, set the current version in the options
119
- if ($upgradeOk && $savedVersion != $codeVersion) {
120
- $this->saveInstalledVersion();
121
- }
122
- }
123
-
124
- public function addActionsAndFilters() {
125
- // Add options administration page
126
- // http://plugin.michael-simpson.com/?page_id=47
127
- add_action( 'admin_menu', array(&$this, 'createSettingsMenu'), 9 );
128
-
129
- // Example adding a script & style just for the options administration page
130
- // http://plugin.michael-simpson.com/?page_id=47
131
- // if (strpos($_SERVER['REQUEST_URI'], $this->getSettingsSlug()) !== false) {
132
- // wp_enqueue_script('my-script', plugins_url('/js/my-script.js', __FILE__));
133
- // wp_enqueue_style('my-style', plugins_url('/css/my-style.css', __FILE__));
134
- // }
135
-
136
-
137
- // Add Actions & Filters
138
- // http://plugin.michael-simpson.com/?page_id=37
139
- add_filter( 'plugin_action_links', array( &$this, 'registerPluginActionLinks'), 10, 5 );
140
- add_filter( 'wp_mail', array( &$this, 'log_email' ), PHP_INT_MAX );
141
- add_filter( 'set-screen-option', array( &$this, 'save_screen_options' ), 10, 3);
142
- add_filter( 'wpml_get_plugin_version', array( &$this, 'getVersion' ) );
143
- add_filter( 'wpml_get_plugin_name', array( &$this, 'getPluginDisplayName' ) );
144
- add_filter( 'wpml_get_date_time_format', array( &$this, 'getDateTimeFormatString' ) );
145
- // Adding scripts & styles to all pages
146
- // Examples:
147
- // wp_enqueue_script('jquery');
148
- // wp_enqueue_style('my-style', plugins_url('/css/my-style.css', __FILE__));
149
- // wp_enqueue_script('my-script', plugins_url('/js/my-script.js', __FILE__));
150
-
151
-
152
- // Register short codes
153
- // http://plugin.michael-simpson.com/?page_id=39
154
-
155
-
156
- // Register AJAX hooks
157
- // http://plugin.michael-simpson.com/?page_id=41
158
-
159
- }
160
-
161
- private function extractReceiver( $receiver ) {
162
- return is_array( $receiver ) ? implode( ',\n', $receiver ) : $receiver;
163
- }
164
-
165
- private function extractHeader( $headers ) {
166
- return is_array( $headers ) ? implode( ',\n', $headers ) : $headers;
167
- }
168
-
169
- private function extractAttachments( $attachments ) {
170
- $attachments = is_array( $attachments ) ? $attachments : array( $attachments );
171
- $attachment_urls = array();
172
- $uploads = wp_upload_dir();
173
- $basename = 'uploads';
174
- $basename_needle = '/'.$basename.'/';
175
- foreach ( $attachments as $attachment ) {
176
- $append_url = substr( $attachment, strrpos( $attachment, $basename_needle ) + strlen($basename_needle) - 1 );
177
- $attachment_urls[] = $append_url;
178
- }
179
- return implode( ',\n', $attachment_urls );
180
- }
181
-
182
- private function extractMessage( $mail ) {
183
- if ( isset($mail['message']) ) {
184
- // usually the message is stored in the message field
185
- return $mail['message'];
186
- } elseif ( isset($mail['html']) ) {
187
- // for example Mandrill stores the message in the 'html' field (see gh-22)
188
- return $mail['html'];
189
- }
190
- return "";
191
- }
192
-
193
-
194
- private function extractFields( $mail ) {
195
- return array(
196
- 'receiver' => $this->extractReceiver( $mail['to'] ),
197
- 'subject' => $mail['subject'],
198
- 'message' => $this->extractMessage( $mail ),
199
- 'headers' => $this->extractHeader( $mail['headers'] ),
200
- 'attachments' => $this->extractAttachments( $mail['attachments'] ),
201
- 'plugin_version' => $this->getVersionSaved(),
202
- 'timestamp' => current_time( 'mysql' ),
203
- 'host' => isset( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : ''
204
- );
205
- }
206
-
207
- public function log_email( $mailOriginal ) {
208
- // make copy to avoid any changes on the original mail
209
- $mail = $mailOriginal;
210
-
211
- $fields = $this->extractFields( $mail );
212
- Mail::create($fields)->save();
213
-
214
- return $mailOriginal;
215
- }
216
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML;
4
+
5
+ use No3x\WPML\Model\WPML_Mail as Mail;
6
+
7
+ // Exit if accessed directly.
8
+ if ( ! defined( 'ABSPATH' ) ) exit;
9
+
10
+ class WPML_Plugin extends WPML_LifeCycle {
11
+
12
+ protected $emailLogList;
13
+
14
+ const HOOK_LOGGING_COLUMNS = 'wpml_hook_mail_columns';
15
+ const HOOK_LOGGING_COLUMNS_RENDER = 'wpml_hook_mail_columns_render';
16
+ const HOOK_LOGGING_SUPPORTED_FORMATS = 'wpml_hook_supported_formats';
17
+ const HOOK_LOGGING_FORMAT_CONTENT = 'wpml_hook_format_content';
18
+
19
+ public static function getTablename( $name ) {
20
+ global $wpdb;
21
+ return $wpdb->prefix . 'wpml_' . $name;
22
+ }
23
+
24
+ public function getPluginDisplayName() {
25
+ return 'WP Mail Logging';
26
+ }
27
+
28
+ public function getMainPluginFileName() {
29
+ return 'wp-mail-logging.php';
30
+ }
31
+
32
+ public function getVersionSaved() {
33
+ return parent::getVersionSaved();
34
+ }
35
+
36
+ /**
37
+ * See: http://plugin.michael-simpson.com/?page_id=101
38
+ * Called by install() to create any database tables if needed.
39
+ * Best Practice:
40
+ * (1) Prefix all table names with $wpdb->prefix
41
+ * (2) make table names lower case only
42
+ * @return void
43
+ */
44
+ protected function installDatabaseTables() {
45
+ global $wpdb;
46
+ $tableName = WPML_Plugin::getTablename('mails');
47
+ $wpdb->query("CREATE TABLE IF NOT EXISTS `$tableName` (
48
+ `mail_id` INT NOT NULL AUTO_INCREMENT,
49
+ `timestamp` TIMESTAMP NOT NULL,
50
+ `host` VARCHAR(200) NOT NULL DEFAULT '0',
51
+ `receiver` VARCHAR(200) NOT NULL DEFAULT '0',
52
+ `subject` VARCHAR(200) NOT NULL DEFAULT '0',
53
+ `message` TEXT NULL,
54
+ `headers` TEXT NULL,
55
+ `attachments` VARCHAR(800) NOT NULL DEFAULT '0',
56
+ `error` VARCHAR(400) NULL DEFAULT '',
57
+ `plugin_version` VARCHAR(200) NOT NULL DEFAULT '0',
58
+ PRIMARY KEY (`mail_id`)
59
+ ) DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE utf8_general_ci;");
60
+ }
61
+
62
+ /**
63
+ * See: http://plugin.michael-simpson.com/?page_id=101
64
+ * Drop plugin-created tables on uninstall.
65
+ * @return void
66
+ */
67
+ protected function unInstallDatabaseTables() {
68
+ global $wpdb;
69
+ $tableName = WPML_Plugin::getTablename('mails');
70
+ $wpdb->query("DROP TABLE IF EXISTS `$tableName`");
71
+ }
72
+
73
+ /**
74
+ * Perform actions when upgrading from version X to version Y
75
+ * See: http://plugin.michael-simpson.com/?page_id=35
76
+ * @return void
77
+ */
78
+ public function upgrade() {
79
+ global $wpdb;
80
+
81
+ $savedVersion = $this->getVersionSaved();
82
+ if(! $this->isInstalled() || empty( $savedVersion ) ) {
83
+ // The plugin must be installed before any upgrades
84
+ return;
85
+ }
86
+
87
+ $upgradeOk = true;
88
+ $savedVersion = $this->getVersionSaved();
89
+ $codeVersion = $this->getVersion();
90
+ $tableName = $this->getTablename('mails');
91
+
92
+ /* check for downgrade or beta
93
+ if( $this->isVersionLessThan($codeVersion, $savedVersion)
94
+ || false !== strpos($savedVersion, 'beta') ) {
95
+ $upgradeOk = false;
96
+ // This is only be the case if the user had a beta version installed
97
+ if ( is_admin() ) {
98
+ wp_die( "[{$this->getPluginDisplayName()}] You have installed version {$savedVersion} but try to install {$codeVersion}! This would require a database downgrade which is not supported! You need to install {$savedVersion} again, enable \"Cleanup\" in the settings and disable the plugin.");
99
+ }
100
+ }*/
101
+
102
+ if ($this->isVersionLessThan($savedVersion, '2.0')) {
103
+ if ($this->isVersionLessThan($savedVersion, '1.2')) {
104
+ $wpdb->query("ALTER TABLE `$tableName` CHANGE COLUMN `to` `receiver` VARCHAR(200)");
105
+ }
106
+ if ($this->isVersionLessThan($savedVersion, '1.3')) {
107
+ $wpdb->query("ALTER TABLE `$tableName` MODIFY COLUMN `attachments` VARCHAR(800) NOT NULL DEFAULT '0'");
108
+ }
109
+ if ($this->isVersionLessThan($savedVersion, '1.4')) {
110
+ $wpdb->query("ALTER TABLE `$tableName` CHARACTER SET utf8 COLLATE utf8_general_ci;");
111
+ }
112
+ if ($this->isVersionLessThan($savedVersion, '1.7')) {
113
+ $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN `host` VARCHAR(200) NOT NULL DEFAULT '0' AFTER `timestamp`;");
114
+ }
115
+ if ($this->isVersionLessThan($savedVersion, '1.8')) {
116
+ // Due to upgrade bug upgrades from 1.6.2 to 1.7.0 failed. Redo the schema change if required
117
+ $results = $wpdb->get_results( $wpdb->prepare( "SHOW COLUMNS FROM `$tableName` LIKE %s", 'host' ) );
118
+ $column_exists = ( count( $results ) > 0 ) ? true : false;
119
+
120
+ if ( false === $column_exists && is_array( $results ) ) {
121
+ $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN `host` VARCHAR(200) NOT NULL DEFAULT '0' AFTER `timestamp`;");
122
+ }
123
+
124
+ $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN `error` VARCHAR(400) NULL DEFAULT '' AFTER `attachments`;");
125
+ }
126
+ }
127
+
128
+ if ( !empty( $wpdb->last_error ) ) {
129
+ $upgradeOk = false;
130
+ if ( is_admin() ) {
131
+ echo "There was at least one error while upgrading the database schema. Please report the following error: {$wpdb->last_error}";
132
+ }
133
+ }
134
+
135
+ // Post-upgrade, set the current version in the options
136
+ if ($upgradeOk && $savedVersion != $codeVersion) {
137
+ $this->saveInstalledVersion();
138
+ }
139
+ }
140
+
141
+ public function addActionsAndFilters() {
142
+ // Add options administration page
143
+ // http://plugin.michael-simpson.com/?page_id=47
144
+ add_action( 'admin_menu', array(&$this, 'createSettingsMenu'), 9 );
145
+
146
+ // Example adding a script & style just for the options administration page
147
+ // http://plugin.michael-simpson.com/?page_id=47
148
+ // if (strpos($_SERVER['REQUEST_URI'], $this->getSettingsSlug()) !== false) {
149
+ // wp_enqueue_script('my-script', plugins_url('/js/my-script.js', __FILE__));
150
+ // wp_enqueue_style('my-style', plugins_url('/css/my-style.css', __FILE__));
151
+ // }
152
+
153
+
154
+ // Add Actions & Filters
155
+ // http://plugin.michael-simpson.com/?page_id=37
156
+ add_filter( 'plugin_action_links', array( &$this, 'registerPluginActionLinks'), 10, 5 );
157
+ add_filter( 'wp_mail', array( &$this, 'log_email' ), PHP_INT_MAX );
158
+ add_action( 'wp_mail_failed', array( &$this, 'log_email_failed' ) );
159
+ add_filter( 'set-screen-option', array( &$this, 'save_screen_options' ), 10, 3);
160
+ add_filter( 'wpml_get_plugin_version', array( &$this, 'getVersion' ) );
161
+ add_filter( 'wpml_get_plugin_name', array( &$this, 'getPluginDisplayName' ) );
162
+ add_filter( 'wpml_get_date_time_format', array( &$this, 'getDateTimeFormatString' ) );
163
+ // Adding scripts & styles to all pages
164
+ // Examples:
165
+ // wp_enqueue_script('jquery');
166
+ // wp_enqueue_style('my-style', plugins_url('/css/my-style.css', __FILE__));
167
+ // wp_enqueue_script('my-script', plugins_url('/js/my-script.js', __FILE__));
168
+
169
+
170
+ // Register short codes
171
+ // http://plugin.michael-simpson.com/?page_id=39
172
+
173
+
174
+ // Register AJAX hooks
175
+ // http://plugin.michael-simpson.com/?page_id=41
176
+ }
177
+
178
+ /**
179
+ * Action to log errors for mails failed to send.
180
+ *
181
+ * @since 1.8.0
182
+ * @global $wpml_current_mail_id
183
+ * @param \WP_Error $wperror
184
+ */
185
+ public function log_email_failed( $wperror ) {
186
+ global $wpml_current_mail_id;
187
+ if(!isset($wpml_current_mail_id)) return;
188
+ $failed_mail = Mail::find_one($wpml_current_mail_id);
189
+ $failed_mail->set_error($wperror->get_error_message())->save();
190
+ }
191
+
192
+ private function extractReceiver( $receiver ) {
193
+ return is_array( $receiver ) ? implode( ',\n', $receiver ) : $receiver;
194
+ }
195
+
196
+ private function extractHeader( $headers ) {
197
+ return is_array( $headers ) ? implode( ',\n', $headers ) : $headers;
198
+ }
199
+
200
+ private function extractAttachments( $attachments ) {
201
+ $attachments = is_array( $attachments ) ? $attachments : array( $attachments );
202
+ $attachment_urls = array();
203
+ $uploads = wp_upload_dir();
204
+ $basename = 'uploads';
205
+ $basename_needle = '/'.$basename.'/';
206
+ foreach ( $attachments as $attachment ) {
207
+ $append_url = substr( $attachment, strrpos( $attachment, $basename_needle ) + strlen($basename_needle) - 1 );
208
+ $attachment_urls[] = $append_url;
209
+ }
210
+ return implode( ',\n', $attachment_urls );
211
+ }
212
+
213
+ private function extractMessage( $mail ) {
214
+ if ( isset($mail['message']) ) {
215
+ // usually the message is stored in the message field
216
+ return $mail['message'];
217
+ } elseif ( isset($mail['html']) ) {
218
+ // for example Mandrill stores the message in the 'html' field (see gh-22)
219
+ return $mail['html'];
220
+ }
221
+ return "";
222
+ }
223
+
224
+
225
+ private function extractFields( $mail ) {
226
+ return array(
227
+ 'receiver' => $this->extractReceiver( $mail['to'] ),
228
+ 'subject' => $mail['subject'],
229
+ 'message' => $this->extractMessage( $mail ),
230
+ 'headers' => $this->extractHeader( $mail['headers'] ),
231
+ 'attachments' => $this->extractAttachments( $mail['attachments'] ),
232
+ 'plugin_version' => $this->getVersionSaved(),
233
+ 'timestamp' => current_time( 'mysql' ),
234
+ 'host' => isset( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : ''
235
+ );
236
+ }
237
+
238
+
239
+ /**
240
+ * Logs mail to database.
241
+ *
242
+ * @param array $mailOriginal
243
+ * @global $wpml_current_mail_id
244
+ * @since 1.0
245
+ * @return array $mailOriginal
246
+ */
247
+ public function log_email( $mailOriginal ) {
248
+ global $wpml_current_mail_id;
249
+ // make copy to avoid any changes on the original mail
250
+ $mail = $mailOriginal;
251
+
252
+ $fields = $this->extractFields( $mail );
253
+ $wpml_current_mail_id = Mail::create($fields)->save();
254
+
255
+ return $mailOriginal;
256
+ }
257
+ }
WPML_ShortCodeLoader.php CHANGED
@@ -26,42 +26,42 @@ if ( ! defined( 'ABSPATH' ) ) exit;
26
 
27
  abstract class WPML_ShortCodeLoader {
28
 
29
- /**
30
- * @param $shortcodeName mixed either string name of the shortcode
31
- * (as it would appear in a post, e.g. [shortcodeName])
32
- * or an array of such names in case you want to have more than one name
33
- * for the same shortcode
34
- * @return void
35
- */
36
- public function register($shortcodeName) {
37
- $this->registerShortcodeToFunction($shortcodeName, 'handleShortcode');
38
- }
39
 
40
- /**
41
- * @param $shortcodeName mixed either string name of the shortcode
42
- * (as it would appear in a post, e.g. [shortcodeName])
43
- * or an array of such names in case you want to have more than one name
44
- * for the same shortcode
45
- * @param $functionName string name of public function in this class to call as the
46
- * shortcode handler
47
- * @return void
48
- */
49
- protected function registerShortcodeToFunction($shortcodeName, $functionName) {
50
- if (is_array($shortcodeName)) {
51
- foreach ($shortcodeName as $aName) {
52
- add_shortcode($aName, array($this, $functionName));
53
- }
54
- }
55
- else {
56
- add_shortcode($shortcodeName, array($this, $functionName));
57
- }
58
- }
59
 
60
- /**
61
- * @abstract Override this function and add actual shortcode handling here
62
- * @param $atts shortcode inputs
63
- * @return string shortcode content
64
- */
65
- public abstract function handleShortcode($atts);
66
 
67
  }
26
 
27
  abstract class WPML_ShortCodeLoader {
28
 
29
+ /**
30
+ * @param $shortcodeName mixed either string name of the shortcode
31
+ * (as it would appear in a post, e.g. [shortcodeName])
32
+ * or an array of such names in case you want to have more than one name
33
+ * for the same shortcode
34
+ * @return void
35
+ */
36
+ public function register($shortcodeName) {
37
+ $this->registerShortcodeToFunction($shortcodeName, 'handleShortcode');
38
+ }
39
 
40
+ /**
41
+ * @param $shortcodeName mixed either string name of the shortcode
42
+ * (as it would appear in a post, e.g. [shortcodeName])
43
+ * or an array of such names in case you want to have more than one name
44
+ * for the same shortcode
45
+ * @param $functionName string name of public function in this class to call as the
46
+ * shortcode handler
47
+ * @return void
48
+ */
49
+ protected function registerShortcodeToFunction($shortcodeName, $functionName) {
50
+ if (is_array($shortcodeName)) {
51
+ foreach ($shortcodeName as $aName) {
52
+ add_shortcode($aName, array($this, $functionName));
53
+ }
54
+ }
55
+ else {
56
+ add_shortcode($shortcodeName, array($this, $functionName));
57
+ }
58
+ }
59
 
60
+ /**
61
+ * @abstract Override this function and add actual shortcode handling here
62
+ * @param $atts shortcode inputs
63
+ * @return string shortcode content
64
+ */
65
+ public abstract function handleShortcode($atts);
66
 
67
  }
WPML_ShortCodeScriptLoader.php CHANGED
@@ -33,37 +33,37 @@ if ( ! defined( 'ABSPATH' ) ) exit;
33
  */
34
  abstract class WPML_ShortCodeScriptLoader extends WPML_ShortCodeLoader {
35
 
36
- var $doAddScript;
37
 
38
- public function register($shortcodeName) {
39
- $this->registerShortcodeToFunction($shortcodeName, 'handleShortcodeWrapper');
40
 
41
- // It will be too late to enqueue the script in the header,
42
- // but can add them to the footer
43
- add_action('wp_footer', array($this, 'addScriptWrapper'));
44
- }
45
 
46
- public function handleShortcodeWrapper($atts) {
47
- // Flag that we need to add the script
48
- $this->doAddScript = true;
49
- return $this->handleShortcode($atts);
50
- }
51
 
52
 
53
- public function addScriptWrapper() {
54
- // Only add the script if the shortcode was actually called
55
- if ($this->doAddScript) {
56
- $this->addScript();
57
- }
58
- }
59
 
60
- /**
61
- * @abstract override this function with calls to insert scripts needed by your shortcode in the footer
62
- * Example:
63
- * wp_register_script('my-script', plugins_url('js/my-script.js', __FILE__), array('jquery'), '1.0', true);
64
- * wp_print_scripts('my-script');
65
- * @return void
66
- */
67
- public abstract function addScript();
68
 
69
  }
33
  */
34
  abstract class WPML_ShortCodeScriptLoader extends WPML_ShortCodeLoader {
35
 
36
+ var $doAddScript;
37
 
38
+ public function register($shortcodeName) {
39
+ $this->registerShortcodeToFunction($shortcodeName, 'handleShortcodeWrapper');
40
 
41
+ // It will be too late to enqueue the script in the header,
42
+ // but can add them to the footer
43
+ add_action('wp_footer', array($this, 'addScriptWrapper'));
44
+ }
45
 
46
+ public function handleShortcodeWrapper($atts) {
47
+ // Flag that we need to add the script
48
+ $this->doAddScript = true;
49
+ return $this->handleShortcode($atts);
50
+ }
51
 
52
 
53
+ public function addScriptWrapper() {
54
+ // Only add the script if the shortcode was actually called
55
+ if ($this->doAddScript) {
56
+ $this->addScript();
57
+ }
58
+ }
59
 
60
+ /**
61
+ * @abstract override this function with calls to insert scripts needed by your shortcode in the footer
62
+ * Example:
63
+ * wp_register_script('my-script', plugins_url('js/my-script.js', __FILE__), array('jquery'), '1.0', true);
64
+ * wp_print_scripts('my-script');
65
+ * @return void
66
+ */
67
+ public abstract function addScript();
68
 
69
  }
WPML_Utils.php CHANGED
@@ -11,108 +11,108 @@ if ( ! defined( 'ABSPATH' ) ) exit;
11
  * @since 1.6.0
12
  */
13
  class WPML_Utils {
14
- /**
15
- * Ensure value is subset of given set
16
- * @since 1.6.0
17
- * @param string $value expected value.
18
- * @param array $allowed_values allowed values.
19
- * @param string $default_value default value.
20
- * @return mixed
21
- */
22
- public static function sanitize_expected_value( $value, $allowed_values, $default_value = null ) {
23
- $allowed_values = (is_array( $allowed_values ) ) ? $allowed_values : array( $allowed_values );
24
- if ( $value && in_array( $value, $allowed_values ) ) {
25
- return $value;
26
- }
27
- if ( null !== $default_value ) {
28
- return $default_value;
29
- }
30
- return false;
31
- }
32
 
33
- /**
34
- * Multilevel array_search
35
- * @since 1.3
36
- * @param string $needle the searched value.
37
- * @param array $haystack the array.
38
- * @return mixed Returns the value if needle is found in the array, false otherwise.
39
- * @see array_search()
40
- */
41
- public static function recursive_array_search( $needle, $haystack ) {
42
- foreach ( $haystack as $key => $value ) {
43
- $current_key = $key;
44
- if ( $needle === $value or ( is_array( $value ) && self::recursive_array_search( $needle, $value ) !== false ) ) {
45
- return $current_key;
46
- }
47
- }
48
- return false;
49
- }
50
 
51
- /**
52
- * Determines appropriate fa icon for a file
53
- * @sine 1.3
54
- * @param string $file_path path to file.
55
- * @return string returns the most suitable icon or generic one if not possible.
56
- */
57
- public static function determine_fa_icon( $file_path ) {
58
  $default_icon = '<i class="fa fa-file-o"></i>';
59
- $supported = array(
60
- 'archive' => array(
61
- 'application/zip',
62
- 'application/x-rar-compressed',
63
- 'application/x-rar',
64
- 'application/x-gzip',
65
- 'application/x-msdownload',
66
- 'application/x-msdownload',
67
- 'application/vnd.ms-cab-compressed',
68
- ),
69
- 'audio',
70
- 'code' => array(
71
- 'text/x-c',
72
- 'text/x-c++',
73
- ),
74
- 'excel' => array( 'application/vnd.ms-excel'
75
- ),
76
- 'image', 'text', 'movie', 'pdf', 'photo', 'picture',
77
- 'powerpoint' => array(
78
- 'application/vnd.ms-powerpoint'
79
- ), 'sound', 'video', 'word' => array(
80
- 'application/msword'
81
- ), 'zip'
82
- );
83
-
84
  if( !function_exists('mime_content_type') ) {
85
  return $default_icon;
86
  }
87
 
88
- $mime = mime_content_type( $file_path );
89
- $mime_parts = explode( '/', $mime );
90
- $attribute = $mime_parts[0];
91
- $type = $mime_parts[1];
92
 
93
- $fa_icon = false;
94
- if ( ($key = self::recursive_array_search( $mime, $supported ) ) !== false ) {
95
- // Use specific icon for mime first.
96
- $fa_icon = $key;
97
- } elseif ( in_array( $attribute, $supported ) ) {
98
- // Use generic file icon.
99
- $fa_icon = $attribute;
100
- }
101
 
102
- if ( false === $fa_icon ) {
103
- return $default_icon;
104
- } else {
105
- return '<i class="fa fa-file-' . $fa_icon . '-o"></i>';
106
- }
107
- }
108
 
109
- /**
110
- * Find appropriate fa icon from file path
111
- * @since 1.3
112
- * @param string $file_path path to file.
113
- * @return string
114
- */
115
- public static function generate_attachment_icon( $file_path ) {
116
- return self::determine_fa_icon( $file_path );
117
- }
118
  }
11
  * @since 1.6.0
12
  */
13
  class WPML_Utils {
14
+ /**
15
+ * Ensure value is subset of given set
16
+ * @since 1.6.0
17
+ * @param string $value expected value.
18
+ * @param array $allowed_values allowed values.
19
+ * @param string $default_value default value.
20
+ * @return mixed
21
+ */
22
+ public static function sanitize_expected_value( $value, $allowed_values, $default_value = null ) {
23
+ $allowed_values = (is_array( $allowed_values ) ) ? $allowed_values : array( $allowed_values );
24
+ if ( $value && in_array( $value, $allowed_values ) ) {
25
+ return $value;
26
+ }
27
+ if ( null !== $default_value ) {
28
+ return $default_value;
29
+ }
30
+ return false;
31
+ }
32
 
33
+ /**
34
+ * Multilevel array_search
35
+ * @since 1.3
36
+ * @param string $needle the searched value.
37
+ * @param array $haystack the array.
38
+ * @return mixed Returns the value if needle is found in the array, false otherwise.
39
+ * @see array_search()
40
+ */
41
+ public static function recursive_array_search( $needle, $haystack ) {
42
+ foreach ( $haystack as $key => $value ) {
43
+ $current_key = $key;
44
+ if ( $needle === $value or ( is_array( $value ) && self::recursive_array_search( $needle, $value ) !== false ) ) {
45
+ return $current_key;
46
+ }
47
+ }
48
+ return false;
49
+ }
50
 
51
+ /**
52
+ * Determines appropriate fa icon for a file
53
+ * @sine 1.3
54
+ * @param string $file_path path to file.
55
+ * @return string returns the most suitable icon or generic one if not possible.
56
+ */
57
+ public static function determine_fa_icon( $file_path ) {
58
  $default_icon = '<i class="fa fa-file-o"></i>';
59
+ $supported = array(
60
+ 'archive' => array(
61
+ 'application/zip',
62
+ 'application/x-rar-compressed',
63
+ 'application/x-rar',
64
+ 'application/x-gzip',
65
+ 'application/x-msdownload',
66
+ 'application/x-msdownload',
67
+ 'application/vnd.ms-cab-compressed',
68
+ ),
69
+ 'audio',
70
+ 'code' => array(
71
+ 'text/x-c',
72
+ 'text/x-c++',
73
+ ),
74
+ 'excel' => array( 'application/vnd.ms-excel'
75
+ ),
76
+ 'image', 'text', 'movie', 'pdf', 'photo', 'picture',
77
+ 'powerpoint' => array(
78
+ 'application/vnd.ms-powerpoint'
79
+ ), 'sound', 'video', 'word' => array(
80
+ 'application/msword'
81
+ ), 'zip'
82
+ );
83
+
84
  if( !function_exists('mime_content_type') ) {
85
  return $default_icon;
86
  }
87
 
88
+ $mime = mime_content_type( $file_path );
89
+ $mime_parts = explode( '/', $mime );
90
+ $attribute = $mime_parts[0];
91
+ $type = $mime_parts[1];
92
 
93
+ $fa_icon = false;
94
+ if ( ($key = self::recursive_array_search( $mime, $supported ) ) !== false ) {
95
+ // Use specific icon for mime first.
96
+ $fa_icon = $key;
97
+ } elseif ( in_array( $attribute, $supported ) ) {
98
+ // Use generic file icon.
99
+ $fa_icon = $attribute;
100
+ }
101
 
102
+ if ( false === $fa_icon ) {
103
+ return $default_icon;
104
+ } else {
105
+ return '<i class="fa fa-file-' . $fa_icon . '-o"></i>';
106
+ }
107
+ }
108
 
109
+ /**
110
+ * Find appropriate fa icon from file path
111
+ * @since 1.3
112
+ * @param string $file_path path to file.
113
+ * @return string
114
+ */
115
+ public static function generate_attachment_icon( $file_path ) {
116
+ return self::determine_fa_icon( $file_path );
117
+ }
118
  }
css/modal.css CHANGED
@@ -2,7 +2,7 @@
2
  display: none;
3
  }
4
  #wp-mail-logging-modal-backdrop {
5
- background: none repeat scroll 0 0 #000000;
6
  bottom: 0;
7
  left: 0;
8
  min-height: 360px;
@@ -13,7 +13,7 @@
13
  z-index: 159900;
14
  }
15
  #wp-mail-logging-modal-content {
16
- background: none repeat scroll 0 0 #ffffff;
17
  bottom: 0;
18
  left: 0;
19
  margin: auto;
@@ -33,8 +33,8 @@
33
  z-index: 300010;
34
  }
35
  #wp-mail-logging-modal-content-header {
36
- background-color: #eeeeee;
37
- border-bottom: 1px solid #cccccc;
38
  left: 0;
39
  padding: 15px 10px 15px 10px;
40
  position: absolute;
@@ -47,7 +47,7 @@
47
  position: absolute;
48
  right: 10px;
49
  text-decoration: none;
50
- top: 25%;
51
  width: 30px;
52
  z-index: 1000;
53
  }
@@ -79,13 +79,22 @@
79
  position: relative;
80
  padding: 15px;
81
  }
 
 
 
 
 
 
 
 
 
82
  #wp-mail-logging-modal-content-body .title {
83
  font-weight: bold;
84
  display: block;
85
  }
86
  #wp-mail-logging-modal-content-footer {
87
- background-color: #eeeeee;
88
- border-top: 1px solid #cccccc;
89
  bottom: 0;
90
  left: 0;
91
  padding: 15px 10px 15px 10px;
2
  display: none;
3
  }
4
  #wp-mail-logging-modal-backdrop {
5
+ background: none repeat scroll 0 0 #000;
6
  bottom: 0;
7
  left: 0;
8
  min-height: 360px;
13
  z-index: 159900;
14
  }
15
  #wp-mail-logging-modal-content {
16
+ background: none repeat scroll 0 0 #fff;
17
  bottom: 0;
18
  left: 0;
19
  margin: auto;
33
  z-index: 300010;
34
  }
35
  #wp-mail-logging-modal-content-header {
36
+ background-color: #eee;
37
+ border-bottom: 1px solid #ccc;
38
  left: 0;
39
  padding: 15px 10px 15px 10px;
40
  position: absolute;
47
  position: absolute;
48
  right: 10px;
49
  text-decoration: none;
50
+ top: 35%;
51
  width: 30px;
52
  z-index: 1000;
53
  }
79
  position: relative;
80
  padding: 15px;
81
  }
82
+ #wp-mail-logging-modal-content-body .info {
83
+ border-left: 4px solid #ffba00;
84
+ display: block;
85
+ background: #FFF9E9;
86
+ -webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
87
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
88
+ margin: 5px 5px 2px;
89
+ padding: 1px 12px;
90
+ }
91
  #wp-mail-logging-modal-content-body .title {
92
  font-weight: bold;
93
  display: block;
94
  }
95
  #wp-mail-logging-modal-content-footer {
96
+ background-color: #eee;
97
+ border-top: 1px solid #ccc;
98
  bottom: 0;
99
  left: 0;
100
  padding: 15px 10px 15px 10px;
css/modal.less CHANGED
@@ -73,7 +73,7 @@
73
  position: absolute;
74
  right: 10px;
75
  text-decoration: none;
76
- top: 25%;
77
  width: 30px;
78
  z-index: 1000;
79
  }
@@ -110,7 +110,17 @@
110
  position: relative;
111
  padding: @body-content-padding;
112
  }
113
-
 
 
 
 
 
 
 
 
 
 
114
  & .title {
115
  font-weight: bold;
116
  display: block;
73
  position: absolute;
74
  right: 10px;
75
  text-decoration: none;
76
+ top: 35%;
77
  width: 30px;
78
  z-index: 1000;
79
  }
110
  position: relative;
111
  padding: @body-content-padding;
112
  }
113
+
114
+ & .info {
115
+ border-left: 4px solid #ffba00;
116
+ display: block;
117
+ background: #FFF9E9;
118
+ -webkit-box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
119
+ box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
120
+ margin: 5px 5px 2px;
121
+ padding: 1px 12px;
122
+ }
123
+
124
  & .title {
125
  font-weight: bold;
126
  display: block;
inc/class-wp-list-table.php CHANGED
@@ -1,974 +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
- }
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
+ }
inc/redux/WPML_Redux_Framework_config.php CHANGED
@@ -2,247 +2,247 @@
2
 
3
  namespace No3x\WPML\Settings;
4
 
5
- /**
6
- * ReduxFramework Sample Config File
7
- * For full documentation, please visit: http://docs.reduxframework.com/
8
- */
9
 
10
  if (!class_exists('WPML_Redux_Framework_config')) {
11
 
12
  class WPML_Redux_Framework_config {
13
 
14
- public $args = array();
15
- public $sections = array();
16
- public $theme;
17
- public $ReduxFramework;
18
- protected $plugin_meta = array();
19
-
20
- public function __construct( $plugin_meta ) {
21
- $this->plugin_meta = $plugin_meta;
22
 
23
- if ( ! class_exists( 'ReduxFramework' ) ) {
24
- return;
25
- }
26
 
27
- // This is needed. Bah WordPress bugs. ;)
28
- if ( true == \Redux_Helpers::isTheme( __FILE__ ) ) {
29
- $this->initSettings();
30
- } else {
31
- add_action( 'plugins_loaded', array( $this, 'initSettings' ), 10 );
32
- }
33
 
 
 
 
 
 
34
  }
35
 
36
- public function initSettings() {
37
 
38
- // Just for demo purposes. Not needed per say.
39
- $this->theme = wp_get_theme();
40
 
41
- // Set the default arguments
42
- $this->setArguments();
43
 
44
- // Set a few help tabs so you can see how it's done
45
- // $this->setHelpTabs();
46
 
47
- // Create the sections and fields
48
- $this->setSections();
49
 
50
- if ( ! isset( $this->args['opt_name'] ) ) { // No errors please
51
- return;
52
- }
53
 
54
- // If Redux is running as a plugin, this will remove the demo notice and links
55
- //add_action( 'redux/loaded', array( $this, 'remove_demo' ) );
 
56
 
57
- // Function to test the compiler hook and demo CSS output.
58
- // Above 10 is a priority, but 2 in necessary to include the dynamically generated CSS to be sent to the function.
59
- //add_filter('redux/options/'.$this->args['opt_name'].'/compiler', array( $this, 'compiler_action' ), 10, 3);
60
 
61
- // Change the arguments after they've been declared, but before the panel is created
62
- //add_filter('redux/options/'.$this->args['opt_name'].'/args', array( $this, 'change_arguments' ) );
 
63
 
64
- // Change the default value of a field after it's been set, but before it's been useds
65
- //add_filter('redux/options/'.$this->args['opt_name'].'/defaults', array( $this,'change_defaults' ) );
66
 
67
- // Dynamically add a section. Can be also used to modify sections/fields
68
- //add_filter('redux/options/' . $this->args['opt_name'] . '/sections', array($this, 'dynamic_section'));
69
 
70
- $this->ReduxFramework = new \ReduxFramework( $this->sections, $this->args );
71
- }
72
 
73
- /**
74
- * This is a test function that will let you see when the compiler hook occurs.
75
- * It only runs if a field set with compiler=>true is changed.
76
- * */
77
- function compiler_action( $options, $css, $changed_values ) {
78
- echo '<h1>The compiler hook has run!</h1>';
79
- echo "<pre>";
80
- print_r( $changed_values ); // Values that have changed since the last save
81
- echo "</pre>";
82
- //print_r($options); //Option values
83
- //print_r($css); // Compiler selector CSS values compiler => array( CSS SELECTORS )
84
-
85
- /*
86
- // Demo of how to use the dynamic CSS and write your own static CSS file
87
- $filename = dirname(__FILE__) . '/style' . '.css';
88
- global $wp_filesystem;
89
- if( empty( $wp_filesystem ) ) {
90
- require_once( ABSPATH .'/wp-admin/includes/file.php' );
91
- WP_Filesystem();
92
- }
93
-
94
- if( $wp_filesystem ) {
95
- $wp_filesystem->put_contents(
96
- $filename,
97
- $css,
98
- FS_CHMOD_FILE // predefined mode settings for WP files
99
- );
100
- }
101
- */
102
- }
103
 
104
- /**
105
- * Custom function for filtering the sections array. Good for child themes to override or add to the sections.
106
- * Simply include this function in the child themes functions.php file.
107
- * NOTE: the defined constants for URLs, and directories will NOT be available at this point in a child theme,
108
- * so you must use get_template_directory_uri() if you want to use any of the built in icons
109
- * */
110
- function dynamic_section( $sections ) {
111
- //$sections = array();
112
- $sections[] = array(
113
- 'title' => __( 'Section via hook', 'redux-framework-demo' ),
114
- 'desc' => __( '<p class="description">This is a section created by adding a filter to the sections array. Can be used by child themes to add/remove sections from the options.</p>', 'redux-framework-demo' ),
115
- 'icon' => 'el-icon-paper-clip',
116
- // Leave this as a blank section, no options just some intro text set above.
117
- 'fields' => array()
118
- );
119
-
120
- return $sections;
121
- }
122
 
123
- /**
124
- * Filter hook for filtering the args. Good for child themes to override or add to the args array. Can also be used in other functions.
125
- * */
126
- function change_arguments( $args ) {
127
- //$args['dev_mode'] = true;
 
 
 
128
 
129
- return $args;
130
- }
 
 
 
 
 
 
 
131
 
132
- /**
133
- * Filter hook for filtering the default value of any given field. Very useful in development mode.
134
- * */
135
- function change_defaults( $defaults ) {
136
- $defaults['str_replace'] = 'Testing filter hook!';
 
 
 
 
 
 
 
 
 
 
137
 
138
- return $defaults;
139
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
- // Remove the demo link and the notice of integrated demo from the redux-framework plugin
142
- function remove_demo() {
 
 
 
143
 
144
- // Used to hide the demo mode link from the plugin page. Only used when Redux is a plugin.
145
- if ( class_exists( 'ReduxFrameworkPlugin' ) ) {
146
- remove_filter( 'plugin_row_meta', array(
147
- ReduxFrameworkPlugin::instance(),
148
- 'plugin_metalinks'
149
- ), null, 2 );
150
 
151
- // Used to hide the activation notice informing users of the demo panel. Only used when Redux is a plugin.
152
- remove_action( 'admin_notices', array( ReduxFrameworkPlugin::instance(), 'admin_notices' ) );
153
- }
154
  }
 
155
 
156
- public function setSections() {
157
 
158
- /**
159
- * Used within different fields. Simply examples. Search for ACTUAL DECLARATION for field examples
160
- * */
161
- // Background Patterns Reader
162
- $sample_patterns_path = \ReduxFramework::$_dir . '../sample/patterns/';
163
- $sample_patterns_url = \ReduxFramework::$_url . '../sample/patterns/';
164
- $sample_patterns = array();
165
 
166
- if ( is_dir( $sample_patterns_path ) ) :
167
 
168
- if ( $sample_patterns_dir = opendir( $sample_patterns_path ) ) :
169
- $sample_patterns = array();
170
 
171
- while ( ( $sample_patterns_file = readdir( $sample_patterns_dir ) ) !== false ) {
172
 
173
- if ( stristr( $sample_patterns_file, '.png' ) !== false || stristr( $sample_patterns_file, '.jpg' ) !== false ) {
174
- $name = explode( '.', $sample_patterns_file );
175
- $name = str_replace( '.' . end( $name ), '', $sample_patterns_file );
176
- $sample_patterns[] = array(
177
- 'alt' => $name,
178
- 'img' => $sample_patterns_url . $sample_patterns_file
179
- );
180
- }
181
  }
182
- endif;
183
  endif;
184
-
185
- ob_start();
186
-
187
- $ct = wp_get_theme();
188
- $this->theme = $ct;
189
- $item_name = $this->theme->get( 'Name' );
190
- $tags = $this->theme->Tags;
191
- $screenshot = $this->theme->get_screenshot();
192
- $class = $screenshot ? 'has-screenshot' : '';
193
-
194
- $customize_title = sprintf( __( 'Customize &#8220;%s&#8221;', 'redux-framework-demo' ), $this->theme->display( 'Name' ) );
195
-
196
- ?>
197
- <div id="current-theme" class="<?php echo esc_attr( $class ); ?>">
198
- <?php if ( $screenshot ) : ?>
199
- <?php if ( current_user_can( 'edit_theme_options' ) ) : ?>
200
- <a href="<?php echo wp_customize_url(); ?>" class="load-customize hide-if-no-customize"
201
- title="<?php echo esc_attr( $customize_title ); ?>">
202
- <img src="<?php echo esc_url( $screenshot ); ?>"
203
- alt="<?php esc_attr_e( 'Current theme preview', 'redux-framework-demo' ); ?>"/>
204
- </a>
205
- <?php endif; ?>
206
- <img class="hide-if-customize" src="<?php echo esc_url( $screenshot ); ?>"
207
- alt="<?php esc_attr_e( 'Current theme preview', 'redux-framework-demo' ); ?>"/>
208
  <?php endif; ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
- <h4><?php echo $this->theme->display( 'Name' ); ?></h4>
211
-
212
- <div>
213
- <ul class="theme-info">
214
- <li><?php printf( __( 'By %s', 'redux-framework-demo' ), $this->theme->display( 'Author' ) ); ?></li>
215
- <li><?php printf( __( 'Version %s', 'redux-framework-demo' ), $this->theme->display( 'Version' ) ); ?></li>
216
- <li><?php echo '<strong>' . __( 'Tags', 'redux-framework-demo' ) . ':</strong> '; ?><?php printf( $this->theme->display( 'Tags' ) ); ?></li>
217
- </ul>
218
- <p class="theme-description"><?php echo $this->theme->display( 'Description' ); ?></p>
219
- <?php
220
- if ( $this->theme->parent() ) {
221
- printf( ' <p class="howto">' . __( 'This <a href="%1$s">child theme</a> requires its parent theme, %2$s.', 'redux-framework-demo' ) . '</p>', __( 'http://codex.wordpress.org/Child_Themes', 'redux-framework-demo' ), $this->theme->parent()->display( 'Name' ) );
222
- }
223
- ?>
224
-
225
- </div>
226
  </div>
 
227
 
228
- <?php
229
- $item_info = ob_get_contents();
230
 
231
- ob_end_clean();
232
 
233
- $sampleHTML = '';
234
- if ( file_exists( dirname( __FILE__ ) . '/info-html.html' ) ) {
235
- Redux_Functions::initWpFilesystem();
236
 
237
- global $wp_filesystem;
238
 
239
- $sampleHTML = $wp_filesystem->get_contents( dirname( __FILE__ ) . '/info-html.html' );
240
- }
241
 
242
  // ACTUAL DECLARATION OF SECTIONS
243
  $this->sections[] = array(
244
- 'title' => __('General Settings', 'wpml'),
245
- 'desc' => __('', 'wpml'),
246
  'icon' => 'el-icon-cogs',
247
  // 'submenu' => false, // Setting submenu to false on a given section will hide it from the WordPress sidebar menu!
248
  'fields' => array(
@@ -250,354 +250,300 @@ if (!class_exists('WPML_Redux_Framework_config')) {
250
  array(
251
  'id' => 'delete-on-deactivation',
252
  'type' => 'switch',
253
- 'title' => __('Cleanup', 'wpml' ),
254
- 'subtitle' => __('Delete all data on deactivation? (emails and settings)?', 'wpml'),
255
  'default' => 0,
256
- 'on' => 'Enabled',
257
- 'off' => 'Disabled',
 
 
 
 
 
 
 
 
258
  ),
259
- array(
260
- 'id' => 'can-see-submission-data',
261
- 'type' => 'select',
262
- 'data' => 'capabilities',
263
- 'default' => 'manage_options',
264
- 'title' => __('Can See Submission data', 'wpml'),
265
- 'subtitle' => __('Select the minimum role.', 'wpml'),
266
- ),
267
  array(
268
  'id' => 'datetimeformat-use-wordpress',
269
  'type' => 'switch',
270
- 'title' => __('WordPress Date Time Format', 'wpml' ),
271
- 'subtitle' => __( sprintf("Use format from WordPress settings (%s)", date_i18n( $this->wordpress_default_format(), current_time( 'timestamp' ) ) ), 'wpml'),
272
  'default' => 0,
273
- 'on' => 'Enabled',
274
- 'off' => 'Disabled',
 
 
 
 
 
 
 
 
 
 
 
 
275
  ),
276
- array(
277
- 'id' => 'preferred-mail-format',
278
- 'type' => 'select',
279
- 'options' => array(
280
- 'html' => 'html',
281
- 'raw' => 'raw',
282
- 'json' => 'json'
283
- ),
284
- 'default' => 'html',
285
- 'title' => __('Default Format for Message', 'wpml'),
286
- 'subtitle' => __('Select your preferred display format.', 'wpml'),
287
- ),
288
  array(
289
  'id' => 'display-host',
290
  'type' => 'switch',
291
- 'title' => __('Display Host', 'wpml' ),
292
- 'subtitle' => __('Display host column in list.', 'wpml'),
293
  'hint' => array(
294
  'title' => 'Host',
295
  'content' => 'Display the IP of the host WordPress is running on. This is useful when running it on multiple servers at the same time.',
296
  ),
297
  'default' => 0,
298
- 'on' => 'Enabled',
299
- 'off' => 'Disabled',
300
  ),
301
- array(
302
  'id' => 'section-log-rotation-start',
303
  'type' => 'section',
304
- 'title' => __('Log Rotation', 'wpml' ),
305
- 'subtitle' => __('Save space by deleting logs regularly.', 'wpml'),
306
  'indent' => true, // Indent all options below until the next 'section' option is set.
307
- ),
308
- array(
309
  'id' => 'log-rotation-limit-amout',
310
  'type' => 'switch',
311
- 'title' => __('Cleanup by Amount', 'wpml' ),
312
- 'subtitle' => __('Setup a automated cleanup routine!', 'wpml'),
313
  'default' => 0,
314
- 'on' => 'Enabled',
315
- 'off' => 'Disabled',
316
- ),
317
- array(
318
  'id' => 'log-rotation-limit-amout-keep',
319
  'type' => 'slider',
320
  'required' => array('log-rotation-limit-amout', '=', '1'),
321
- 'title' => __('Amount', 'wpml' ),
322
- 'subtitle' => __('When should mails are deleted?', 'wpml'),
323
- 'desc' => __('Cleanup when the stored mails exceed...', 'wpml'),
324
  'default' => 75,
325
  'min' => 25,
326
  'step' => 50,
327
  'max' => 3000,
328
  'display_value' => 'text'
329
- ),
330
- array(
331
  'id' => 'log-rotation-delete-time',
332
  'type' => 'switch',
333
- 'title' => __('Cleanup by Time', 'wpml' ),
334
- 'subtitle' => __('Setup a automated cleanup routine!', 'wpml'),
335
  'default' => 0,
336
- 'on' => 'Enabled',
337
- 'off' => 'Disabled',
338
- ),
339
- array(
340
  'id' => 'log-rotation-delete-time-days',
341
  'type' => 'slider',
342
  'required' => array('log-rotation-delete-time', '=', '1'),
343
- 'title' => __('Time', 'wpml' ),
344
- 'subtitle' => __('When should mails are deleted?', 'wpml'),
345
- 'desc' => __('Delete mails older than days...', 'wpml'),
346
  'default' => 30,
347
  'min' => 1,
348
  'step' => 7,
349
  'max' => 400,
350
  'display_value' => 'text'
351
- ),
352
- array(
353
  'id' => 'section-log-rotation-end',
354
  'type' => 'section',
355
  'indent' => false // Indent all options below until the next 'section' option is set.
356
- ),
357
  ),
358
  );
359
  }
360
 
361
- public function wordpress_default_format()
362
- {
363
- $date_format = get_option( 'date_format' );
364
- $time_format = get_option( 'time_format' );
365
- $date_format = empty( $date_format ) ? 'F j, Y' : $date_format;
366
- $time_format = empty( $time_format ) ? 'g:i a' : $time_format;
367
- return "{$date_format} {$time_format}";
368
- }
369
 
370
- public function setHelpTabs() {
371
 
372
- // Custom page help tabs, displayed using the help API. Tabs are shown in order of definition.
373
- $this->args['help_tabs'][] = array(
374
- 'id' => 'redux-help-tab-1',
375
- 'title' => __( 'Theme Information 1', 'redux-framework-demo' ),
376
- 'content' => __( '<p>This is the tab content, HTML is allowed.</p>', 'redux-framework-demo' )
377
- );
378
 
379
- $this->args['help_tabs'][] = array(
380
- 'id' => 'redux-help-tab-2',
381
- 'title' => __( 'Theme Information 2', 'redux-framework-demo' ),
382
- 'content' => __( '<p>This is the tab content, HTML is allowed.</p>', 'redux-framework-demo' )
383
- );
384
 
385
- // Set the help sidebar
386
- $this->args['help_sidebar'] = __( '<p>This is the sidebar content, HTML is allowed.</p>', 'redux-framework-demo' );
387
- }
388
 
389
- /**
390
- * All the possible arguments for Redux.
391
- * For full documentation on arguments, please refer to: https://github.com/ReduxFramework/ReduxFramework/wiki/Arguments
392
- * */
393
- public function setArguments() {
394
-
395
- $theme = wp_get_theme(); // For use with some settings. Not necessary.
396
-
397
- $this->args = array(
398
- // TYPICAL -> Change these values as you need/desire
399
- 'opt_name' => 'wpml_settings',
400
- // This is where your data is stored in the database and also becomes your global variable name.
401
- 'display_name' => 'WP Mail Logging Settings',
402
- // Name that appears at the top of your panel
403
- 'display_version' => $this->plugin_meta['version_installed'],
404
- // Version that appears at the top of your panel
405
- 'menu_type' => 'submenu',
406
- //Specify if the admin menu should appear or not. Options: menu or submenu (Under appearance only)
407
- 'allow_sub_menu' => true,
408
- // Show the sections below the admin menu item or not
409
- 'menu_title' => 'Settings',
410
- 'page_title' => $this->plugin_meta['display_name'],
411
- // You will need to generate a Google API key to use this feature.
412
- // Please visit: https://developers.google.com/fonts/docs/developer_api#Auth
413
- 'google_api_key' => '',
414
- // Set it you want google fonts to update weekly. A google_api_key value is required.
415
- 'google_update_weekly' => false,
416
- // Must be defined to add google fonts to the typography module
417
- 'async_typography' => true,
418
- // Use a asynchronous font on the front end or font string
419
- //'disable_google_fonts_link' => true, // Disable this in case you want to create your own google fonts loader
420
- 'admin_bar' => false,
421
- // Show the panel pages on the admin bar
422
- 'admin_bar_icon' => 'dashicons-portfolio',
423
- // Choose an icon for the admin bar menu
424
- 'admin_bar_priority' => 50,
425
- // Choose an priority for the admin bar menu
426
- 'global_variable' => '',
427
- // Set a different name for your global variable other than the opt_name
428
- 'dev_mode' => false,
429
- // Show the time the page took to load, etc
430
- 'update_notice' => true,
431
- // If dev_mode is enabled, will notify developer of updated versions available in the GitHub Repo
432
- 'customizer' => false,
433
- // Enable basic customizer support
434
- //'open_expanded' => true, // Allow you to start the panel in an expanded way initially.
435
- //'disable_save_warn' => true, // Disable the save warning when a user changes a field
436
-
437
- // OPTIONAL -> Give you extra features
438
- 'page_priority' => null,
439
- // Order where the menu appears in the admin area. If there is any conflict, something will not show. Warning.
440
- 'page_parent' => 'wpml_plugin_log',
441
- // For a full list of options, visit: http://codex.wordpress.org/Function_Reference/add_submenu_page#Parameters
442
- 'page_permissions' => 'manage_options',
443
- // Permissions needed to access the options panel.
444
- 'menu_icon' => '',
445
- // Specify a custom URL to an icon
446
- 'last_tab' => '',
447
- // Force your panel to always open to a specific tab (by id)
448
- 'page_icon' => 'icon-themes',
449
- // Icon displayed in the admin panel next to your menu_title
450
- 'page_slug' => 'wpml_plugin_settings',
451
- // Page slug used to denote the panel
452
- 'save_defaults' => true,
453
- // On load save the defaults to DB before user clicks save or not
454
- 'default_show' => false,
455
- // If true, shows the default value next to each field that is not the default value.
456
- 'default_mark' => '*',
457
- // What to print by the field's title if the value shown is default. Suggested: *
458
- 'show_import_export' => true,
459
- // Shows the Import/Export panel when not used as a field.
460
-
461
- // CAREFUL -> These options are for advanced use only
462
- 'transient_time' => 60 * MINUTE_IN_SECONDS,
463
- 'output' => true,
464
- // Global shut-off for dynamic CSS output by the framework. Will also disable google fonts output
465
- 'output_tag' => true,
466
- // Allows dynamic CSS to be generated for customizer and google fonts, but stops the dynamic CSS from going to the head
467
- // 'footer_credit' => '', // Disable the footer credit of Redux. Please leave if you can help it.
468
-
469
- // FUTURE -> Not in use yet, but reserved or partially implemented. Use at your own risk.
470
- 'database' => '',
471
- // possible: options, theme_mods, theme_mods_expanded, transient. Not fully functional, warning!
472
- 'system_info' => false,
473
- // REMOVE
474
-
475
- // HINTS
476
- 'hints' => array(
477
- 'icon' => 'el el-question-sign',
478
- 'icon_position' => 'right',
479
- 'icon_color' => 'lightgray',
480
- 'icon_size' => 'normal',
481
- 'tip_style' => array(
482
- 'color' => 'light',
483
- 'shadow' => true,
484
- 'rounded' => false,
485
- 'style' => 'bootstrap',
486
- ),
487
- 'tip_position' => array(
488
- 'my' => 'top left',
489
- 'at' => 'bottom right',
 
 
 
 
 
 
490
  ),
491
- 'tip_effect' => array(
492
- 'show' => array(
493
- 'effect' => 'slide',
494
- 'duration' => '500',
495
- 'event' => 'mouseover',
496
- ),
497
- 'hide' => array(
498
- 'effect' => 'slide',
499
- 'duration' => '500',
500
- 'event' => 'click mouseleave',
501
- ),
502
  ),
503
- )
504
- );
505
-
506
- // ADMIN BAR LINKS -> Setup custom links in the admin bar menu as external items.
507
- $this->args['admin_bar_links'][] = array(
508
- 'id' => 'redux-docs',
509
- 'href' => 'http://docs.reduxframework.com/',
510
- 'title' => __( 'Documentation', 'redux-framework-demo' ),
511
- );
512
-
513
- $this->args['admin_bar_links'][] = array(
514
- //'id' => 'redux-support',
515
- 'href' => 'https://github.com/ReduxFramework/redux-framework/issues',
516
- 'title' => __( 'Support', 'redux-framework-demo' ),
517
- );
518
-
519
- $this->args['admin_bar_links'][] = array(
520
- 'id' => 'redux-extensions',
521
- 'href' => 'reduxframework.com/extensions',
522
- 'title' => __( 'Extensions', 'redux-framework-demo' ),
523
- );
524
-
525
- // SOCIAL ICONS -> Setup custom links in the footer for quick links in your panel footer icons.
526
- $this->args['share_icons'][] = array(
527
- 'url' => 'https://github.com/No3x/wp-mail-logging',
528
- 'title' => 'Visit us on GitHub',
529
- 'icon' => 'el-icon-github'
530
- //'img' => '', // You can use icon OR img. IMG needs to be a full URL.
531
- );
532
- $this->args['share_icons'][] = array(
533
- 'url' => $this->plugin_meta['wp_uri'],
534
- 'title' => 'Visit us on WordPress',
535
- 'icon' => 'el-icon-wordpress'
536
- );
537
-
538
-
539
- // Add content before the form.
540
- // $this->args['intro_text'] = __( '<p>This text is displayed above the options panel. It isn\'t required, but more info is always better! The intro_text field accepts all HTML.</p>', 'redux-framework-demo' );
541
-
542
- // Add content after the form.
543
- // $this->args['footer_text'] = __( '<p>This text is displayed below the options panel. It isn\'t required, but more info is always better! The footer_text field accepts all HTML.</p>', 'redux-framework-demo' );
544
- }
545
 
546
- public function validate_callback_function( $field, $value, $existing_value ) {
547
- $error = true;
548
- $value = 'just testing';
549
-
550
- /*
551
- do your validation
552
-
553
- if(something) {
554
- $value = $value;
555
- } elseif(something else) {
556
- $error = true;
557
- $value = $existing_value;
558
-
559
- }
560
- */
561
-
562
- $return['value'] = $value;
563
- $field['msg'] = 'your custom error message';
564
- if ( $error == true ) {
565
- $return['error'] = $field;
566
- }
567
-
568
- return $return;
569
- }
570
 
571
- public function class_field_callback( $field, $value ) {
572
- print_r( $field );
573
- echo '<br/>CLASS CALLBACK';
574
- print_r( $value );
575
- }
576
 
577
- }
 
 
 
 
578
 
579
- global $reduxConfig;
580
- //$reduxConfig = new WPML_Redux_Framework_config();
581
- } else {
582
- echo "The class named Redux_Framework_sample_config has already been called. <strong>Developers, you need to prefix this class with your company name or you'll run into problems!</strong>";
583
- }
 
 
 
 
 
 
 
584
 
585
- /**
586
- * Custom function for the callback referenced above
587
- */
588
- if ( ! function_exists( 'redux_my_custom_field' ) ):
589
- function redux_my_custom_field( $field, $value ) {
590
- print_r( $field );
591
- echo '<br/>';
592
- print_r( $value );
593
  }
594
- endif;
595
 
596
- /**
597
- * Custom function for the callback validation referenced above
598
- * */
599
- if ( ! function_exists( 'redux_validate_callback_function' ) ):
600
- function redux_validate_callback_function( $field, $value, $existing_value ) {
601
  $error = true;
602
  $value = 'just testing';
603
 
@@ -609,7 +555,7 @@ if (!class_exists('WPML_Redux_Framework_config')) {
609
  } elseif(something else) {
610
  $error = true;
611
  $value = $existing_value;
612
-
613
  }
614
  */
615
 
@@ -621,4 +567,58 @@ if (!class_exists('WPML_Redux_Framework_config')) {
621
 
622
  return $return;
623
  }
624
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  namespace No3x\WPML\Settings;
4
 
5
+ /**
6
+ * ReduxFramework Sample Config File
7
+ * For full documentation, please visit: http://docs.reduxframework.com/
8
+ */
9
 
10
  if (!class_exists('WPML_Redux_Framework_config')) {
11
 
12
  class WPML_Redux_Framework_config {
13
 
14
+ public $args = array();
15
+ public $sections = array();
16
+ public $theme;
17
+ public $ReduxFramework;
18
+ protected $plugin_meta = array();
 
 
 
19
 
20
+ public function __construct( $plugin_meta ) {
21
+ $this->plugin_meta = $plugin_meta;
 
22
 
23
+ if ( ! class_exists( 'ReduxFramework' ) ) {
24
+ return;
25
+ }
 
 
 
26
 
27
+ // This is needed. Bah WordPress bugs. ;)
28
+ if ( true == \Redux_Helpers::isTheme( __FILE__ ) ) {
29
+ $this->initSettings();
30
+ } else {
31
+ add_action( 'plugins_loaded', array( $this, 'initSettings' ), 10 );
32
  }
33
 
34
+ }
35
 
36
+ public function initSettings() {
 
37
 
38
+ // Just for demo purposes. Not needed per say.
39
+ $this->theme = wp_get_theme();
40
 
41
+ // Set the default arguments
42
+ $this->setArguments();
43
 
44
+ // Set a few help tabs so you can see how it's done
45
+ // $this->setHelpTabs();
46
 
47
+ // Create the sections and fields
48
+ $this->setSections();
 
49
 
50
+ if ( ! isset( $this->args['opt_name'] ) ) { // No errors please
51
+ return;
52
+ }
53
 
54
+ // If Redux is running as a plugin, this will remove the demo notice and links
55
+ //add_action( 'redux/loaded', array( $this, 'remove_demo' ) );
 
56
 
57
+ // Function to test the compiler hook and demo CSS output.
58
+ // Above 10 is a priority, but 2 in necessary to include the dynamically generated CSS to be sent to the function.
59
+ //add_filter('redux/options/'.$this->args['opt_name'].'/compiler', array( $this, 'compiler_action' ), 10, 3);
60
 
61
+ // Change the arguments after they've been declared, but before the panel is created
62
+ //add_filter('redux/options/'.$this->args['opt_name'].'/args', array( $this, 'change_arguments' ) );
63
 
64
+ // Change the default value of a field after it's been set, but before it's been useds
65
+ //add_filter('redux/options/'.$this->args['opt_name'].'/defaults', array( $this,'change_defaults' ) );
66
 
67
+ // Dynamically add a section. Can be also used to modify sections/fields
68
+ //add_filter('redux/options/' . $this->args['opt_name'] . '/sections', array($this, 'dynamic_section'));
69
 
70
+ $this->ReduxFramework = new \ReduxFramework( $this->sections, $this->args );
71
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ /**
74
+ * This is a test function that will let you see when the compiler hook occurs.
75
+ * It only runs if a field set with compiler=>true is changed.
76
+ * */
77
+ function compiler_action( $options, $css, $changed_values ) {
78
+ echo '<h1>The compiler hook has run!</h1>';
79
+ echo "<pre>";
80
+ print_r( $changed_values ); // Values that have changed since the last save
81
+ echo "</pre>";
82
+ //print_r($options); //Option values
83
+ //print_r($css); // Compiler selector CSS values compiler => array( CSS SELECTORS )
 
 
 
 
 
 
 
84
 
85
+ /*
86
+ // Demo of how to use the dynamic CSS and write your own static CSS file
87
+ $filename = dirname(__FILE__) . '/style' . '.css';
88
+ global $wp_filesystem;
89
+ if( empty( $wp_filesystem ) ) {
90
+ require_once( ABSPATH .'/wp-admin/includes/file.php' );
91
+ WP_Filesystem();
92
+ }
93
 
94
+ if( $wp_filesystem ) {
95
+ $wp_filesystem->put_contents(
96
+ $filename,
97
+ $css,
98
+ FS_CHMOD_FILE // predefined mode settings for WP files
99
+ );
100
+ }
101
+ */
102
+ }
103
 
104
+ /**
105
+ * Custom function for filtering the sections array. Good for child themes to override or add to the sections.
106
+ * Simply include this function in the child themes functions.php file.
107
+ * NOTE: the defined constants for URLs, and directories will NOT be available at this point in a child theme,
108
+ * so you must use get_template_directory_uri() if you want to use any of the built in icons
109
+ * */
110
+ function dynamic_section( $sections ) {
111
+ //$sections = array();
112
+ $sections[] = array(
113
+ 'title' => 'Section via hook',
114
+ 'desc' => '<p class="description">This is a section created by adding a filter to the sections array. Can be used by child themes to add/remove sections from the options.</p>',
115
+ 'icon' => 'el-icon-paper-clip',
116
+ // Leave this as a blank section, no options just some intro text set above.
117
+ 'fields' => array()
118
+ );
119
 
120
+ return $sections;
121
+ }
122
+
123
+ /**
124
+ * Filter hook for filtering the args. Good for child themes to override or add to the args array. Can also be used in other functions.
125
+ * */
126
+ function change_arguments( $args ) {
127
+ //$args['dev_mode'] = true;
128
+
129
+ return $args;
130
+ }
131
+
132
+ /**
133
+ * Filter hook for filtering the default value of any given field. Very useful in development mode.
134
+ * */
135
+ function change_defaults( $defaults ) {
136
+ $defaults['str_replace'] = 'Testing filter hook!';
137
 
138
+ return $defaults;
139
+ }
140
+
141
+ // Remove the demo link and the notice of integrated demo from the redux-framework plugin
142
+ function remove_demo() {
143
 
144
+ // Used to hide the demo mode link from the plugin page. Only used when Redux is a plugin.
145
+ if ( class_exists( 'ReduxFrameworkPlugin' ) ) {
146
+ remove_filter( 'plugin_row_meta', array(
147
+ ReduxFrameworkPlugin::instance(),
148
+ 'plugin_metalinks'
149
+ ), null, 2 );
150
 
151
+ // Used to hide the activation notice informing users of the demo panel. Only used when Redux is a plugin.
152
+ remove_action( 'admin_notices', array( ReduxFrameworkPlugin::instance(), 'admin_notices' ) );
 
153
  }
154
+ }
155
 
156
+ public function setSections() {
157
 
158
+ /**
159
+ * Used within different fields. Simply examples. Search for ACTUAL DECLARATION for field examples
160
+ * */
161
+ // Background Patterns Reader
162
+ $sample_patterns_path = \ReduxFramework::$_dir . '../sample/patterns/';
163
+ $sample_patterns_url = \ReduxFramework::$_url . '../sample/patterns/';
164
+ $sample_patterns = array();
165
 
166
+ if ( is_dir( $sample_patterns_path ) ) :
167
 
168
+ if ( $sample_patterns_dir = opendir( $sample_patterns_path ) ) :
169
+ $sample_patterns = array();
170
 
171
+ while ( ( $sample_patterns_file = readdir( $sample_patterns_dir ) ) !== false ) {
172
 
173
+ if ( stristr( $sample_patterns_file, '.png' ) !== false || stristr( $sample_patterns_file, '.jpg' ) !== false ) {
174
+ $name = explode( '.', $sample_patterns_file );
175
+ $name = str_replace( '.' . end( $name ), '', $sample_patterns_file );
176
+ $sample_patterns[] = array(
177
+ 'alt' => $name,
178
+ 'img' => $sample_patterns_url . $sample_patterns_file
179
+ );
 
180
  }
181
+ }
182
  endif;
183
+ endif;
184
+
185
+ ob_start();
186
+
187
+ $ct = wp_get_theme();
188
+ $this->theme = $ct;
189
+ $item_name = $this->theme->get( 'Name' );
190
+ $tags = $this->theme->Tags;
191
+ $screenshot = $this->theme->get_screenshot();
192
+ $class = $screenshot ? 'has-screenshot' : '';
193
+
194
+ $customize_title = sprintf( __( 'Customize &#8220;%s&#8221;', 'redux-framework-demo' ), $this->theme->display( 'Name' ) );
195
+
196
+ ?>
197
+ <div id="current-theme" class="<?php echo esc_attr( $class ); ?>">
198
+ <?php if ( $screenshot ) : ?>
199
+ <?php if ( current_user_can( 'edit_theme_options' ) ) : ?>
200
+ <a href="<?php echo wp_customize_url(); ?>" class="load-customize hide-if-no-customize"
201
+ title="<?php echo esc_attr( $customize_title ); ?>">
202
+ <img src="<?php echo esc_url( $screenshot ); ?>"
203
+ alt="<?php esc_attr_e( 'Current theme preview', 'redux-framework-demo' ); ?>"/>
204
+ </a>
 
 
205
  <?php endif; ?>
206
+ <img class="hide-if-customize" src="<?php echo esc_url( $screenshot ); ?>"
207
+ alt="<?php esc_attr_e( 'Current theme preview', 'redux-framework-demo' ); ?>"/>
208
+ <?php endif; ?>
209
+
210
+ <h4><?php echo $this->theme->display( 'Name' ); ?></h4>
211
+
212
+ <div>
213
+ <ul class="theme-info">
214
+ <li><?php printf( __( 'By %s', 'redux-framework-demo' ), $this->theme->display( 'Author' ) ); ?></li>
215
+ <li><?php printf( __( 'Version %s', 'redux-framework-demo' ), $this->theme->display( 'Version' ) ); ?></li>
216
+ <li><?php echo '<strong>' . __( 'Tags', 'redux-framework-demo' ) . ':</strong> '; ?><?php printf( $this->theme->display( 'Tags' ) ); ?></li>
217
+ </ul>
218
+ <p class="theme-description"><?php echo $this->theme->display( 'Description' ); ?></p>
219
+ <?php
220
+ if ( $this->theme->parent() ) {
221
+ printf( ' <p class="howto">' . __( 'This <a href="%1$s">child theme</a> requires its parent theme, %2$s.', 'redux-framework-demo' ) . '</p>', __( 'http://codex.wordpress.org/Child_Themes', 'redux-framework-demo' ), $this->theme->parent()->display( 'Name' ) );
222
+ }
223
+ ?>
224
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  </div>
226
+ </div>
227
 
228
+ <?php
229
+ $item_info = ob_get_contents();
230
 
231
+ ob_end_clean();
232
 
233
+ $sampleHTML = '';
234
+ if ( file_exists( dirname( __FILE__ ) . '/info-html.html' ) ) {
235
+ Redux_Functions::initWpFilesystem();
236
 
237
+ global $wp_filesystem;
238
 
239
+ $sampleHTML = $wp_filesystem->get_contents( dirname( __FILE__ ) . '/info-html.html' );
240
+ }
241
 
242
  // ACTUAL DECLARATION OF SECTIONS
243
  $this->sections[] = array(
244
+ 'title' => __('General Settings', 'wp-mail-logging'),
245
+ 'desc' => __('', 'wp-mail-logging'),
246
  'icon' => 'el-icon-cogs',
247
  // 'submenu' => false, // Setting submenu to false on a given section will hide it from the WordPress sidebar menu!
248
  'fields' => array(
250
  array(
251
  'id' => 'delete-on-deactivation',
252
  'type' => 'switch',
253
+ 'title' => __('Cleanup', 'wp-mail-logging' ),
254
+ 'subtitle' => __('Delete all data on deactivation? (emails and settings)?', 'wp-mail-logging'),
255
  'default' => 0,
256
+ 'on' => __(__('Enabled', 'wp-mail-logging' ), 'wp-mail-logging' ),
257
+ 'off' => __(__('Disabled', 'wp-mail-logging' ), 'wp-mail-logging' ),
258
+ ),
259
+ array(
260
+ 'id' => 'can-see-submission-data',
261
+ 'type' => 'select',
262
+ 'data' => 'capabilities',
263
+ 'default' => 'manage_options',
264
+ 'title' => __('Can See Submission data', 'wp-mail-logging'),
265
+ 'subtitle' => __('Select the minimum role.', 'wp-mail-logging'),
266
  ),
 
 
 
 
 
 
 
 
267
  array(
268
  'id' => 'datetimeformat-use-wordpress',
269
  'type' => 'switch',
270
+ 'title' => __('WordPress Date Time Format', 'wp-mail-logging' ),
271
+ 'subtitle' => sprintf( __( "Use format from WordPress settings (%s)", 'wp-mail-logging' ), date_i18n( $this->wordpress_default_format(), current_time( 'timestamp' ) ) ),
272
  'default' => 0,
273
+ 'on' => __('Enabled', 'wp-mail-logging' ),
274
+ 'off' => __('Disabled', 'wp-mail-logging' ),
275
+ ),
276
+ array(
277
+ 'id' => 'preferred-mail-format',
278
+ 'type' => 'select',
279
+ 'options' => array(
280
+ 'html' => 'html',
281
+ 'raw' => 'raw',
282
+ 'json' => 'json'
283
+ ),
284
+ 'default' => 'html',
285
+ 'title' => __('Default Format for Message', 'wp-mail-logging'),
286
+ 'subtitle' => __('Select your preferred display format.', 'wp-mail-logging'),
287
  ),
 
 
 
 
 
 
 
 
 
 
 
 
288
  array(
289
  'id' => 'display-host',
290
  'type' => 'switch',
291
+ 'title' => __('Display Host', 'wp-mail-logging' ),
292
+ 'subtitle' => __('Display host column in list.', 'wp-mail-logging'),
293
  'hint' => array(
294
  'title' => 'Host',
295
  'content' => 'Display the IP of the host WordPress is running on. This is useful when running it on multiple servers at the same time.',
296
  ),
297
  'default' => 0,
298
+ 'on' => __('Enabled', 'wp-mail-logging' ),
299
+ 'off' => __('Disabled', 'wp-mail-logging' ),
300
  ),
301
+ array(
302
  'id' => 'section-log-rotation-start',
303
  'type' => 'section',
304
+ 'title' => __('Log Rotation', 'wp-mail-logging' ),
305
+ 'subtitle' => __('Save space by deleting logs regularly.', 'wp-mail-logging'),
306
  'indent' => true, // Indent all options below until the next 'section' option is set.
307
+ ),
308
+ array(
309
  'id' => 'log-rotation-limit-amout',
310
  'type' => 'switch',
311
+ 'title' => __('Cleanup by Amount', 'wp-mail-logging' ),
312
+ 'subtitle' => __('Setup a automated cleanup routine!', 'wp-mail-logging'),
313
  'default' => 0,
314
+ 'on' => __('Enabled', 'wp-mail-logging' ),
315
+ 'off' => __('Disabled', 'wp-mail-logging' ),
316
+ ),
317
+ array(
318
  'id' => 'log-rotation-limit-amout-keep',
319
  'type' => 'slider',
320
  'required' => array('log-rotation-limit-amout', '=', '1'),
321
+ 'title' => __('Amount', 'wp-mail-logging' ),
322
+ 'subtitle' => __('When should mails are deleted?', 'wp-mail-logging'),
323
+ 'desc' => __('Cleanup when the stored mails exceed...', 'wp-mail-logging'),
324
  'default' => 75,
325
  'min' => 25,
326
  'step' => 50,
327
  'max' => 3000,
328
  'display_value' => 'text'
329
+ ),
330
+ array(
331
  'id' => 'log-rotation-delete-time',
332
  'type' => 'switch',
333
+ 'title' => __('Cleanup by Time', 'wp-mail-logging' ),
334
+ 'subtitle' => __('Setup a automated cleanup routine!', 'wp-mail-logging'),
335
  'default' => 0,
336
+ 'on' => __('Enabled', 'wp-mail-logging' ),
337
+ 'off' => __('Disabled', 'wp-mail-logging' ),
338
+ ),
339
+ array(
340
  'id' => 'log-rotation-delete-time-days',
341
  'type' => 'slider',
342
  'required' => array('log-rotation-delete-time', '=', '1'),
343
+ 'title' => __('Time', 'wp-mail-logging' ),
344
+ 'subtitle' => __('When should mails are deleted?', 'wp-mail-logging'),
345
+ 'desc' => __('Delete mails older than days...', 'wp-mail-logging'),
346
  'default' => 30,
347
  'min' => 1,
348
  'step' => 7,
349
  'max' => 400,
350
  'display_value' => 'text'
351
+ ),
352
+ array(
353
  'id' => 'section-log-rotation-end',
354
  'type' => 'section',
355
  'indent' => false // Indent all options below until the next 'section' option is set.
356
+ ),
357
  ),
358
  );
359
  }
360
 
361
+ public function wordpress_default_format()
362
+ {
363
+ $date_format = get_option( 'date_format' );
364
+ $time_format = get_option( 'time_format' );
365
+ $date_format = empty( $date_format ) ? 'F j, Y' : $date_format;
366
+ $time_format = empty( $time_format ) ? 'g:i a' : $time_format;
367
+ return "{$date_format} {$time_format}";
368
+ }
369
 
370
+ public function setHelpTabs() {
371
 
372
+ // Custom page help tabs, displayed using the help API. Tabs are shown in order of definition.
373
+ $this->args['help_tabs'][] = array(
374
+ 'id' => 'redux-help-tab-1',
375
+ 'title' => 'Theme Information 1',
376
+ 'content' => '<p>This is the tab content, HTML is allowed.</p>'
377
+ );
378
 
379
+ $this->args['help_tabs'][] = array(
380
+ 'id' => 'redux-help-tab-2',
381
+ 'title' => 'Theme Information 2', 'redux-framework-demo',
382
+ 'content' => '<p>This is the tab content, HTML is allowed.</p>'
383
+ );
384
 
385
+ // Set the help sidebar
386
+ $this->args['help_sidebar'] = '<p>This is the sidebar content, HTML is allowed.</p>';
387
+ }
388
 
389
+ /**
390
+ * All the possible arguments for Redux.
391
+ * For full documentation on arguments, please refer to: https://github.com/ReduxFramework/ReduxFramework/wiki/Arguments
392
+ * */
393
+ public function setArguments() {
394
+
395
+ $theme = wp_get_theme(); // For use with some settings. Not necessary.
396
+
397
+ $this->args = array(
398
+ // TYPICAL -> Change these values as you need/desire
399
+ 'opt_name' => 'wpml_settings',
400
+ // This is where your data is stored in the database and also becomes your global variable name.
401
+ 'display_name' => 'WP Mail Logging Settings',
402
+ // Name that appears at the top of your panel
403
+ 'display_version' => $this->plugin_meta['version_installed'],
404
+ // Version that appears at the top of your panel
405
+ 'menu_type' => 'submenu',
406
+ //Specify if the admin menu should appear or not. Options: menu or submenu (Under appearance only)
407
+ 'allow_sub_menu' => true,
408
+ // Show the sections below the admin menu item or not
409
+ 'menu_title' => 'Settings',
410
+ 'page_title' => $this->plugin_meta['display_name'],
411
+ // You will need to generate a Google API key to use this feature.
412
+ // Please visit: https://developers.google.com/fonts/docs/developer_api#Auth
413
+ 'google_api_key' => '',
414
+ // Set it you want google fonts to update weekly. A google_api_key value is required.
415
+ 'google_update_weekly' => false,
416
+ // Must be defined to add google fonts to the typography module
417
+ 'async_typography' => true,
418
+ // Use a asynchronous font on the front end or font string
419
+ //'disable_google_fonts_link' => true, // Disable this in case you want to create your own google fonts loader
420
+ 'admin_bar' => false,
421
+ // Show the panel pages on the admin bar
422
+ 'admin_bar_icon' => 'dashicons-portfolio',
423
+ // Choose an icon for the admin bar menu
424
+ 'admin_bar_priority' => 50,
425
+ // Choose an priority for the admin bar menu
426
+ 'global_variable' => '',
427
+ // Set a different name for your global variable other than the opt_name
428
+ 'dev_mode' => false,
429
+ // Show the time the page took to load, etc
430
+ 'update_notice' => true,
431
+ // If dev_mode is enabled, will notify developer of updated versions available in the GitHub Repo
432
+ 'customizer' => false,
433
+ // Enable basic customizer support
434
+ //'open_expanded' => true, // Allow you to start the panel in an expanded way initially.
435
+ //'disable_save_warn' => true, // Disable the save warning when a user changes a field
436
+
437
+ // OPTIONAL -> Give you extra features
438
+ 'page_priority' => null,
439
+ // Order where the menu appears in the admin area. If there is any conflict, something will not show. Warning.
440
+ 'page_parent' => 'wpml_plugin_log',
441
+ // For a full list of options, visit: http://codex.wordpress.org/Function_Reference/add_submenu_page#Parameters
442
+ 'page_permissions' => 'manage_options',
443
+ // Permissions needed to access the options panel.
444
+ 'menu_icon' => '',
445
+ // Specify a custom URL to an icon
446
+ 'last_tab' => '',
447
+ // Force your panel to always open to a specific tab (by id)
448
+ 'page_icon' => 'icon-themes',
449
+ // Icon displayed in the admin panel next to your menu_title
450
+ 'page_slug' => 'wpml_plugin_settings',
451
+ // Page slug used to denote the panel
452
+ 'save_defaults' => true,
453
+ // On load save the defaults to DB before user clicks save or not
454
+ 'default_show' => false,
455
+ // If true, shows the default value next to each field that is not the default value.
456
+ 'default_mark' => '*',
457
+ // What to print by the field's title if the value shown is default. Suggested: *
458
+ 'show_import_export' => true,
459
+ // Shows the Import/Export panel when not used as a field.
460
+
461
+ // CAREFUL -> These options are for advanced use only
462
+ 'transient_time' => 60 * MINUTE_IN_SECONDS,
463
+ 'output' => true,
464
+ // Global shut-off for dynamic CSS output by the framework. Will also disable google fonts output
465
+ 'output_tag' => true,
466
+ // Allows dynamic CSS to be generated for customizer and google fonts, but stops the dynamic CSS from going to the head
467
+ // 'footer_credit' => '', // Disable the footer credit of Redux. Please leave if you can help it.
468
+
469
+ // FUTURE -> Not in use yet, but reserved or partially implemented. Use at your own risk.
470
+ 'database' => '',
471
+ // possible: options, theme_mods, theme_mods_expanded, transient. Not fully functional, warning!
472
+ 'system_info' => false,
473
+ // REMOVE
474
+
475
+ // HINTS
476
+ 'hints' => array(
477
+ 'icon' => 'el el-question-sign',
478
+ 'icon_position' => 'right',
479
+ 'icon_color' => 'lightgray',
480
+ 'icon_size' => 'normal',
481
+ 'tip_style' => array(
482
+ 'color' => 'light',
483
+ 'shadow' => true,
484
+ 'rounded' => false,
485
+ 'style' => 'bootstrap',
486
+ ),
487
+ 'tip_position' => array(
488
+ 'my' => 'top left',
489
+ 'at' => 'bottom right',
490
+ ),
491
+ 'tip_effect' => array(
492
+ 'show' => array(
493
+ 'effect' => 'slide',
494
+ 'duration' => '500',
495
+ 'event' => 'mouseover',
496
  ),
497
+ 'hide' => array(
498
+ 'effect' => 'slide',
499
+ 'duration' => '500',
500
+ 'event' => 'click mouseleave',
 
 
 
 
 
 
 
501
  ),
502
+ ),
503
+ )
504
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
+ // ADMIN BAR LINKS -> Setup custom links in the admin bar menu as external items.
507
+ $this->args['admin_bar_links'][] = array(
508
+ 'id' => 'redux-docs',
509
+ 'href' => 'http://docs.reduxframework.com/',
510
+ 'title' => __( 'Documentation', 'redux-framework-demo' ),
511
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
 
513
+ $this->args['admin_bar_links'][] = array(
514
+ //'id' => 'redux-support',
515
+ 'href' => 'https://github.com/ReduxFramework/redux-framework/issues',
516
+ 'title' => __( 'Support', 'redux-framework-demo' ),
517
+ );
518
 
519
+ $this->args['admin_bar_links'][] = array(
520
+ 'id' => 'redux-extensions',
521
+ 'href' => 'reduxframework.com/extensions',
522
+ 'title' => __( 'Extensions', 'redux-framework-demo' ),
523
+ );
524
 
525
+ // SOCIAL ICONS -> Setup custom links in the footer for quick links in your panel footer icons.
526
+ $this->args['share_icons'][] = array(
527
+ 'url' => 'https://github.com/No3x/wp-mail-logging',
528
+ 'title' => 'Visit us on GitHub',
529
+ 'icon' => 'el-icon-github'
530
+ //'img' => '', // You can use icon OR img. IMG needs to be a full URL.
531
+ );
532
+ $this->args['share_icons'][] = array(
533
+ 'url' => $this->plugin_meta['wp_uri'],
534
+ 'title' => 'Visit us on WordPress',
535
+ 'icon' => 'el-icon-wordpress'
536
+ );
537
 
538
+
539
+ // Add content before the form.
540
+ // $this->args['intro_text'] = __( '<p>This text is displayed above the options panel. It isn\'t required, but more info is always better! The intro_text field accepts all HTML.</p>', 'redux-framework-demo' );
541
+
542
+ // Add content after the form.
543
+ // $this->args['footer_text'] = __( '<p>This text is displayed below the options panel. It isn\'t required, but more info is always better! The footer_text field accepts all HTML.</p>', 'redux-framework-demo' );
 
 
544
  }
 
545
 
546
+ public function validate_callback_function( $field, $value, $existing_value ) {
 
 
 
 
547
  $error = true;
548
  $value = 'just testing';
549
 
555
  } elseif(something else) {
556
  $error = true;
557
  $value = $existing_value;
558
+
559
  }
560
  */
561
 
567
 
568
  return $return;
569
  }
570
+
571
+ public function class_field_callback( $field, $value ) {
572
+ print_r( $field );
573
+ echo '<br/>CLASS CALLBACK';
574
+ print_r( $value );
575
+ }
576
+
577
+ }
578
+
579
+ global $reduxConfig;
580
+ //$reduxConfig = new WPML_Redux_Framework_config();
581
+ } else {
582
+ echo "The class named Redux_Framework_sample_config has already been called. <strong>Developers, you need to prefix this class with your company name or you'll run into problems!</strong>";
583
+ }
584
+
585
+ /**
586
+ * Custom function for the callback referenced above
587
+ */
588
+ if ( ! function_exists( 'redux_my_custom_field' ) ):
589
+ function redux_my_custom_field( $field, $value ) {
590
+ print_r( $field );
591
+ echo '<br/>';
592
+ print_r( $value );
593
+ }
594
+ endif;
595
+
596
+ /**
597
+ * Custom function for the callback validation referenced above
598
+ * */
599
+ if ( ! function_exists( 'redux_validate_callback_function' ) ):
600
+ function redux_validate_callback_function( $field, $value, $existing_value ) {
601
+ $error = true;
602
+ $value = 'just testing';
603
+
604
+ /*
605
+ do your validation
606
+
607
+ if(something) {
608
+ $value = $value;
609
+ } elseif(something else) {
610
+ $error = true;
611
+ $value = $existing_value;
612
+
613
+ }
614
+ */
615
+
616
+ $return['value'] = $value;
617
+ $field['msg'] = 'your custom error message';
618
+ if ( $error == true ) {
619
+ $return['error'] = $field;
620
+ }
621
+
622
+ return $return;
623
+ }
624
+ endif;
inc/redux/admin-init.php CHANGED
@@ -2,10 +2,10 @@
2
 
3
  // Load the embedded Redux Framework
4
  if ( file_exists( dirname( __FILE__ ).'/../../lib/vendor/redux-framework/framework.php' ) ) {
5
- require_once dirname(__FILE__).'/../../lib/vendor/redux-framework/framework.php';
6
  }
7
  // Load the theme/plugin options
8
  if ( file_exists( dirname( __FILE__ ) . '/options-init.php' ) ) {
9
- require_once dirname( __FILE__ ) . '/options-init.php';
10
  }
11
 
2
 
3
  // Load the embedded Redux Framework
4
  if ( file_exists( dirname( __FILE__ ).'/../../lib/vendor/redux-framework/framework.php' ) ) {
5
+ require_once dirname(__FILE__).'/../../lib/vendor/redux-framework/framework.php';
6
  }
7
  // Load the theme/plugin options
8
  if ( file_exists( dirname( __FILE__ ) . '/options-init.php' ) ) {
9
+ require_once dirname( __FILE__ ) . '/options-init.php';
10
  }
11
 
languages/wp-mail-logging-de_DE.mo ADDED
Binary file
languages/wp-mail-logging-de_DE.po ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2016 Christian Z&ouml;ller
2
+ # This file is distributed under the GPLv3.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WP Mail Logging 1.8.0\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-logging\n"
7
+ "POT-Creation-Date: 2017-01-03 19:59+0100\n"
8
+ "PO-Revision-Date: 2017-01-03 20:08+0100\n"
9
+ "Last-Translator: \n"
10
+ "Language-Team: \n"
11
+ "Language: de_DE\n"
12
+ "MIME-Version: 1.0\n"
13
+ "Content-Type: text/plain; charset=UTF-8\n"
14
+ "Content-Transfer-Encoding: 8bit\n"
15
+ "X-Generator: Poedit 1.8.6\n"
16
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17
+
18
+ #: WPML_Email_Log_List.php:59
19
+ msgid "No email found."
20
+ msgstr "Keine E-Mail gefunden."
21
+
22
+ #: WPML_Email_Log_List.php:71
23
+ msgid "ID"
24
+ msgstr "ID"
25
+
26
+ #: WPML_Email_Log_List.php:72 inc/redux/WPML_Redux_Framework_config.php:343
27
+ msgid "Time"
28
+ msgstr "Zeit"
29
+
30
+ #: WPML_Email_Log_List.php:73
31
+ msgid "Receiver"
32
+ msgstr "Empfänger"
33
+
34
+ #: WPML_Email_Log_List.php:74
35
+ msgid "Subject"
36
+ msgstr "Betreff"
37
+
38
+ #: WPML_Email_Log_List.php:75 WPML_OptionsManager.php:497
39
+ msgid "Message"
40
+ msgstr "Nachricht"
41
+
42
+ #: WPML_Email_Log_List.php:76
43
+ msgid "Headers"
44
+ msgstr "Header"
45
+
46
+ #: WPML_Email_Log_List.php:77
47
+ msgid "Attachments"
48
+ msgstr "Anhänge"
49
+
50
+ #: WPML_Email_Log_List.php:78
51
+ msgid "Error"
52
+ msgstr "Fehler"
53
+
54
+ #: WPML_Email_Log_List.php:79
55
+ msgid "Plugin Version"
56
+ msgstr "Plugin Version"
57
+
58
+ #: WPML_Email_Log_List.php:90
59
+ msgid "Host"
60
+ msgstr "Host"
61
+
62
+ #: WPML_Email_Log_List.php:267 WPML_Email_Log_List.php:301
63
+ msgid "Attachment %s is not present"
64
+ msgstr "Anhang %s ist nicht vorhanden"
65
+
66
+ #: WPML_Email_Log_List.php:495
67
+ msgid "Fallback to raw format because html is not convertible to json."
68
+ msgstr ""
69
+ "Darstellung in raw-Format, da eine html Nachricht nicht als json "
70
+ "dargestellt werden kann."
71
+
72
+ #: WPML_LifeCycle.php:205
73
+ msgid "Settings"
74
+ msgstr "Einstellungen"
75
+
76
+ #: WPML_OptionsManager.php:321 WPML_OptionsManager.php:322
77
+ msgid "WP Mail Log"
78
+ msgstr ""
79
+
80
+ #: WPML_OptionsManager.php:333 WPML_OptionsManager.php:334
81
+ #: WPML_OptionsManager.php:345
82
+ msgid "About"
83
+ msgstr "Über"
84
+
85
+ #: WPML_OptionsManager.php:422
86
+ msgid "About Plugin"
87
+ msgstr "Über das Plugin"
88
+
89
+ #: WPML_OptionsManager.php:431
90
+ msgid "More information"
91
+ msgstr "Mehr Informationen"
92
+
93
+ #: WPML_OptionsManager.php:432
94
+ msgid "Plugin Homepage/support"
95
+ msgstr ""
96
+
97
+ #: WPML_OptionsManager.php:433
98
+ msgid "Plugin author's blog"
99
+ msgstr ""
100
+
101
+ #: WPML_OptionsManager.php:440
102
+ msgid "Entries per page"
103
+ msgstr "Einträge pro Seite"
104
+
105
+ #: WPML_OptionsManager.php:462
106
+ msgid "You do not have sufficient permissions to access this page."
107
+ msgstr "Sie haben keine ausreichende Berechtigung um diese Seite zu öffnen."
108
+
109
+ #: WPML_OptionsManager.php:471
110
+ msgid "Log"
111
+ msgstr "Log"
112
+
113
+ #: WPML_OptionsManager.php:515
114
+ msgid "Close"
115
+ msgstr "Schließen"
116
+
117
+ #: WPML_OptionsManager.php:529
118
+ msgid "Search"
119
+ msgstr "Suchen"
120
+
121
+ #: WPML_OptionsManager.php:554
122
+ msgid "true"
123
+ msgstr ""
124
+
125
+ #: WPML_OptionsManager.php:556
126
+ msgid "false"
127
+ msgstr ""
128
+
129
+ #: WPML_OptionsManager.php:559
130
+ msgid "Administrator"
131
+ msgstr ""
132
+
133
+ #: WPML_OptionsManager.php:561
134
+ msgid "Editor"
135
+ msgstr ""
136
+
137
+ #: WPML_OptionsManager.php:563
138
+ msgid "Author"
139
+ msgstr ""
140
+
141
+ #: WPML_OptionsManager.php:565
142
+ msgid "Contributor"
143
+ msgstr ""
144
+
145
+ #: WPML_OptionsManager.php:567
146
+ msgid "Subscriber"
147
+ msgstr ""
148
+
149
+ #: WPML_OptionsManager.php:569
150
+ msgid "Anyone"
151
+ msgstr ""
152
+
153
+ #: inc/class-wp-list-table.php:191
154
+ msgid "No items found."
155
+ msgstr "Keine Einträge gefunden."
156
+
157
+ #: inc/class-wp-list-table.php:315
158
+ msgid "Bulk Actions"
159
+ msgstr ""
160
+
161
+ #: inc/class-wp-list-table.php:325
162
+ msgid "Apply"
163
+ msgstr "Anwenden"
164
+
165
+ #: inc/class-wp-list-table.php:409
166
+ msgid "Show all dates"
167
+ msgstr ""
168
+
169
+ #. translators: 1: month name, 2: 4-digit year
170
+ #: inc/class-wp-list-table.php:422
171
+ msgid "%1$s %2$d"
172
+ msgstr ""
173
+
174
+ #: inc/class-wp-list-table.php:438
175
+ msgid "List View"
176
+ msgstr ""
177
+
178
+ #: inc/class-wp-list-table.php:439
179
+ msgid "Excerpt View"
180
+ msgstr ""
181
+
182
+ #: inc/class-wp-list-table.php:465
183
+ msgid "%s pending"
184
+ msgstr ""
185
+
186
+ #: inc/class-wp-list-table.php:533 inc/class-wp-list-table.php:948
187
+ msgid "1 item"
188
+ msgid_plural "%s items"
189
+ msgstr[0] ""
190
+ msgstr[1] ""
191
+
192
+ #: inc/class-wp-list-table.php:551
193
+ msgid "Go to the first page"
194
+ msgstr ""
195
+
196
+ #: inc/class-wp-list-table.php:558
197
+ msgid "Go to the previous page"
198
+ msgstr ""
199
+
200
+ #: inc/class-wp-list-table.php:567
201
+ msgid "Current page"
202
+ msgstr ""
203
+
204
+ #: inc/class-wp-list-table.php:577
205
+ msgid "Go to the next page"
206
+ msgstr ""
207
+
208
+ #: inc/class-wp-list-table.php:584
209
+ msgid "Go to the last page"
210
+ msgstr ""
211
+
212
+ #: inc/class-wp-list-table.php:720
213
+ msgid "Select All"
214
+ msgstr ""
215
+
216
+ #: inc/redux/WPML_Redux_Framework_config.php:194
217
+ msgid "Customize &#8220;%s&#8221;"
218
+ msgstr ""
219
+
220
+ #: inc/redux/WPML_Redux_Framework_config.php:203
221
+ #: inc/redux/WPML_Redux_Framework_config.php:207
222
+ msgid "Current theme preview"
223
+ msgstr ""
224
+
225
+ #: inc/redux/WPML_Redux_Framework_config.php:214
226
+ msgid "By %s"
227
+ msgstr ""
228
+
229
+ #: inc/redux/WPML_Redux_Framework_config.php:215
230
+ msgid "Version %s"
231
+ msgstr ""
232
+
233
+ #: inc/redux/WPML_Redux_Framework_config.php:216
234
+ msgid "Tags"
235
+ msgstr ""
236
+
237
+ #: inc/redux/WPML_Redux_Framework_config.php:221
238
+ msgid "This <a href=\"%1$s\">child theme</a> requires its parent theme, %2$s."
239
+ msgstr ""
240
+
241
+ #: inc/redux/WPML_Redux_Framework_config.php:221
242
+ msgid "http://codex.wordpress.org/Child_Themes"
243
+ msgstr ""
244
+
245
+ #: inc/redux/WPML_Redux_Framework_config.php:244
246
+ msgid "General Settings"
247
+ msgstr ""
248
+
249
+ #: inc/redux/WPML_Redux_Framework_config.php:253
250
+ msgid "Cleanup"
251
+ msgstr "Aufräumen"
252
+
253
+ #: inc/redux/WPML_Redux_Framework_config.php:254
254
+ msgid "Delete all data on deactivation? (emails and settings)?"
255
+ msgstr "Alle Daten bei Deaktivierung löschen? (E-Mails und Einstellungen)"
256
+
257
+ #: inc/redux/WPML_Redux_Framework_config.php:264
258
+ msgid "Can See Submission data"
259
+ msgstr "Daten sichtbar für"
260
+
261
+ #: inc/redux/WPML_Redux_Framework_config.php:265
262
+ msgid "Select the minimum role."
263
+ msgstr "Wähle die Mindestberechtigung"
264
+
265
+ #: inc/redux/WPML_Redux_Framework_config.php:270
266
+ msgid "WordPress Date Time Format"
267
+ msgstr "WordPress Zeitstempel Format"
268
+
269
+ #: inc/redux/WPML_Redux_Framework_config.php:271
270
+ msgid "Use format from WordPress settings (%s)"
271
+ msgstr "Benutze das WordPress Datumsformat aus den Einstellungen (%s)"
272
+
273
+ #: inc/redux/WPML_Redux_Framework_config.php:273
274
+ #: inc/redux/WPML_Redux_Framework_config.php:298
275
+ #: inc/redux/WPML_Redux_Framework_config.php:314
276
+ #: inc/redux/WPML_Redux_Framework_config.php:336
277
+ msgid "Enabled"
278
+ msgstr "Ein"
279
+
280
+ #: inc/redux/WPML_Redux_Framework_config.php:274
281
+ #: inc/redux/WPML_Redux_Framework_config.php:299
282
+ #: inc/redux/WPML_Redux_Framework_config.php:315
283
+ #: inc/redux/WPML_Redux_Framework_config.php:337
284
+ msgid "Disabled"
285
+ msgstr "Aus"
286
+
287
+ #: inc/redux/WPML_Redux_Framework_config.php:285
288
+ msgid "Default Format for Message"
289
+ msgstr "Standardformat für Nachrichten"
290
+
291
+ #: inc/redux/WPML_Redux_Framework_config.php:286
292
+ msgid "Select your preferred display format."
293
+ msgstr "Bevorzugtes Standardformat"
294
+
295
+ #: inc/redux/WPML_Redux_Framework_config.php:291
296
+ msgid "Display Host"
297
+ msgstr "Zeige Host"
298
+
299
+ #: inc/redux/WPML_Redux_Framework_config.php:292
300
+ msgid "Display host column in list."
301
+ msgstr "Zeige Host Spalte in Liste"
302
+
303
+ #: inc/redux/WPML_Redux_Framework_config.php:304
304
+ msgid "Log Rotation"
305
+ msgstr ""
306
+
307
+ #: inc/redux/WPML_Redux_Framework_config.php:305
308
+ msgid "Save space by deleting logs regularly."
309
+ msgstr "Platz sparen durch regelmäßiges Löschen des Logs."
310
+
311
+ #: inc/redux/WPML_Redux_Framework_config.php:311
312
+ msgid "Cleanup by Amount"
313
+ msgstr "Löschen nach Anzahl"
314
+
315
+ #: inc/redux/WPML_Redux_Framework_config.php:312
316
+ #: inc/redux/WPML_Redux_Framework_config.php:334
317
+ msgid "Setup a automated cleanup routine!"
318
+ msgstr "Erstelle eine automatische Aufräumroutine!"
319
+
320
+ #: inc/redux/WPML_Redux_Framework_config.php:321
321
+ msgid "Amount"
322
+ msgstr "Anzahl"
323
+
324
+ #: inc/redux/WPML_Redux_Framework_config.php:322
325
+ #: inc/redux/WPML_Redux_Framework_config.php:344
326
+ msgid "When should mails are deleted?"
327
+ msgstr "Wann sollen Mails gelöscht werden?"
328
+
329
+ #: inc/redux/WPML_Redux_Framework_config.php:323
330
+ msgid "Cleanup when the stored mails exceed..."
331
+ msgstr "Lösche wenn die Mails den Betrag übersteigen..."
332
+
333
+ #: inc/redux/WPML_Redux_Framework_config.php:333
334
+ msgid "Cleanup by Time"
335
+ msgstr "Löschen nach Zeit"
336
+
337
+ #: inc/redux/WPML_Redux_Framework_config.php:345
338
+ msgid "Delete mails older than days..."
339
+ msgstr "Lösche Mails älter als ... Tage"
340
+
341
+ #: inc/redux/WPML_Redux_Framework_config.php:510
342
+ msgid "Documentation"
343
+ msgstr "Dokumentation"
344
+
345
+ #: inc/redux/WPML_Redux_Framework_config.php:516
346
+ msgid "Support"
347
+ msgstr "Hilfe"
348
+
349
+ #: inc/redux/WPML_Redux_Framework_config.php:522
350
+ msgid "Extensions"
351
+ msgstr "Erweiterungen"
352
+
353
+ #: wp-mail-logging.php:48
354
+ msgid ""
355
+ "Error: plugin \"WP Mail Logging\" requires a newer version of PHP to be "
356
+ "running."
357
+ msgstr ""
358
+ "Fehler: Das Plugin \"WP Mail Logging\" benötigt eine neuere PHP Version."
359
+
360
+ #: wp-mail-logging.php:49
361
+ msgid "Minimal version of PHP required: "
362
+ msgstr "Mindestanforderung PHP Version:"
363
+
364
+ #: wp-mail-logging.php:50
365
+ msgid "Your server's PHP version: "
366
+ msgstr "Die PHP Version des Servers:"
367
+
368
+ #. Description of the plugin/theme
369
+ msgid "Logs each email sent by WordPress."
370
+ msgstr ""
371
+
372
+ #: inc/class-wp-list-table.php:573
373
+ msgctxt "paging"
374
+ msgid "%1$s of %2$s"
375
+ msgstr "%1$s von %2$s"
languages/wp-mail-logging-zh_CN.mo ADDED
Binary file
languages/wp-mail-logging-zh_CN.po ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2017 Christian Z&ouml;ller
2
+ # This file is distributed under the GPLv3.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WP Mail Logging 1.8.0\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-logging\n"
7
+ "POT-Creation-Date: 2017-02-12 02:34+0800\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2017-02-15 09:29+0800\n"
12
+ "Language-Team: \n"
13
+ "X-Generator: Poedit 1.8.1\n"
14
+ "Plural-Forms: nplurals=1; plural=0;\n"
15
+ "Language: zh_CN\n"
16
+ "Last-Translator: \n"
17
+
18
+ #: WPML_Email_Log_List.php:59
19
+ msgid "No email found."
20
+ msgstr "未找到电子邮件。"
21
+
22
+ #: WPML_Email_Log_List.php:71
23
+ msgid "ID"
24
+ msgstr "ID"
25
+
26
+ #: WPML_Email_Log_List.php:72 inc/redux/WPML_Redux_Framework_config.php:343
27
+ msgid "Time"
28
+ msgstr "时间"
29
+
30
+ #: WPML_Email_Log_List.php:73
31
+ msgid "Receiver"
32
+ msgstr "收件人"
33
+
34
+ #: WPML_Email_Log_List.php:74
35
+ msgid "Subject"
36
+ msgstr "主题"
37
+
38
+ #: WPML_Email_Log_List.php:75 WPML_OptionsManager.php:497
39
+ msgid "Message"
40
+ msgstr "消息"
41
+
42
+ #: WPML_Email_Log_List.php:76
43
+ msgid "Headers"
44
+ msgstr "邮件头"
45
+
46
+ #: WPML_Email_Log_List.php:77
47
+ msgid "Attachments"
48
+ msgstr "附件"
49
+
50
+ #: WPML_Email_Log_List.php:78
51
+ msgid "Error"
52
+ msgstr "错误"
53
+
54
+ #: WPML_Email_Log_List.php:79
55
+ msgid "Plugin Version"
56
+ msgstr "插件版本"
57
+
58
+ #: WPML_Email_Log_List.php:90
59
+ msgid "Host"
60
+ msgstr "主机名"
61
+
62
+ #: WPML_Email_Log_List.php:267 WPML_Email_Log_List.php:301
63
+ msgid "Attachment %s is not present"
64
+ msgstr "附件 %s 不存在"
65
+
66
+ #: WPML_Email_Log_List.php:495
67
+ msgid "Fallback to raw format because html is not convertible to json."
68
+ msgstr "回退到原始格式,因为 html 不是转换为 json。"
69
+
70
+ #: WPML_LifeCycle.php:205
71
+ msgid "Settings"
72
+ msgstr "设置"
73
+
74
+ #: WPML_OptionsManager.php:321 WPML_OptionsManager.php:322
75
+ msgid "WP Mail Log"
76
+ msgstr "WP Mail Log"
77
+
78
+ #: WPML_OptionsManager.php:333 WPML_OptionsManager.php:334
79
+ #: WPML_OptionsManager.php:345
80
+ msgid "About"
81
+ msgstr "关于"
82
+
83
+ #: WPML_OptionsManager.php:422
84
+ msgid "About Plugin"
85
+ msgstr "关于插件"
86
+
87
+ #: WPML_OptionsManager.php:431
88
+ msgid "More information"
89
+ msgstr "更多信息"
90
+
91
+ #: WPML_OptionsManager.php:432
92
+ msgid "Plugin Homepage/support"
93
+ msgstr "插件主页/支持"
94
+
95
+ #: WPML_OptionsManager.php:433
96
+ msgid "Plugin author's blog"
97
+ msgstr "插件作者的博客"
98
+
99
+ #: WPML_OptionsManager.php:440
100
+ msgid "Entries per page"
101
+ msgstr "每页的项目数:"
102
+
103
+ #: WPML_OptionsManager.php:462
104
+ msgid "You do not have sufficient permissions to access this page."
105
+ msgstr "您没有足够的权限来访问该页面。"
106
+
107
+ #: WPML_OptionsManager.php:471
108
+ msgid "Log"
109
+ msgstr "日志"
110
+
111
+ #: WPML_OptionsManager.php:515
112
+ msgid "Close"
113
+ msgstr "关闭"
114
+
115
+ #: WPML_OptionsManager.php:529
116
+ msgid "Search"
117
+ msgstr "搜索"
118
+
119
+ #: WPML_OptionsManager.php:554
120
+ msgid "true"
121
+ msgstr "true"
122
+
123
+ #: WPML_OptionsManager.php:556
124
+ msgid "false"
125
+ msgstr "false"
126
+
127
+ #: WPML_OptionsManager.php:559
128
+ msgid "Administrator"
129
+ msgstr "管理员"
130
+
131
+ #: WPML_OptionsManager.php:561
132
+ msgid "Editor"
133
+ msgstr "编辑"
134
+
135
+ #: WPML_OptionsManager.php:563
136
+ msgid "Author"
137
+ msgstr "作者"
138
+
139
+ #: WPML_OptionsManager.php:565
140
+ msgid "Contributor"
141
+ msgstr "贡献者"
142
+
143
+ #: WPML_OptionsManager.php:567
144
+ msgid "Subscriber"
145
+ msgstr "订阅者"
146
+
147
+ #: WPML_OptionsManager.php:569
148
+ msgid "Anyone"
149
+ msgstr "任何人"
150
+
151
+ #: inc/class-wp-list-table.php:191
152
+ msgid "No items found."
153
+ msgstr "没有找到项目。"
154
+
155
+ #: inc/class-wp-list-table.php:315
156
+ msgid "Bulk Actions"
157
+ msgstr "批量操作"
158
+
159
+ #: inc/class-wp-list-table.php:325
160
+ msgid "Apply"
161
+ msgstr "应用"
162
+
163
+ #: inc/class-wp-list-table.php:409
164
+ msgid "Show all dates"
165
+ msgstr "显示所有日期"
166
+
167
+ #. translators: 1: month name, 2: 4-digit year
168
+ #: inc/class-wp-list-table.php:422
169
+ msgid "%1$s %2$d"
170
+ msgstr "%1$s %2$d"
171
+
172
+ #: inc/class-wp-list-table.php:438
173
+ msgid "List View"
174
+ msgstr "列表视图"
175
+
176
+ #: inc/class-wp-list-table.php:439
177
+ msgid "Excerpt View"
178
+ msgstr "摘要"
179
+
180
+ #: inc/class-wp-list-table.php:465
181
+ msgid "%s pending"
182
+ msgstr "%s 待处理"
183
+
184
+ #: inc/class-wp-list-table.php:533 inc/class-wp-list-table.php:948
185
+ msgid "1 item"
186
+ msgid_plural "%s items"
187
+ msgstr[0] "1 项"
188
+
189
+ #: inc/class-wp-list-table.php:551
190
+ msgid "Go to the first page"
191
+ msgstr "转到第一页"
192
+
193
+ #: inc/class-wp-list-table.php:558
194
+ msgid "Go to the previous page"
195
+ msgstr "转到前一页"
196
+
197
+ #: inc/class-wp-list-table.php:567
198
+ msgid "Current page"
199
+ msgstr "当前页面"
200
+
201
+ #: inc/class-wp-list-table.php:577
202
+ msgid "Go to the next page"
203
+ msgstr "转到下一页"
204
+
205
+ #: inc/class-wp-list-table.php:584
206
+ msgid "Go to the last page"
207
+ msgstr "转到最后一页"
208
+
209
+ #: inc/class-wp-list-table.php:720
210
+ msgid "Select All"
211
+ msgstr "全选"
212
+
213
+ #: inc/redux/WPML_Redux_Framework_config.php:194
214
+ msgid "Customize &#8220;%s&#8221;"
215
+ msgstr "自定义 &#8220;%s&#8221;"
216
+
217
+ #: inc/redux/WPML_Redux_Framework_config.php:203
218
+ #: inc/redux/WPML_Redux_Framework_config.php:207
219
+ msgid "Current theme preview"
220
+ msgstr "预览当前主题"
221
+
222
+ #: inc/redux/WPML_Redux_Framework_config.php:214
223
+ msgid "By %s"
224
+ msgstr "由 %s"
225
+
226
+ #: inc/redux/WPML_Redux_Framework_config.php:215
227
+ msgid "Version %s"
228
+ msgstr "版本 %s"
229
+
230
+ #: inc/redux/WPML_Redux_Framework_config.php:216
231
+ msgid "Tags"
232
+ msgstr "标签"
233
+
234
+ #: inc/redux/WPML_Redux_Framework_config.php:221
235
+ msgid "This <a href=\"%1$s\">child theme</a> requires its parent theme, %2$s."
236
+ msgstr "此<a href=\"%1$s\">子主题</a>需要其父主题 %2$s。"
237
+
238
+ #: inc/redux/WPML_Redux_Framework_config.php:221
239
+ msgid "http://codex.wordpress.org/Child_Themes"
240
+ msgstr "http://codex.wordpress.org/Child_Themes"
241
+
242
+ #: inc/redux/WPML_Redux_Framework_config.php:244
243
+ msgid "General Settings"
244
+ msgstr "常规设置"
245
+
246
+ #: inc/redux/WPML_Redux_Framework_config.php:253
247
+ msgid "Cleanup"
248
+ msgstr "清理"
249
+
250
+ #: inc/redux/WPML_Redux_Framework_config.php:254
251
+ msgid "Delete all data on deactivation? (emails and settings)?"
252
+ msgstr "删除所有停用的数据吗?(电子邮件和设置)?"
253
+
254
+ #: inc/redux/WPML_Redux_Framework_config.php:264
255
+ msgid "Can See Submission data"
256
+ msgstr "可以看到提交数据"
257
+
258
+ #: inc/redux/WPML_Redux_Framework_config.php:265
259
+ msgid "Select the minimum role."
260
+ msgstr "选择的最小的角色。"
261
+
262
+ #: inc/redux/WPML_Redux_Framework_config.php:270
263
+ msgid "WordPress Date Time Format"
264
+ msgstr "WordPress 的日期时间格式"
265
+
266
+ #: inc/redux/WPML_Redux_Framework_config.php:271
267
+ msgid "Use format from WordPress settings (%s)"
268
+ msgstr "从 WordPress 使用格式设置 (%s)"
269
+
270
+ #: inc/redux/WPML_Redux_Framework_config.php:273
271
+ #: inc/redux/WPML_Redux_Framework_config.php:298
272
+ #: inc/redux/WPML_Redux_Framework_config.php:314
273
+ #: inc/redux/WPML_Redux_Framework_config.php:336
274
+ msgid "Enabled"
275
+ msgstr "启用"
276
+
277
+ #: inc/redux/WPML_Redux_Framework_config.php:274
278
+ #: inc/redux/WPML_Redux_Framework_config.php:299
279
+ #: inc/redux/WPML_Redux_Framework_config.php:315
280
+ #: inc/redux/WPML_Redux_Framework_config.php:337
281
+ msgid "Disabled"
282
+ msgstr "禁用"
283
+
284
+ #: inc/redux/WPML_Redux_Framework_config.php:285
285
+ msgid "Default Format for Message"
286
+ msgstr "消息的默认格式"
287
+
288
+ #: inc/redux/WPML_Redux_Framework_config.php:286
289
+ msgid "Select your preferred display format."
290
+ msgstr "选择您喜欢的显示格式。"
291
+
292
+ #: inc/redux/WPML_Redux_Framework_config.php:291
293
+ msgid "Display Host"
294
+ msgstr "显示主机"
295
+
296
+ #: inc/redux/WPML_Redux_Framework_config.php:292
297
+ msgid "Display host column in list."
298
+ msgstr "在列表中显示主机。"
299
+
300
+ #: inc/redux/WPML_Redux_Framework_config.php:304
301
+ msgid "Log Rotation"
302
+ msgstr "日志轮转"
303
+
304
+ #: inc/redux/WPML_Redux_Framework_config.php:305
305
+ msgid "Save space by deleting logs regularly."
306
+ msgstr "定期删除日志以节省空间。"
307
+
308
+ #: inc/redux/WPML_Redux_Framework_config.php:311
309
+ msgid "Cleanup by Amount"
310
+ msgstr "按数量清理"
311
+
312
+ #: inc/redux/WPML_Redux_Framework_config.php:312
313
+ #: inc/redux/WPML_Redux_Framework_config.php:334
314
+ msgid "Setup a automated cleanup routine!"
315
+ msgstr "设置自动清理"
316
+
317
+ #: inc/redux/WPML_Redux_Framework_config.php:321
318
+ msgid "Amount"
319
+ msgstr "数量"
320
+
321
+ #: inc/redux/WPML_Redux_Framework_config.php:322
322
+ #: inc/redux/WPML_Redux_Framework_config.php:344
323
+ msgid "When should mails are deleted?"
324
+ msgstr "邮件何时被删除?"
325
+
326
+ #: inc/redux/WPML_Redux_Framework_config.php:323
327
+ msgid "Cleanup when the stored mails exceed..."
328
+ msgstr "当存储的邮件超过上述数量时开始清理邮件..."
329
+
330
+ #: inc/redux/WPML_Redux_Framework_config.php:333
331
+ msgid "Cleanup by Time"
332
+ msgstr "按时间清理"
333
+
334
+ #: inc/redux/WPML_Redux_Framework_config.php:345
335
+ msgid "Delete mails older than days..."
336
+ msgstr "删除超过上述天数的邮件..."
337
+
338
+ #: inc/redux/WPML_Redux_Framework_config.php:510
339
+ msgid "Documentation"
340
+ msgstr "文档"
341
+
342
+ #: inc/redux/WPML_Redux_Framework_config.php:516
343
+ msgid "Support"
344
+ msgstr "支持"
345
+
346
+ #: inc/redux/WPML_Redux_Framework_config.php:522
347
+ msgid "Extensions"
348
+ msgstr "扩展"
349
+
350
+ #: wp-mail-logging.php:48
351
+ msgid ""
352
+ "Error: plugin \"WP Mail Logging\" requires a newer version of PHP to be "
353
+ "running."
354
+ msgstr "错误:插件“WP Mail Logging”需要运行较新版本的PHP。"
355
+
356
+ #: wp-mail-logging.php:49
357
+ msgid "Minimal version of PHP required: "
358
+ msgstr "PHP的最低版本要求:"
359
+
360
+ #: wp-mail-logging.php:50
361
+ msgid "Your server's PHP version: "
362
+ msgstr "您的服务器的PHP版本:"
363
+
364
+ #. Description of the plugin/theme
365
+ msgid "Logs each email sent by WordPress."
366
+ msgstr "记录WordPress发送的每封电子邮件。"
367
+
368
+ #: inc/class-wp-list-table.php:573
369
+ msgctxt "paging"
370
+ msgid "%1$s of %2$s"
371
+ msgstr "%1$s of %2$s"
languages/wp-mail-logging.pot ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2017 Christian Z&ouml;ller
2
+ # This file is distributed under the GPLv3.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WP Mail Logging 1.8.0\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-logging\n"
7
+ "POT-Creation-Date: 2017-01-03 18:59:13+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=utf-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "X-Generator: grunt-wp-i18n 0.5.4\n"
15
+ "Language: en_GB\n"
16
+
17
+ #: WPML_Email_Log_List.php:59
18
+ msgid "No email found."
19
+ msgstr ""
20
+
21
+ #: WPML_Email_Log_List.php:71
22
+ msgid "ID"
23
+ msgstr ""
24
+
25
+ #: WPML_Email_Log_List.php:72 inc/redux/WPML_Redux_Framework_config.php:343
26
+ msgid "Time"
27
+ msgstr ""
28
+
29
+ #: WPML_Email_Log_List.php:73
30
+ msgid "Receiver"
31
+ msgstr ""
32
+
33
+ #: WPML_Email_Log_List.php:74
34
+ msgid "Subject"
35
+ msgstr ""
36
+
37
+ #: WPML_Email_Log_List.php:75 WPML_OptionsManager.php:497
38
+ msgid "Message"
39
+ msgstr ""
40
+
41
+ #: WPML_Email_Log_List.php:76
42
+ msgid "Headers"
43
+ msgstr ""
44
+
45
+ #: WPML_Email_Log_List.php:77
46
+ msgid "Attachments"
47
+ msgstr ""
48
+
49
+ #: WPML_Email_Log_List.php:78
50
+ msgid "Error"
51
+ msgstr ""
52
+
53
+ #: WPML_Email_Log_List.php:79
54
+ msgid "Plugin Version"
55
+ msgstr ""
56
+
57
+ #: WPML_Email_Log_List.php:90
58
+ msgid "Host"
59
+ msgstr ""
60
+
61
+ #: WPML_Email_Log_List.php:267 WPML_Email_Log_List.php:301
62
+ msgid "Attachment %s is not present"
63
+ msgstr ""
64
+
65
+ #: WPML_Email_Log_List.php:495
66
+ msgid "Fallback to raw format because html is not convertible to json."
67
+ msgstr ""
68
+
69
+ #: WPML_LifeCycle.php:205
70
+ msgid "Settings"
71
+ msgstr ""
72
+
73
+ #: WPML_OptionsManager.php:321 WPML_OptionsManager.php:322
74
+ msgid "WP Mail Log"
75
+ msgstr ""
76
+
77
+ #: WPML_OptionsManager.php:333 WPML_OptionsManager.php:334
78
+ #: WPML_OptionsManager.php:345
79
+ msgid "About"
80
+ msgstr ""
81
+
82
+ #: WPML_OptionsManager.php:422
83
+ msgid "About Plugin"
84
+ msgstr ""
85
+
86
+ #: WPML_OptionsManager.php:431
87
+ msgid "More information"
88
+ msgstr ""
89
+
90
+ #: WPML_OptionsManager.php:432
91
+ msgid "Plugin Homepage/support"
92
+ msgstr ""
93
+
94
+ #: WPML_OptionsManager.php:433
95
+ msgid "Plugin author's blog"
96
+ msgstr ""
97
+
98
+ #: WPML_OptionsManager.php:440
99
+ msgid "Entries per page"
100
+ msgstr ""
101
+
102
+ #: WPML_OptionsManager.php:462
103
+ msgid "You do not have sufficient permissions to access this page."
104
+ msgstr ""
105
+
106
+ #: WPML_OptionsManager.php:471
107
+ msgid "Log"
108
+ msgstr ""
109
+
110
+ #: WPML_OptionsManager.php:515
111
+ msgid "Close"
112
+ msgstr ""
113
+
114
+ #: WPML_OptionsManager.php:529
115
+ msgid "Search"
116
+ msgstr ""
117
+
118
+ #: WPML_OptionsManager.php:554
119
+ msgid "true"
120
+ msgstr ""
121
+
122
+ #: WPML_OptionsManager.php:556
123
+ msgid "false"
124
+ msgstr ""
125
+
126
+ #: WPML_OptionsManager.php:559
127
+ msgid "Administrator"
128
+ msgstr ""
129
+
130
+ #: WPML_OptionsManager.php:561
131
+ msgid "Editor"
132
+ msgstr ""
133
+
134
+ #: WPML_OptionsManager.php:563
135
+ msgid "Author"
136
+ msgstr ""
137
+
138
+ #: WPML_OptionsManager.php:565
139
+ msgid "Contributor"
140
+ msgstr ""
141
+
142
+ #: WPML_OptionsManager.php:567
143
+ msgid "Subscriber"
144
+ msgstr ""
145
+
146
+ #: WPML_OptionsManager.php:569
147
+ msgid "Anyone"
148
+ msgstr ""
149
+
150
+ #: inc/class-wp-list-table.php:191
151
+ msgid "No items found."
152
+ msgstr ""
153
+
154
+ #: inc/class-wp-list-table.php:315
155
+ msgid "Bulk Actions"
156
+ msgstr ""
157
+
158
+ #: inc/class-wp-list-table.php:325
159
+ msgid "Apply"
160
+ msgstr ""
161
+
162
+ #: inc/class-wp-list-table.php:409
163
+ msgid "Show all dates"
164
+ msgstr ""
165
+
166
+ #: inc/class-wp-list-table.php:422
167
+ #. translators: 1: month name, 2: 4-digit year
168
+ msgid "%1$s %2$d"
169
+ msgstr ""
170
+
171
+ #: inc/class-wp-list-table.php:438
172
+ msgid "List View"
173
+ msgstr ""
174
+
175
+ #: inc/class-wp-list-table.php:439
176
+ msgid "Excerpt View"
177
+ msgstr ""
178
+
179
+ #: inc/class-wp-list-table.php:465
180
+ msgid "%s pending"
181
+ msgstr ""
182
+
183
+ #: inc/class-wp-list-table.php:533 inc/class-wp-list-table.php:948
184
+ msgid "1 item"
185
+ msgid_plural "%s items"
186
+ msgstr[0] ""
187
+ msgstr[1] ""
188
+
189
+ #: inc/class-wp-list-table.php:551
190
+ msgid "Go to the first page"
191
+ msgstr ""
192
+
193
+ #: inc/class-wp-list-table.php:558
194
+ msgid "Go to the previous page"
195
+ msgstr ""
196
+
197
+ #: inc/class-wp-list-table.php:567
198
+ msgid "Current page"
199
+ msgstr ""
200
+
201
+ #: inc/class-wp-list-table.php:577
202
+ msgid "Go to the next page"
203
+ msgstr ""
204
+
205
+ #: inc/class-wp-list-table.php:584
206
+ msgid "Go to the last page"
207
+ msgstr ""
208
+
209
+ #: inc/class-wp-list-table.php:720
210
+ msgid "Select All"
211
+ msgstr ""
212
+
213
+ #: inc/redux/WPML_Redux_Framework_config.php:194
214
+ msgid "Customize &#8220;%s&#8221;"
215
+ msgstr ""
216
+
217
+ #: inc/redux/WPML_Redux_Framework_config.php:203
218
+ #: inc/redux/WPML_Redux_Framework_config.php:207
219
+ msgid "Current theme preview"
220
+ msgstr ""
221
+
222
+ #: inc/redux/WPML_Redux_Framework_config.php:214
223
+ msgid "By %s"
224
+ msgstr ""
225
+
226
+ #: inc/redux/WPML_Redux_Framework_config.php:215
227
+ msgid "Version %s"
228
+ msgstr ""
229
+
230
+ #: inc/redux/WPML_Redux_Framework_config.php:216
231
+ msgid "Tags"
232
+ msgstr ""
233
+
234
+ #: inc/redux/WPML_Redux_Framework_config.php:221
235
+ msgid "This <a href=\"%1$s\">child theme</a> requires its parent theme, %2$s."
236
+ msgstr ""
237
+
238
+ #: inc/redux/WPML_Redux_Framework_config.php:221
239
+ msgid "http://codex.wordpress.org/Child_Themes"
240
+ msgstr ""
241
+
242
+ #: inc/redux/WPML_Redux_Framework_config.php:244
243
+ msgid "General Settings"
244
+ msgstr ""
245
+
246
+ #: inc/redux/WPML_Redux_Framework_config.php:253
247
+ msgid "Cleanup"
248
+ msgstr ""
249
+
250
+ #: inc/redux/WPML_Redux_Framework_config.php:254
251
+ msgid "Delete all data on deactivation? (emails and settings)?"
252
+ msgstr ""
253
+
254
+ #: inc/redux/WPML_Redux_Framework_config.php:264
255
+ msgid "Can See Submission data"
256
+ msgstr ""
257
+
258
+ #: inc/redux/WPML_Redux_Framework_config.php:265
259
+ msgid "Select the minimum role."
260
+ msgstr ""
261
+
262
+ #: inc/redux/WPML_Redux_Framework_config.php:270
263
+ msgid "WordPress Date Time Format"
264
+ msgstr ""
265
+
266
+ #: inc/redux/WPML_Redux_Framework_config.php:271
267
+ msgid "Use format from WordPress settings (%s)"
268
+ msgstr ""
269
+
270
+ #: inc/redux/WPML_Redux_Framework_config.php:273
271
+ #: inc/redux/WPML_Redux_Framework_config.php:298
272
+ #: inc/redux/WPML_Redux_Framework_config.php:314
273
+ #: inc/redux/WPML_Redux_Framework_config.php:336
274
+ msgid "Enabled"
275
+ msgstr ""
276
+
277
+ #: inc/redux/WPML_Redux_Framework_config.php:274
278
+ #: inc/redux/WPML_Redux_Framework_config.php:299
279
+ #: inc/redux/WPML_Redux_Framework_config.php:315
280
+ #: inc/redux/WPML_Redux_Framework_config.php:337
281
+ msgid "Disabled"
282
+ msgstr ""
283
+
284
+ #: inc/redux/WPML_Redux_Framework_config.php:285
285
+ msgid "Default Format for Message"
286
+ msgstr ""
287
+
288
+ #: inc/redux/WPML_Redux_Framework_config.php:286
289
+ msgid "Select your preferred display format."
290
+ msgstr ""
291
+
292
+ #: inc/redux/WPML_Redux_Framework_config.php:291
293
+ msgid "Display Host"
294
+ msgstr ""
295
+
296
+ #: inc/redux/WPML_Redux_Framework_config.php:292
297
+ msgid "Display host column in list."
298
+ msgstr ""
299
+
300
+ #: inc/redux/WPML_Redux_Framework_config.php:304
301
+ msgid "Log Rotation"
302
+ msgstr ""
303
+
304
+ #: inc/redux/WPML_Redux_Framework_config.php:305
305
+ msgid "Save space by deleting logs regularly."
306
+ msgstr ""
307
+
308
+ #: inc/redux/WPML_Redux_Framework_config.php:311
309
+ msgid "Cleanup by Amount"
310
+ msgstr ""
311
+
312
+ #: inc/redux/WPML_Redux_Framework_config.php:312
313
+ #: inc/redux/WPML_Redux_Framework_config.php:334
314
+ msgid "Setup a automated cleanup routine!"
315
+ msgstr ""
316
+
317
+ #: inc/redux/WPML_Redux_Framework_config.php:321
318
+ msgid "Amount"
319
+ msgstr ""
320
+
321
+ #: inc/redux/WPML_Redux_Framework_config.php:322
322
+ #: inc/redux/WPML_Redux_Framework_config.php:344
323
+ msgid "When should mails are deleted?"
324
+ msgstr ""
325
+
326
+ #: inc/redux/WPML_Redux_Framework_config.php:323
327
+ msgid "Cleanup when the stored mails exceed..."
328
+ msgstr ""
329
+
330
+ #: inc/redux/WPML_Redux_Framework_config.php:333
331
+ msgid "Cleanup by Time"
332
+ msgstr ""
333
+
334
+ #: inc/redux/WPML_Redux_Framework_config.php:345
335
+ msgid "Delete mails older than days..."
336
+ msgstr ""
337
+
338
+ #: inc/redux/WPML_Redux_Framework_config.php:510
339
+ msgid "Documentation"
340
+ msgstr ""
341
+
342
+ #: inc/redux/WPML_Redux_Framework_config.php:516
343
+ msgid "Support"
344
+ msgstr ""
345
+
346
+ #: inc/redux/WPML_Redux_Framework_config.php:522
347
+ msgid "Extensions"
348
+ msgstr ""
349
+
350
+ #: wp-mail-logging.php:48
351
+ msgid ""
352
+ "Error: plugin \"WP Mail Logging\" requires a newer version of PHP to be "
353
+ "running."
354
+ msgstr ""
355
+
356
+ #: wp-mail-logging.php:49
357
+ msgid "Minimal version of PHP required: "
358
+ msgstr ""
359
+
360
+ #: wp-mail-logging.php:50
361
+ msgid "Your server's PHP version: "
362
+ msgstr ""
363
+
364
+ #. Description of the plugin/theme
365
+ msgid "Logs each email sent by WordPress."
366
+ msgstr ""
367
+
368
+ #: inc/class-wp-list-table.php:573
369
+ msgctxt "paging"
370
+ msgid "%1$s of %2$s"
371
+ msgstr ""
model/WPML_Mail.php CHANGED
@@ -10,7 +10,7 @@ if(!defined( 'ABSPATH' )) exit;
10
 
11
  /**
12
  * WPML Mail model.
13
- * @sine 1.6.0
14
  * @author No3x
15
  */
16
  class WPML_Mail extends BaseModel
@@ -55,6 +55,11 @@ class WPML_Mail extends BaseModel
55
  */
56
  protected $attachments;
57
 
 
 
 
 
 
58
  /**
59
  * @var string
60
  */
10
 
11
  /**
12
  * WPML Mail model.
13
+ * @since 1.6.0
14
  * @author No3x
15
  */
16
  class WPML_Mail extends BaseModel
55
  */
56
  protected $attachments;
57
 
58
+ /**
59
+ * @var string
60
+ */
61
+ protected $error;
62
+
63
  /**
64
  * @var string
65
  */
readme.txt CHANGED
@@ -5,8 +5,8 @@ Tags: mail, email, log, logging, debug, list, store, collect, view
5
  License: GPLv3
6
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
7
  Requires at least: 3.0
8
- Tested up to: 4.6.1
9
- Stable tag: 1.7.0
10
 
11
  Logs each email sent by WordPress.
12
 
@@ -17,6 +17,7 @@ Logs each email sent by WordPress. This can be useful if you don't want to lose
17
  Features of the plugin include:
18
 
19
  * Complete list of sent mails - view and search through the mails.
 
20
  * Zero-configuration - just install and enjoy.
21
  * Log rotation - decide which emails you want to keep.
22
  * DevOP: IP of server sent the mail
@@ -33,11 +34,15 @@ Features of the plugin include:
33
 
34
  == Installation ==
35
  Just install and activate wp-mail-logging. The plugin will do the work for you! You can list all logged mails on the plugin site.
36
-
 
 
37
 
38
  == Frequently Asked Questions ==
39
- = How do I know the Mail was delivered? =
40
- The logged email has been sent by WordPress but please note this does NOT mean it has been delivered. With the given functionality of WordPress you can't determine if a mail was sent successfully.
 
 
41
 
42
  == Screenshots ==
43
  1. The List
@@ -45,13 +50,26 @@ The logged email has been sent by WordPress but please note this does NOT mean i
45
  3. The Settings
46
 
47
  == Upgrade Notice ==
48
- = 1.7.0 =
49
- - New: Storing host IP
50
- - Fix: passing search term for pagination
51
- - Tweak: close modal with ESC
 
 
 
 
52
 
53
  == Changelog ==
54
 
 
 
 
 
 
 
 
 
 
55
  = 1.7.0, November 6, 2016 =
56
  - New: logging host IP
57
  - Fix: passing search term for pagination
@@ -99,7 +117,7 @@ The logged email has been sent by WordPress but please note this does NOT mean i
99
 
100
  = 1.3, August 24, 2014 =
101
  - New: clean mail listing including:
102
- Modal window for mail details.
103
  Attachment support with appropriate icon for mime type.
104
  - Tweak: Performance improvement
105
  - Fix: screen option for mails per page
@@ -110,7 +128,7 @@ The logged email has been sent by WordPress but please note this does NOT mean i
110
  - Fix: deletion of mails regardless of options (on update to 1.2 your mails will be deleted hopefully this happens for the last time)
111
 
112
  = 1.1 =
113
- - Tweak: Modified readme.
114
 
115
  = 1.0 =
116
  - Initial Revision
5
  License: GPLv3
6
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
7
  Requires at least: 3.0
8
+ Tested up to: 4.7.2
9
+ Stable tag: 1.8.0
10
 
11
  Logs each email sent by WordPress.
12
 
17
  Features of the plugin include:
18
 
19
  * Complete list of sent mails - view and search through the mails.
20
+ * Error status from mail implementation is logged
21
  * Zero-configuration - just install and enjoy.
22
  * Log rotation - decide which emails you want to keep.
23
  * DevOP: IP of server sent the mail
34
 
35
  == Installation ==
36
  Just install and activate wp-mail-logging. The plugin will do the work for you! You can list all logged mails on the plugin site.
37
+ I recommend the following plugins if you want to send mails via SMTP because they are technically well integrated into WordPress and provide error messages on failure.:
38
+ - [WP Mail SMTP](https://de.wordpress.org/plugins/wp-mail-smtp/)
39
+ - [SMTP Mailer](https://de.wordpress.org/plugins/smtp-mailer/)
40
 
41
  == Frequently Asked Questions ==
42
+ = How do I know the mail was sent? =
43
+ If there is no error logged chances are high the mail was sent. There are plugins that overwrite (do not customize) the default mailing mechanism of WordPress - they maybe do not inform about failure so it can't be logged by WP Mail Logging.
44
+ = How do I know the wail was delivered? =
45
+ The logged email has been sent by WordPress but please note this does NOT mean it has been delivered. With the given functionality of WordPress you can't determine if a mail was delivered successfully.
46
 
47
  == Screenshots ==
48
  1. The List
50
  3. The Settings
51
 
52
  == Upgrade Notice ==
53
+ = 1.8.0 =
54
+ - New: Error status from mail implementation is logged
55
+ - New: Resend mail
56
+ - New: Added translation files
57
+ - New: Added translation for de_DE and zh_CN
58
+ - Fix: raw mode of message renders attachments as text
59
+ - Fix: fallback to raw mode for json mode if mail is containing html
60
+ - Tweak: Pretty print json
61
 
62
  == Changelog ==
63
 
64
+ = 1.8.0, February 15, 2017 =
65
+ - New: Error status from mail implementation is logged
66
+ - New: Resend mail
67
+ - New: Added translation files
68
+ - New: Added translation for de_DE and zh_CN
69
+ - Fix: raw mode of message renders attachments as text
70
+ - Fix: fallback to raw mode for json mode if mail is containing html
71
+ - Tweak: Pretty print json
72
+
73
  = 1.7.0, November 6, 2016 =
74
  - New: logging host IP
75
  - Fix: passing search term for pagination
117
 
118
  = 1.3, August 24, 2014 =
119
  - New: clean mail listing including:
120
+ Modal window for mail details.
121
  Attachment support with appropriate icon for mime type.
122
  - Tweak: Performance improvement
123
  - Fix: screen option for mails per page
128
  - Fix: deletion of mails regardless of options (on update to 1.2 your mails will be deleted hopefully this happens for the last time)
129
 
130
  = 1.1 =
131
+ - Tweak: Modified readme.
132
 
133
  = 1.0 =
134
  - Initial Revision
wp-mail-logging.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Mail Logging
4
  Plugin URI: http://wordpress.org/extend/plugins/wp-mail-logging/
5
  Support URI: https://github.com/No3x/wp-mail-logging/issues
6
- Version: 1.7.0
7
  Author: Christian Z&ouml;ller
8
  Author URI: http://no3x.de/
9
  Description: Logs each email sent by WordPress.
@@ -33,12 +33,10 @@
33
 
34
  namespace No3x\WPML;
35
 
36
- use No3x\WPML\WPML_Init;
37
-
38
  // Exit if accessed directly.
39
  if ( ! defined( 'ABSPATH' ) ) exit;
40
 
41
- $WPML_minimalRequiredPhpVersion = '5.4';
42
 
43
  /**
44
  * Check the PHP version and give a useful error message if the user's version is less than the required version
@@ -46,22 +44,20 @@ $WPML_minimalRequiredPhpVersion = '5.4';
46
  * an error message on the Admin page
47
  */
48
  function WPML_noticePhpVersionWrong() {
49
- global $WPML_minimalRequiredPhpVersion;
50
- echo '<div class="error">' .
51
- __( 'Error: plugin "WP Mail Logging" requires a newer version of PHP to be running.', 'wpml' ).
52
- '<br/>' . __( 'Minimal version of PHP required: ', 'wpml' ) . '<strong>' . $WPML_minimalRequiredPhpVersion . '</strong>' .
53
- '<br/>' . __( 'Your server\'s PHP version: ', 'wpml' ) . '<strong>' . phpversion() . '</strong>' .
54
- '</div>';
55
  }
56
 
57
 
58
  function WPML_PhpVersionCheck() {
59
- global $WPML_minimalRequiredPhpVersion;
60
- if ( version_compare( phpversion(), $WPML_minimalRequiredPhpVersion ) < 0 ) {
61
- add_action( 'admin_notices', __NAMESPACE__ . '\WPML_noticePhpVersionWrong' );
62
- return false;
63
- }
64
- return true;
65
  }
66
 
67
 
@@ -73,8 +69,8 @@ function WPML_PhpVersionCheck() {
73
  * @return void
74
  */
75
  function WPML_i18n_init() {
76
- $pluginDir = dirname(plugin_basename(__FILE__));
77
- load_plugin_textdomain('wpml', false, $pluginDir . '/languages/');
78
  }
79
 
80
 
@@ -89,24 +85,24 @@ WPML_i18n_init();
89
  // Next, run the version check.
90
  // If it is successful, continue with initialization for this plugin
91
  if (WPML_PhpVersionCheck()) {
92
- // Only init and run the init function if we know PHP version can parse it
93
- require __DIR__ . '/autoload.php';
94
-
95
- // Create a new instance of the autoloader
96
- $loader = new \WPML_Psr4AutoloaderClass();
97
-
98
- // Register this instance
99
- $loader->register();
100
-
101
- // Add our namespace and the folder it maps to
102
- require_once __DIR__ . '/inc/redux/admin-init.php';
103
- $loader->addNamespace('No3x\\WPML\\', __DIR__ );
104
- $loader->addNamespace('No3x\\WPML\\Model\\', __DIR__ . '/model' );
105
- $loader->addNamespace('No3x\\WPML\\Settings\\', __DIR__ . '/inc/redux');
106
- $loader->addNamespace('No3x\\WPML\\ORM\\', __DIR__ . '/lib/vendor/brandonwamboldt/wp-orm/src');
107
- $loader->addNamespace('No3x\\WPML\\Pimple\\', __DIR__ . '/lib/vendor/pimple/pimple/src');
108
- if( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
109
- require_once __DIR__ . '/vendor/autoload.php';
110
- }
111
- WPML_Init::getInstance()->init( __FILE__ );
112
  }
3
  Plugin Name: WP Mail Logging
4
  Plugin URI: http://wordpress.org/extend/plugins/wp-mail-logging/
5
  Support URI: https://github.com/No3x/wp-mail-logging/issues
6
+ Version: 1.8.0
7
  Author: Christian Z&ouml;ller
8
  Author URI: http://no3x.de/
9
  Description: Logs each email sent by WordPress.
33
 
34
  namespace No3x\WPML;
35
 
 
 
36
  // Exit if accessed directly.
37
  if ( ! defined( 'ABSPATH' ) ) exit;
38
 
39
+ define('WPML_PHP_MIN_VERSION', '5.4');
40
 
41
  /**
42
  * Check the PHP version and give a useful error message if the user's version is less than the required version
44
  * an error message on the Admin page
45
  */
46
  function WPML_noticePhpVersionWrong() {
47
+ echo '<div class="error">' .
48
+ __( 'Error: plugin "WP Mail Logging" requires a newer version of PHP to be running.', 'wp-mail-logging' ).
49
+ '<br/>' . __( 'Minimal version of PHP required: ', 'wp-mail-logging' ) . '<strong>' . WPML_PHP_MIN_VERSION . '</strong>' .
50
+ '<br/>' . __( 'Your server\'s PHP version: ', 'wp-mail-logging' ) . '<strong>' . phpversion() . '</strong>' .
51
+ '</div>';
 
52
  }
53
 
54
 
55
  function WPML_PhpVersionCheck() {
56
+ if ( version_compare( phpversion(), WPML_PHP_MIN_VERSION ) < 0 ) {
57
+ add_action( 'admin_notices', __NAMESPACE__ . '\WPML_noticePhpVersionWrong' );
58
+ return false;
59
+ }
60
+ return true;
 
61
  }
62
 
63
 
69
  * @return void
70
  */
71
  function WPML_i18n_init() {
72
+ $pluginDir = dirname(plugin_basename(__FILE__));
73
+ load_plugin_textdomain('wp-mail-logging', false, $pluginDir . '/languages/');
74
  }
75
 
76
 
85
  // Next, run the version check.
86
  // If it is successful, continue with initialization for this plugin
87
  if (WPML_PhpVersionCheck()) {
88
+ // Only init and run the init function if we know PHP version can parse it
89
+ require __DIR__ . '/autoload.php';
90
+
91
+ // Create a new instance of the autoloader
92
+ $loader = new \WPML_Psr4AutoloaderClass();
93
+
94
+ // Register this instance
95
+ $loader->register();
96
+
97
+ // Add our namespace and the folder it maps to
98
+ require_once __DIR__ . '/inc/redux/admin-init.php';
99
+ $loader->addNamespace('No3x\\WPML\\', __DIR__ );
100
+ $loader->addNamespace('No3x\\WPML\\Model\\', __DIR__ . '/model' );
101
+ $loader->addNamespace('No3x\\WPML\\Settings\\', __DIR__ . '/inc/redux');
102
+ $loader->addNamespace('No3x\\WPML\\ORM\\', __DIR__ . '/lib/vendor/brandonwamboldt/wp-orm/src');
103
+ $loader->addNamespace('No3x\\WPML\\Pimple\\', __DIR__ . '/lib/vendor/pimple/pimple/src');
104
+ if( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
105
+ require_once __DIR__ . '/vendor/autoload.php';
106
+ }
107
+ WPML_Init::getInstance()->init( __FILE__ );
108
  }