WP Mail Logging - Version 1.9.8

Version Description

  • 2021-06-18 =
  • Changed ownership!
Download this release

Release Info

Developer tautvidas
Plugin Icon 128x128 WP Mail Logging
Version 1.9.8
Comparing to
See all releases

Code changes from version 1.9.7 to 1.9.8

Files changed (48) hide show
  1. CONTRIBUTING.md +61 -0
  2. Gruntfile.js +1 -1
  3. README.md +28 -0
  4. bin/install-wp-tests.sh +152 -0
  5. bin/release.sh +211 -0
  6. bin/run-wp-tests.sh +10 -0
  7. composer.json +40 -0
  8. composer.lock +186 -0
  9. languages/wp-mail-logging-de_DE.mo +0 -0
  10. languages/wp-mail-logging-zh_CN.mo +0 -0
  11. languages/wp-mail-logging.pot +26 -38
  12. package.json +3 -3
  13. phpunit.xml +37 -0
  14. readme.txt +7 -6
  15. src/WPML_OptionsManager.php +0 -91
  16. src/inc/redux/WPML_Redux_Framework_config.php +1 -1
  17. tests/helper/WPML_IntegrationTestCase.php +37 -0
  18. tests/helper/WPMailArrayBuilder.php +107 -0
  19. tests/phpunit/includes/bootstrap.php +47 -0
  20. tests/phpunit/integration/WPML_Hook_Remover_Test.php +39 -0
  21. tests/phpunit/integration/WPML_IntegrationTestCase_Test.php +14 -0
  22. tests/phpunit/integration/WPML_LogRotation_Test.php +149 -0
  23. tests/phpunit/integration/WPML_MailRenderer_AJAX_Handler_Test.php +162 -0
  24. tests/phpunit/integration/WPML_Plugin_Test.php +58 -0
  25. tests/phpunit/integration/test-core-settings.php +22 -0
  26. tests/phpunit/unit/WPML_Attachment_Test.php +110 -0
  27. tests/phpunit/unit/WPML_ColumnManager_Test.php +101 -0
  28. tests/phpunit/unit/WPML_Email_Log_List_Test.php +38 -0
  29. tests/phpunit/unit/WPML_Email_Resender_Test.php +107 -0
  30. tests/phpunit/unit/WPML_MailExtractor_Test.php +187 -0
  31. tests/phpunit/unit/WPML_MailRenderer_Test.php +228 -0
  32. tests/phpunit/unit/WPML_MessageSanitizer_Test.php +60 -0
  33. tests/phpunit/unit/WPML_Plugin_Test.php +36 -0
  34. tests/phpunit/unit/WPML_PrivacyController_Test.php +123 -0
  35. tests/phpunit/unit/WPML_Utils_Test.php +80 -0
  36. vendor/autoload.php +7 -0
  37. vendor/composer/ClassLoader.php +481 -0
  38. vendor/composer/InstalledVersions.php +337 -0
  39. vendor/composer/LICENSE +21 -0
  40. vendor/composer/autoload_classmap.php +10 -0
  41. vendor/composer/autoload_namespaces.php +9 -0
  42. vendor/composer/autoload_psr4.php +11 -0
  43. vendor/composer/autoload_real.php +57 -0
  44. vendor/composer/autoload_static.php +41 -0
  45. vendor/composer/installed.json +5 -0
  46. vendor/composer/installed.php +23 -0
  47. vendor/composer/platform_check.php +26 -0
  48. wp-mail-logging.php +5 -5
