WP Mail Logging - Version 1.0

Version Description

  • Initial Revision
Download this release

Release Info

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

Version 1.0

WPML_API_Example.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author No3x
5
+ * The Plugin provides mechanisms to extend the displayed data.
6
+ * This class is not an API class. It is just an example.
7
+ */
8
+ class WPML_API_Example {
9
+
10
+ // require_once('WPML_API_Example.php');
11
+ // $aAPI = new WPML_API_Example();
12
+
13
+ function __construct() {
14
+
15
+ // In this example we are going to add a column 'test' in add_column.
16
+ add_filter( WPML_Plugin::HOOK_LOGGING_COLUMNS, array(&$this, 'add_column' ) );
17
+ add_filter( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, array(&$this, 'render_column' ), 10, 2 );
18
+
19
+ }
20
+
21
+ /**
22
+ * Is called when List Table is gathering columns.
23
+ *
24
+ * @param array $columns Array of columns
25
+ * @return array $columns Updated array of columns
26
+ */
27
+ public function add_column( $columns ) {
28
+ return $columns = array_merge( $columns,
29
+ array('test' => __( 'test', 'wml' ) )
30
+ //,array('test2' => __( 'test2', 'wml' ) ) // ...
31
+ );
32
+ }
33
+
34
+
35
+ /**
36
+ * Is called when the List Table could not find the column. So we can hook in and modify the column.
37
+ *
38
+ * @param array $item A singular item (one full row's worth of data)
39
+ * @param array $column_name The name/slug of the column to be processed
40
+ * @return string Text or HTML to be placed inside the column <td>
41
+ */
42
+ public function render_column( $item, $column_name ) {
43
+
44
+ switch( $column_name ) {
45
+ case 'test':
46
+ 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.";
47
+ default:
48
+ return "";
49
+ }
50
+ }
51
+ }
WPML_InstallIndicator.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
4
+
5
+ This file is part of WordPress Plugin Template for WordPress.
6
+
7
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ WordPress Plugin Template is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Contact Form to Database Extension.
19
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
20
+ */
21
+
22
+ include_once('WPML_OptionsManager.php');
23
+
24
+ class WPML_InstallIndicator extends WPML_OptionsManager {
25
+
26
+ const optionInstalled = '_installed';
27
+ const optionVersion = '_version';
28
+
29
+ /**
30
+ * @return bool indicating if the plugin is installed already
31
+ */
32
+ public function isInstalled() {
33
+ return $this->getOption(self::optionInstalled) == true;
34
+ }
35
+
36
+ /**
37
+ * Note in DB that the plugin is installed
38
+ * @return null
39
+ */
40
+ protected function markAsInstalled() {
41
+ return $this->updateOption(self::optionInstalled, true);
42
+ }
43
+
44
+ /**
45
+ * Note in DB that the plugin is uninstalled
46
+ * @return bool returned form delete_option.
47
+ * true implies the plugin was installed at the time of this call,
48
+ * false implies it was not.
49
+ */
50
+ protected function markAsUnInstalled() {
51
+ return $this->deleteOption(self::optionInstalled);
52
+ }
53
+
54
+ /**
55
+ * Set a version string in the options. This is useful if you install upgrade and
56
+ * need to check if an older version was installed to see if you need to do certain
57
+ * upgrade housekeeping (e.g. changes to DB schema).
58
+ * @return null
59
+ */
60
+ protected function getVersionSaved() {
61
+ return $this->getOption(self::optionVersion);
62
+ }
63
+
64
+ /**
65
+ * Set a version string in the options.
66
+ * need to check if
67
+ * @param $version string best practice: use a dot-delimited string like '1.2.3' so version strings can be easily
68
+ * compared using version_compare (http://php.net/manual/en/function.version-compare.php)
69
+ * @return null
70
+ */
71
+ protected function setVersionSaved($version) {
72
+ return $this->updateOption(self::optionVersion, $version);
73
+ }
74
+
75
+ /**
76
+ * @return string name of the main plugin file that has the header section with
77
+ * "Plugin Name", "Version", "Description", "Text Domain", etc.
78
+ */
79
+ protected function getMainPluginFileName() {
80
+ return basename(dirname(__FILE__)) . 'php';
81
+ }
82
+
83
+ /**
84
+ * Get a value for input key in the header section of main plugin file.
85
+ * E.g. "Plugin Name", "Version", "Description", "Text Domain", etc.
86
+ * @param $key string plugin header key
87
+ * @return string if found, otherwise null
88
+ */
89
+ public function getPluginHeaderValue($key) {
90
+ // Read the string from the comment header of the main plugin file
91
+ $data = file_get_contents($this->getPluginDir() . DIRECTORY_SEPARATOR . $this->getMainPluginFileName());
92
+ $match = array();
93
+ preg_match('/' . $key . ':\s*(\S+)/', $data, $match);
94
+ if (count($match) >= 1) {
95
+ return $match[1];
96
+ }
97
+ return null;
98
+ }
99
+
100
+ /**
101
+ * If your subclass of this class lives in a different directory,
102
+ * override this method with the exact same code. Since __FILE__ will
103
+ * be different, you will then get the right dir returned.
104
+ * @return string
105
+ */
106
+ protected function getPluginDir() {
107
+ return dirname(__FILE__);
108
+ }
109
+
110
+ /**
111
+ * Version of this code.
112
+ * Best practice: define version strings to be easily compared using version_compare()
113
+ * (http://php.net/manual/en/function.version-compare.php)
114
+ * NOTE: You should manually make this match the SVN tag for your main plugin file 'Version' release and 'Stable tag' in readme.txt
115
+ * @return string
116
+ */
117
+ public function getVersion() {
118
+ return $this->getPluginHeaderValue('Version');
119
+ }
120
+
121
+
122
+ /**
123
+ * Useful when checking for upgrades, can tell if the currently installed version is earlier than the
124
+ * newly installed code. This case indicates that an upgrade has been installed and this is the first time it
125
+ * has been activated, so any upgrade actions should be taken.
126
+ * @return bool true if the version saved in the options is earlier than the version declared in getVersion().
127
+ * true indicates that new code is installed and this is the first time it is activated, so upgrade actions
128
+ * should be taken. Assumes that version string comparable by version_compare, examples: '1', '1.1', '1.1.1', '2.0', etc.
129
+ */
130
+ public function isInstalledCodeAnUpgrade() {
131
+ return $this->isSavedVersionLessThan($this->getVersion());
132
+ }
133
+
134
+ /**
135
+ * Used to see if the installed code is an earlier version than the input version
136
+ * @param $aVersion string
137
+ * @return bool true if the saved version is earlier (by natural order) than the input version
138
+ */
139
+ public function isSavedVersionLessThan($aVersion) {
140
+ return $this->isVersionLessThan($this->getVersionSaved(), $aVersion);
141
+ }
142
+
143
+ /**
144
+ * Used to see if the installed code is the same or earlier than the input version.
145
+ * Useful when checking for an upgrade. If you haven't specified the number of the newer version yet,
146
+ * but the last version (installed) was 2.3 (for example) you could check if
147
+ * For example, $this->isSavedVersionLessThanEqual('2.3') == true indicates that the saved version is not upgraded
148
+ * past 2.3 yet and therefore you would perform some appropriate upgrade action.
149
+ * @param $aVersion string
150
+ * @return bool true if the saved version is earlier (by natural order) than the input version
151
+ */
152
+ public function isSavedVersionLessThanEqual($aVersion) {
153
+ return $this->isVersionLessThanEqual($this->getVersionSaved(), $aVersion);
154
+ }
155
+
156
+ /**
157
+ * @param $version1 string a version string such as '1', '1.1', '1.1.1', '2.0', etc.
158
+ * @param $version2 string a version string such as '1', '1.1', '1.1.1', '2.0', etc.
159
+ * @return bool true if version_compare of $versions1 and $version2 shows $version1 as the same or earlier
160
+ */
161
+ public function isVersionLessThanEqual($version1, $version2) {
162
+ return (version_compare($version1, $version2) <= 0);
163
+ }
164
+
165
+ /**
166
+ * @param $version1 string a version string such as '1', '1.1', '1.1.1', '2.0', etc.
167
+ * @param $version2 string a version string such as '1', '1.1', '1.1.1', '2.0', etc.
168
+ * @return bool true if version_compare of $versions1 and $version2 shows $version1 as earlier
169
+ */
170
+ public function isVersionLessThan($version1, $version2) {
171
+ return (version_compare($version1, $version2) < 0);
172
+ }
173
+
174
+ /**
175
+ * Record the installed version to options.
176
+ * This helps track was version is installed so when an upgrade is installed, it should call this when finished
177
+ * upgrading to record the new current version
178
+ * @return void
179
+ */
180
+ protected function saveInstalledVersion() {
181
+ $this->setVersionSaved($this->getVersion());
182
+ }
183
+
184
+
185
+ }
WPML_LifeCycle.php ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
4
+
5
+ This file is part of WordPress Plugin Template for WordPress.
6
+
7
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ WordPress Plugin Template is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Contact Form to Database Extension.
19
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
20
+ */
21
+
22
+ include_once('WPML_InstallIndicator.php');
23
+
24
+ class WPML_LifeCycle extends WPML_InstallIndicator {
25
+
26
+ public function install() {
27
+
28
+ // Initialize Plugin Options
29
+ $this->initOptions();
30
+
31
+ // Initialize DB Tables used by the plugin
32
+ $this->installDatabaseTables();
33
+
34
+ // Other Plugin initialization - for the plugin writer to override as needed
35
+ $this->otherInstall();
36
+
37
+ // Record the installed version
38
+ $this->saveInstalledVersion();
39
+
40
+ // To avoid running install() more then once
41
+ $this->markAsInstalled();
42
+ }
43
+
44
+ public function uninstall() {
45
+ $this->otherUninstall();
46
+ if( get_option( $this->prefix('DeleteOnDeactivation') , false) == true ) {
47
+ //TOOD: is multi site?
48
+ $this->unInstallDatabaseTables();
49
+ $this->deleteSavedOptions();
50
+ }
51
+ $this->markAsUnInstalled();
52
+ }
53
+
54
+ /**
55
+ * Perform any version-upgrade activities prior to activation (e.g. database changes)
56
+ * @return void
57
+ */
58
+ public function upgrade() {
59
+ }
60
+
61
+ /**
62
+ * See: http://plugin.michael-simpson.com/?page_id=105
63
+ * @return void
64
+ */
65
+ public function activate() {
66
+ }
67
+
68
+ /**
69
+ * See: http://plugin.michael-simpson.com/?page_id=105
70
+ * @return void
71
+ */
72
+ public function deactivate() {
73
+ $this->uninstall();
74
+ }
75
+
76
+ /**
77
+ * See: http://plugin.michael-simpson.com/?page_id=31
78
+ * @return void
79
+ */
80
+ protected function initOptions() {
81
+ }
82
+
83
+ public function addActionsAndFilters() {
84
+ }
85
+
86
+ /**
87
+ * See: http://plugin.michael-simpson.com/?page_id=101
88
+ * Called by install() to create any database tables if needed.
89
+ * Best Practice:
90
+ * (1) Prefix all table names with $wpdb->prefix
91
+ * (2) make table names lower case only
92
+ * @return void
93
+ */
94
+ protected function installDatabaseTables() {
95
+ }
96
+
97
+ /**
98
+ * See: http://plugin.michael-simpson.com/?page_id=101
99
+ * Drop plugin-created tables on uninstall.
100
+ * @return void
101
+ */
102
+ protected function unInstallDatabaseTables() {
103
+ }
104
+
105
+ /**
106
+ * Override to add any additional actions to be done at install time
107
+ * See: http://plugin.michael-simpson.com/?page_id=33
108
+ * @return void
109
+ */
110
+ protected function otherInstall() {
111
+ }
112
+
113
+ /**
114
+ * Override to add any additional actions to be done at uninstall time
115
+ * See: http://plugin.michael-simpson.com/?page_id=33
116
+ * @return void
117
+ */
118
+ protected function otherUninstall() {
119
+ }
120
+
121
+ /**
122
+ * Puts the configuration page in the Plugins menu by default.
123
+ * Override to put it elsewhere or create a set of submenus
124
+ * Override with an empty implementation if you don't want a configuration page
125
+ * @return void
126
+ */
127
+ public function addSettingsSubMenuPage() {
128
+ $this->addSettingsSubMenuPageToPluginsMenu();
129
+ //$this->addSettingsSubMenuPageToSettingsMenu();
130
+ }
131
+
132
+
133
+ protected function requireExtraPluginFiles() {
134
+ require_once(ABSPATH . 'wp-includes/pluggable.php');
135
+ require_once(ABSPATH . 'wp-admin/includes/plugin.php');
136
+ }
137
+
138
+ /**
139
+ * @return string Slug name for the URL to the Setting page
140
+ * (i.e. the page for setting options)
141
+ */
142
+ protected function getSettingsSlug() {
143
+ return get_class($this) . 'Settings';
144
+ }
145
+
146
+ protected function addSettingsSubMenuPageToPluginsMenu() {
147
+ $this->requireExtraPluginFiles();
148
+ $displayName = $this->getPluginDisplayName();
149
+ add_submenu_page('plugins.php',
150
+ $displayName,
151
+ $displayName,
152
+ 'manage_options',
153
+ $this->getSettingsSlug(),
154
+ array(&$this, 'settingsPage'));
155
+ }
156
+
157
+
158
+ protected function addSettingsSubMenuPageToSettingsMenu() {
159
+ $this->requireExtraPluginFiles();
160
+ $displayName = $this->getPluginDisplayName();
161
+ add_options_page($displayName,
162
+ $displayName,
163
+ 'manage_options',
164
+ $this->getSettingsSlug(),
165
+ array(&$this, 'settingsPage'));
166
+ }
167
+
168
+ /**
169
+ * @param $name string name of a database table
170
+ * @return string input prefixed with the WordPress DB table prefix
171
+ * plus the prefix for this plugin (lower-cased) to avoid table name collisions.
172
+ * The plugin prefix is lower-cases as a best practice that all DB table names are lower case to
173
+ * avoid issues on some platforms
174
+ */
175
+ protected function prefixTableName($name) {
176
+ global $wpdb;
177
+ return $wpdb->prefix . strtolower($this->prefix($name));
178
+ }
179
+
180
+
181
+ /**
182
+ * Convenience function for creating AJAX URLs.
183
+ *
184
+ * @param $actionName string the name of the ajax action registered in a call like
185
+ * add_action('wp_ajax_actionName', array(&$this, 'functionName'));
186
+ * and/or
187
+ * add_action('wp_ajax_nopriv_actionName', array(&$this, 'functionName'));
188
+ *
189
+ * If have an additional parameters to add to the Ajax call, e.g. an "id" parameter,
190
+ * you could call this function and append to the returned string like:
191
+ * $url = $this->getAjaxUrl('myaction&id=') . urlencode($id);
192
+ * or more complex:
193
+ * $url = sprintf($this->getAjaxUrl('myaction&id=%s&var2=%s&var3=%s'), urlencode($id), urlencode($var2), urlencode($var3));
194
+ *
195
+ * @return string URL that can be used in a web page to make an Ajax call to $this->functionName
196
+ */
197
+ public function getAjaxUrl($actionName) {
198
+ return admin_url('admin-ajax.php') . '?action=' . $actionName;
199
+ }
200
+
201
+ }
WPML_OptionsManager.php ADDED
@@ -0,0 +1,534 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
4
+
5
+ This file is part of WordPress Plugin Template for WordPress.
6
+
7
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ WordPress Plugin Template is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Contact Form to Database Extension.
19
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
20
+ */
21
+
22
+ class WPML_OptionsManager {
23
+
24
+ public function getOptionNamePrefix() {
25
+ return get_class($this) . '_';
26
+ }
27
+
28
+
29
+ /**
30
+ * Define your options meta data here as an array, where each element in the array
31
+ * @return array of key=>display-name and/or key=>array(display-name, choice1, choice2, ...)
32
+ * key: an option name for the key (this name will be given a prefix when stored in
33
+ * the database to ensure it does not conflict with other plugin options)
34
+ * value: can be one of two things:
35
+ * (1) string display name for displaying the name of the option to the user on a web page
36
+ * (2) array where the first element is a display name (as above) and the rest of
37
+ * the elements are choices of values that the user can select
38
+ * e.g.
39
+ * array(
40
+ * 'item' => 'Item:', // key => display-name
41
+ * 'rating' => array( // key => array ( display-name, choice1, choice2, ...)
42
+ * 'CanDoOperationX' => array('Can do Operation X', 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber'),
43
+ * 'Rating:', 'Excellent', 'Good', 'Fair', 'Poor')
44
+ */
45
+ public function getOptionMetaData() {
46
+ return array();
47
+ }
48
+
49
+ /**
50
+ * @return array of string name of options
51
+ */
52
+ public function getOptionNames() {
53
+ return array_keys($this->getOptionMetaData());
54
+ }
55
+
56
+ /**
57
+ * Override this method to initialize options to default values and save to the database with add_option
58
+ * @return void
59
+ */
60
+ protected function initOptions() {
61
+ }
62
+
63
+ /**
64
+ * Cleanup: remove all options from the DB
65
+ * @return void
66
+ */
67
+ protected function deleteSavedOptions() {
68
+ $optionMetaData = $this->getOptionMetaData();
69
+ if (is_array($optionMetaData)) {
70
+ foreach ($optionMetaData as $aOptionKey => $aOptionMeta) {
71
+ $prefixedOptionName = $this->prefix($aOptionKey); // how it is stored in DB
72
+ delete_option($prefixedOptionName);
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @return string display name of the plugin to show as a name/title in HTML.
79
+ * Just returns the class name. Override this method to return something more readable
80
+ */
81
+ public function getPluginDisplayName() {
82
+ return get_class($this);
83
+ }
84
+
85
+ /**
86
+ * Get the prefixed version input $name suitable for storing in WP options
87
+ * Idempotent: if $optionName is already prefixed, it is not prefixed again, it is returned without change
88
+ * @param $name string option name to prefix. Defined in settings.php and set as keys of $this->optionMetaData
89
+ * @return string
90
+ */
91
+ public function prefix($name) {
92
+ $optionNamePrefix = $this->getOptionNamePrefix();
93
+ if (strpos($name, $optionNamePrefix) === 0) { // 0 but not false
94
+ return $name; // already prefixed
95
+ }
96
+ return $optionNamePrefix . $name;
97
+ }
98
+
99
+ /**
100
+ * Remove the prefix from the input $name.
101
+ * Idempotent: If no prefix found, just returns what was input.
102
+ * @param $name string
103
+ * @return string $optionName without the prefix.
104
+ */
105
+ public function &unPrefix($name) {
106
+ $optionNamePrefix = $this->getOptionNamePrefix();
107
+ if (strpos($name, $optionNamePrefix) === 0) {
108
+ return substr($name, strlen($optionNamePrefix));
109
+ }
110
+ return $name;
111
+ }
112
+
113
+ /**
114
+ * A wrapper function delegating to WP get_option() but it prefixes the input $optionName
115
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
116
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
117
+ * @param $default string default value to return if the option is not set
118
+ * @return string the value from delegated call to get_option(), or optional default value
119
+ * if option is not set.
120
+ */
121
+ public function getOption($optionName, $default = null) {
122
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
123
+ $retVal = get_option($prefixedOptionName);
124
+ if (!$retVal && $default) {
125
+ $retVal = $default;
126
+ }
127
+ return $retVal;
128
+ }
129
+
130
+ /**
131
+ * A wrapper function delegating to WP delete_option() but it prefixes the input $optionName
132
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
133
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
134
+ * @return bool from delegated call to delete_option()
135
+ */
136
+ public function deleteOption($optionName) {
137
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
138
+ return delete_option($prefixedOptionName);
139
+ }
140
+
141
+ /**
142
+ * A wrapper function delegating to WP add_option() but it prefixes the input $optionName
143
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
144
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
145
+ * @param $value mixed the new value
146
+ * @return null from delegated call to delete_option()
147
+ */
148
+ public function addOption($optionName, $value) {
149
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
150
+ return add_option($prefixedOptionName, $value);
151
+ }
152
+
153
+ /**
154
+ * A wrapper function delegating to WP add_option() but it prefixes the input $optionName
155
+ * to enforce "scoping" the options in the WP options table thereby avoiding name conflicts
156
+ * @param $optionName string defined in settings.php and set as keys of $this->optionMetaData
157
+ * @param $value mixed the new value
158
+ * @return null from delegated call to delete_option()
159
+ */
160
+ public function updateOption($optionName, $value) {
161
+ $prefixedOptionName = $this->prefix($optionName); // how it is stored in DB
162
+ return update_option($prefixedOptionName, $value);
163
+ }
164
+
165
+ /**
166
+ * A Role Option is an option defined in getOptionMetaData() as a choice of WP standard roles, e.g.
167
+ * 'CanDoOperationX' => array('Can do Operation X', 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber')
168
+ * The idea is use an option to indicate what role level a user must minimally have in order to do some operation.
169
+ * So if a Role Option 'CanDoOperationX' is set to 'Editor' then users which role 'Editor' or above should be
170
+ * able to do Operation X.
171
+ * Also see: canUserDoRoleOption()
172
+ * @param $optionName
173
+ * @return string role name
174
+ */
175
+ public function getRoleOption($optionName) {
176
+ $roleAllowed = $this->getOption($optionName);
177
+ if (!$roleAllowed || $roleAllowed == '') {
178
+ $roleAllowed = 'Administrator';
179
+ }
180
+ return $roleAllowed;
181
+ }
182
+
183
+ /**
184
+ * Given a WP role name, return a WP capability which only that role and roles above it have
185
+ * http://codex.wordpress.org/Roles_and_Capabilities
186
+ * @param $roleName
187
+ * @return string a WP capability or '' if unknown input role
188
+ */
189
+ protected function roleToCapability($roleName) {
190
+ switch ($roleName) {
191
+ case 'Super Admin':
192
+ return 'manage_options';
193
+ case 'Administrator':
194
+ return 'manage_options';
195
+ case 'Editor':
196
+ return 'publish_pages';
197
+ case 'Author':
198
+ return 'publish_posts';
199
+ case 'Contributor':
200
+ return 'edit_posts';
201
+ case 'Subscriber':
202
+ return 'read';
203
+ case 'Anyone':
204
+ return 'read';
205
+ }
206
+ return '';
207
+ }
208
+
209
+ /**
210
+ * @param $roleName string a standard WP role name like 'Administrator'
211
+ * @return bool
212
+ */
213
+ public function isUserRoleEqualOrBetterThan($roleName) {
214
+ if ('Anyone' == $roleName) {
215
+ return true;
216
+ }
217
+ $capability = $this->roleToCapability($roleName);
218
+ return current_user_can($capability);
219
+ }
220
+
221
+ /**
222
+ * @param $optionName string name of a Role option (see comments in getRoleOption())
223
+ * @return bool indicates if the user has adequate permissions
224
+ */
225
+ public function canUserDoRoleOption($optionName) {
226
+ $roleAllowed = $this->getRoleOption($optionName);
227
+ if ('Anyone' == $roleAllowed) {
228
+ return true;
229
+ }
230
+ return $this->isUserRoleEqualOrBetterThan($roleAllowed);
231
+ }
232
+
233
+ /**
234
+ * see: http://codex.wordpress.org/Creating_Options_Pages
235
+ * @return void
236
+ */
237
+ public function createSettingsMenu() {
238
+ $pluginName = $this->getPluginDisplayName();
239
+ //create new top-level menu
240
+ add_menu_page(__('WP Mail Log', 'wpml'),
241
+ __('WP Mail Log', 'wpml'),
242
+ 'administrator',
243
+ get_class($this) . '_log',
244
+ array(&$this, 'LogMenu')
245
+ /*,plugins_url('/images/icon.png', __FILE__)*/); // if you call 'plugins_url; be sure to "require_once" it
246
+
247
+ //call register settings function
248
+ add_action('admin_init', array(&$this, 'registerSettings'));
249
+
250
+ add_submenu_page(get_class($this) . '_log',
251
+ __('Settings', 'wpml'),
252
+ __('Settings', 'wpml'),
253
+ 'administrator',
254
+ get_class($this) . '_settings',
255
+ array(&$this, 'LogSubMenuSettings') );
256
+
257
+ add_action( "contextual_help", array( &$this, 'create_settings_panel' ), 10, 3 );
258
+ }
259
+
260
+ public function registerSettings() {
261
+ $settingsGroup = get_class($this) . '-settings-group';
262
+ $optionMetaData = $this->getOptionMetaData();
263
+ foreach ($optionMetaData as $aOptionKey => $aOptionMeta) {
264
+ register_setting($settingsGroup, $aOptionMeta);
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Add settings Panel
270
+ */
271
+ function create_settings_panel($contextual_help, $screen_id, $screen) {
272
+
273
+ global $hook_suffix;
274
+
275
+ // Just add if we are at the plugin page
276
+ if( strpos($hook_suffix, get_class($this) . '_log' ) == false )
277
+ return $contextual_help;
278
+
279
+ // The add_help_tab function for screen was introduced in WordPress 3.3.
280
+ if ( ! method_exists( $screen, 'add_help_tab' ) )
281
+ return $contextual_help;
282
+
283
+
284
+ // List screen properties
285
+ $variables = '<ul style="width:50%;float:left;"> <strong>Screen variables </strong>'
286
+ . sprintf( '<li> Screen id : %s</li>', $screen_id )
287
+ . sprintf( '<li> Screen base : %s</li>', $screen->base )
288
+ . sprintf( '<li>Parent base : %s</li>', $screen->parent_base )
289
+ . sprintf( '<li> Parent file : %s</li>', $screen->parent_file )
290
+ . sprintf( '<li> Hook suffix : %s</li>', $hook_suffix )
291
+ . '</ul>';
292
+
293
+ // Append global $hook_suffix to the hook stems
294
+ $hooks = array(
295
+ "load-$hook_suffix",
296
+ "admin_print_styles-$hook_suffix",
297
+ "admin_print_scripts-$hook_suffix",
298
+ "admin_head-$hook_suffix",
299
+ "admin_footer-$hook_suffix"
300
+ );
301
+
302
+ // If add_meta_boxes or add_meta_boxes_{screen_id} is used, list these too
303
+ if ( did_action( 'add_meta_boxes_' . $screen_id ) )
304
+ $hooks[] = 'add_meta_boxes_' . $screen_id;
305
+
306
+ if ( did_action( 'add_meta_boxes' ) )
307
+ $hooks[] = 'add_meta_boxes';
308
+
309
+ // Get List HTML for the hooks
310
+ $hooks = '<ul style="width:50%;float:left;"> <strong>Hooks </strong> <li>';
311
+
312
+
313
+ // Combine $variables list with $hooks list.
314
+ $help_content = $variables . $hooks;
315
+
316
+ /**
317
+ * Content specified inline
318
+ */
319
+ $screen->add_help_tab(
320
+ array(
321
+ 'title' => __('About Plugin', 'wpml'),
322
+ 'id' => 'about_tab',
323
+ 'content' => '<p>' . __( "{$this->getPluginDisplayName()}, logs each email sent by WordPress.", 'wpml') . '</p>' . $help_content,
324
+ 'callback' => false
325
+ )
326
+ );
327
+
328
+ // Add help sidebar
329
+ $screen->set_help_sidebar(
330
+ '<p><strong>' . __('More information', 'wpml') . '</strong></p>' .
331
+ '<p><a href = "http://wordpress.org/extend/plugins/wp-mail-logging/">' . __('Plugin Homepage/support', 'wpml') . '</a></p>' .
332
+ '<p><a href = "http://no3x.de/">' . __("Plugin author's blog", 'wpml') . '</a></p>'
333
+ );
334
+
335
+ // Add screen options
336
+ $screen->add_option(
337
+ 'per_page',
338
+ array(
339
+ 'label' => __('Entries per page', 'wpml'),
340
+ 'default' => 25,
341
+ 'option' => 'per_page'
342
+ )
343
+ );
344
+
345
+ return $contextual_help;
346
+ }
347
+
348
+ public function LogMenu() {
349
+ if (!current_user_can('manage_options')) {
350
+ wp_die(__('You do not have sufficient permissions to access this page.', 'wpml'));
351
+ }
352
+
353
+
354
+ if (!class_exists( 'Email_Log_List_Table' ) ) {
355
+ require_once dirname( __FILE__ ) . '/inc/class-email-logging-list-table.php';
356
+ }
357
+
358
+ ?>
359
+ <div class="wrap">
360
+ <h2><?php echo $this->getPluginDisplayName(); echo ' '; _e('Log', 'wpml'); ?></h2>
361
+ <?php
362
+ $emailLoggingListTable = new Email_Logging_ListTable();
363
+ $emailLoggingListTable->prepare_items();
364
+ ?>
365
+ <form id="email-list" method="get">
366
+ <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
367
+ <?php
368
+ $emailLoggingListTable->display();
369
+ ?>
370
+ </form>
371
+
372
+
373
+ </div>
374
+ <?php
375
+ }
376
+
377
+ /**
378
+ * Creates HTML for the Administration page to set options for this plugin.
379
+ * Override this method to create a customized page.
380
+ * @return void
381
+ */
382
+ public function LogSubMenuSettings() {
383
+ if (!current_user_can('manage_options')) {
384
+ wp_die(__('You do not have sufficient permissions to access this page.', 'wpml'));
385
+ }
386
+
387
+ $optionMetaData = $this->getOptionMetaData();
388
+
389
+ // Save Posted Options
390
+ if ($optionMetaData != null) {
391
+ foreach ($optionMetaData as $aOptionKey => $aOptionMeta) {
392
+ if (isset($_POST[$aOptionKey])) {
393
+ $this->updateOption($aOptionKey, $_POST[$aOptionKey]);
394
+ }
395
+ }
396
+ }
397
+
398
+ // HTML for the page
399
+ $settingsGroup = get_class($this) . '-settings-group';
400
+ ?>
401
+ <div class="wrap">
402
+
403
+ <h2><?php echo $this->getPluginDisplayName(); echo ' '; _e('Settings', 'wpml'); ?></h2>
404
+
405
+ <form method="post" action="">
406
+ <?php settings_fields($settingsGroup); ?>
407
+ <table class="form-table"><tbody>
408
+ <?php
409
+ if ($optionMetaData != null) {
410
+ foreach ($optionMetaData as $aOptionKey => $aOptionMeta) {
411
+ $displayText = is_array($aOptionMeta) ? $aOptionMeta[0] : $aOptionMeta;
412
+ ?>
413
+ <tr valign="top">
414
+ <th scope="row"><p><label for="<?php echo $aOptionKey ?>"><?php echo $displayText ?></label></p></th>
415
+ <td>
416
+ <?php $this->createFormControl($aOptionKey, $aOptionMeta, $this->getOption($aOptionKey)); ?>
417
+ </td>
418
+ </tr>
419
+ <?php
420
+ }
421
+ }
422
+ ?>
423
+ </tbody></table>
424
+ <p class="submit">
425
+ <input type="submit" class="button-primary"
426
+ value="<?php _e('Save Changes', 'wpml') ?>"/>
427
+ </p>
428
+ </form>
429
+ </div>
430
+ <?php
431
+
432
+ }
433
+
434
+ /**
435
+ * Helper-function outputs the correct form element (input tag, select tag) for the given item
436
+ * @param $aOptionKey string name of the option (un-prefixed)
437
+ * @param $aOptionMeta mixed meta-data for $aOptionKey (either a string display-name or an array(display-name, option1, option2, ...)
438
+ * @param $savedOptionValue string current value for $aOptionKey
439
+ * @return void
440
+ */
441
+ protected function createFormControl($aOptionKey, $aOptionMeta, $savedOptionValue) {
442
+ if (is_array($aOptionMeta) && count($aOptionMeta) >= 2) { // Drop-down list
443
+ $choices = array_slice($aOptionMeta, 1);
444
+ ?>
445
+ <p><select name="<?php echo $aOptionKey ?>" id="<?php echo $aOptionKey ?>">
446
+ <?php
447
+ foreach ($choices as $aChoice) {
448
+ $selected = ($aChoice == $savedOptionValue) ? 'selected' : '';
449
+ ?>
450
+ <option value="<?php echo $aChoice ?>" <?php echo $selected ?>><?php echo $this->getOptionValueI18nString($aChoice) ?></option>
451
+ <?php
452
+ }
453
+ ?>
454
+ </select></p>
455
+ <?php
456
+
457
+ }
458
+ else { // Simple input field
459
+ ?>
460
+ <p><input type="text" name="<?php echo $aOptionKey ?>" id="<?php echo $aOptionKey ?>"
461
+ value="<?php echo esc_attr($savedOptionValue) ?>" size="50"/></p>
462
+ <?php
463
+
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Override this method and follow its format.
469
+ * The purpose of this method is to provide i18n display strings for the values of options.
470
+ * For example, you may create a options with values 'true' or 'false'.
471
+ * In the options page, this will show as a drop down list with these choices.
472
+ * But when the the language is not English, you would like to display different strings
473
+ * for 'true' and 'false' while still keeping the value of that option that is actually saved in
474
+ * the DB as 'true' or 'false'.
475
+ * To do this, follow the convention of defining option values in getOptionMetaData() as canonical names
476
+ * (what you want them to literally be, like 'true') and then add each one to the switch statement in this
477
+ * function, returning the "__()" i18n name of that string.
478
+ * @param $optionValue string
479
+ * @return string __($optionValue) if it is listed in this method, otherwise just returns $optionValue
480
+ */
481
+ protected function getOptionValueI18nString($optionValue) {
482
+ switch ($optionValue) {
483
+ case 'true':
484
+ return __('true', 'wpml');
485
+ case 'false':
486
+ return __('false', 'wpml');
487
+
488
+ case 'Administrator':
489
+ return __('Administrator', 'wpml');
490
+ case 'Editor':
491
+ return __('Editor', 'wpml');
492
+ case 'Author':
493
+ return __('Author', 'wpml');
494
+ case 'Contributor':
495
+ return __('Contributor', 'wpml');
496
+ case 'Subscriber':
497
+ return __('Subscriber', 'wpml');
498
+ case 'Anyone':
499
+ return __('Anyone', 'wpml');
500
+ }
501
+ return $optionValue;
502
+ }
503
+
504
+ /**
505
+ * Query MySQL DB for its version
506
+ * @return string|false
507
+ */
508
+ protected function getMySqlVersion() {
509
+ global $wpdb;
510
+ $rows = $wpdb->get_results('select version() as mysqlversion');
511
+ if (!empty($rows)) {
512
+ return $rows[0]->mysqlversion;
513
+ }
514
+ return false;
515
+ }
516
+
517
+ /**
518
+ * If you want to generate an email address like "no-reply@your-site.com" then
519
+ * you can use this to get the domain name part.
520
+ * E.g. 'no-reply@' . $this->getEmailDomain();
521
+ * This code was stolen from the wp_mail function, where it generates a default
522
+ * from "wordpress@your-site.com"
523
+ * @return string domain name
524
+ */
525
+ public function getEmailDomain() {
526
+ // Get the site domain and get rid of www.
527
+ $sitename = strtolower($_SERVER['SERVER_NAME']);
528
+ if (substr($sitename, 0, 4) == 'www.') {
529
+ $sitename = substr($sitename, 4);
530
+ }
531
+ return $sitename;
532
+ }
533
+ }
534
+
WPML_Plugin.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ include_once('WPML_LifeCycle.php');
5
+
6
+ class WPML_Plugin extends WPML_LifeCycle {
7
+
8
+ const HOOK_LOGGING_COLUMNS = 'wpml_hook_mail_columns';
9
+ const HOOK_LOGGING_COLUMNS_RENDER = 'wpml_hook_mail_columns_render';
10
+
11
+ /**
12
+ * See: http://plugin.michael-simpson.com/?page_id=31
13
+ * @return array of option meta data.
14
+ */
15
+ public function getOptionMetaData() {
16
+ // http://plugin.michael-simpson.com/?page_id=31
17
+ return array(
18
+ //'_version' => array('Installed Version'), // Leave this one commented-out. Uncomment to test upgrades.
19
+ //'ATextInput' => array(__('Enter in some text', 'wml')),
20
+ 'DeleteOnDeactivation' => array(__('Delete all data on deactivation? (emails and settings)', 'wml'), 'false', 'true'),
21
+ 'CanSeeSubmitData' => array(__('Can See Submission data', 'wml'),
22
+ 'Administrator', 'Editor', 'Author', 'Contributor', 'Subscriber', 'Anyone')
23
+ );
24
+ }
25
+
26
+ // protected function getOptionValueI18nString($optionValue) {
27
+ // $i18nValue = parent::getOptionValueI18nString($optionValue);
28
+ // return $i18nValue;
29
+ // }
30
+
31
+ protected function initOptions() {
32
+ $options = $this->getOptionMetaData();
33
+ if (!empty($options)) {
34
+ foreach ($options as $key => $arr) {
35
+ if (is_array($arr) && count($arr > 1)) {
36
+ $this->addOption($key, $arr[1]);
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ public function getPluginDisplayName() {
43
+ return 'WP Mail Logging';
44
+ }
45
+
46
+ protected function getMainPluginFileName() {
47
+ return 'wp-mail-logging.php';
48
+ }
49
+
50
+ /**
51
+ * See: http://plugin.michael-simpson.com/?page_id=101
52
+ * Called by install() to create any database tables if needed.
53
+ * Best Practice:
54
+ * (1) Prefix all table names with $wpdb->prefix
55
+ * (2) make table names lower case only
56
+ * @return void
57
+ */
58
+ protected function installDatabaseTables() {
59
+ global $wpdb;
60
+ $tableName = _get_tablename('mails');
61
+ $wpdb->query("CREATE TABLE IF NOT EXISTS `$tableName` (
62
+ `mail_id` INT NOT NULL AUTO_INCREMENT,
63
+ `timestamp` TIMESTAMP NOT NULL,
64
+ `to` VARCHAR(200) NOT NULL DEFAULT '0',
65
+ `subject` VARCHAR(200) NOT NULL DEFAULT '0',
66
+ `message` TEXT NULL,
67
+ `headers` TEXT NULL,
68
+ `attachments` TINYINT(1) NOT NULL DEFAULT '0',
69
+ `plugin_version` VARCHAR(200) NOT NULL DEFAULT '0',
70
+ PRIMARY KEY (`mail_id`)
71
+ );");
72
+ }
73
+
74
+
75
+ /**
76
+ * See: http://plugin.michael-simpson.com/?page_id=101
77
+ * Drop plugin-created tables on uninstall.
78
+ * @return void
79
+ */
80
+ protected function unInstallDatabaseTables() {
81
+ global $wpdb;
82
+ $tableName = _get_tablename('mails');
83
+ $wpdb->query("DROP TABLE IF EXISTS `$tableName`");
84
+ }
85
+
86
+
87
+ /**
88
+ * Perform actions when upgrading from version X to version Y
89
+ * See: http://plugin.michael-simpson.com/?page_id=35
90
+ * @return void
91
+ */
92
+ public function upgrade() {
93
+ // global $wpdb;
94
+ // $upgradeOk = true;
95
+ // $savedVersion = $this->getVersionSaved();
96
+
97
+ // if ($this->isVersionLessThan($savedVersion, '1.0')) {
98
+ // if ($this->isVersionLessThan($savedVersion, '0.2')) {
99
+ // $tableName = $this->prefixTableName('mail_logging');
100
+ // $wpdb->query("ALTER TABLE `$tableName` ADD COLUMN ( `plugin_version` VARCHAR(200) NOT NULL DEFAULT '0')");
101
+ // }
102
+ // }
103
+
104
+ // // Post-upgrade, set the current version in the options
105
+ // $codeVersion = $this->getVersion();
106
+ // if ($upgradeOk && $savedVersion != $codeVersion) {
107
+ // $this->saveInstalledVersion();
108
+ // }
109
+ }
110
+
111
+ public function addActionsAndFilters() {
112
+
113
+ // Add options administration page
114
+ // http://plugin.michael-simpson.com/?page_id=47
115
+ add_action('admin_menu', array(&$this, 'createSettingsMenu'));
116
+
117
+ // Example adding a script & style just for the options administration page
118
+ // http://plugin.michael-simpson.com/?page_id=47
119
+ // if (strpos($_SERVER['REQUEST_URI'], $this->getSettingsSlug()) !== false) {
120
+ // wp_enqueue_script('my-script', plugins_url('/js/my-script.js', __FILE__));
121
+ // wp_enqueue_style('my-style', plugins_url('/css/my-style.css', __FILE__));
122
+ // }
123
+
124
+
125
+ // Add Actions & Filters
126
+ // http://plugin.michael-simpson.com/?page_id=37
127
+
128
+ add_filter( 'wp_mail', array(&$this, 'log_email' ) );
129
+
130
+ // Adding scripts & styles to all pages
131
+ // Examples:
132
+ // wp_enqueue_script('jquery');
133
+ // wp_enqueue_style('my-style', plugins_url('/css/my-style.css', __FILE__));
134
+ // wp_enqueue_script('my-script', plugins_url('/js/my-script.js', __FILE__));
135
+
136
+
137
+ // Register short codes
138
+ // http://plugin.michael-simpson.com/?page_id=39
139
+
140
+
141
+ // Register AJAX hooks
142
+ // http://plugin.michael-simpson.com/?page_id=41
143
+
144
+ }
145
+
146
+ public function log_email( $mailOriginal ) {
147
+ $mail = $mailOriginal;
148
+ global $wpdb;
149
+ /*
150
+ [to] => to@example.com
151
+ [subject] => Test Mail
152
+ [message] => Test Mail
153
+
154
+ [headers] =>
155
+ [attachments] => Array
156
+ (
157
+ )
158
+ */
159
+ $to = is_array($mail["to"]) ? implode(",\n", $mail['to']) : $mail['to'];
160
+ $subject = $mail["subject"];
161
+ $message = $mail["message"];
162
+ $headers = is_array($mail["headers"]) ? implode(",\n", $mail['headers']) : $mail['headers'];
163
+ $hasAttachments = (count ($mail['attachments']) > 0) ? "true" : "false";
164
+
165
+ $tableName = _get_tablename('mails');
166
+ $wpdb->insert($tableName, array(
167
+ 'to' => $to,
168
+ 'timestamp' => current_time('mysql'),
169
+ 'subject' => $subject,
170
+ 'message' => $message,
171
+ 'headers' => $headers,
172
+ 'attachments' => $hasAttachments,
173
+ 'plugin_version' => $this->getVersion()
174
+ ));
175
+
176
+ return $mailOriginal;
177
+ }
178
+ }
WPML_ShortCodeLoader.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
4
+
5
+ This file is part of WordPress Plugin Template for WordPress.
6
+
7
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ WordPress Plugin Template is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Contact Form to Database Extension.
19
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
20
+ */
21
+
22
+ abstract class WPML_ShortCodeLoader {
23
+
24
+ /**
25
+ * @param $shortcodeName mixed either string name of the shortcode
26
+ * (as it would appear in a post, e.g. [shortcodeName])
27
+ * or an array of such names in case you want to have more than one name
28
+ * for the same shortcode
29
+ * @return void
30
+ */
31
+ public function register($shortcodeName) {
32
+ $this->registerShortcodeToFunction($shortcodeName, 'handleShortcode');
33
+ }
34
+
35
+ /**
36
+ * @param $shortcodeName mixed either string name of the shortcode
37
+ * (as it would appear in a post, e.g. [shortcodeName])
38
+ * or an array of such names in case you want to have more than one name
39
+ * for the same shortcode
40
+ * @param $functionName string name of public function in this class to call as the
41
+ * shortcode handler
42
+ * @return void
43
+ */
44
+ protected function registerShortcodeToFunction($shortcodeName, $functionName) {
45
+ if (is_array($shortcodeName)) {
46
+ foreach ($shortcodeName as $aName) {
47
+ add_shortcode($aName, array($this, $functionName));
48
+ }
49
+ }
50
+ else {
51
+ add_shortcode($shortcodeName, array($this, $functionName));
52
+ }
53
+ }
54
+
55
+ /**
56
+ * @abstract Override this function and add actual shortcode handling here
57
+ * @param $atts shortcode inputs
58
+ * @return string shortcode content
59
+ */
60
+ public abstract function handleShortcode($atts);
61
+
62
+ }
WPML_ShortCodeScriptLoader.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
4
+
5
+ This file is part of WordPress Plugin Template for WordPress.
6
+
7
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ WordPress Plugin Template is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Contact Form to Database Extension.
19
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
20
+ */
21
+
22
+ include_once('WPML_ShortCodeLoader.php');
23
+
24
+ /**
25
+ * Adapted from this excellent article:
26
+ * http://scribu.net/wordpress/optimal-script-loading.html
27
+ *
28
+ * The idea is you have a shortcode that needs a script loaded, but you only
29
+ * want to load it if the shortcode is actually called.
30
+ */
31
+ abstract class WPML_ShortCodeScriptLoader extends WPML_ShortCodeLoader {
32
+
33
+ var $doAddScript;
34
+
35
+ public function register($shortcodeName) {
36
+ $this->registerShortcodeToFunction($shortcodeName, 'handleShortcodeWrapper');
37
+
38
+ // It will be too late to enqueue the script in the header,
39
+ // but can add them to the footer
40
+ add_action('wp_footer', array($this, 'addScriptWrapper'));
41
+ }
42
+
43
+ public function handleShortcodeWrapper($atts) {
44
+ // Flag that we need to add the script
45
+ $this->doAddScript = true;
46
+ return $this->handleShortcode($atts);
47
+ }
48
+
49
+
50
+ public function addScriptWrapper() {
51
+ // Only add the script if the shortcode was actually called
52
+ if ($this->doAddScript) {
53
+ $this->addScript();
54
+ }
55
+ }
56
+
57
+ /**
58
+ * @abstract override this function with calls to insert scripts needed by your shortcode in the footer
59
+ * Example:
60
+ * wp_register_script('my-script', plugins_url('js/my-script.js', __FILE__), array('jquery'), '1.0', true);
61
+ * wp_print_scripts('my-script');
62
+ * @return void
63
+ */
64
+ public abstract function addScript();
65
+
66
+ }
assets/images/screenshots/screenshot-1.png ADDED
Binary file
assets/images/screenshots/screenshot-2.png ADDED
Binary file
css/.gitignore ADDED
File without changes
images/.gitignore ADDED
File without changes
inc/class-email-logging-list-table.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!class_exists('WP_List_Table')){
4
+ require_once( ABSPATH . WPINC . '/class-wp-list-table.php' );
5
+ }
6
+
7
+ class Email_Logging_ListTable extends WP_List_Table {
8
+
9
+ function __construct(){
10
+ global $status, $page, $hook_suffix;
11
+
12
+ parent::__construct( array(
13
+ 'singular' => __( 'Email', 'wml' ),//singular name of the listed records
14
+ 'plural' => __( 'Emails', 'wml' ),//plural name of the listed records
15
+ 'ajax' => false //does this table support ajax?
16
+ ) );
17
+ }
18
+
19
+ /* (non-PHPdoc)
20
+ * @see WP_List_Table::get_columns()
21
+ */
22
+ function get_columns(){
23
+ $columns = array(
24
+ 'cb' => '<input type="checkbox" />',
25
+ 'mail_id' => __( 'ID', 'wml'),
26
+ 'timestamp' => __( 'Time', 'wml'),
27
+ 'to' => __( 'To', 'wml'),
28
+ 'subject' => __( 'Subject', 'wml'),
29
+ 'message' => __( 'Message', 'wml'),
30
+ 'headers' => __( 'Headers', 'wml'),
31
+ 'attachments' => __( 'Attachments', 'wml'),
32
+ 'plugin_version'=> __( 'Plugin Version', 'wml')
33
+ );
34
+
35
+ $columns = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS, $columns );
36
+
37
+ $special = array('_title', 'comment', 'media', 'name', 'title', 'username', 'blogname');
38
+
39
+ foreach ( $special as $key ) {
40
+ if( array_key_exists( $key, $columns ) ) {
41
+ echo "You should avoid $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!";
42
+ break;
43
+ }
44
+ }
45
+
46
+ return $columns;
47
+ }
48
+
49
+ /**
50
+ * Define which columns are hidden
51
+ *
52
+ * @return Array
53
+ */
54
+ function get_hidden_columns() {
55
+ return array(
56
+ 'plugin_version',
57
+ 'mail_id'
58
+ );
59
+ }
60
+
61
+ /* (non-PHPdoc)
62
+ * @see WP_List_Table::get_columns()
63
+ */
64
+ function prepare_items() {
65
+ global $wpdb;
66
+ $tableName = _get_tablename('mails');
67
+
68
+ $columns = $this->get_columns();
69
+ $hidden = $this->get_hidden_columns($screen);
70
+ $sortable = $this->get_sortable_columns();
71
+ $this->_column_headers = array($columns, $hidden, $sortable);
72
+
73
+ $this->process_bulk_action();
74
+
75
+ $per_page = $this->get_items_per_page( 'per_page', 25 );
76
+ $current_page = $this->get_pagenum();
77
+ $total_items = $wpdb->get_var("SELECT COUNT(*) FROM `$tableName`");
78
+ $limit = $per_page*$current_page;
79
+ //TODO: make option for default order
80
+ $orderby_default = "mail_id";
81
+ $order_default = "desc";
82
+ $orderby = ( !empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : $orderby_default;
83
+ $order = ( !empty($_GET['order'] ) ) ? $_GET['order'] : $order_default;
84
+
85
+ $found_data = $wpdb->get_results("SELECT * FROM `$tableName` ORDER BY $orderby $order LIMIT $limit", ARRAY_A);
86
+
87
+ $dataset = array_slice( $found_data,( ( $current_page-1 )* $per_page ), $per_page );
88
+
89
+ $this->set_pagination_args( array(
90
+ 'total_items' => $total_items, //WE have to calculate the total number of items
91
+ 'per_page' => $per_page //WE have to determine how many items to show on a page
92
+ ) );
93
+ $this->items = $dataset;
94
+ }
95
+
96
+
97
+ /**
98
+ * Renders the cell.
99
+ * 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)
100
+ * @param array $item
101
+ * @param string $column_name
102
+ * @return string The cell content
103
+ */
104
+ function column_default( $item, $column_name ) {
105
+ switch( $column_name ) {
106
+ case 'mail_id':
107
+ case 'timestamp':
108
+ case 'to':
109
+ case 'subject':
110
+ case 'message':
111
+ case 'headers':
112
+ case 'attachments':
113
+ case 'plugin_version':
114
+ return $item[ $column_name ];
115
+ default:
116
+ // 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)
117
+ return (is_array( $res = apply_filters( WPML_Plugin::HOOK_LOGGING_COLUMNS_RENDER, $item, $column_name ) ) ) ? "" : $res;
118
+ }
119
+ }
120
+
121
+ /* (non-PHPdoc)
122
+ * @see WP_List_Table::get_bulk_actions()
123
+ */
124
+ function get_bulk_actions() {
125
+ $actions = array(
126
+ 'delete' => 'Delete'
127
+ );
128
+ return $actions;
129
+ }
130
+
131
+ function process_bulk_action() {
132
+ global $wpdb;
133
+ $name = $this->_args['singular'];
134
+ $tableName = _get_tablename('mails');
135
+
136
+ //Detect when a bulk action is being triggered...
137
+ if( 'delete' == $this->current_action() ) {
138
+ foreach($_REQUEST[$name] as $item_id) {
139
+ $wpdb->query("DELETE FROM `$tableName` WHERE mail_id = $item_id");
140
+ }
141
+ }
142
+ }
143
+
144
+
145
+ /**
146
+ * Render the cb column
147
+ * @param object $item The current item
148
+ * @return string the rendered cb cell content
149
+ */
150
+ function column_cb($item) {
151
+ $name = $this->_args['singular'];
152
+ return sprintf(
153
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />', $name, $item['mail_id']
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Define the sortable columns
159
+ *
160
+ * @return Array
161
+ */
162
+ function get_sortable_columns() {
163
+ $sortable_columns = array(
164
+ // column_name => array( 'display_name', true[asc] | false[desc] )
165
+ 'mail_id' => array('mail_id', false),
166
+ 'timestamp' => array('timestamp', true),
167
+ 'to' => array('to', true),
168
+ 'subject' => array('subject', true),
169
+ 'message' => array('message', true),
170
+ 'headers' => array('headers', true),
171
+ 'attachments' => array('attachments', true),
172
+ 'plugin_version' => array('plugin_version', true)
173
+ );
174
+ return $sortable_columns;
175
+ }
176
+
177
+ /* (non-PHPdoc)
178
+ * @see WP_List_Table::no_items()
179
+ */
180
+ function no_items() {
181
+ _e( 'No ' . $this->_args['singular'] . ' logged yet.' );
182
+ return;
183
+ }
184
+ }
185
+
186
+ ?>
js/.gitignore ADDED
File without changes
languages/.gitignore ADDED
File without changes
readme.txt ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Mail Logging ===
2
+ Contributors: No3x
3
+ Donate link:
4
+ Tags:
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: 3.9.1
9
+ Stable tag: 1.0
10
+
11
+ Logs each email sent by WordPress.
12
+
13
+ == Description ==
14
+
15
+ Logs each email sent by WordPress.
16
+
17
+ == Installation ==
18
+ 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.
19
+
20
+
21
+ == Frequently Asked Questions ==
22
+
23
+
24
+ == Screenshots ==
25
+ 1. The List
26
+ 2. The Settings
27
+
28
+
29
+ == Changelog ==
30
+
31
+ = 1.0 =
32
+ - Initial Revision
wp-mail-logging.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: WP Mail Logging
4
+ Plugin URI: http://wordpress.org/extend/plugins/wp-mail-logging/
5
+ Version: 0.1
6
+ Author: Christian Z&ouml;ller
7
+ Author URI: http://no3x.de/
8
+ Description: Logs each email sent by WordPress.
9
+ Text Domain: wpml
10
+ License: GPLv3
11
+ */
12
+
13
+ /*
14
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
15
+
16
+ This following part of this file is part of WordPress Plugin Template for WordPress.
17
+
18
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
19
+ it under the terms of the GNU General Public License as published by
20
+ the Free Software Foundation, either version 3 of the License, or
21
+ (at your option) any later version.
22
+
23
+ WordPress Plugin Template is distributed in the hope that it will be useful,
24
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
+ GNU General Public License for more details.
27
+
28
+ You should have received a copy of the GNU General Public License
29
+ along with Contact Form to Database Extension.
30
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
31
+ */
32
+
33
+ $WPML_minimalRequiredPhpVersion = '5.0';
34
+
35
+ /**
36
+ * Check the PHP version and give a useful error message if the user's version is less than the required version
37
+ * @return boolean true if version check passed. If false, triggers an error which WP will handle, by displaying
38
+ * an error message on the Admin page
39
+ */
40
+ function WPML_noticePhpVersionWrong() {
41
+ global $WPML_minimalRequiredPhpVersion;
42
+ echo '<div class="updated fade">' .
43
+ __('Error: plugin "WP Mail Logging" requires a newer version of PHP to be running.', 'wpml').
44
+ '<br/>' . __('Minimal version of PHP required: ', 'wpml') . '<strong>' . $WPML_minimalRequiredPhpVersion . '</strong>' .
45
+ '<br/>' . __('Your server\'s PHP version: ', 'wpml') . '<strong>' . phpversion() . '</strong>' .
46
+ '</div>';
47
+ }
48
+
49
+
50
+ function WPML_PhpVersionCheck() {
51
+ global $WPML_minimalRequiredPhpVersion;
52
+ if (version_compare(phpversion(), $WPML_minimalRequiredPhpVersion) < 0) {
53
+ add_action('admin_notices', 'WPML_noticePhpVersionWrong');
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+
59
+
60
+ /**
61
+ * Initialize internationalization (i18n) for this plugin.
62
+ * References:
63
+ * http://codex.wordpress.org/I18n_for_WordPress_Developers
64
+ * http://www.wdmac.com/how-to-create-a-po-language-translation#more-631
65
+ * @return void
66
+ */
67
+ function WPML_i18n_init() {
68
+ $pluginDir = dirname(plugin_basename(__FILE__));
69
+ load_plugin_textdomain('wpml', false, $pluginDir . '/languages/');
70
+ }
71
+
72
+
73
+ //////////////////////////////////
74
+ // Run initialization
75
+ /////////////////////////////////
76
+
77
+ // First initialize i18n
78
+ WPML_i18n_init();
79
+
80
+
81
+ // Next, run the version check.
82
+ // If it is successful, continue with initialization for this plugin
83
+ if (WPML_PhpVersionCheck()) {
84
+ // Only load and run the init function if we know PHP version can parse it
85
+ include_once('wp-mail-logging_init.php');
86
+ WPML_init(__FILE__);
87
+ }
wp-mail-logging_init.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ "WordPress Plugin Template" Copyright (C) 2013 Michael Simpson (email : michael.d.simpson@gmail.com)
4
+
5
+ This file is part of WordPress Plugin Template for WordPress.
6
+
7
+ WordPress Plugin Template is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ WordPress Plugin Template is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Contact Form to Database Extension.
19
+ If not, see http://www.gnu.org/licenses/gpl-3.0.html
20
+ */
21
+
22
+ function WPML_init($file) {
23
+
24
+ require_once('WPML_Plugin.php');
25
+ $aPlugin = new WPML_Plugin();
26
+
27
+ // Install the plugin
28
+ // NOTE: this file gets run each time you *activate* the plugin.
29
+ // So in WP when you "install" the plugin, all that does it dump its files in the plugin-templates directory
30
+ // but it does not call any of its code.
31
+ // So here, the plugin tracks whether or not it has run its install operation, and we ensure it is run only once
32
+ // on the first activation
33
+ if (!$aPlugin->isInstalled()) {
34
+ $aPlugin->install();
35
+ }
36
+ else {
37
+ // Perform any version-upgrade activities prior to activation (e.g. database changes)
38
+ $aPlugin->upgrade();
39
+ }
40
+
41
+ // Add callbacks to hooks
42
+ $aPlugin->addActionsAndFilters();
43
+
44
+ if (!$file) {
45
+ $file = __FILE__;
46
+ }
47
+ // Register the Plugin Activation Hook
48
+ register_activation_hook($file, array(&$aPlugin, 'activate'));
49
+
50
+
51
+ // Register the Plugin Deactivation Hook
52
+ register_deactivation_hook($file, array(&$aPlugin, 'deactivate'));
53
+ }
54
+
55
+ function _get_tablename( $name ) {
56
+ global $wpdb;
57
+ return $wpdb->prefix . 'wpml_' . $name;
58
+ }