CONTRIBUTING.md ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Contribute To wp-mail-logging
2
+
3
+ Community made patches, localisations, bug reports and contributions are always welcome and are crucial to ensure the quality of wp-mail-logging.
4
+
5
+ When contributing please ensure you follow the guidelines below.
6
+
7
+ ## Environment
8
+ All dependencies are managed via [composer](http://getcomposer.org).
9
+ To install the plugin with development dependencies run:
10
+
11
+ ```composer install```
12
+
13
+ To get a working plugin installation run:
14
+
15
+ ```composer install --no-dev --no-scripts --prefer-dist``` (perfect for making a release by zipping the resulting environment)
16
+
17
+ To execute unit tests use the phpunit version downloaded by composer:
18
+
19
+ ```
20
+ cd /srv/www/wordpress-develop/public_html/src/wp-content/plugins/wp-mail-logging
21
+ vendor/phpunit/phpunit/phpunit
22
+ ```
23
+ If you use VVV make sure to be in the `/srv` directory instead of `/vagrant/www`. Otherwise there are errors like:
24
+ ```
25
+ PHP Fatal error: Cannot redeclare composerRequire553f4f88fd2d0873cede8d164ece3593() (previously declared in /vagrant/www/wordpress-develop/public_html/src/wp-content/plugins/wp-mail-logging/vendor/composer/autoload_real.php:63) in /srv/www/wordpress-develop/public_html/src/wp-content/plugins/wp-mail-logging/vendor/composer/autoload_real.php on line 70
26
+ ```
27
+ ## IDE
28
+
29
+ Please insure to be conform with the development guidelines.
30
+ * use tabs and not spaces. The tab indent size should be 4 for all wp-mail-logging code.
31
+
32
+ ## Getting Started
33
+
34
+ * Submit a ticket for your issue, assuming one does not already exist or fix an issue
35
+ * Raise it on our [Issue Tracker](https://github.com/kgjerstad/wp-mail-logging/issues)
36
+ * Clearly describe the issue including steps to reproduce the bug.
37
+ * Make sure you fill in the earliest version that you know has the issue as well as the version of WordPress you're using.
38
+
39
+ ## Making Changes
40
+
41
+ * Fork the repository on GitHub.
42
+ * Create a new branch like 'feature1'.
43
+ * Make the changes to your branch.
44
+ * Ensure you stick to the [WordPress Coding Standards](http://codex.wordpress.org/WordPress_Coding_Standards).
45
+ * Always leave the code cleaner than you found it. (The Boy Scouts Rule)
46
+ * Reference your issue (if present) and include a note about the fix in the commit message.
47
+ * If possible, and if applicable, please also add/update unit tests for your changes.
48
+ * Finally submit a pull request to the 'master' branch of the wp-mail-logging repository.
49
+
50
+ ## Code Documentation
51
+
52
+ * We ensure that every wp-mail-logging function is documented well and follows the standards set by phpDoc.
53
+ * Please make sure that every function is documented so that when we update our API Documentation things don't go awry!
54
+ * If you're adding/editing a function in a class, make sure to add `@access {private|public|protected}`.
55
+
56
+ At this point you're waiting on us to merge your pull request. We'll review all pull requests, and make suggestions and changes if necessary.
57
+
58
+ # Additional Resources
59
+ * [General GitHub Documentation](http://help.github.com/)
60
+ * [GitHub Pull Request documentation](http://help.github.com/send-pull-requests/)
61
+ * [PHPUnit Tests Guide](http://phpunit.de/manual/current/en/writing-tests-for-phpunit.html)
Gruntfile.js CHANGED
@@ -125,7 +125,7 @@ module.exports = function (grunt) {
125
  },
126
  'github-release': {
127
  options: {
128
- repository: 'No3x/wp-mail-logging', // Path to repository
129
  auth: grunt.file.readJSON('credentials.json'),
130
  release: {
131
  tag_name: 'release/<%= pkg.version %>',
125
  },
126
  'github-release': {
127
  options: {
128
+ repository: 'kgjerstad/wp-mail-logging', // Path to repository
129
  auth: grunt.file.readJSON('credentials.json'),
130
  release: {
131
  tag_name: 'release/<%= pkg.version %>',
README.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # :envelope: wp-mail-logging [![Build Status](https://travis-ci.org/kgjerstad/wp-mail-logging.png?branch=master)](https://travis-ci.org/kgjerstad/wp-mail-logging) [![codecov](https://codecov.io/gh/kgjerstad/wp-mail-logging/branch/master/graph/badge.svg)](https://codecov.io/gh/kgjerstad/wp-mail-logging) [![Join the chat at https://gitter.im/kgjerstad/wp-mail-logging](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kgjerstad/wp-mail-logging?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2
+ ## Welcome ##
3
+ wp-mail-logging is a WordPress plugin that logs each mail sent by WordPress.
4
+
5
+ ## Installation ##
6
+
7
+ Install the plugin from the official [Wordpress Plugin Repository](https://wordpress.org/plugins/wp-mail-logging/).
8
+
9
+ ## Bugs ##
10
+ If you find an issue, let us know in the [Tracker](https://github.com/kgjerstad/wp-mail-logging/issues?state=open)!
11
+
12
+ ## Contributions ##
13
+ Anyone is welcome to contribute to wp-mail-logging. Please create an pull request for each change you want to contribute.
14
+
15
+ There are various ways you can contribute:
16
+
17
+ ### As Developer ###
18
+ 1. You can clone the git repository: 'https://github.com/kgjerstad/wp-mail-logging.git'
19
+ 2. Or download it directly as a ZIP file: 'https://github.com/kgjerstad/wp-mail-logging/archive/master.zip'
20
+
21
+ This will download the latest developer copy of wp-mail-logging.
22
+
23
+ Please read [Contributing](https://github.com/kgjerstad/wp-mail-logging/blob/master/CONTRIBUTING.md) if you plan to contribute.
24
+
25
+ ### As Assistant ###
26
+ 1. Raise an [Issue](https://github.com/kgjerstad/wp-mail-logging/issues?state=open)
27
+ 3. Translate wp-mail-logging into different languages
28
+ 4. Provide feedback and suggestions on [enhancements](https://github.com/kgjerstad/wp-mail-logging/issues?direction=desc&labels=Enhancement%2Cenhancement&page=1&sort=created&state=open)
bin/install-wp-tests.sh ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ if [ $# -lt 3 ]; then
4
+ echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
5
+ exit 1
6
+ fi
7
+
8
+ DB_NAME=$1
9
+ DB_USER=$2
10
+ DB_PASS=$3
11
+ DB_HOST=${4-localhost}
12
+ WP_VERSION=${5-latest}
13
+ SKIP_DB_CREATE=${6-false}
14
+
15
+ TMPDIR=${TMPDIR-/tmp}
16
+ TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
17
+ WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
18
+ WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
19
+
20
+ download() {
21
+ if [ `which curl` ]; then
22
+ curl -s "$1" > "$2";
23
+ elif [ `which wget` ]; then
24
+ wget -nv -O "$2" "$1"
25
+ fi
26
+ }
27
+
28
+ if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
29
+ WP_TESTS_TAG="branches/$WP_VERSION"
30
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
31
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
32
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
33
+ WP_TESTS_TAG="tags/${WP_VERSION%??}"
34
+ else
35
+ WP_TESTS_TAG="tags/$WP_VERSION"
36
+ fi
37
+ elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
38
+ WP_TESTS_TAG="trunk"
39
+ else
40
+ # http serves a single offer, whereas https serves multiple. we only want one
41
+ download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
42
+ grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
43
+ LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
44
+ if [[ -z "$LATEST_VERSION" ]]; then
45
+ echo "Latest WordPress version could not be found"
46
+ exit 1
47
+ fi
48
+ WP_TESTS_TAG="tags/$LATEST_VERSION"
49
+ fi
50
+
51
+ set -ex
52
+
53
+ install_wp() {
54
+
55
+ if [ -d $WP_CORE_DIR ]; then
56
+ return;
57
+ fi
58
+
59
+ mkdir -p $WP_CORE_DIR
60
+
61
+ if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
62
+ mkdir -p $TMPDIR/wordpress-nightly
63
+ download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
64
+ unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
65
+ mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
66
+ else
67
+ if [ $WP_VERSION == 'latest' ]; then
68
+ local ARCHIVE_NAME='latest'
69
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
70
+ # https serves multiple offers, whereas http serves single.
71
+ download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
72
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
73
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
74
+ LATEST_VERSION=${WP_VERSION%??}
75
+ else
76
+ # otherwise, scan the releases and get the most up to date minor version of the major release
77
+ local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
78
+ LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
79
+ fi
80
+ if [[ -z "$LATEST_VERSION" ]]; then
81
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
82
+ else
83
+ local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
84
+ fi
85
+ else
86
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
87
+ fi
88
+ download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
89
+ tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
90
+ fi
91
+
92
+ download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
93
+ }
94
+
95
+ install_test_suite() {
96
+ # portable in-place argument for both GNU sed and Mac OSX sed
97
+ if [[ $(uname -s) == 'Darwin' ]]; then
98
+ local ioption='-i .bak'
99
+ else
100
+ local ioption='-i'
101
+ fi
102
+
103
+ # set up testing suite if it doesn't yet exist
104
+ if [ ! -d $WP_TESTS_DIR ]; then
105
+ # set up testing suite
106
+ mkdir -p $WP_TESTS_DIR
107
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
108
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
109
+ fi
110
+
111
+ if [ ! -f wp-tests-config.php ]; then
112
+ download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
113
+ # remove all forward slashes in the end
114
+ WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
115
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
116
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
117
+ sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
118
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
119
+ sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
120
+ fi
121
+
122
+ }
123
+
124
+ install_db() {
125
+
126
+ if [ ${SKIP_DB_CREATE} = "true" ]; then
127
+ return 0
128
+ fi
129
+
130
+ # parse DB_HOST for port or socket references
131
+ local PARTS=(${DB_HOST//\:/ })
132
+ local DB_HOSTNAME=${PARTS[0]};
133
+ local DB_SOCK_OR_PORT=${PARTS[1]};
134
+ local EXTRA=""
135
+
136
+ if ! [ -z $DB_HOSTNAME ] ; then
137
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
138
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
139
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
140
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
141
+ elif ! [ -z $DB_HOSTNAME ] ; then
142
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
143
+ fi
144
+ fi
145
+
146
+ # create database
147
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
148
+ }
149
+
150
+ install_wp
151
+ install_test_suite
152
+ install_db
bin/release.sh ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #! /bin/bash
2
+ # See https://github.com/GaryJones/wordpress-plugin-git-flow-svn-deploy for instructions and credits.
3
+
4
+ echo
5
+ echo "WordPress Plugin Git-Flow SVN Deploy v1.0.0-dev2"
6
+ echo
7
+ echo "Step 1. Let's collect some information first."
8
+ echo
9
+ echo "Default values are in brackets - just hit enter to accept them."
10
+ echo
11
+
12
+ # Get some user input
13
+ # Can't use the -i flag for read, since that doesn't work for bash 3
14
+ #printf "1a) WordPress Repo Plugin Slug e.g. my-awesome-plugin: "
15
+ #read -e PLUGINSLUG
16
+ #echo
17
+
18
+ # Set up some default values. Feel free to change these in your own script
19
+ CURRENTDIR=`pwd`
20
+ PLUGINSLUG="wp-mail-logging"
21
+ default_svnpath="/tmp/$PLUGINSLUG-release"
22
+ default_svnurl="http://plugins.svn.wordpress.org/$PLUGINSLUG"
23
+ default_svnuser="wysija"
24
+ default_plugindir="$CURRENTDIR/../../$PLUGINSLUG"
25
+ default_mainfile="$PLUGINSLUG.php"
26
+
27
+ echo "1b) Path to a local directory where a temporary SVN checkout can be made."
28
+ printf "No trailing slash and don't add trunk ($default_svnpath): "
29
+ read -e input
30
+ input="${input%/}" # Strip trailing slash
31
+ SVNPATH="${input:-$default_svnpath}" # Populate with default if empty
32
+ echo
33
+
34
+ echo "1c) Remote SVN repo on WordPress.org. No trailing slash."
35
+ printf "($default_svnurl): "
36
+ read -e input
37
+ input="${input%/}" # Strip trailing slash
38
+ SVNURL="${input:-$default_svnurl}" # Populate with default if empty
39
+ echo
40
+
41
+ printf "1d) Your WordPress repo SVN username ($default_svnuser): "
42
+ read -e input
43
+ SVNUSER="${input:-$default_svnuser}" # Populate with default if empty
44
+ echo
45
+
46
+ echo "1e) Your local plugin root directory, the Git repo. No trailing slash."
47
+ printf "($default_plugindir): "
48
+ read -e input
49
+ input="${input%/}" # Strip trailing slash
50
+ PLUGINDIR="${input:-$default_plugindir}" # Populate with default if empty
51
+ echo
52
+
53
+ printf "1f) Name of the main plugin file ($default_mainfile): "
54
+ read -e input
55
+ MAINFILE="${input:-$default_mainfile}" # Populate with default if empty
56
+ echo
57
+
58
+ echo "That's all of the data collected."
59
+ echo
60
+ echo "Slug: $PLUGINSLUG"
61
+ echo "Temp checkout path: $SVNPATH"
62
+ echo "Remote SVN repo: $SVNURL"
63
+ echo "SVN username: $SVNUSER"
64
+ echo "Plugin directory: $PLUGINDIR"
65
+ echo "Main file: $MAINFILE"
66
+ echo
67
+
68
+ printf "OK to proceed (y|n)? "
69
+ read -e input
70
+ PROCEED="${input:-y}"
71
+ echo
72
+
73
+ # Allow user cancellation
74
+ if [ "$PROCEED" != "y" ]; then echo "Aborting..."; exit 1; fi
75
+
76
+ # git config
77
+ GITPATH="$PLUGINDIR/" # this file should be in the base of your git repository
78
+
79
+ # Let's begin...
80
+ echo ".........................................."
81
+ echo
82
+ echo "Preparing to deploy WordPress plugin"
83
+ echo
84
+ echo ".........................................."
85
+ echo
86
+
87
+ # Check version in readme.txt is the same as plugin file after translating both to unix line breaks to work around grep's failure to identify mac line breaks
88
+ NEWVERSION1=`grep "^Stable tag:" $GITPATH/readme.txt | awk -F' ' '{print $NF}' | tr -d '\r'`
89
+ echo "readme.txt version: $NEWVERSION1"
90
+ NEWVERSION2=`grep "Version:" $GITPATH/$MAINFILE | awk -F' ' '{print $NF}' | tr -d '\r'`
91
+ echo "$MAINFILE version: $NEWVERSION2"
92
+
93
+ if [ "$NEWVERSION1" != "$NEWVERSION2" ]; then echo "Version in readme.txt & $MAINFILE don't match. Exiting...."; exit 1; fi
94
+
95
+ echo "Versions match in readme.txt and $MAINFILE. Let's proceed..."
96
+
97
+ # GaryJ: Ignore check for git tag, as git flow release finish creates this.
98
+ #if git show-ref --tags --quiet --verify -- "refs/tags/$NEWVERSION1"
99
+ # then
100
+ # echo "Version $NEWVERSION1 already exists as git tag. Exiting....";
101
+ # exit 1;
102
+ # else
103
+ # echo "Git version does not exist. Let's proceed..."
104
+ #fi
105
+
106
+ echo "Changing to $GITPATH"
107
+ cd $GITPATH
108
+ # GaryJ: Commit message variable not needed . Hard coded for SVN trunk commit for consistency.
109
+ echo -e "Enter a commit message for this new version: \c"
110
+ read COMMITMSG
111
+ # GaryJ: git flow release finish already covers this commit.
112
+ git commit -am "$COMMITMSG"
113
+
114
+ # GaryJ: git flow release finish already covers this tag creation.
115
+ echo "Tagging new version in git"
116
+ git tag -a "release/$NEWVERSION1" -m "Tagging version $NEWVERSION1"
117
+
118
+ echo "Pushing git master to origin, with tags"
119
+ git push origin master
120
+ git push origin master --tags
121
+
122
+ echo
123
+ echo "Creating local copy of SVN repo trunk ..."
124
+ svn checkout $SVNURL $SVNPATH --depth immediates
125
+ svn update --quiet $SVNPATH/trunk --set-depth infinity
126
+
127
+ echo "Ignoring GitHub specific files"
128
+ svn propset svn:ignore "README.md
129
+ CONTRIBUTING.md
130
+ README.md
131
+ phpunit.xml
132
+ Thumbs.db
133
+ .travis.yml
134
+ .git
135
+ .gitignore
136
+ tests
137
+ bin
138
+ composer.json
139
+ composer.lock" "$SVNPATH/trunk/"
140
+
141
+ echo "Exporting the HEAD of master from git to the trunk of SVN"
142
+ git checkout-index -a -f --prefix=$SVNPATH/trunk/
143
+
144
+ # If submodule exist, recursively check out their indexes
145
+ if [ -f ".gitmodules" ]
146
+ then
147
+ echo "Exporting the HEAD of each submodule from git to the trunk of SVN"
148
+ git submodule init
149
+ git submodule update
150
+ git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
151
+ while read path_key path
152
+ do
153
+ #url_key=$(echo $path_key | sed 's/\.path/.url/')
154
+ #url=$(git config -f .gitmodules --get "$url_key")
155
+ #git submodule add $url $path
156
+ echo "This is the submodule path: $path"
157
+ echo "The following line is the command to checkout the submodule."
158
+ echo "git submodule foreach --recursive 'git checkout-index -a -f --prefix=$SVNPATH/trunk/$path/'"
159
+ git submodule foreach --recursive 'git checkout-index -a -f --prefix=$SVNPATH/trunk/$path/'
160
+ done
161
+ fi
162
+
163
+ # Support for the /assets folder on the .org repo.
164
+ echo "Moving assets"
165
+ # Make the directory if it doesn't already exist
166
+ mkdir -p $SVNPATH/assets/
167
+ mv $SVNPATH/trunk/assets/* $SVNPATH/assets/
168
+ svn add --force $SVNPATH/assets/
169
+ svn delete --force $SVNPATH/trunk/assets
170
+
171
+ printf "OK to proceed? This will actually release $PLUGINSLUG $NEWVERSION1 (y|n)? "
172
+ read -e input
173
+ PROCEED="${input:-y}"
174
+ echo
175
+
176
+ # Allow user cancellation
177
+ if [ "$PROCEED" != "y" ]; then echo "Aborting..."; exit 1; fi
178
+
179
+ echo "Changing directory to SVN and committing to trunk"
180
+ cd $SVNPATH/trunk/
181
+ # Delete all files that should not now be added.
182
+ svn status | grep -v "^.[ \t]*\..*" | grep "^\!" | awk '{print $2}' | xargs svn del
183
+ # Add all new files that are not set to be ignored
184
+ svn status | grep -v "^.[ \t]*\..*" | grep "^?" | awk '{print $2}' | xargs svn add
185
+ svn commit --username=$SVNUSER -m "Preparing for $NEWVERSION1 release"
186
+
187
+ echo "Updating WordPress plugin repo assets and committing"
188
+ cd $SVNPATH/assets/
189
+ # Delete all new files that are not set to be ignored
190
+ svn status | grep -v "^.[ \t]*\..*" | grep "^\!" | awk '{print $2}' | xargs svn del
191
+ # Add all new files that are not set to be ignored
192
+ svn status | grep -v "^.[ \t]*\..*" | grep "^?" | awk '{print $2}' | xargs svn add
193
+ svn update --accept mine-full $SVNPATH/assets/*
194
+ svn commit --username=$SVNUSER -m "Updating assets"
195
+
196
+ echo "Creating new SVN tag and committing it"
197
+ cd $SVNPATH
198
+ svn update --quiet $SVNPATH/tags/$NEWVERSION1
199
+ svn copy --quiet trunk/ tags/$NEWVERSION1/
200
+ # Remove assets and trunk directories from tag directory
201
+ svn delete --force --quiet $SVNPATH/tags/$NEWVERSION1/assets
202
+ svn delete --force --quiet $SVNPATH/tags/$NEWVERSION1/trunk
203
+ cd $SVNPATH/tags/$NEWVERSION1
204
+ svn commit --username=$SVNUSER -m "Tagging version $NEWVERSION1"
205
+
206
+ echo "Removing temporary directory $SVNPATH"
207
+ cd $SVNPATH
208
+ cd ..
209
+ rm -fr $SVNPATH/
210
+
211
+ echo "*** FIN ***"
bin/run-wp-tests.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ #! /bin/sh
2
+
3
+ # Simpler test script for running tests iteratively.
4
+ # You must have executd first install-wp-tests.
5
+
6
+ export WP_TESTS_DIR=/tmp/wordpress-testing
7
+ export WP_DIR=/tmp/wordpress
8
+ SCRIPTS_DIR=`dirname $0`
9
+
10
+ bash $SCRIPTS_DIR/install-wp-tests.sh wordpress_test wp 'wp' localhost
composer.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "no3x/wp-mail-logging",
3
+ "description": "WordPress plugin that logs each email sent by WordPress.",
4
+ "type": "wordpress-plugin",
5
+ "keywords": ["mail", "email", "log", "logging", "debug", "list", "store", "collect", "view"],
6
+ "homepage": "https://github.com/kgjerstad/wp-mail-logging",
7
+ "require": {
8
+ "php": ">=5.4"
9
+ },
10
+ "require-dev": {
11
+ "bocharsky-bw/arrayzy": "v0.1.1",
12
+ "mockery/mockery": "^0.9.9"
13
+ },
14
+ "license": "GPL-2.0",
15
+ "authors": [
16
+ {
17
+ "name": "No3x",
18
+ "email": "no3x@no3x.de",
19
+ "homepage": "http://no3x.de",
20
+ "role": "Developer"
21
+ }
22
+ ],
23
+ "support": {
24
+ "issues": "https://github.com/kgjerstad/wp-mail-logging/issues",
25
+ "source": "https://github.com/kgjerstad/wp-mail-logging/releases"
26
+
27
+ },
28
+ "extra": {
29
+ "installer-paths": {
30
+ "vendor/redux-framework/redux-framework": ["redux-framework/redux-framework"]
31
+ }
32
+ },
33
+ "minimum-stability": "dev",
34
+ "autoload": {
35
+ "psr-4": {
36
+ "No3x\\WPML\\Tests\\": "tests/phpunit/tests",
37
+ "No3x\\WPML\\Tests\\Helper\\": "tests/helper"
38
+ }
39
+ }
40
+ }
composer.lock ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "5112a4988dade121d66ea3f9730fb3d9",
8
+ "packages": [],
9
+ "packages-dev": [
10
+ {
11
+ "name": "bocharsky-bw/arrayzy",
12
+ "version": "v0.1.1",
13
+ "source": {
14
+ "type": "git",
15
+ "url": "https://github.com/bocharsky-bw/Arrayzy.git",
16
+ "reference": "364cda2180574868c37ed84fb5734fcf4fb6853a"
17
+ },
18
+ "dist": {
19
+ "type": "zip",
20
+ "url": "https://api.github.com/repos/bocharsky-bw/Arrayzy/zipball/364cda2180574868c37ed84fb5734fcf4fb6853a",
21
+ "reference": "364cda2180574868c37ed84fb5734fcf4fb6853a",
22
+ "shasum": ""
23
+ },
24
+ "require": {
25
+ "ext-json": "*",
26
+ "php": ">=5.4"
27
+ },
28
+ "require-dev": {
29
+ "phpunit/phpunit": "~4.5"
30
+ },
31
+ "type": "library",
32
+ "autoload": {
33
+ "psr-4": {
34
+ "Arrayzy\\": "src/"
35
+ }
36
+ },
37
+ "notification-url": "https://packagist.org/downloads/",
38
+ "license": [
39
+ "MIT"
40
+ ],
41
+ "authors": [
42
+ {
43
+ "name": "Bocharsky Victor",
44
+ "email": "bocharsky.bw@gmail.com"
45
+ }
46
+ ],
47
+ "description": "A PHP array easy manipulation library in OOP way",
48
+ "homepage": "https://github.com/bocharsky-bw/Arrayzy",
49
+ "keywords": [
50
+ "OOP",
51
+ "array",
52
+ "arrayzy",
53
+ "helper",
54
+ "manipulation",
55
+ "utility",
56
+ "wrapper"
57
+ ],
58
+ "time": "2015-07-12T19:31:25+00:00"
59
+ },
60
+ {
61
+ "name": "hamcrest/hamcrest-php",
62
+ "version": "1.2.x-dev",
63
+ "source": {
64
+ "type": "git",
65
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
66
+ "reference": "26968e9810ff0d1aa48f6d67a862559d92a51884"
67
+ },
68
+ "dist": {
69
+ "type": "zip",
70
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/26968e9810ff0d1aa48f6d67a862559d92a51884",
71
+ "reference": "26968e9810ff0d1aa48f6d67a862559d92a51884",
72
+ "shasum": ""
73
+ },
74
+ "require": {
75
+ "php": "^5.3|^7.0"
76
+ },
77
+ "replace": {
78
+ "cordoval/hamcrest-php": "*",
79
+ "davedevelopment/hamcrest-php": "*",
80
+ "kodova/hamcrest-php": "*"
81
+ },
82
+ "require-dev": {
83
+ "phpunit/php-file-iterator": "1.3.3",
84
+ "phpunit/phpunit": "~4.0",
85
+ "satooshi/php-coveralls": "^1.0"
86
+ },
87
+ "type": "library",
88
+ "extra": {
89
+ "branch-alias": {
90
+ "dev-master": "1.2-dev"
91
+ }
92
+ },
93
+ "autoload": {
94
+ "classmap": [
95
+ "hamcrest"
96
+ ],
97
+ "files": [
98
+ "hamcrest/Hamcrest.php"
99
+ ]
100
+ },
101
+ "notification-url": "https://packagist.org/downloads/",
102
+ "license": [
103
+ "BSD-3-Clause"
104
+ ],
105
+ "description": "This is the PHP port of Hamcrest Matchers",
106
+ "keywords": [
107
+ "test"
108
+ ],
109
+ "time": "2018-02-19T09:04:07+00:00"
110
+ },
111
+ {
112
+ "name": "mockery/mockery",
113
+ "version": "0.9.x-dev",
114
+ "source": {
115
+ "type": "git",
116
+ "url": "https://github.com/mockery/mockery.git",
117
+ "reference": "e206aa48a851b95a91eeb61bfcce536d51b7cd96"
118
+ },
119
+ "dist": {
120
+ "type": "zip",
121
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/e206aa48a851b95a91eeb61bfcce536d51b7cd96",
122
+ "reference": "e206aa48a851b95a91eeb61bfcce536d51b7cd96",
123
+ "shasum": ""
124
+ },
125
+ "require": {
126
+ "hamcrest/hamcrest-php": "~1.1",
127
+ "lib-pcre": ">=7.0",
128
+ "php": ">=5.3.2"
129
+ },
130
+ "require-dev": {
131
+ "phpunit/phpunit": "~4.0"
132
+ },
133
+ "type": "library",
134
+ "extra": {
135
+ "branch-alias": {
136
+ "dev-master": "0.9.x-dev"
137
+ }
138
+ },
139
+ "autoload": {
140
+ "psr-0": {
141
+ "Mockery": "library/"
142
+ }
143
+ },
144
+ "notification-url": "https://packagist.org/downloads/",
145
+ "license": [
146
+ "BSD-3-Clause"
147
+ ],
148
+ "authors": [
149
+ {
150
+ "name": "Pádraic Brady",
151
+ "email": "padraic.brady@gmail.com",
152
+ "homepage": "http://blog.astrumfutura.com"
153
+ },
154
+ {
155
+ "name": "Dave Marshall",
156
+ "email": "dave.marshall@atstsolutions.co.uk",
157
+ "homepage": "http://davedevelopment.co.uk"
158
+ }
159
+ ],
160
+ "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
161
+ "homepage": "http://github.com/padraic/mockery",
162
+ "keywords": [
163
+ "BDD",
164
+ "TDD",
165
+ "library",
166
+ "mock",
167
+ "mock objects",
168
+ "mockery",
169
+ "stub",
170
+ "test",
171
+ "test double",
172
+ "testing"
173
+ ],
174
+ "time": "2018-02-02T16:28:49+00:00"
175
+ }
176
+ ],
177
+ "aliases": [],
178
+ "minimum-stability": "dev",
179
+ "stability-flags": [],
180
+ "prefer-stable": false,
181
+ "prefer-lowest": false,
182
+ "platform": {
183
+ "php": ">=5.4"
184
+ },
185
+ "platform-dev": []
186
+ }
languages/wp-mail-logging-de_DE.mo CHANGED
Binary file
languages/wp-mail-logging-zh_CN.mo CHANGED
Binary file
languages/wp-mail-logging.pot CHANGED
@@ -1,14 +1,14 @@
1
- # Copyright (C) 2019 Christian Z&ouml;ller
2
  # This file is distributed under the GPLv3.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: WP Mail Logging 1.9.7\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-logging\n"
7
- "POT-Creation-Date: 2019-04-18 13:01:12+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: 2019-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"
@@ -40,7 +40,7 @@ msgstr ""
40
  msgid "Subject"
41
  msgstr ""
42
 
43
- #: src/Renderer/WPML_ColumnManager.php:36 src/WPML_OptionsManager.php:504
44
  msgid "Message"
45
  msgstr ""
46
 
@@ -68,7 +68,7 @@ msgstr ""
68
  msgid "No email found."
69
  msgstr ""
70
 
71
- #: src/WPML_LifeCycle.php:205
72
  msgid "Settings"
73
  msgstr ""
74
 
@@ -76,76 +76,64 @@ msgstr ""
76
  msgid "WP Mail Log"
77
  msgstr ""
78
 
79
- #: src/WPML_OptionsManager.php:340 src/WPML_OptionsManager.php:341
80
- #: src/WPML_OptionsManager.php:352
81
  msgid "About"
82
  msgstr ""
83
 
84
- #: src/WPML_OptionsManager.php:429
85
- msgid "About Plugin"
86
- msgstr ""
87
-
88
- #: src/WPML_OptionsManager.php:438
89
- msgid "More information"
90
- msgstr ""
91
-
92
- #: src/WPML_OptionsManager.php:439
93
- msgid "Plugin Homepage/support"
94
- msgstr ""
95
-
96
- #: src/WPML_OptionsManager.php:440
97
- msgid "Plugin author's blog"
98
- msgstr ""
99
-
100
- #: src/WPML_OptionsManager.php:447
101
  msgid "Entries per page"
102
  msgstr ""
103
 
104
- #: src/WPML_OptionsManager.php:469
105
  msgid "You do not have sufficient permissions to access this page."
106
  msgstr ""
107
 
108
- #: src/WPML_OptionsManager.php:478
109
  msgid "Log"
110
  msgstr ""
111
 
112
- #: src/WPML_OptionsManager.php:521
 
 
 
 
113
  msgid "Close"
114
  msgstr ""
115
 
116
- #: src/WPML_OptionsManager.php:535
117
  msgid "Search"
118
  msgstr ""
119
 
120
- #: src/WPML_OptionsManager.php:560
121
  msgid "true"
122
  msgstr ""
123
 
124
- #: src/WPML_OptionsManager.php:562
125
  msgid "false"
126
  msgstr ""
127
 
128
- #: src/WPML_OptionsManager.php:565
129
  msgid "Administrator"
130
  msgstr ""
131
 
132
- #: src/WPML_OptionsManager.php:567
133
  msgid "Editor"
134
  msgstr ""
135
 
136
- #: src/WPML_OptionsManager.php:569
137
  msgid "Author"
138
  msgstr ""
139
 
140
- #: src/WPML_OptionsManager.php:571
141
  msgid "Contributor"
142
  msgstr ""
143
 
144
- #: src/WPML_OptionsManager.php:573
145
  msgid "Subscriber"
146
  msgstr ""
147
 
148
- #: src/WPML_OptionsManager.php:575
149
  msgid "Anyone"
150
  msgstr ""
151
 
@@ -346,4 +334,4 @@ msgstr ""
346
  #: src/inc/class-wp-list-table.php:573
347
  msgctxt "paging"
348
  msgid "%1$s of %2$s"
349
- msgstr ""
1
+ # Copyright (C) 2021 Wysija
2
  # This file is distributed under the GPLv3.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: WP Mail Logging 1.9.8\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-logging\n"
7
+ "POT-Creation-Date: 2021-07-03 13:37:23+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: 2021-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"
40
  msgid "Subject"
41
  msgstr ""
42
 
43
+ #: src/Renderer/WPML_ColumnManager.php:36 src/WPML_OptionsManager.php:503
44
  msgid "Message"
45
  msgstr ""
46
 
68
  msgid "No email found."
69
  msgstr ""
70
 
71
+ #: src/WPML_LifeCycle.php:205 src/WPML_OptionsManager.php:444
72
  msgid "Settings"
73
  msgstr ""
74
 
76
  msgid "WP Mail Log"
77
  msgstr ""
78
 
79
+ #: src/WPML_OptionsManager.php:339 src/WPML_OptionsManager.php:340
80
+ #: src/WPML_OptionsManager.php:360
81
  msgid "About"
82
  msgstr ""
83
 
84
+ #: src/WPML_OptionsManager.php:349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  msgid "Entries per page"
86
  msgstr ""
87
 
88
+ #: src/WPML_OptionsManager.php:415 src/WPML_OptionsManager.php:478
89
  msgid "You do not have sufficient permissions to access this page."
90
  msgstr ""
91
 
92
+ #: src/WPML_OptionsManager.php:426
93
  msgid "Log"
94
  msgstr ""
95
 
96
+ #: src/WPML_OptionsManager.php:440
97
+ msgid "Email log"
98
+ msgstr ""
99
+
100
+ #: src/WPML_OptionsManager.php:520
101
  msgid "Close"
102
  msgstr ""
103
 
104
+ #: src/WPML_OptionsManager.php:534
105
  msgid "Search"
106
  msgstr ""
107
 
108
+ #: src/WPML_OptionsManager.php:558
109
  msgid "true"
110
  msgstr ""
111
 
112
+ #: src/WPML_OptionsManager.php:560
113
  msgid "false"
114
  msgstr ""
115
 
116
+ #: src/WPML_OptionsManager.php:563
117
  msgid "Administrator"
118
  msgstr ""
119
 
120
+ #: src/WPML_OptionsManager.php:565
121
  msgid "Editor"
122
  msgstr ""
123
 
124
+ #: src/WPML_OptionsManager.php:567
125
  msgid "Author"
126
  msgstr ""
127
 
128
+ #: src/WPML_OptionsManager.php:569
129
  msgid "Contributor"
130
  msgstr ""
131
 
132
+ #: src/WPML_OptionsManager.php:571
133
  msgid "Subscriber"
134
  msgstr ""
135
 
136
+ #: src/WPML_OptionsManager.php:573
137
  msgid "Anyone"
138
  msgstr ""
139
 
334
  #: src/inc/class-wp-list-table.php:573
335
  msgctxt "paging"
336
  msgid "%1$s of %2$s"
337
+ msgstr ""
package.json CHANGED
@@ -1,15 +1,15 @@
1
  {
2
  "name": "wp-mail-logging",
3
- "version": "1.9.7",
4
  "description": "WordPress plugin that logs each email sent by WordPress.",
5
  "repository": {
6
  "type": "git",
7
- "url": "https://github.com/mailpoet/wp-mail-logging"
8
  },
9
  "author": "No3x",
10
  "license": "GPL-2.0",
11
  "bugs": {
12
- "url": "https://github.com/mailpoet/wp-mail-logging/issues"
13
  },
14
  "devDependencies": {
15
  "gitignore-to-glob": "^0.1.2",
1
  {
2
  "name": "wp-mail-logging",
3
+ "version": "1.9.8",
4
  "description": "WordPress plugin that logs each email sent by WordPress.",
5
  "repository": {
6
  "type": "git",
7
+ "url": "https://github.com/kgjerstad/wp-mail-logging"
8
  },
9
  "author": "No3x",
10
  "license": "GPL-2.0",
11
  "bugs": {
12
+ "url": "https://github.com/kgjerstad/wp-mail-logging/issues"
13
  },
14
  "devDependencies": {
15
  "gitignore-to-glob": "^0.1.2",
phpunit.xml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <phpunit
2
+ bootstrap="tests/phpunit/includes/bootstrap.php"
3
+ backupGlobals="false"
4
+ colors="true"
5
+ convertErrorsToExceptions="true"
6
+ convertNoticesToExceptions="true"
7
+ convertWarningsToExceptions="true"
8
+ >
9
+ <php>
10
+ <!-- Dirty hack to convince phpunit to use the right includes -->
11
+ <env name="WP_TESTS_DIR" value="/srv/www/wordpress-develop/public_html/tests/phpunit/" force="true" />
12
+ </php>
13
+ <testsuites>
14
+ <testsuite>
15
+ <directory suffix=".php">tests/phpunit</directory>
16
+ </testsuite>
17
+ </testsuites>
18
+ <filter>
19
+ <!--<blacklist>
20
+ <directory>.</directory>
21
+ <directory>tests</directory>
22
+ <directory>vendor</directory>
23
+ <directory>lib</directory>
24
+ <directory>inc</directory>
25
+ </blacklist>-->
26
+ <whitelist>
27
+ <directory suffix=".php">src</directory>
28
+ <exclude>
29
+ <directory>tests</directory>
30
+ </exclude>
31
+ </whitelist>
32
+ </filter>
33
+ <logging>
34
+ <log type="coverage-clover" target="build/logs/clover.xml"/>
35
+ <log type="coverage-text" target="php://stdout" />
36
+ </logging>
37
+ </phpunit>
readme.txt CHANGED
@@ -1,11 +1,11 @@
1
- === WP Mail Logging by MailPoet ===
2
- Contributors: MailPoet, No3x, tripflex
3
  Tags: mail, email, log, logging, email log, debug, smtp, spam, deliverability
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
6
  Requires at least: 5.0
7
  Tested up to: 5.5
8
- Stable tag: 1.9.7
9
 
10
  Log every single email sent by WordPress. Zero configuration. Entirely free.
11
 
@@ -99,8 +99,6 @@ We recommend in this case to send your WordPress email with a service provider,
99
 
100
  ### Credits
101
 
102
- This plugin is maintained by <a href="https://www.mailpoet.com/">MailPoet, the most popular email plugin for WordPress</a>.
103
-
104
  The plugin was created and launched in 2014 by <a href="https://no3x.de/">Christian Zöller</a>.
105
 
106
  == Frequently Asked Questions ==
@@ -109,7 +107,7 @@ We answer in the forums, but only occasionally.
109
  = Where can I report a bug? =
110
  You can do so in the support forums. We'll be happy to review them.
111
  = Can I submit changes to the plugin? =
112
- Yes, directly on <a href="https://github.com/mailpoet/wp-mail-logging" rel="nofollow">GitHub</a>.
113
 
114
  == Screenshots ==
115
  1. The List
@@ -118,6 +116,9 @@ Yes, directly on <a href="https://github.com/mailpoet/wp-mail-logging" rel="nofo
118
 
119
  == Changelog ==
120
 
 
 
 
121
  = 1.9.7 - 2020-09-02 =
122
  - Added: wpml_banner_display filter to hide MailPoet banner;
123
  - Updated: support for WordPress 5.5.
1
+ === WP Mail Logging ===
2
+ Contributors: Wysija, MailPoet, No3x, tripflex
3
  Tags: mail, email, log, logging, email log, debug, smtp, spam, deliverability
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
6
  Requires at least: 5.0
7
  Tested up to: 5.5
8
+ Stable tag: 1.9.8
9
 
10
  Log every single email sent by WordPress. Zero configuration. Entirely free.
11
 
99
 
100
  ### Credits
101
 
 
 
102
  The plugin was created and launched in 2014 by <a href="https://no3x.de/">Christian Zöller</a>.
103
 
104
  == Frequently Asked Questions ==
107
  = Where can I report a bug? =
108
  You can do so in the support forums. We'll be happy to review them.
109
  = Can I submit changes to the plugin? =
110
+ Yes, directly on <a href="https://github.com/kgjerstad/wp-mail-logging/" rel="nofollow">GitHub</a>.
111
 
112
  == Screenshots ==
113
  1. The List
116
 
117
  == Changelog ==
118
 
119
+ = 1.9.8 - 2021-06-18 =
120
+ - Changed ownership!
121
+
122
  = 1.9.7 - 2020-09-02 =
123
  - Added: wpml_banner_display filter to hide MailPoet banner;
124
  - Updated: support for WordPress 5.5.
src/WPML_OptionsManager.php CHANGED
@@ -415,8 +415,6 @@ class WPML_OptionsManager {
415
  wp_die(__('You do not have sufficient permissions to access this page.', 'wp-mail-logging'));
416
  }
417
 
418
- $this->redirectToFreePlan();
419
-
420
  if (!class_exists( 'Email_Log_List_Table' ) ) {
421
  require_once ( plugin_dir_path( __FILE__ ) . 'WPML_Email_Log_List.php' );
422
  }
@@ -537,98 +535,9 @@ class WPML_OptionsManager {
537
  $emailLogList->display();
538
  ?>
539
  </form>
540
- <?php
541
- /**
542
- * Control whether the banner is displayed or not
543
- *
544
- * @since 1.9.7
545
- *
546
- * @param boolean $display Whether the banner should be displayed. (default: true)
547
- *
548
- * @return boolean
549
- */
550
- $display = apply_filters('wpml_banner_display', true);
551
- if ( $display ) {
552
- $this->displayMP3Banner();
553
- }
554
- ?>
555
- <?php
556
- }
557
-
558
- private function displayMP3Banner() {
559
- $option = (int)get_option('wpml_banner_version');
560
- if (!$option) {
561
- $option = rand(1, 2);
562
- add_option('wpml_banner_version', $option);
563
- }
564
- ?>
565
- <div style="background: #fff;border-left: 4px solid #fff;border-radius: 10px;box-shadow: 0 4px 35px rgba(195, 65, 2, .2);clear: both;margin-bottom: 15px;margin-top: 15px;padding: 20px;">
566
- <h3><?php _e( 'Reliable and beautiful emails by MailPoet!', 'wp-mail-logging' );?></h3>
567
- <?php
568
- if ($option === 1) {
569
- $this->displayMP3BannerVersionA();
570
- } else {
571
- $this->displayMP3BannerVersionB();
572
- }
573
- ?>
574
- </div>
575
- <?php
576
- }
577
-
578
- private function displayMP3BannerVersionA() {
579
- ?>
580
- <ul style="list-style-type:disc;list-style-position: inside">
581
- <li><?php _e( '50 email templates to choose from', 'wp-mail-logging' );?></li>
582
- <li><?php _e( 'Fun email designer', 'wp-mail-logging' );?></li>
583
- <li><?php _e( 'Automated email marketing', 'wp-mail-logging' );?></li>
584
- <li><?php _e( 'WooCommerce emails', 'wp-mail-logging' );?></li>
585
- <li><?php _e( '99.1% success email deliverability', 'wp-mail-logging' );?></li>
586
- <li><?php _e( 'Fast and friendly support', 'wp-mail-logging' );?></li>
587
- <li><?php _e( 'Over 100,000 active users', 'wp-mail-logging' );?></li>
588
- </ul>
589
- <a
590
- class="button button-primary"
591
- href="?page=wpml_plugin_log&redirect=free-plan&ref=wml_1"
592
- target="_blank"
593
- >
594
- <?php _e( 'Try MailPoet for free', 'wp-mail-logging' );?>
595
- </a>
596
- <p>
597
- <?php _e( 'Testimonial', 'wp-mail-logging' );?>:
598
- <i>
599
- <?php _e( 'Thanks for this awesome plugin, love love love how it integrates with WordPress. I seriously spent days if not weeks on Mailchimp, and still haven’t been able to do what I did on MailPoet in 1 hour!', 'wp-mail-logging' );?>
600
- </i>
601
- — Kida Shey
602
- </p>
603
  <?php
604
  }
605
 
606
- private function displayMP3BannerVersionB() {
607
- ?>
608
- <p>
609
- <?php _e( 'Create beautiful email campaigns and reach your audience in a breeze with the MailPoet plugin. MailPoet is used by over 300,000 website owners making it the most popular email marketing solution in WordPress. Send beautiful newsletter, notify your readers about last articles and increase your WooCommerce sales!', 'wp-mail-logging' );?>
610
- </p>
611
- <p>
612
- <a href="?page=wpml_plugin_log&redirect=free-plan&ref=wml_2" target="_blank" class="button button-primary">
613
- <?php _e( 'Discover MailPoet for free', 'wp-mail-logging' );?>
614
- </a>
615
- </p>
616
- <?php
617
- }
618
-
619
- private function redirectToFreePlan() {
620
- if (
621
- is_array($_GET)
622
- && array_key_exists('redirect', $_GET)
623
- && array_key_exists('ref', $_GET)
624
- && $_GET['redirect'] === 'free-plan'
625
- ) {
626
- add_option('MAILPOET_REFERRAL_ID', 'wml');
627
- wp_redirect( 'https://www.mailpoet.com/free-plan?ref=' . $_GET['ref'] );
628
- exit;
629
- }
630
- }
631
-
632
  /**
633
  * Override this method and follow its format.
634
  * The purpose of this method is to provide i18n display strings for the values of options.
415
  wp_die(__('You do not have sufficient permissions to access this page.', 'wp-mail-logging'));
416
  }
417
 
 
 
418
  if (!class_exists( 'Email_Log_List_Table' ) ) {
419
  require_once ( plugin_dir_path( __FILE__ ) . 'WPML_Email_Log_List.php' );
420
  }
535
  $emailLogList->display();
536
  ?>
537
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
538
  <?php
539
  }
540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  /**
542
  * Override this method and follow its format.
543
  * The purpose of this method is to provide i18n display strings for the values of options.
src/inc/redux/WPML_Redux_Framework_config.php CHANGED
@@ -334,7 +334,7 @@ if (!class_exists('WPML_Redux_Framework_config')) {
334
 
335
  // SOCIAL ICONS -> Setup custom links in the footer for quick links in your panel footer icons.
336
  $this->args['share_icons'][] = array(
337
- 'url' => 'https://github.com/mailpoet/wp-mail-logging',
338
  'title' => 'Visit us on GitHub',
339
  'icon' => 'el-icon-github'
340
  //'img' => '', // You can use icon OR img. IMG needs to be a full URL.
334
 
335
  // SOCIAL ICONS -> Setup custom links in the footer for quick links in your panel footer icons.
336
  $this->args['share_icons'][] = array(
337
+ 'url' => 'https://github.com/kgjerstad/wp-mail-logging',
338
  'title' => 'Visit us on GitHub',
339
  'icon' => 'el-icon-github'
340
  //'img' => '', // You can use icon OR img. IMG needs to be a full URL.
tests/helper/WPML_IntegrationTestCase.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Helper;
4
+
5
+ use No3x\WPML\WPML_Plugin;
6
+
7
+ /**
8
+ * Class WPML_UnitTestCase
9
+ * @package No3x\WPML\Tests\Helper
10
+ * @group ignore
11
+ */
12
+ class WPML_IntegrationTestCase extends \WP_UnitTestCase {
13
+
14
+ /** @var WPML_Plugin */
15
+ private $plugin;
16
+
17
+ /**
18
+ * @return WPML_Plugin
19
+ */
20
+ public function getPlugin() {
21
+ return $this->plugin;
22
+ }
23
+
24
+ function setUp() {
25
+ parent::setUp();
26
+
27
+ $this->plugin = apply_filters('wpml_get_di_service', 'plugin' );
28
+
29
+ if( ! isset( $_SERVER['SERVER_NAME'] ) ) {
30
+ $_SERVER['SERVER_NAME'] = 'vvv';
31
+ }
32
+ }
33
+
34
+ function tearDown() {
35
+ parent::tearDown();
36
+ }
37
+ }
tests/helper/WPMailArrayBuilder.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Helper;
4
+
5
+ /**
6
+ * Class WPMailArrayBuilder builds message arrays as they are applied to the wp_mail filter.
7
+ * @package No3x\WPML\Tests
8
+ */
9
+ class WPMailArrayBuilder {
10
+ private $to;
11
+ private $subject;
12
+ private $message;
13
+ private $headers;
14
+ private $attachments;
15
+
16
+ private function __construct() {
17
+ $this->to = 'example@exmple.com';
18
+ $this->subject = 'The subject';
19
+ $this->message = 'This is a message';
20
+ $this->headers = '';
21
+ $this->attachments = [];
22
+ }
23
+
24
+ public static function aMail() {
25
+ return new WPMailArrayBuilder();
26
+ }
27
+
28
+ public function withTo($to) {
29
+ $this->to = $to;
30
+ return $this;
31
+ }
32
+
33
+ public function withSubject($subject) {
34
+ $this->subject = $subject;
35
+ return $this;
36
+ }
37
+
38
+ public function withMessage($message) {
39
+ $this->message = $message;
40
+ return $this;
41
+ }
42
+
43
+ public function withNoHeaders() {
44
+ $this->headers = '';
45
+ return $this;
46
+ }
47
+
48
+ public function withHeaders($headers) {
49
+ $this->headers = $headers;
50
+ return $this;
51
+ }
52
+
53
+ public function withNoAttachments() {
54
+ $this->attachments = '';
55
+ return $this;
56
+ }
57
+
58
+ public function withAttachments($attachments) {
59
+ $this->attachments = $attachments;
60
+ return $this;
61
+ }
62
+
63
+ public function but() {
64
+ return WPMailArrayBuilder::aMail()
65
+ ->withTo($this->to)
66
+ ->withSubject($this->subject)
67
+ ->withMessage($this->message)
68
+ ->withHeaders($this->headers)
69
+ ->withAttachments($this->attachments);
70
+ }
71
+
72
+ public function build() {
73
+ return [
74
+ 'to' => $this->to,
75
+ 'subject' => $this->subject,
76
+ 'message' => $this->message,
77
+ 'headers' => $this->headers,
78
+ 'attachments' => $this->attachments
79
+ ];
80
+ }
81
+
82
+ public function buildAsMandrillMail() {
83
+ $mandrillMail = $this->build();
84
+ $mandrillMail['html'] = $mandrillMail['message'];
85
+ unset($mandrillMail['message']);
86
+ return $mandrillMail;
87
+ }
88
+
89
+ public function buildWithoutHeaders() {
90
+ $mailArray = $this->build();
91
+ unset($mailArray['headers']);
92
+ return $mailArray;
93
+ }
94
+
95
+ public function buildWithoutAttachments() {
96
+ $mailArray = $this->build();
97
+ unset($mailArray['attachments']);
98
+ return $mailArray;
99
+ }
100
+
101
+ public function buildWithoutMessage() {
102
+ $mailArray = $this->build();
103
+ unset($mailArray['message']);
104
+ return $mailArray;
105
+ }
106
+
107
+ }
tests/phpunit/includes/bootstrap.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * PHPUnit tests bootstrap.
5
+ */
6
+
7
+ /**
8
+ * The Composer-generated autoloader.
9
+ */
10
+ echo "Executing wp-mail-logging Test Suite" . PHP_EOL;
11
+
12
+ ini_set('error_reporting', E_ALL); // or error_reporting(E_ALL);
13
+ ini_set('display_errors', '1');
14
+ ini_set('display_startup_errors', '1');
15
+
16
+ require_once( dirname( __FILE__ ) . '/../../../vendor/autoload.php' );
17
+
18
+ $_tests_dir = getenv( 'WP_TESTS_DIR' );
19
+
20
+ if ( ! $_tests_dir ) {
21
+ $_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';
22
+ }
23
+
24
+ echo 'Tests folder: ' . $_tests_dir . PHP_EOL;
25
+
26
+ if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
27
+ echo "Could not find $_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?";
28
+ exit( 1 );
29
+ }
30
+
31
+ // Give access to tests_add_filter() function.
32
+ require_once $_tests_dir . '/includes/functions.php';
33
+
34
+ /**
35
+ * Manually load the plugin being tested.
36
+ */
37
+ function _manually_load_plugin() {
38
+ $path = dirname(dirname( dirname( dirname( __FILE__ ) ) ) ) . '/wp-mail-logging.php';
39
+ echo "Plugin path: " . $path;
40
+ require $path;
41
+ }
42
+ tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
43
+
44
+ // Start up the WP testing environment.
45
+ require $_tests_dir . '/includes/bootstrap.php';
46
+
47
+ // EOF
tests/phpunit/integration/WPML_Hook_Remover_Test.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Integration;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+ use No3x\WPML\WPML_Hook_Remover;
7
+
8
+ class WPML_Hook_Remover_Test extends WPML_IntegrationTestCase {
9
+
10
+ private $tag;
11
+ private $callable;
12
+
13
+ public function setUp() {
14
+ parent::setUp();
15
+ $this->tag = 'wp_mail';
16
+ $this->callable = [$this, 'callback_function'];
17
+ add_filter( $this->tag, $this->callable );
18
+ }
19
+
20
+ public function tearDown() {
21
+ remove_filter( $this->tag, $this->callable );
22
+ parent::tearDown();
23
+ }
24
+
25
+ public function callback_function() {}
26
+
27
+ public function testRemove() {
28
+ // The result is true if the hook was removed and it was in place before
29
+ $result = (new WPML_Hook_Remover())->remove_class_hook($this->tag, __CLASS__, 'callback_function');
30
+ $this->assertTrue($result);
31
+ }
32
+
33
+ public function testNotExistentHook() {
34
+ // The result is true if the hook was removed and it was in place before
35
+ $result = (new WPML_Hook_Remover())->remove_class_hook($this->tag, __CLASS__, 'not_existent');
36
+ $this->assertFalse($result);
37
+ }
38
+
39
+ }
tests/phpunit/integration/WPML_IntegrationTestCase_Test.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Unit;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+
7
+ class WPML_IntegrationTestCase_Test extends WPML_IntegrationTestCase {
8
+
9
+ function test_PluginInitialization() {
10
+ $this->assertFalse( null === $this->getPlugin() );
11
+ $this->assertInstanceOf( '\\No3x\\WPML\\WPML_Plugin', $this->getPlugin() );
12
+ }
13
+
14
+ }
tests/phpunit/integration/WPML_LogRotation_Test.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Integration;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+ use No3x\WPML\Model\WPML_Mail as Mail;
7
+ use No3x\WPML\WPML_LogRotation;
8
+ use Arrayzy\ImmutableArray;
9
+
10
+ /**
11
+ * Tests for LogRotation
12
+ * @author No3x
13
+ * @since 1.6.0
14
+ * Tests are written in the AAA-Rule
15
+ * There are three basic sections for our test: Arrange, Act, and Assert.
16
+ */
17
+ class WPML_LogRotation_Test extends WPML_IntegrationTestCase {
18
+
19
+ private function prepareMessages( $amount ) {
20
+ $to = 'email@example.com';
21
+ $subject = "Message Nr. %d";
22
+ $message = "This is a sample message. It is the %d. of this kind.";
23
+
24
+ for( $i = 0; $i < $amount; $i++ ) {
25
+ wp_mail(
26
+ $to,
27
+ sprintf($subject, $i),
28
+ sprintf($message, $i)
29
+ );
30
+ }
31
+ // Check if preparation worked fine
32
+ $this->assertEquals($this->count_mails(), $amount);
33
+ }
34
+
35
+ private function count_mails() {
36
+ return Mail::query()->find(true);
37
+ }
38
+
39
+ /**
40
+ * Query some mails.
41
+ * @param int $amount number of mails to query.
42
+ * @return Mail[] array
43
+ */
44
+ private function query_some_mails( $amount ) {
45
+ return Mail::query()
46
+ ->sort_by( Mail::get_primary_key() )
47
+ ->order( 'desc' )
48
+ ->limit( $amount )
49
+ ->find();
50
+ }
51
+
52
+ /**
53
+ * Get latest mail.
54
+ * @return Mail
55
+ */
56
+ private function latest_mail() {
57
+ return ImmutableArray::create(
58
+ Mail::query()
59
+ ->sort_by( Mail::get_primary_key() )
60
+ ->order( 'desc' )
61
+ ->limit(1)
62
+ ->find()
63
+ )->first();
64
+ }
65
+ /**
66
+ * Get oldest mail.
67
+ * @return Mail
68
+ */
69
+ private function oldest_mail() {
70
+ return ImmutableArray::create(
71
+ Mail::query()
72
+ ->sort_by( Mail::get_primary_key() )
73
+ ->order( 'asc' )
74
+ ->limit(1)
75
+ ->find()
76
+ )->first();
77
+ }
78
+
79
+ /**
80
+ * Test limitNumberOfMailsByAmount - Check if the number of kept messages is correct.
81
+ * The LogRotation supports the limitation of stored mails by amount.
82
+ * This test checks if the amount of left/deleted mails is correct.
83
+ * @since 1.6.0
84
+ * @see WPML_LogRotation::limitNumberOfMailsByAmount
85
+ */
86
+ function test_limitNumberOfMailsByAmount_count() {
87
+ global $wpml_settings;
88
+ $amount = 10;
89
+ $keep = 3;
90
+ $this->prepareMessages( $amount );
91
+
92
+ $wpml_settings['log-rotation-limit-amout'] = '1';
93
+ $wpml_settings['log-rotation-limit-amout-keep'] = $keep;
94
+
95
+ $this->assertEquals($this->count_mails(), $amount);
96
+ WPML_LogRotation::limitNumberOfMailsByAmount();
97
+ $this->assertEquals($this->count_mails(), $keep);
98
+ }
99
+
100
+ /**
101
+ * Test limitNumberOfMailsByAmount - Check if old messages where deleted first.
102
+ * The LogRotation supports the limitation of stored mails by amount.
103
+ * This test checks if old messages are deleted first by asserting that after the policy is run the oldest mail is gone.
104
+ * @since 1.6.0
105
+ * @see WPML_LogRotation::limitNumberOfMailsByAmount
106
+ */
107
+ function test_limitNumberOfMailsByAmount_order() {
108
+ global $wpml_settings;
109
+ $amount = 10;
110
+ $keep = 3;
111
+ $this->prepareMessages( $amount );
112
+ $wpml_settings['log-rotation-limit-amout'] = '1';
113
+ $wpml_settings['log-rotation-limit-amout-keep'] = $keep;
114
+ $oldest_mail_id = $this->oldest_mail()->get_mail_id();
115
+
116
+ WPML_LogRotation::limitNumberOfMailsByAmount();
117
+
118
+ // Assert oldest mail is gone.
119
+ $this->assertFalse( Mail::find_one( $oldest_mail_id ) );
120
+ }
121
+
122
+ /**
123
+ * Test limitNumberOfMailsByTime - Check if all old messages where deleted.
124
+ * The LogRotation supports the limitation of stored mails by date.
125
+ * This test checks of old messages are deleted after given time by comparing the amount of mails.
126
+ * @since 1.6.0
127
+ * @see WPML_LogRotation::limitNumberOfMailsByTime
128
+ */
129
+ function test_limitNumberOfMailsByTime_order() {
130
+ global $wpml_settings;
131
+ $amount = 10;
132
+ $old = 3;
133
+ $days = 5;
134
+ $this->prepareMessages( $amount );
135
+ $wpml_settings['log-rotation-delete-time'] = '1';
136
+ $wpml_settings['log-rotation-delete-time-days'] = $days;
137
+
138
+ // Make #$old mails #$days older:
139
+ foreach( $this->query_some_mails( $old ) as $mail ) {
140
+ $mail->set_timestamp( gmdate( 'Y-m-d H:i:s', ( time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) - ($days+1) * DAY_IN_SECONDS ) ) )
141
+ ->save();
142
+ }
143
+
144
+ WPML_LogRotation::limitNumberOfMailsByTime();
145
+
146
+ // Assert that there are just $amount-$old mails left.
147
+ $this->assertEquals( $amount-$old, $this->count_mails() );
148
+ }
149
+ }
tests/phpunit/integration/WPML_MailRenderer_AJAX_Handler_Test.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Integration;
4
+
5
+
6
+ use Mockery;
7
+ use No3x\WPML\Renderer\WPML_MailRenderer_AJAX_Handler;
8
+ use No3x\WPML\Renderer\WPML_MailRenderer;
9
+
10
+ use \Exception;
11
+ use WPAjaxDieContinueException;
12
+
13
+ class WPML_MailRenderer_AJAX_Handler_Test extends \WP_Ajax_UnitTestCase {
14
+
15
+ /** @var WPML_MailRenderer */
16
+ private $mailRendererMock;
17
+ /** @var WPML_MailRenderer_AJAX_Handler */
18
+ private $mailRendererAjaxHandler;
19
+ private $valid_id = 2;
20
+
21
+ public function setUp() {
22
+ parent::setUp();
23
+
24
+ $this->mailRendererMock = Mockery::mock('No3x\WPML\Renderer\WPML_MailRenderer');
25
+
26
+ $this->mailRendererMock->shouldReceive('getSupportedFormats')
27
+ ->andReturn(['raw', 'html']);
28
+
29
+ $this->mailRendererAjaxHandler = new WPML_MailRenderer_AJAX_Handler($this->mailRendererMock, []);
30
+ }
31
+
32
+ /**
33
+ * Test that the callback saves the value for administrators.
34
+ */
35
+ public function test_nonce_invalid() {
36
+ $_POST['_wpnonce'] = $this->mailRendererAjaxHandler->get_ajax_data()["nonce"] . "invalid";
37
+
38
+ try {
39
+ $this->_handleAjax( WPML_MailRenderer_AJAX_Handler::ACTION );
40
+ $this->fail( 'Expected exception: WPAjaxDieContinueException' );
41
+ } catch ( WPAjaxDieContinueException $e ) {
42
+ // We expected this, do nothing.
43
+ }//end try
44
+
45
+ $this->checkJsonMessage();
46
+ $response = json_decode($this->_last_response, true);
47
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_NONCE_CODE, $response['data']['code']);
48
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_NONCE_MESSAGE, $response['data']['message']);
49
+ }
50
+
51
+ /**
52
+ * Test that the callback saves the value for administrators.
53
+ */
54
+ public function test_id_not_passed() {
55
+ $_POST['_wpnonce'] = $this->mailRendererAjaxHandler->get_ajax_data()["nonce"];
56
+
57
+ try {
58
+ $this->_handleAjax( WPML_MailRenderer_AJAX_Handler::ACTION );
59
+ $this->fail( 'Expected exception: WPAjaxDieContinueException' );
60
+ } catch ( WPAjaxDieContinueException $e ) {
61
+ // We expected this, do nothing.
62
+ }//end try
63
+
64
+ $this->checkJsonMessage();
65
+ $response = json_decode($this->_last_response, true);
66
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_ID_MISSING_CODE, $response['data']['code']);
67
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_ID_MISSING_MESSAGE, $response['data']['message']);
68
+ }
69
+
70
+ /**
71
+ * Test that the callback saves the value for administrators.
72
+ */
73
+ public function test_invalid_id_passed() {
74
+ $invalid_id = -1;
75
+ $this->mailRendererMock->shouldReceive('render')
76
+ ->withArgs([$invalid_id, 'html'])
77
+ ->times(1)
78
+ ->andThrow("\Exception", "Requested mail not found in database.");
79
+
80
+ $_POST['_wpnonce'] = $this->mailRendererAjaxHandler->get_ajax_data()["nonce"];
81
+ $_POST['format'] = 'html';
82
+ $_POST['id'] = $invalid_id;
83
+
84
+ try {
85
+ $this->_handleAjax( WPML_MailRenderer_AJAX_Handler::ACTION );
86
+ $this->fail( 'Expected exception: WPAjaxDieContinueException' );
87
+ } catch ( WPAjaxDieContinueException $e ) {
88
+ // We expected this, do nothing.
89
+ }//end try
90
+
91
+ $this->checkJsonMessage();
92
+ $response = json_decode($this->_last_response, true);
93
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_OTHER_CODE, $response['data']['code']);
94
+ $this->assertEquals("Requested mail not found in database.", $response['data']['message']);
95
+ }
96
+
97
+ /**
98
+ * Test that the callback saves the value for administrators.
99
+ */
100
+ public function test_unknown_format_passed() {
101
+ $this->mailRendererMock->shouldReceive('render')
102
+ ->withAnyArgs()
103
+ ->times(1)
104
+ ->andReturn("rendered");
105
+
106
+ $_POST['_wpnonce'] = $this->mailRendererAjaxHandler->get_ajax_data()["nonce"];
107
+ $_POST['id'] = $this->valid_id;
108
+ $_POST['format'] = 'no-valid-format';
109
+
110
+ try {
111
+ $this->_handleAjax( WPML_MailRenderer_AJAX_Handler::ACTION );
112
+ $this->fail( 'Expected exception: WPAjaxDieContinueException' );
113
+ } catch ( WPAjaxDieContinueException $e ) {
114
+ // We expected this, do nothing.
115
+ }//end try
116
+
117
+ $this->checkJsonMessage();
118
+ $response = json_decode($this->_last_response, true);
119
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_UNKNOWN_FORMAT_CODE, $response['data']['code']);
120
+ $this->assertEquals(WPML_MailRenderer_AJAX_Handler::ERROR_UNKNOWN_FORMAT_MESSAGE, $response['data']['message']);
121
+ }
122
+
123
+ /**
124
+ * Test that the callback saves the value for administrators.
125
+ */
126
+ public function test_render_is_called() {
127
+ $this->mailRendererMock->shouldReceive('render')
128
+ ->withArgs([$this->valid_id, 'html'])
129
+ ->times(1)
130
+ ->andReturn("rendered");
131
+
132
+ $_POST['_wpnonce'] = $this->mailRendererAjaxHandler->get_ajax_data()["nonce"];
133
+ $_POST['id'] = $this->valid_id;
134
+ $_POST['format'] = 'html';
135
+
136
+ try {
137
+ $this->_handleAjax( WPML_MailRenderer_AJAX_Handler::ACTION );
138
+ $this->fail( 'Expected exception: WPAjaxDieContinueException' );
139
+ } catch ( WPAjaxDieContinueException $e ) {
140
+ // We expected this, do nothing.
141
+ }//end try
142
+
143
+ $this->checkJsonMessage(true);
144
+ $response = json_decode($this->_last_response, true);
145
+ $this->mailRendererMock->mockery_verify();
146
+ $this->assertEquals("rendered", $response['data']);
147
+ }
148
+
149
+ private function checkJsonMessage($success_expected = false) {
150
+ $this->assertJson( $this->_last_response );
151
+ $response = json_decode( $this->_last_response, true );
152
+ if( $success_expected ) {
153
+ $this->assertTrue( $response['success'] );
154
+ $this->assertArrayHasKey('data', $response);
155
+ } else {
156
+ $this->assertFalse( $response['success'] );
157
+ $this->assertArrayHasKey('code', $response["data"]);
158
+ $this->assertArrayHasKey('message', $response["data"]);
159
+ }
160
+ }
161
+
162
+ }
tests/phpunit/integration/WPML_Plugin_Test.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Integration;
4
+
5
+
6
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
7
+ use No3x\WPML\WPML_Plugin;
8
+
9
+ class WPML_Plugin_Test extends WPML_IntegrationTestCase {
10
+
11
+ function test_log_email() {
12
+ global $wpdb;
13
+ $tableName = WPML_Plugin::getTablename( 'mails' );
14
+
15
+ $to = array(
16
+ 'email@example.com',
17
+ 'email2@example.com'
18
+ );
19
+
20
+ $subject = rand_str();
21
+ $message = "Hello, this is a test message";
22
+
23
+ wp_mail($to, $subject, $message);
24
+
25
+ $rows = $wpdb->get_results( "SELECT * FROM $tableName WHERE subject = '{$subject}'" );
26
+
27
+ $count = count( $rows );
28
+ $this->assertEquals( 1, $count);
29
+
30
+ $row = $rows[0];
31
+ $this->assertEquals( $subject, $row->subject );
32
+ $this->assertEquals( $message, $row->message );
33
+
34
+ $this->assertTrue( strpos( $row->receiver, $to[0] ) !== false );
35
+ $this->assertTrue( strpos( $row->receiver, $to[1] ) !== false );
36
+ }
37
+
38
+ function test_charset_email() {
39
+ global $wpdb;
40
+ $tableName = WPML_Plugin::getTablename( 'mails' );
41
+
42
+ $to = array(
43
+ 'email@example.com',
44
+ 'email2@example.com'
45
+ );
46
+
47
+ $subject = rand_str();
48
+ $message = "Řekl, že přijde, jestliže v konkurzu zvítězí";
49
+
50
+ wp_mail($to, $subject, $message);
51
+
52
+ $rows = $wpdb->get_results( "SELECT * FROM $tableName" );
53
+ $row = $rows[0];
54
+
55
+ $this->assertTrue( strpos( $row->message, "?" ) === false );
56
+
57
+ }
58
+ }
tests/phpunit/integration/test-core-settings.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Integration;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+ use No3x\WPML\WPML_Plugin;
7
+ use No3x\WPML\WPML_InstallIndicator;
8
+
9
+ /**
10
+ * @author No3x
11
+ * Tests are written in the AAA-Rule
12
+ * There are three basic sections for our test: Arrange, Act, and Assert.
13
+ */
14
+ class WPML_Plugin_CoreSettings extends WPML_IntegrationTestCase {
15
+
16
+ function test_getInstalled() {
17
+
18
+ $this->assertEquals( 'WPML_Plugin__installed', $this->getPlugin()->prefix( WPML_InstallIndicator::optionInstalled ) );
19
+ $this->assertEquals( 'WPML_Plugin__version', $this->getPlugin()->prefix( WPML_InstallIndicator::optionVersion ) );
20
+
21
+ }
22
+ }
tests/phpunit/unit/WPML_Attachment_Test.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests;
4
+
5
+
6
+ use No3x\WPML\WPML_Attachment;
7
+
8
+ class WPML_Attachment_Test extends \PHPUnit_Framework_TestCase {
9
+
10
+ /** @var $fsMock \No3x\WPML\FS\IFilesystem |\Mockery\MockInterface */
11
+ private $fsMock;
12
+
13
+ function setUp()
14
+ {
15
+ parent::setUp();
16
+
17
+ $this->fsMock = self::getMockBuilder('No3x\WPML\FS\IFilesystem')
18
+ ->disableOriginalConstructor()
19
+ ->getMock()
20
+ ;
21
+
22
+ WPML_Attachment::setFS($this->fsMock);
23
+ }
24
+
25
+
26
+ public function test_fromRelativePath() {
27
+
28
+ $this->fsMock->expects(self::once())
29
+ ->method('is_file')
30
+ ->willReturn(true)
31
+ ;
32
+
33
+ // The mimetype should not be determined, if it's not a file
34
+ $this->fsMock->expects(self::once())
35
+ ->method('mime_content_type')
36
+ ->willReturn('image/png')
37
+ ;
38
+
39
+ $path = "/uploads/2000/04/image.png";
40
+
41
+ $attachment = WPML_Attachment::fromRelPath($path);
42
+ $this->assertFalse($attachment->isGone());
43
+ $this->assertEquals("image", $attachment->getIconClass());
44
+ $this->assertEquals(WP_CONTENT_DIR . '/uploads' . $path, $attachment->getPath());
45
+ }
46
+
47
+ public function test_fromRelativePath_InvalidPath() {
48
+
49
+ $this->fsMock->expects(self::once())
50
+ ->method('is_file')
51
+ ->willReturn(false)
52
+ ;
53
+
54
+ // The mimetype should not be determined, if it's not a file
55
+ $this->fsMock->expects(self::never())
56
+ ->method('mime_content_type')
57
+ ;
58
+
59
+ $path = "/invalid";
60
+
61
+ $attachment = WPML_Attachment::fromRelPath($path);
62
+ $this->assertTrue($attachment->isGone());
63
+ $this->assertEquals("file", $attachment->getIconClass());
64
+ $this->assertEquals("/invalid", $attachment->getPath());
65
+ }
66
+
67
+ function test_fromAbsolutePath() {
68
+
69
+ $this->fsMock->expects(self::never())
70
+ ->method('is_file')
71
+ ;
72
+
73
+ // The mimetype should not be determined, if it's not a file
74
+ $this->fsMock->expects(self::once())
75
+ ->method('mime_content_type')
76
+ ->willReturn('image/png')
77
+ ;
78
+
79
+ $path = WP_CONTENT_DIR . '/uploads/2000/04/image.png';
80
+
81
+ $attachment = WPML_Attachment::fromAbsPath($path);
82
+ $this->assertFalse($attachment->isGone());
83
+ $this->assertEquals("image", $attachment->getIconClass());
84
+ $this->assertEquals($path, $attachment->getPath());
85
+
86
+ }
87
+
88
+ function test_fromAbsolutePath_InvalidPath() {
89
+
90
+ $this->fsMock->expects(self::never())
91
+ ->method('is_file')
92
+ ;
93
+
94
+ // The mimetype should not be determined, if it's not a file
95
+ $this->fsMock->expects(self::once())
96
+ ->method('mime_content_type')
97
+ ->willReturn(false)
98
+ ;
99
+
100
+ $path = WP_CONTENT_DIR . '/a/file/somewhere/else.png';
101
+ $path = WP_CONTENT_DIR . '../../else.png';
102
+
103
+ $attachment = WPML_Attachment::fromAbsPath($path);
104
+ $this->assertEquals("file", $attachment->getIconClass());
105
+ $this->assertEquals($path, $attachment->getPath());
106
+ $this->assertEquals("else.png", $attachment->toRelPath());
107
+
108
+ }
109
+
110
+ }
tests/phpunit/unit/WPML_ColumnManager_Test.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests;
4
+
5
+
6
+ use No3x\WPML\Model\WPML_Mail;
7
+ use No3x\WPML\Renderer\Column\ColumnFormat;
8
+ use No3x\WPML\Renderer\Exception\ColumnDoesntExistException;
9
+ use No3x\WPML\Renderer\WPML_ColumnManager;
10
+ use No3x\WPML\Tests\Helper\WPMailArrayBuilder;
11
+ use No3x\WPML\WPML_MailExtractor;
12
+
13
+ class WPML_ColumnManager_Test extends \PHPUnit_Framework_TestCase {
14
+
15
+ private $columnManager;
16
+ /** @var WPML_Mail */
17
+ private $mail;
18
+ private $item;
19
+
20
+ public function setUp() {
21
+ $this->columnManager = new WPML_ColumnManager();
22
+
23
+ $exampleAttachment1 = WP_CONTENT_DIR . '/uploads/2018/05/file.pdf';
24
+ $exampleAttachment2 = WP_CONTENT_DIR . '/uploads/2018/01/bill.pdf';
25
+
26
+ $mailArrayBuilder = WPMailArrayBuilder::aMail()
27
+ ->withSubject("Test")
28
+ ->withTo("example@exmple.com")
29
+ ->withMessage("<b>Bold</b>")
30
+ ->withHeaders("From: \"admin\" <admin@local.test>\r\n,\nCc: example2@example.com,\nReply-To: admin <admin@local.test>\r\n")
31
+ ->withAttachments([$exampleAttachment1, $exampleAttachment2])
32
+ ;
33
+
34
+ /** @var $mail WPML_Mail */
35
+ $mail = (new WPML_MailExtractor())->extract($mailArrayBuilder->build());
36
+ $mail->set_mail_id(2);
37
+ $mail->set_plugin_version('1.8.5');
38
+ $mail->set_timestamp('2018-09-24 16:02:11');
39
+ $mail->set_host('127.0.0.1');
40
+ $mail->set_error('bli');
41
+
42
+ $this->item = $mail->to_array();
43
+ }
44
+
45
+ public function test_columns() {
46
+ $columns = ['mail_id', 'timestamp', 'host', 'receiver', 'subject', 'message', 'headers', 'attachments', 'error', 'plugin_version'];
47
+ $this->assertEquals($columns, $this->columnManager->getColumnNames());
48
+ }
49
+
50
+ /**
51
+ * @requires PHPUnit 5.2
52
+ */
53
+ public function test_nonexistentdata() {
54
+ $column_name = 'host';
55
+ unset($this->item[$column_name]);
56
+ $this->expectException(ColumnDoesntExistException::get_class());
57
+ $this->expectExceptionMessage(sprintf(ColumnDoesntExistException::MESSAGE, $column_name));
58
+ $this->columnManager->getColumnRenderer($column_name)->render($this->item, ColumnFormat::FULL);
59
+ }
60
+
61
+ public function test_column_host() {
62
+ $actual = $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_HOST)->render($this->item, ColumnFormat::FULL);
63
+ $this->assertEquals('127.0.0.1', $actual);
64
+ }
65
+
66
+ //TODO: need to test both time formats
67
+ public function test_column_timestamp() {
68
+ $actual = $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_TIMESTAMP)->render($this->item, ColumnFormat::FULL);
69
+ $this->assertEquals('2018-09-24 16:02:11', $actual);
70
+ }
71
+
72
+ public function test_column_attachments_simple() {
73
+ $example1And2Expected = '/2018/05/file.pdf,\n/2018/01/bill.pdf';
74
+ $actual = $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_ATTACHMENTS)->render($this->item, ColumnFormat::SIMPLE);
75
+ $this->assertEquals($example1And2Expected, $actual);
76
+ }
77
+
78
+ public function test_column_attachments_full() {
79
+ $example1And2Expected = '<i class="fa fa-times" title="Attachment file.pdf is not present"></i><i class="fa fa-times" title="Attachment bill.pdf is not present"></i>';
80
+ $actual = $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_ATTACHMENTS)->render($this->item, ColumnFormat::FULL);
81
+ $this->assertEquals($example1And2Expected, $actual);
82
+ }
83
+
84
+ public function test_column_error_simple() {
85
+ $example1And2Expected = "bli";
86
+ $actual = $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_ERROR)->render($this->item, ColumnFormat::SIMPLE);
87
+ $this->assertEquals($example1And2Expected, $actual);
88
+ }
89
+
90
+ public function test_column_error_empty() {
91
+ $expected = '';
92
+ $this->item['error'] = "";
93
+ $this->assertEquals($expected, $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_ERROR)->render($this->item, ColumnFormat::FULL));
94
+ }
95
+
96
+ public function test_column_error_full() {
97
+ $example1And2Expected = '<i class="fa fa-exclamation-circle" title="bli"></i>';
98
+ $actual = $this->columnManager->getColumnRenderer(WPML_ColumnManager::COLUMN_ERROR)->render($this->item, ColumnFormat::FULL);
99
+ $this->assertEquals($example1And2Expected, $actual);
100
+ }
101
+ }
tests/phpunit/unit/WPML_Email_Log_List_Test.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace No3x\WPML\Tests;
3
+
4
+
5
+ use No3x\WPML\Model\WPML_Mail;
6
+ use No3x\WPML\Tests\Helper\WPMailArrayBuilder;
7
+ use No3x\WPML\WPML_Email_Log_List;
8
+ use No3x\WPML\WPML_MailExtractor;
9
+
10
+ class WPML_Email_Log_List_Test extends \PHPUnit_Framework_TestCase {
11
+
12
+ private $logListTable;
13
+
14
+ private $item;
15
+ private $item_id;
16
+
17
+ public function setUp() {
18
+ $this->logListTable = new WPML_Email_Log_List(null);
19
+
20
+ $this->item_id = 2;
21
+
22
+ $wpMailArrayBuilder = WPMailArrayBuilder::aMail()
23
+ ->withSubject("Test")
24
+ ->withTo("example@exmple.com")
25
+ ->withMessage("Hello World")
26
+ ;
27
+ /** @var $mail WPML_Mail */
28
+ $mail = (new WPML_MailExtractor())->extract($wpMailArrayBuilder->build());
29
+ $mail->set_mail_id($this->item_id);
30
+
31
+ $this->item = $mail->to_array();
32
+ }
33
+
34
+ public function test_column_message() {
35
+ $expected = '<a class="wp-mail-logging-view-message button button-secondary" href="#" data-mail-id="' . $this->item_id . '">View</a>';
36
+ $this->assertEquals($expected, $this->logListTable->column_message($this->item));
37
+ }
38
+ }
tests/phpunit/unit/WPML_Email_Resender_Test.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Unit;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+ use No3x\WPML\WPML_Email_Resender;
7
+
8
+ /**
9
+ * Test resend feature.
10
+ */
11
+
12
+ class WPML_Email_Resender_Test extends \PHPUnit_Framework_TestCase {
13
+
14
+ /** @var WPML_Email_Resender $emailResender */
15
+ private $emailResender;
16
+
17
+ /** @var \No3x\WPML\WPML_Email_Dispatcher $dispatcherMock */
18
+ private $dispatcherMock;
19
+
20
+ /** @var \No3x\WPML\Model\WPML_Mail|\PHPUnit_Framework_MockObject_MockObject $mailMock */
21
+ private $mailMock;
22
+
23
+ function setUp() {
24
+ parent::setUp();
25
+ $this->dispatcherMock = self::getMockBuilder('No3x\WPML\WPML_Email_Dispatcher')
26
+ ->disableOriginalConstructor()
27
+ ->getMock()
28
+ ;
29
+ $this->emailResender = new WPML_Email_Resender($this->dispatcherMock);
30
+ $this->mailMock = self::getMockBuilder('No3x\WPML\Model\WPML_Mail')
31
+ ->disableOriginalConstructor()
32
+ ->setMethods( array('get_headers', 'get_attachments', 'get_receiver') )
33
+ ->getMock()
34
+ ;
35
+ }
36
+
37
+ /**
38
+ * The mail contents are stored with line breaks encoded as php string literals in the database.
39
+ * When loading this mail from the database to resend it in it's original format
40
+ * at least the headers need to be fixed. To do so the header string is split on the
41
+ * line endings (encoded as string literals) into an array.
42
+ * @dataProvider headersProvider
43
+ * @param string $headers header as string
44
+ * @param array $expectedHeaders expected header parsed as array
45
+ */
46
+ function test_resendHeaders($headers, $expectedHeaders) {
47
+
48
+ $this->mailMock->expects(self::once())
49
+ ->method('get_headers')
50
+ ->willReturn($headers)
51
+ ;
52
+
53
+ $this->dispatcherMock->expects(self::once())
54
+ ->method('dispatch')
55
+ ->with($this->anything(), $this->anything(), $this->anything(), $expectedHeaders, $this->anything())
56
+ ;
57
+
58
+ $this->emailResender->resendMail($this->mailMock);
59
+ }
60
+
61
+ function test_multiple_receivers() {
62
+
63
+ $receivers_array = ['recipient1@example.com', 'recipient2@foo.example.com'];
64
+ $receivers = $receivers_array[0] . '\n' . $receivers_array[1];
65
+
66
+ $this->mailMock->expects(self::once())
67
+ ->method('get_receiver')
68
+ ->willReturn($receivers)
69
+ ;
70
+
71
+ $this->dispatcherMock->expects(self::once())
72
+ ->method('dispatch')
73
+ ->with($this->equalTo($receivers_array), $this->mailMock->get_subject(), $this->mailMock->get_message(), $this->anything(), $this->anything())
74
+ ;
75
+
76
+ $this->emailResender->resendMail($this->mailMock);
77
+
78
+ }
79
+
80
+ function headersProvider() {
81
+ return array(
82
+ "withoutFrom" => array(
83
+ "example@example.com,\\nReply-To: example@com,\\nBcc: example@example.com,\\nContent-type: text/html; charset=UTF-8",
84
+ array(
85
+ "example@example.com",
86
+ "Reply-To: example@com",
87
+ "Bcc: example@example.com",
88
+ "Content-type: text/html; charset=UTF-8"
89
+ )
90
+ ),
91
+ "withRN" => array(
92
+ "example@example.com,\\r\\nReply-To: example@com",
93
+ array(
94
+ "example@example.com",
95
+ "Reply-To: example@com",
96
+ )
97
+ ),
98
+ "withFrom" => array(
99
+ "From: \"example@example.com\" <example@example.de>,\\nContent-type: text/html; charset=UTF-8",
100
+ array(
101
+ "From: \"example@example.com\" <example@example.de>",
102
+ "Content-type: text/html; charset=UTF-8"
103
+ )
104
+ )
105
+ );
106
+ }
107
+ }
tests/phpunit/unit/WPML_MailExtractor_Test.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Unit;
4
+
5
+ use No3x\WPML\Tests\Helper\WPMailArrayBuilder;
6
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
7
+ use No3x\WPML\WPML_MailExtractor;
8
+
9
+ /**
10
+ * Class WPML_MailExtractor_Test tests the mail extraction from a WordPress mail array specified at the codex
11
+ * @see https://developer.wordpress.org/reference/functions/wp_mail/
12
+ * @package No3x\WPML\Tests
13
+ */
14
+ class WPML_MailExtractor_Test extends \PHPUnit_Framework_TestCase {
15
+
16
+ /** @var WPML_MailExtractor */
17
+ private $mailExtractor;
18
+
19
+ function setUp() {
20
+ parent::setUp();
21
+ $this->mailExtractor = new WPML_MailExtractor();
22
+ }
23
+
24
+ /**
25
+ * Codex: (string|array) (Required) Array or comma-separated list of email addresses to send message.
26
+ * @dataProvider receiverProvider
27
+ * @param $mailArray array the mailArray
28
+ * @param $expected string the expected output
29
+ */
30
+ function test_receiver($mailArray, $expected) {
31
+ $this->assertEquals($expected, $this->mailExtractor->extract($mailArray)->get_receiver());
32
+ }
33
+
34
+ function receiverProvider() {
35
+ $exampleEmail1 = 'example@example.com';
36
+ $exampleEmail2 = 'example2@example.com';
37
+ $example1And2Expected = $exampleEmail1 . ',\n' . $exampleEmail2;
38
+ return [
39
+ 'single receiver' => [
40
+ WPMailArrayBuilder::aMail()->withTo($exampleEmail1)->build(),
41
+ $exampleEmail1
42
+ ],
43
+ 'multiple receivers' => [
44
+ WPMailArrayBuilder::aMail()->withTo($exampleEmail1 . ',' . $exampleEmail2)->build(),
45
+ $example1And2Expected
46
+ ],
47
+ 'array with single receiver' => [
48
+ WPMailArrayBuilder::aMail()->withTo([$exampleEmail1])->build(),
49
+ $exampleEmail1
50
+ ],
51
+ 'array with multiple receivers' => [
52
+ WPMailArrayBuilder::aMail()->withTo([$exampleEmail1, $exampleEmail2])->build(),
53
+ $example1And2Expected
54
+ ],
55
+ ];
56
+ }
57
+
58
+ /**
59
+ * Codex: $subject (string) (Required) Email subject
60
+ */
61
+ function test_subject() {
62
+ $subject = "This is a subject";
63
+ $mailArray = WPMailArrayBuilder::aMail()->withSubject($subject)->build();
64
+ $this->assertEquals($subject, $this->mailExtractor->extract($mailArray)->get_subject());
65
+ }
66
+
67
+ /**
68
+ * Codex: $message (string) (Required) Message contents
69
+ */
70
+ function test_message() {
71
+ $message = "This is a message";
72
+ $mailArray = WPMailArrayBuilder::aMail()->withMessage($message)->build();
73
+ $this->assertEquals($message, $this->mailExtractor->extract($mailArray)->get_message());
74
+ }
75
+
76
+ /**
77
+ * @requires PHPUnit 5.7
78
+ * expectException is not present in older phpunit version.
79
+ */
80
+ function test_no_messageField_throws_exception() {
81
+ $mailArray = WPMailArrayBuilder::aMail()->buildWithoutMessage();
82
+ $this->expectException("Exception");
83
+ $this->expectExceptionMessage(WPML_MailExtractor::ERROR_NO_FIELD);
84
+ $this->mailExtractor->extract($mailArray);
85
+ }
86
+
87
+ /**
88
+ * Mandrill stores the message in the 'html' field instead of the 'message' field (see gh-22)
89
+ */
90
+ function test_mandrillMail() {
91
+ $message = "This is a message in the html field";
92
+ $mailArray = WPMailArrayBuilder::aMail()->withMessage($message)->buildAsMandrillMail();
93
+ $this->assertEquals($message, $this->mailExtractor->extract($mailArray)->get_message());
94
+ }
95
+
96
+ /**
97
+ * Codex: $headers (string|array) (Optional) Additional headers.
98
+ * Default value: ''
99
+ * @dataProvider headersProvider
100
+ * @param $mailArray array the mailArray
101
+ * @param $expected string the expected output
102
+ */
103
+ function test_headers($mailArray, $expected) {
104
+ $this->assertEquals($expected, $this->mailExtractor->extract($mailArray)->get_headers());
105
+ }
106
+
107
+ function headersProvider() {
108
+ $exampleHeader = 'Content-Type: text/html; charset=UTF-8';
109
+ $example1And2Expected = $exampleHeader . ',\n' . $exampleHeader;
110
+ return [
111
+ 'none header' => [
112
+ WPMailArrayBuilder::aMail()->buildWithoutHeaders(),
113
+ ''
114
+ ],
115
+ 'empty header' => [
116
+ WPMailArrayBuilder::aMail()->but()->withNoHeaders()->build(),
117
+ ''
118
+ ],
119
+ 'single header' => [
120
+ WPMailArrayBuilder::aMail()->withHeaders($exampleHeader)->build(),
121
+ $exampleHeader
122
+ ],
123
+ 'multiple header' => [
124
+ WPMailArrayBuilder::aMail()->withHeaders($exampleHeader . ',\n' . $exampleHeader)->build(),
125
+ $example1And2Expected
126
+ ],
127
+ 'array with single header' => [
128
+ WPMailArrayBuilder::aMail()->withHeaders([$exampleHeader])->build(),
129
+ $exampleHeader
130
+ ],
131
+ 'array with multiple headers' => [
132
+ WPMailArrayBuilder::aMail()->withHeaders([$exampleHeader, $exampleHeader])->build(),
133
+ $example1And2Expected
134
+ ],
135
+ ];
136
+ }
137
+
138
+ /**
139
+ * Codex: $attachments (string|array) (Optional) Files to attach.
140
+ * Default value: array()
141
+ * @dataProvider attachmentsProvider
142
+ * @param $mailArray array the mailArray
143
+ * @param $expected string the expected output
144
+ */
145
+ function test_attachments($mailArray, $expected) {
146
+ $this->assertEquals($expected, $this->mailExtractor->extract($mailArray)->get_attachments());
147
+ }
148
+
149
+ function attachmentsProvider() {
150
+ $exampleAttachment1 = WP_CONTENT_DIR . '/uploads/2018/05/file.pdf';
151
+ $exampleAttachment2 = WP_CONTENT_DIR . '/uploads/2018/01/bill.pdf';
152
+
153
+ $exampleAttachment1Expected = '/2018/05/file.pdf';
154
+ $example1And2Expected = '/2018/05/file.pdf,\n/2018/01/bill.pdf';
155
+
156
+ return [
157
+ 'none attachments' => [
158
+ WPMailArrayBuilder::aMail()->buildWithoutAttachments(),
159
+ ''
160
+ ],
161
+ 'empty attachments' => [
162
+ WPMailArrayBuilder::aMail()->but()->withNoAttachments()->build(),
163
+ ''
164
+ ],
165
+ 'no wp-content in path' => [
166
+ WPMailArrayBuilder::aMail()->withAttachments('/tmp/0file.png')->build(),
167
+ '0file.png'
168
+ ],
169
+ 'single attachment' => [
170
+ WPMailArrayBuilder::aMail()->withAttachments($exampleAttachment1)->build(),
171
+ $exampleAttachment1Expected
172
+ ],
173
+ 'multiple attachments' => [
174
+ WPMailArrayBuilder::aMail()->withAttachments($exampleAttachment1 . ',\n' . $exampleAttachment2)->build(),
175
+ $example1And2Expected
176
+ ],
177
+ 'array with single attachment' => [
178
+ WPMailArrayBuilder::aMail()->withAttachments([$exampleAttachment1])->build(),
179
+ $exampleAttachment1Expected
180
+ ],
181
+ 'array with multiple attachments' => [
182
+ WPMailArrayBuilder::aMail()->withAttachments([$exampleAttachment1, $exampleAttachment2])->build(),
183
+ $example1And2Expected
184
+ ],
185
+ ];
186
+ }
187
+ }
tests/phpunit/unit/WPML_MailRenderer_Test.php ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests;
4
+
5
+ use Mockery;
6
+ use No3x\WPML\Model\WPML_Mail;
7
+ use No3x\WPML\Renderer\WPML_MailRenderer;
8
+ use No3x\WPML\Tests\Helper\WPMailArrayBuilder;
9
+ use No3x\WPML\WPML_MailExtractor;
10
+
11
+ class WPML_MailRenderer_Test extends \PHPUnit_Framework_TestCase {
12
+
13
+ /** @var WPML_MailRenderer */
14
+ private $mailRenderer;
15
+
16
+ /** @var $mailServiceMock \No3x\WPML\Model\IMailService|\Mockery\MockInterface */
17
+ private $mailServiceMock;
18
+
19
+ private $id = 2;
20
+
21
+ public function setUp() {
22
+ $this->mailServiceMock = Mockery::mock('No3x\WPML\Model\IMailService');
23
+
24
+ /** @var $mail WPML_Mail */
25
+ $mail = (new WPML_MailExtractor())->extract(WPMailArrayBuilder::aMail()
26
+ ->withSubject("Test")
27
+ ->withTo("example@exmple.com")
28
+ ->withHeaders("From: \"admin\" <admin@local.test>\r\n,\nCc: example2@example.com,\nReply-To: admin <admin@local.test>\r\n")
29
+ ->withMessage("<b>Bold</b><script>alert('xss');</script>")
30
+ ->withAttachments(["file.pdf"])
31
+ ->build());
32
+ $mail->set_mail_id($this->id);
33
+ $mail->set_plugin_version('1.8.5');
34
+ $mail->set_timestamp('2018-09-24 16:02:11');
35
+ $mail->set_host('127.0.0.1');
36
+ $mail->set_error('a');
37
+
38
+ $this->mailServiceMock->shouldReceive('find_one')
39
+ ->times(1)
40
+ ->with( $this->id )
41
+ ->andReturn( $mail );
42
+
43
+ $this->mailRenderer = new WPML_MailRenderer($this->mailServiceMock);
44
+ }
45
+
46
+ public function test_print_mail_json() {
47
+ $expected = '<pre>{
48
+ "mail_id": "2",
49
+ "timestamp": "2018-09-24 16:02:11",
50
+ "host": "127.0.0.1",
51
+ "receiver": "example@exmple.com",
52
+ "subject": "Test",
53
+ "message": "&lt;b&gt;Bold&lt;\/b&gt;&lt;script&gt;alert(\'xss\');&lt;\/script&gt;",
54
+ "headers": "From: &quot;admin&quot; &lt;admin@local.test&gt;\r\n,\nCc: example2@example.com,\nReply-To: admin &lt;admin@local.test&gt;\r\n",
55
+ "attachments": "file.pdf",
56
+ "error": "a",
57
+ "plugin_version": "1.8.5"
58
+ }</pre>';
59
+
60
+ $this->assertEquals($expected, $this->mailRenderer->render($this->id, WPML_MailRenderer::FORMAT_JSON));
61
+ $this->mailServiceMock->mockery_verify();
62
+ }
63
+
64
+ public function test_supported_formats() {
65
+ $this->assertEquals(['raw', 'html', 'json'], $this->mailRenderer->getSupportedFormats());
66
+ }
67
+
68
+ public function test_print_mail_json_fallback_to_raw() {
69
+ $this->mailServiceMock = Mockery::mock('No3x\WPML\Model\IMailService');
70
+
71
+ /** @var $mail WPML_Mail */
72
+ $mail = (new WPML_MailExtractor())->extract(WPMailArrayBuilder::aMail()
73
+ ->withSubject("Test")
74
+ ->withTo("example@exmple.com")
75
+ ->withHeaders("Content-Type: text/html")
76
+ ->withMessage("Message")
77
+ ->build());
78
+ $mail->set_mail_id($this->id);
79
+ $mail->set_plugin_version('1.8.5');
80
+ $mail->set_timestamp('2018-09-24 16:02:11');
81
+ $mail->set_host('127.0.0.1');
82
+ $mail->set_error('a');
83
+
84
+ $this->mailServiceMock->shouldReceive('find_one')
85
+ ->times(1)
86
+ ->with( $this->id )
87
+ ->andReturn( $mail );
88
+
89
+ $this->mailRenderer = new WPML_MailRenderer($this->mailServiceMock);
90
+ $this->assertContains("Fallback", $this->mailRenderer->render($this->id, WPML_MailRenderer::FORMAT_JSON));
91
+ $this->mailServiceMock->mockery_verify();
92
+ }
93
+
94
+ public function test_print_mail_raw() {
95
+ $expected = '<span class="title">Time: </span>2018-09-24 16:02:11<span class="title">Receiver: </span>example@exmple.com<span class="title">Subject: </span>Test<span class="title">Message: </span>&lt;b&gt;Bold&lt;/b&gt;<span class="title">Headers: </span>From: &quot;admin&quot; ,\nCc: example2@example.com,\nReply-To: admin <span class="title">Attachments: </span><span class="title">Error: </span><i class="fa fa-exclamation-circle" title="a"></i>';
96
+ $actual = $this->mailRenderer->render($this->id, WPML_MailRenderer::FORMAT_RAW);
97
+ $this->assertContains('2018-09-24 16:02:11', $actual, "The timestamp should be in the rendered mail");
98
+ $this->assertContains('Test', $actual, "The subject should be in the rendered mail");
99
+ $this->assertContains('&lt;b&gt;Bold&lt;/b&gt;', $actual, "The rendered mail must have html tags (<b>) escaped");
100
+ $this->assertNotContains('<script>alert(', $actual, "The rendered mail must strip out evil tags to protect against xss");
101
+ $this->assertNotContains('<i', $actual, "The rendered mail has no icons set because it show the and attachments raw");
102
+ }
103
+
104
+ public function test_print_mail_html() {
105
+ $expected = '<span class="title">Time: </span>2018-09-24 16:02:11<span class="title">Receiver: </span>example@exmple.com<span class="title">Subject: </span>Test<span class="title">Message: </span><b>Bold</b><span class="title">Headers: </span>From: "admin" ,\nCc: example2@example.com,\nReply-To: admin <span class="title">Attachments: </span><span class="title">Error: </span><i class="fa fa-exclamation-circle" title="a"></i>';
106
+ $actual = $this->mailRenderer->render($this->id, WPML_MailRenderer::FORMAT_HTML);
107
+ $this->assertContains('2018-09-24 16:02:11', $actual, "The timestamp should be in the rendered mail");
108
+ $this->assertContains('Test', $actual, "The subject should be in the rendered mail");
109
+ $this->assertContains('<b>Bold</b>', $actual, "The rendered mail must have html tags (<b>) not escaped");
110
+ $this->assertNotContains('<script>alert(', $actual, "The rendered mail must strip out evil tags to protect against xss");
111
+ $this->assertContains('<i class="fa fa-exclamation-circle"', $actual, "The rendered mail has icons for the error returned as html, it must not be escaped");
112
+ $this->assertContains('<i class="fa fa-times"', $actual, "The rendered mail has icons for the attachments returned as html, it must not be escaped");
113
+ }
114
+
115
+ /**
116
+ * @dataProvider evilTextProvider
117
+ * @param $evilText string the message to be rendered
118
+ * @param $expected string the expected output
119
+ */
120
+ function test_messageSanitationOnMessageAndSubject($evilText, $expected) {
121
+
122
+ $this->mailServiceMock = Mockery::mock('No3x\WPML\Model\IMailService');
123
+
124
+ /** @var $mail_raw WPML_Mail */
125
+ $mail_raw = (new WPML_MailExtractor())->extract(WPMailArrayBuilder::aMail()
126
+ ->withSubject($evilText)
127
+ ->withTo("example@exmple.com")
128
+ ->withHeaders("From: \"admin\" <admin@local.test>\r\n,\nCc: example2@example.com,\nReply-To: admin <admin@local.test>\r\n")
129
+ ->withMessage($evilText)
130
+ ->build())
131
+ ;
132
+ $mail_raw->set_mail_id($this->id);
133
+ $mail_raw->set_plugin_version('1.8.5');
134
+ $mail_raw->set_timestamp('2018-09-24 16:02:11');
135
+ $mail_raw->set_host('127.0.0.1');
136
+ $mail_raw->set_error('a');
137
+
138
+ /** @var $mail_html WPML_Mail */
139
+ $mail_html = WPML_Mail::create($mail_raw->to_array());
140
+ $mail_html->set_subject($evilText);
141
+ $mail_html->set_message($evilText);
142
+
143
+ $this->mailServiceMock->shouldReceive('find_one')
144
+ ->times(1)
145
+ ->with( $this->id )
146
+ ->andReturn( $mail_raw )
147
+ // And then return $mail_html
148
+ ->andReturn( $mail_html )
149
+ ;
150
+
151
+ $this->mailRenderer = new WPML_MailRenderer($this->mailServiceMock);
152
+
153
+ $this->assertMessageAndTitleEqual($expected[0], WPML_MailRenderer::FORMAT_RAW);
154
+ $this->assertMessageAndTitleEqual($expected[1], WPML_MailRenderer::FORMAT_HTML);
155
+ }
156
+
157
+ private function assertMessageAndTitleEqual($expected, $format) {
158
+ $mail = $this->mailRenderer->render($this->id, $format);
159
+ $mail_message = $this->get_string_between($mail, 'Message: </span>', '<span ');
160
+ $mail_subject = $this->get_string_between($mail, 'Subject: </span>', '<span ');
161
+ $this->assertEquals($expected, $mail_message);
162
+ $this->assertEquals($expected, $mail_subject);
163
+ }
164
+
165
+ private function get_string_between($string, $start, $end){
166
+ $string = ' ' . $string;
167
+ $ini = strpos($string, $start);
168
+ if ($ini == 0) return "Could not find '{$start}' in the string";
169
+ $ini += strlen($start);
170
+ $len = strpos($string, $end, $ini) - $ini;
171
+ return substr($string, $ini, $len);
172
+ }
173
+
174
+ function evilTextProvider() {
175
+ return [
176
+ "plaintext" => [
177
+ "Hello World",
178
+ [
179
+ "Hello World",
180
+ "Hello World",
181
+ ]
182
+ ],
183
+ "html bold" => [
184
+ "<b>Hello World</b>",
185
+ [
186
+ "&lt;b&gt;Hello World&lt;/b&gt;",
187
+ "<b>Hello World</b>",
188
+ ]
189
+ ],
190
+ "style" => [
191
+ "<style>body {background-color: red;}</style>",
192
+ [
193
+ "&lt;style&gt;body {background-color: red;}&lt;/style&gt;",
194
+ "<style>body {background-color: red;}</style>",
195
+ ]
196
+ ],
197
+ "script alert()" => [
198
+ "<script>alert('XSS hacking!');</script>",
199
+ [
200
+ "&lt;script&gt;alert('XSS hacking!');&lt;/script&gt;",
201
+ "alert('XSS hacking!');",
202
+ ]
203
+ ],
204
+ "html comment" => [
205
+ "<!-- Comment -->",
206
+ [
207
+ "&lt;!-- Comment --&gt;",
208
+ "<!-- Comment -->",
209
+ ]
210
+ ],
211
+ "html encoded comment" => [
212
+ "&lt;!-- Comment --&gt;",
213
+ [
214
+ "&lt;!-- Comment --&gt;",
215
+ "&lt;!-- Comment --&gt;",
216
+ ]
217
+ ],
218
+ "html embedded tag in comment" => [
219
+ "<!-- <b>This is commented out actually</b> -->",
220
+ [
221
+ "&lt;!-- &lt;b&gt;This is commented out actually&lt;/b&gt; --&gt;",
222
+ "<!-- <b>This is commented out actually</b> -->",
223
+ ]
224
+ ],
225
+ ];
226
+ }
227
+
228
+ }
tests/phpunit/unit/WPML_MessageSanitizer_Test.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Unit;
4
+
5
+ use No3x\WPML\WPML_MessageSanitizer;
6
+
7
+ class WPML_MessageSanitizer_Test extends \PHPUnit_Framework_TestCase {
8
+
9
+ /** @var WPML_MessageSanitizer */
10
+ private $messageSanitizer;
11
+
12
+ function setUp() {
13
+ parent::setUp();
14
+ $this->messageSanitizer = new WPML_MessageSanitizer();
15
+ }
16
+
17
+ /**
18
+ * The sanitizer removes evil code from the text to output.
19
+ * It removes unsafe html and keeps html comments.
20
+ * @dataProvider messagesProvider
21
+ * @param $message string the message to be sanitized
22
+ * @param $expected string the expected output
23
+ */
24
+ function test_messageSanitation($message, $expected) {
25
+ $this->assertEquals($expected, $this->messageSanitizer->sanitize($message));
26
+ }
27
+
28
+ function messagesProvider() {
29
+ return [
30
+ "plaintext" => [
31
+ "Hello World",
32
+ "Hello World"
33
+ ],
34
+ "html bold" => [
35
+ "<b>Hello World</b>",
36
+ "<b>Hello World</b>"
37
+ ],
38
+ "style" => [
39
+ "<style>body {background-color: red;}</style>",
40
+ "<style>body {background-color: red;}</style>"
41
+ ],
42
+ "script alert()" => [
43
+ "<script>alert('XSS hacking!');</script>",
44
+ "alert('XSS hacking!');"
45
+ ],
46
+ "html comment" => [
47
+ "<!-- Comment -->",
48
+ "<!-- Comment -->"
49
+ ],
50
+ "html encoded comment" => [
51
+ "&lt;!-- Comment --&gt;",
52
+ "&lt;!-- Comment --&gt;"
53
+ ],
54
+ "html embedded tag in comment" => [
55
+ "<!-- <b>This is commented out actually</b> -->",
56
+ "<!-- <b>This is commented out actually</b> -->"
57
+ ],
58
+ ];
59
+ }
60
+ }
tests/phpunit/unit/WPML_Plugin_Test.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Unit;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+ use No3x\WPML\WPML_Plugin;
7
+
8
+ /**
9
+ * @author No3x
10
+ * Tests are written in the AAA-Rule
11
+ * There are three basic sections for our test: Arrange, Act, and Assert.
12
+ */
13
+ class WPML_Plugin_Test extends \PHPUnit_Framework_TestCase {
14
+
15
+ function test_getTablename() {
16
+ global $wpdb;
17
+
18
+ // Arrange
19
+ $tableName = 'testTable';
20
+ // Act
21
+ $prefixed = WPML_Plugin::getTablename( $tableName );
22
+ // Assert
23
+ $this->assertEquals( $wpdb->prefix . 'wpml_testTable', $prefixed );
24
+ }
25
+
26
+ function test_version_compare() {
27
+ $this->assertEquals( 1, version_compare( '1.6.0', '1.4.0' ) );
28
+ $this->assertEquals( 1, version_compare( '1.4.0', '1.4.0_betaR1' ) );
29
+ $this->assertEquals( 1, version_compare( '1.4.0_betaR2', '1.4.0_betaR1' ) );
30
+ $this->assertEquals( 1, version_compare( '1.6.0_betaR2', '1.4.0_betaR1' ) );
31
+ }
32
+
33
+ function test_getClass() {
34
+ $this->assertEquals( 'No3x\WPML\WPML_Plugin', WPML_Plugin::getClass());
35
+ }
36
+ }
tests/phpunit/unit/WPML_PrivacyController_Test.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests\Unit;
4
+
5
+ use No3x\WPML\Model\WPML_Mail;
6
+ use No3x\WPML\ORM\DefaultQueryFactory;
7
+ use No3x\WPML\ORM\QueryFactory;
8
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
9
+ use No3x\WPML\WPML_PrivacyController;
10
+
11
+ /**
12
+ * Class WPML_MailExtractor_Test tests the function of the privacy integration
13
+ * @package No3x\WPML\Tests
14
+ */
15
+ class WPML_PrivacyController_Test extends \PHPUnit_Framework_TestCase {
16
+
17
+ const emailAddress = 'example@example.com';
18
+
19
+ /** @var WPML_PrivacyController */
20
+ private $privacyController;
21
+
22
+ /** @var \No3x\WPML\ORM\Query|\PHPUnit_Framework_MockObject_MockObject $queryMock */
23
+ private $queryMock;
24
+
25
+ function setUp() {
26
+ parent::setUp();
27
+
28
+ $this->queryMock = self::getMockBuilder('No3x\WPML\ORM\Query')
29
+ ->disableOriginalConstructor()
30
+ ->setMethods(['find'])
31
+ ->getMock()
32
+ ;
33
+
34
+ WPML_Mail::setQueryFactory(new QueryMockFactory($this->queryMock));
35
+
36
+ $this->privacyController = new WPML_PrivacyController(null);
37
+ }
38
+
39
+ public function testQueryMockFactory() {
40
+ $this->assertInstanceOf('No3x\WPML\ORM\Query', WPML_Mail::query());
41
+ }
42
+
43
+ private function mockMail($id) {
44
+ $mail1 = self::getMockBuilder('No3x\WPML\ORM\Query')
45
+ ->disableOriginalConstructor()
46
+ ->setMethods(['get_mail_id', 'to_array', 'delete'])
47
+ ->getMock()
48
+ ;
49
+
50
+ $mail1->expects(self::any())
51
+ ->method('get_mail_id')
52
+ ->willReturn($id)
53
+ ;
54
+
55
+ $mail1->expects(self::any())
56
+ ->method('to_array')
57
+ ->willReturn(['mail_id' => $id])
58
+ ;
59
+
60
+ $mail1->expects(self::any())
61
+ ->method('delete')
62
+ ->willReturn(true)
63
+ ;
64
+
65
+ return $mail1;
66
+ }
67
+
68
+ private function setupPrivacyControllerDataQuery() {
69
+
70
+ $mail1 = $this->mockMail(1);
71
+ $mail2 = $this->mockMail(2);
72
+
73
+ $data = [$mail1, $mail2];
74
+
75
+ $this->queryMock->expects(self::once())
76
+ ->method('find')
77
+ ->willReturn($data)
78
+ ;
79
+
80
+ $idsOfMocks = [1, 2];
81
+
82
+ return $idsOfMocks;
83
+ }
84
+
85
+ public function testExport() {
86
+ $idsExpected = $this->setupPrivacyControllerDataQuery();
87
+
88
+ $export = $this->privacyController->export(self::emailAddress);
89
+
90
+ $idsActual = [
91
+ $export['data'][0]['data'][0]['value'],
92
+ $export['data'][1]['data'][0]['value']
93
+ ];
94
+
95
+ $this->assertEquals($idsExpected, $idsActual);
96
+ $this->assertTrue($export['done']);
97
+ }
98
+
99
+ public function testErasure() {
100
+ $this->setupPrivacyControllerDataQuery();
101
+
102
+ $erase = $this->privacyController->erase(self::emailAddress);
103
+
104
+ $this->assertTrue($erase['done']);
105
+ $this->assertTrue($erase['items_removed']);
106
+ $this->assertFalse($erase['items_retained']);
107
+ }
108
+
109
+
110
+ }
111
+
112
+ class QueryMockFactory implements QueryFactory {
113
+
114
+ private $queryMock;
115
+
116
+ public function __construct($queryMock) {
117
+ $this->queryMock = $queryMock;
118
+ }
119
+
120
+ public function buildQuery($modelClass) {
121
+ return $this->queryMock;
122
+ }
123
+ }
tests/phpunit/unit/WPML_Utils_Test.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace No3x\WPML\Tests;
4
+
5
+ use No3x\WPML\Tests\Helper\WPML_IntegrationTestCase;
6
+ use No3x\WPML\WPML_Utils;
7
+
8
+ class WPML_Utils_Test extends \PHPUnit_Framework_TestCase {
9
+
10
+ public function test_determine_fa_icon() {
11
+ // test file icon rendered properly (gh-90)
12
+ $this->assertNotSame('<i class="fa fa-file-file-o"></i>', WPML_Utils::determine_fa_icon("file"));
13
+ $this->assertSame('<i class="fa fa-file-o"></i>', WPML_Utils::determine_fa_icon("file"));
14
+ $this->assertSame('<i class="fa fa-file-archive-o"></i>', WPML_Utils::determine_fa_icon("archive"));
15
+ }
16
+
17
+ /**
18
+ * The sanitizer removes evil code from the text to output.
19
+ * It removes unsafe html and keeps html comments.
20
+ * @dataProvider expectedValuesProvider
21
+ * @param $in string the message to be sanitized
22
+ * @param $expected string the expected output
23
+ */
24
+ function test_sanitizeExpectedValue($in, $expected) {
25
+ $this->assertSame($expected, WPML_Utils::sanitize_expected_value($in['value'], $in['allowed_values'], $in['default_value']));
26
+ }
27
+
28
+ function expectedValuesProvider() {
29
+ return [
30
+ 'allowed value is allowed value' => [
31
+ [
32
+ 'value' => 'a',
33
+ 'allowed_values' => 'a',
34
+ 'default_value' => null
35
+ ],
36
+ 'a'
37
+ ],
38
+ 'allowed value in set' => [
39
+ [
40
+ 'value' => 'a',
41
+ 'allowed_values' => [
42
+ 'a'
43
+ ],
44
+ 'default_value' => null
45
+ ],
46
+ 'a'
47
+ ],
48
+ 'allowed value in set with others' => [
49
+ [
50
+ 'value' => 'a',
51
+ 'allowed_values' => [
52
+ 'b', 'a', 'c'
53
+ ],
54
+ 'default_value' => null
55
+ ],
56
+ 'a'
57
+ ],
58
+ 'default if no match' => [
59
+ [
60
+ 'value' => 'a',
61
+ 'allowed_values' => [
62
+ 'b'
63
+ ],
64
+ 'default_value' => 'hello world'
65
+ ],
66
+ 'hello world'
67
+ ],
68
+ 'false if no match and no meaningful default' => [
69
+ [
70
+ 'value' => 'a',
71
+ 'allowed_values' => [
72
+ 'b'
73
+ ],
74
+ 'default_value' => null
75
+ ],
76
+ false
77
+ ],
78
+ ];
79
+ }
80
+ }
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit64bcd231a77bf5f7870b38c2e715eb2f::getLoader();
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see https://www.php-fig.org/psr/psr-0/
41
+ * @see https://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ private $vendorDir;
46
+
47
+ // PSR-4
48
+ private $prefixLengthsPsr4 = array();
49
+ private $prefixDirsPsr4 = array();
50
+ private $fallbackDirsPsr4 = array();
51
+
52
+ // PSR-0
53
+ private $prefixesPsr0 = array();
54
+ private $fallbackDirsPsr0 = array();
55
+
56
+ private $useIncludePath = false;
57
+ private $classMap = array();
58
+ private $classMapAuthoritative = false;
59
+ private $missingClasses = array();
60
+ private $apcuPrefix;
61
+
62
+ private static $registeredLoaders = array();
63
+
64
+ public function __construct($vendorDir = null)
65
+ {
66
+ $this->vendorDir = $vendorDir;
67
+ }
68
+
69
+ public function getPrefixes()
70
+ {
71
+ if (!empty($this->prefixesPsr0)) {
72
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
73
+ }
74
+
75
+ return array();
76
+ }
77
+
78
+ public function getPrefixesPsr4()
79
+ {
80
+ return $this->prefixDirsPsr4;
81
+ }
82
+
83
+ public function getFallbackDirs()
84
+ {
85
+ return $this->fallbackDirsPsr0;
86
+ }
87
+
88
+ public function getFallbackDirsPsr4()
89
+ {
90
+ return $this->fallbackDirsPsr4;
91
+ }
92
+
93
+ public function getClassMap()
94
+ {
95
+ return $this->classMap;
96
+ }
97
+
98
+ /**
99
+ * @param array $classMap Class to filename map
100
+ */
101
+ public function addClassMap(array $classMap)
102
+ {
103
+ if ($this->classMap) {
104
+ $this->classMap = array_merge($this->classMap, $classMap);
105
+ } else {
106
+ $this->classMap = $classMap;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Registers a set of PSR-0 directories for a given prefix, either
112
+ * appending or prepending to the ones previously set for this prefix.
113
+ *
114
+ * @param string $prefix The prefix
115
+ * @param array|string $paths The PSR-0 root directories
116
+ * @param bool $prepend Whether to prepend the directories
117
+ */
118
+ public function add($prefix, $paths, $prepend = false)
119
+ {
120
+ if (!$prefix) {
121
+ if ($prepend) {
122
+ $this->fallbackDirsPsr0 = array_merge(
123
+ (array) $paths,
124
+ $this->fallbackDirsPsr0
125
+ );
126
+ } else {
127
+ $this->fallbackDirsPsr0 = array_merge(
128
+ $this->fallbackDirsPsr0,
129
+ (array) $paths
130
+ );
131
+ }
132
+
133
+ return;
134
+ }
135
+
136
+ $first = $prefix[0];
137
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
138
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
139
+
140
+ return;
141
+ }
142
+ if ($prepend) {
143
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
144
+ (array) $paths,
145
+ $this->prefixesPsr0[$first][$prefix]
146
+ );
147
+ } else {
148
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
149
+ $this->prefixesPsr0[$first][$prefix],
150
+ (array) $paths
151
+ );
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Registers a set of PSR-4 directories for a given namespace, either
157
+ * appending or prepending to the ones previously set for this namespace.
158
+ *
159
+ * @param string $prefix The prefix/namespace, with trailing '\\'
160
+ * @param array|string $paths The PSR-4 base directories
161
+ * @param bool $prepend Whether to prepend the directories
162
+ *
163
+ * @throws \InvalidArgumentException
164
+ */
165
+ public function addPsr4($prefix, $paths, $prepend = false)
166
+ {
167
+ if (!$prefix) {
168
+ // Register directories for the root namespace.
169
+ if ($prepend) {
170
+ $this->fallbackDirsPsr4 = array_merge(
171
+ (array) $paths,
172
+ $this->fallbackDirsPsr4
173
+ );
174
+ } else {
175
+ $this->fallbackDirsPsr4 = array_merge(
176
+ $this->fallbackDirsPsr4,
177
+ (array) $paths
178
+ );
179
+ }
180
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
181
+ // Register directories for a new namespace.
182
+ $length = strlen($prefix);
183
+ if ('\\' !== $prefix[$length - 1]) {
184
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
185
+ }
186
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
187
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
188
+ } elseif ($prepend) {
189
+ // Prepend directories for an already registered namespace.
190
+ $this->prefixDirsPsr4[$prefix] = array_merge(
191
+ (array) $paths,
192
+ $this->prefixDirsPsr4[$prefix]
193
+ );
194
+ } else {
195
+ // Append directories for an already registered namespace.
196
+ $this->prefixDirsPsr4[$prefix] = array_merge(
197
+ $this->prefixDirsPsr4[$prefix],
198
+ (array) $paths
199
+ );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Registers a set of PSR-0 directories for a given prefix,
205
+ * replacing any others previously set for this prefix.
206
+ *
207
+ * @param string $prefix The prefix
208
+ * @param array|string $paths The PSR-0 base directories
209
+ */
210
+ public function set($prefix, $paths)
211
+ {
212
+ if (!$prefix) {
213
+ $this->fallbackDirsPsr0 = (array) $paths;
214
+ } else {
215
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Registers a set of PSR-4 directories for a given namespace,
221
+ * replacing any others previously set for this namespace.
222
+ *
223
+ * @param string $prefix The prefix/namespace, with trailing '\\'
224
+ * @param array|string $paths The PSR-4 base directories
225
+ *
226
+ * @throws \InvalidArgumentException
227
+ */
228
+ public function setPsr4($prefix, $paths)
229
+ {
230
+ if (!$prefix) {
231
+ $this->fallbackDirsPsr4 = (array) $paths;
232
+ } else {
233
+ $length = strlen($prefix);
234
+ if ('\\' !== $prefix[$length - 1]) {
235
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
236
+ }
237
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
238
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Turns on searching the include path for class files.
244
+ *
245
+ * @param bool $useIncludePath
246
+ */
247
+ public function setUseIncludePath($useIncludePath)
248
+ {
249
+ $this->useIncludePath = $useIncludePath;
250
+ }
251
+
252
+ /**
253
+ * Can be used to check if the autoloader uses the include path to check
254
+ * for classes.
255
+ *
256
+ * @return bool
257
+ */
258
+ public function getUseIncludePath()
259
+ {
260
+ return $this->useIncludePath;
261
+ }
262
+
263
+ /**
264
+ * Turns off searching the prefix and fallback directories for classes
265
+ * that have not been registered with the class map.
266
+ *
267
+ * @param bool $classMapAuthoritative
268
+ */
269
+ public function setClassMapAuthoritative($classMapAuthoritative)
270
+ {
271
+ $this->classMapAuthoritative = $classMapAuthoritative;
272
+ }
273
+
274
+ /**
275
+ * Should class lookup fail if not found in the current class map?
276
+ *
277
+ * @return bool
278
+ */
279
+ public function isClassMapAuthoritative()
280
+ {
281
+ return $this->classMapAuthoritative;
282
+ }
283
+
284
+ /**
285
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
286
+ *
287
+ * @param string|null $apcuPrefix
288
+ */
289
+ public function setApcuPrefix($apcuPrefix)
290
+ {
291
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
292
+ }
293
+
294
+ /**
295
+ * The APCu prefix in use, or null if APCu caching is not enabled.
296
+ *
297
+ * @return string|null
298
+ */
299
+ public function getApcuPrefix()
300
+ {
301
+ return $this->apcuPrefix;
302
+ }
303
+
304
+ /**
305
+ * Registers this instance as an autoloader.
306
+ *
307
+ * @param bool $prepend Whether to prepend the autoloader or not
308
+ */
309
+ public function register($prepend = false)
310
+ {
311
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
312
+
313
+ if (null === $this->vendorDir) {
314
+ return;
315
+ }
316
+
317
+ if ($prepend) {
318
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
319
+ } else {
320
+ unset(self::$registeredLoaders[$this->vendorDir]);
321
+ self::$registeredLoaders[$this->vendorDir] = $this;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Unregisters this instance as an autoloader.
327
+ */
328
+ public function unregister()
329
+ {
330
+ spl_autoload_unregister(array($this, 'loadClass'));
331
+
332
+ if (null !== $this->vendorDir) {
333
+ unset(self::$registeredLoaders[$this->vendorDir]);
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Loads the given class or interface.
339
+ *
340
+ * @param string $class The name of the class
341
+ * @return true|null True if loaded, null otherwise
342
+ */
343
+ public function loadClass($class)
344
+ {
345
+ if ($file = $this->findFile($class)) {
346
+ includeFile($file);
347
+
348
+ return true;
349
+ }
350
+
351
+ return null;
352
+ }
353
+
354
+ /**
355
+ * Finds the path to the file where the class is defined.
356
+ *
357
+ * @param string $class The name of the class
358
+ *
359
+ * @return string|false The path if found, false otherwise
360
+ */
361
+ public function findFile($class)
362
+ {
363
+ // class map lookup
364
+ if (isset($this->classMap[$class])) {
365
+ return $this->classMap[$class];
366
+ }
367
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
368
+ return false;
369
+ }
370
+ if (null !== $this->apcuPrefix) {
371
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
372
+ if ($hit) {
373
+ return $file;
374
+ }
375
+ }
376
+
377
+ $file = $this->findFileWithExtension($class, '.php');
378
+
379
+ // Search for Hack files if we are running on HHVM
380
+ if (false === $file && defined('HHVM_VERSION')) {
381
+ $file = $this->findFileWithExtension($class, '.hh');
382
+ }
383
+
384
+ if (null !== $this->apcuPrefix) {
385
+ apcu_add($this->apcuPrefix.$class, $file);
386
+ }
387
+
388
+ if (false === $file) {
389
+ // Remember that this class does not exist.
390
+ $this->missingClasses[$class] = true;
391
+ }
392
+
393
+ return $file;
394
+ }
395
+
396
+ /**
397
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
398
+ *
399
+ * @return self[]
400
+ */
401
+ public static function getRegisteredLoaders()
402
+ {
403
+ return self::$registeredLoaders;
404
+ }
405
+
406
+ private function findFileWithExtension($class, $ext)
407
+ {
408
+ // PSR-4 lookup
409
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
410
+
411
+ $first = $class[0];
412
+ if (isset($this->prefixLengthsPsr4[$first])) {
413
+ $subPath = $class;
414
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
415
+ $subPath = substr($subPath, 0, $lastPos);
416
+ $search = $subPath . '\\';
417
+ if (isset($this->prefixDirsPsr4[$search])) {
418
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
419
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
420
+ if (file_exists($file = $dir . $pathEnd)) {
421
+ return $file;
422
+ }
423
+ }
424
+ }
425
+ }
426
+ }
427
+
428
+ // PSR-4 fallback dirs
429
+ foreach ($this->fallbackDirsPsr4 as $dir) {
430
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
431
+ return $file;
432
+ }
433
+ }
434
+
435
+ // PSR-0 lookup
436
+ if (false !== $pos = strrpos($class, '\\')) {
437
+ // namespaced class name
438
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
439
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
440
+ } else {
441
+ // PEAR-like class name
442
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
443
+ }
444
+
445
+ if (isset($this->prefixesPsr0[$first])) {
446
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
447
+ if (0 === strpos($class, $prefix)) {
448
+ foreach ($dirs as $dir) {
449
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
450
+ return $file;
451
+ }
452
+ }
453
+ }
454
+ }
455
+ }
456
+
457
+ // PSR-0 fallback dirs
458
+ foreach ($this->fallbackDirsPsr0 as $dir) {
459
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
460
+ return $file;
461
+ }
462
+ }
463
+
464
+ // PSR-0 include paths.
465
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
466
+ return $file;
467
+ }
468
+
469
+ return false;
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Scope isolated include.
475
+ *
476
+ * Prevents access to $this/self from included files.
477
+ */
478
+ function includeFile($file)
479
+ {
480
+ include $file;
481
+ }
vendor/composer/InstalledVersions.php ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer;
14
+
15
+ use Composer\Autoload\ClassLoader;
16
+ use Composer\Semver\VersionParser;
17
+
18
+ /**
19
+ * This class is copied in every Composer installed project and available to all
20
+ *
21
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
+ *
23
+ * To require it's presence, you can require `composer-runtime-api ^2.0`
24
+ */
25
+ class InstalledVersions
26
+ {
27
+ private static $installed;
28
+ private static $canGetVendors;
29
+ private static $installedByVendor = array();
30
+
31
+ /**
32
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
33
+ *
34
+ * @return string[]
35
+ * @psalm-return list<string>
36
+ */
37
+ public static function getInstalledPackages()
38
+ {
39
+ $packages = array();
40
+ foreach (self::getInstalled() as $installed) {
41
+ $packages[] = array_keys($installed['versions']);
42
+ }
43
+
44
+ if (1 === \count($packages)) {
45
+ return $packages[0];
46
+ }
47
+
48
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
49
+ }
50
+
51
+ /**
52
+ * Returns a list of all package names with a specific type e.g. 'library'
53
+ *
54
+ * @param string $type
55
+ * @return string[]
56
+ * @psalm-return list<string>
57
+ */
58
+ public static function getInstalledPackagesByType($type)
59
+ {
60
+ $packagesByType = array();
61
+
62
+ foreach (self::getInstalled() as $installed) {
63
+ foreach ($installed['versions'] as $name => $package) {
64
+ if (isset($package['type']) && $package['type'] === $type) {
65
+ $packagesByType[] = $name;
66
+ }
67
+ }
68
+ }
69
+
70
+ return $packagesByType;
71
+ }
72
+
73
+ /**
74
+ * Checks whether the given package is installed
75
+ *
76
+ * This also returns true if the package name is provided or replaced by another package
77
+ *
78
+ * @param string $packageName
79
+ * @param bool $includeDevRequirements
80
+ * @return bool
81
+ */
82
+ public static function isInstalled($packageName, $includeDevRequirements = true)
83
+ {
84
+ foreach (self::getInstalled() as $installed) {
85
+ if (isset($installed['versions'][$packageName])) {
86
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
87
+ }
88
+ }
89
+
90
+ return false;
91
+ }
92
+
93
+ /**
94
+ * Checks whether the given package satisfies a version constraint
95
+ *
96
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
97
+ *
98
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
99
+ *
100
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
101
+ * @param string $packageName
102
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
103
+ * @return bool
104
+ */
105
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
106
+ {
107
+ $constraint = $parser->parseConstraints($constraint);
108
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
109
+
110
+ return $provided->matches($constraint);
111
+ }
112
+
113
+ /**
114
+ * Returns a version constraint representing all the range(s) which are installed for a given package
115
+ *
116
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
117
+ * whether a given version of a package is installed, and not just whether it exists
118
+ *
119
+ * @param string $packageName
120
+ * @return string Version constraint usable with composer/semver
121
+ */
122
+ public static function getVersionRanges($packageName)
123
+ {
124
+ foreach (self::getInstalled() as $installed) {
125
+ if (!isset($installed['versions'][$packageName])) {
126
+ continue;
127
+ }
128
+
129
+ $ranges = array();
130
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
131
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
132
+ }
133
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
134
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
135
+ }
136
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
137
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
138
+ }
139
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
140
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
141
+ }
142
+
143
+ return implode(' || ', $ranges);
144
+ }
145
+
146
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
147
+ }
148
+
149
+ /**
150
+ * @param string $packageName
151
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
152
+ */
153
+ public static function getVersion($packageName)
154
+ {
155
+ foreach (self::getInstalled() as $installed) {
156
+ if (!isset($installed['versions'][$packageName])) {
157
+ continue;
158
+ }
159
+
160
+ if (!isset($installed['versions'][$packageName]['version'])) {
161
+ return null;
162
+ }
163
+
164
+ return $installed['versions'][$packageName]['version'];
165
+ }
166
+
167
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
168
+ }
169
+
170
+ /**
171
+ * @param string $packageName
172
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
173
+ */
174
+ public static function getPrettyVersion($packageName)
175
+ {
176
+ foreach (self::getInstalled() as $installed) {
177
+ if (!isset($installed['versions'][$packageName])) {
178
+ continue;
179
+ }
180
+
181
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
182
+ return null;
183
+ }
184
+
185
+ return $installed['versions'][$packageName]['pretty_version'];
186
+ }
187
+
188
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
189
+ }
190
+
191
+ /**
192
+ * @param string $packageName
193
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
194
+ */
195
+ public static function getReference($packageName)
196
+ {
197
+ foreach (self::getInstalled() as $installed) {
198
+ if (!isset($installed['versions'][$packageName])) {
199
+ continue;
200
+ }
201
+
202
+ if (!isset($installed['versions'][$packageName]['reference'])) {
203
+ return null;
204
+ }
205
+
206
+ return $installed['versions'][$packageName]['reference'];
207
+ }
208
+
209
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
210
+ }
211
+
212
+ /**
213
+ * @param string $packageName
214
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
215
+ */
216
+ public static function getInstallPath($packageName)
217
+ {
218
+ foreach (self::getInstalled() as $installed) {
219
+ if (!isset($installed['versions'][$packageName])) {
220
+ continue;
221
+ }
222
+
223
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
224
+ }
225
+
226
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
227
+ }
228
+
229
+ /**
230
+ * @return array
231
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
232
+ */
233
+ public static function getRootPackage()
234
+ {
235
+ $installed = self::getInstalled();
236
+
237
+ return $installed[0]['root'];
238
+ }
239
+
240
+ /**
241
+ * Returns the raw installed.php data for custom implementations
242
+ *
243
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
244
+ * @return array[]
245
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
246
+ */
247
+ public static function getRawData()
248
+ {
249
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
250
+
251
+ if (null === self::$installed) {
252
+ // only require the installed.php file if this file is loaded from its dumped location,
253
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
254
+ if (substr(__DIR__, -8, 1) !== 'C') {
255
+ self::$installed = include __DIR__ . '/installed.php';
256
+ } else {
257
+ self::$installed = array();
258
+ }
259
+ }
260
+
261
+ return self::$installed;
262
+ }
263
+
264
+ /**
265
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
266
+ *
267
+ * @return array[]
268
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
269
+ */
270
+ public static function getAllRawData()
271
+ {
272
+ return self::getInstalled();
273
+ }
274
+
275
+ /**
276
+ * Lets you reload the static array from another file
277
+ *
278
+ * This is only useful for complex integrations in which a project needs to use
279
+ * this class but then also needs to execute another project's autoloader in process,
280
+ * and wants to ensure both projects have access to their version of installed.php.
281
+ *
282
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
283
+ * the data it needs from this class, then call reload() with
284
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
285
+ * the project in which it runs can then also use this class safely, without
286
+ * interference between PHPUnit's dependencies and the project's dependencies.
287
+ *
288
+ * @param array[] $data A vendor/composer/installed.php data set
289
+ * @return void
290
+ *
291
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
292
+ */
293
+ public static function reload($data)
294
+ {
295
+ self::$installed = $data;
296
+ self::$installedByVendor = array();
297
+ }
298
+
299
+ /**
300
+ * @return array[]
301
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
302
+ */
303
+ private static function getInstalled()
304
+ {
305
+ if (null === self::$canGetVendors) {
306
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
307
+ }
308
+
309
+ $installed = array();
310
+
311
+ if (self::$canGetVendors) {
312
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
313
+ if (isset(self::$installedByVendor[$vendorDir])) {
314
+ $installed[] = self::$installedByVendor[$vendorDir];
315
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
316
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
317
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
318
+ self::$installed = $installed[count($installed) - 1];
319
+ }
320
+ }
321
+ }
322
+ }
323
+
324
+ if (null === self::$installed) {
325
+ // only require the installed.php file if this file is loaded from its dumped location,
326
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
327
+ if (substr(__DIR__, -8, 1) !== 'C') {
328
+ self::$installed = require __DIR__ . '/installed.php';
329
+ } else {
330
+ self::$installed = array();
331
+ }
332
+ }
333
+ $installed[] = self::$installed;
334
+
335
+ return $installed;
336
+ }
337
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
10
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'No3x\\WPML\\Tests\\Helper\\' => array($baseDir . '/tests/helper'),
10
+ 'No3x\\WPML\\Tests\\' => array($baseDir . '/tests/phpunit/tests'),
11
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit64bcd231a77bf5f7870b38c2e715eb2f
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @return \Composer\Autoload\ClassLoader
18
+ */
19
+ public static function getLoader()
20
+ {
21
+ if (null !== self::$loader) {
22
+ return self::$loader;
23
+ }
24
+
25
+ require __DIR__ . '/platform_check.php';
26
+
27
+ spl_autoload_register(array('ComposerAutoloaderInit64bcd231a77bf5f7870b38c2e715eb2f', 'loadClassLoader'), true, true);
28
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29
+ spl_autoload_unregister(array('ComposerAutoloaderInit64bcd231a77bf5f7870b38c2e715eb2f', 'loadClassLoader'));
30
+
31
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
+ if ($useStaticLoader) {
33
+ require __DIR__ . '/autoload_static.php';
34
+
35
+ call_user_func(\Composer\Autoload\ComposerStaticInit64bcd231a77bf5f7870b38c2e715eb2f::getInitializer($loader));
36
+ } else {
37
+ $map = require __DIR__ . '/autoload_namespaces.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->set($namespace, $path);
40
+ }
41
+
42
+ $map = require __DIR__ . '/autoload_psr4.php';
43
+ foreach ($map as $namespace => $path) {
44
+ $loader->setPsr4($namespace, $path);
45
+ }
46
+
47
+ $classMap = require __DIR__ . '/autoload_classmap.php';
48
+ if ($classMap) {
49
+ $loader->addClassMap($classMap);
50
+ }
51
+ }
52
+
53
+ $loader->register(true);
54
+
55
+ return $loader;
56
+ }
57
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit64bcd231a77bf5f7870b38c2e715eb2f
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'N' =>
11
+ array (
12
+ 'No3x\\WPML\\Tests\\Helper\\' => 23,
13
+ 'No3x\\WPML\\Tests\\' => 16,
14
+ ),
15
+ );
16
+
17
+ public static $prefixDirsPsr4 = array (
18
+ 'No3x\\WPML\\Tests\\Helper\\' =>
19
+ array (
20
+ 0 => __DIR__ . '/../..' . '/tests/helper',
21
+ ),
22
+ 'No3x\\WPML\\Tests\\' =>
23
+ array (
24
+ 0 => __DIR__ . '/../..' . '/tests/phpunit/tests',
25
+ ),
26
+ );
27
+
28
+ public static $classMap = array (
29
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
30
+ );
31
+
32
+ public static function getInitializer(ClassLoader $loader)
33
+ {
34
+ return \Closure::bind(function () use ($loader) {
35
+ $loader->prefixLengthsPsr4 = ComposerStaticInit64bcd231a77bf5f7870b38c2e715eb2f::$prefixLengthsPsr4;
36
+ $loader->prefixDirsPsr4 = ComposerStaticInit64bcd231a77bf5f7870b38c2e715eb2f::$prefixDirsPsr4;
37
+ $loader->classMap = ComposerStaticInit64bcd231a77bf5f7870b38c2e715eb2f::$classMap;
38
+
39
+ }, null, ClassLoader::class);
40
+ }
41
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ {
2
+ "packages": [],
3
+ "dev": false,
4
+ "dev-package-names": []
5
+ }
vendor/composer/installed.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php return array(
2
+ 'root' => array(
3
+ 'pretty_version' => 'dev-master',
4
+ 'version' => 'dev-master',
5
+ 'type' => 'wordpress-plugin',
6
+ 'install_path' => __DIR__ . '/../../',
7
+ 'aliases' => array(),
8
+ 'reference' => '82f63ffa7dcfa1746f9b199d64a6cfcfcf9df33b',
9
+ 'name' => 'no3x/wp-mail-logging',
10
+ 'dev' => false,
11
+ ),
12
+ 'versions' => array(
13
+ 'no3x/wp-mail-logging' => array(
14
+ 'pretty_version' => 'dev-master',
15
+ 'version' => 'dev-master',
16
+ 'type' => 'wordpress-plugin',
17
+ 'install_path' => __DIR__ . '/../../',
18
+ 'aliases' => array(),
19
+ 'reference' => '82f63ffa7dcfa1746f9b199d64a6cfcfcf9df33b',
20
+ 'dev_requirement' => false,
21
+ ),
22
+ ),
23
+ );
vendor/composer/platform_check.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // platform_check.php @generated by Composer
4
+
5
+ $issues = array();
6
+
7
+ if (!(PHP_VERSION_ID >= 50400)) {
8
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 5.4.0". You are running ' . PHP_VERSION . '.';
9
+ }
10
+
11
+ if ($issues) {
12
+ if (!headers_sent()) {
13
+ header('HTTP/1.1 500 Internal Server Error');
14
+ }
15
+ if (!ini_get('display_errors')) {
16
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18
+ } elseif (!headers_sent()) {
19
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20
+ }
21
+ }
22
+ trigger_error(
23
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
24
+ E_USER_ERROR
25
+ );
26
+ }
wp-mail-logging.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
  /*
3
  Plugin Name: WP Mail Logging
4
- Plugin URI: http://wordpress.org/extend/plugins/wp-mail-logging/
5
- Support URI: https://github.com/mailpoet/wp-mail-logging/issues
6
- Version: 1.9.7
7
- Author: MailPoet
8
- Author URI: https://www.mailpoet.com/
9
  Description: Logs each email sent by WordPress.
10
  Text Domain: wp-mail-logging
11
  License: GPLv3
1
  <?php
2
  /*
3
  Plugin Name: WP Mail Logging
4
+ Plugin URI: https://wordpress.org/plugins/wp-mail-logging/
5
+ Support URI: https://github.com/kgjerstad/wp-mail-logging/issues
6
+ Version: 1.9.8
7
+ Author: Wysija
8
+ Author URI: https://profiles.wordpress.org/wysija/
9
  Description: Logs each email sent by WordPress.
10
  Text Domain: wp-mail-logging
11
  License: GPLv3