diff --git a/.gitignore b/.gitignore index 2729c73bd..f64f1a3d8 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ node_modules test_shared/ **/cache/ storage/ +phpunit.xml diff --git a/composer.json b/composer.json index 0d41343b2..7b71616fc 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "monolog/monolog": "1.19.0", "geshi/geshi": "dev-master", "libchart/libchart": "1.4.0", - "phpmailer/phpmailer": "5.2.4", + "phpmailer/phpmailer": "5.2.27", "pear/archive_tar": "1.4.*", "pear/console_getopt": "1.4.*", "TYPO3/class-alias-loader": "^1.0", @@ -48,7 +48,10 @@ "smarty/smarty": "2.6.30", "pdepend/pdepend": "@stable", "chumper/zipper": "^1.0", - "php-imap/php-imap": "^3.0" + "php-imap/php-imap": "^3.0", + "nikic/php-parser": "3.1.5", + "laravel/tinker": "^1.0" + }, "require-dev": { "fzaninotto/faker": "^1.7", diff --git a/composer.lock b/composer.lock index de7acb1d9..f63ea0385 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3242f53453d9e9a3d7fdf3a56961ae40", + "content-hash": "a9ba65f7fb68be7c36dd45b62216b2c6", "packages": [ { "name": "bshaffer/oauth2-server-php", @@ -209,6 +209,39 @@ ], "time": "2018-03-09T06:07:41+00:00" }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "@stable" + }, + "type": "project", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "time": "2014-10-24T07:27:01+00:00" + }, { "name": "doctrine/inflector", "version": "v1.1.0", @@ -405,6 +438,94 @@ ], "time": "2015-10-16T22:11:08+00:00" }, + { + "name": "jakub-onderka/php-console-color", + "version": "v0.2", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", + "reference": "d5deaecff52a0d61ccb613bb3804088da0307191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/d5deaecff52a0d61ccb613bb3804088da0307191", + "reference": "d5deaecff52a0d61ccb613bb3804088da0307191", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "jakub-onderka/php-code-style": "1.0", + "jakub-onderka/php-parallel-lint": "1.0", + "jakub-onderka/php-var-dump-check": "0.*", + "phpunit/phpunit": "~4.3", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "JakubOnderka\\PhpConsoleColor\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com" + } + ], + "time": "2018-09-29T17:23:10+00:00" + }, + { + "name": "jakub-onderka/php-console-highlighter", + "version": "v0.4", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", + "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/9f7a229a69d52506914b4bc61bfdb199d90c5547", + "reference": "9f7a229a69d52506914b4bc61bfdb199d90c5547", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "jakub-onderka/php-console-color": "~0.2", + "php": ">=5.4.0" + }, + "require-dev": { + "jakub-onderka/php-code-style": "~1.0", + "jakub-onderka/php-parallel-lint": "~1.0", + "jakub-onderka/php-var-dump-check": "~0.1", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "JakubOnderka\\PhpConsoleHighlighter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "acci@acci.cz", + "homepage": "http://www.acci.cz/" + } + ], + "description": "Highlight PHP code in terminal", + "time": "2018-09-29T18:48:56+00:00" + }, { "name": "laravel/framework", "version": "v5.4.36", @@ -534,6 +655,69 @@ ], "time": "2017-08-30T09:26:16+00:00" }, + { + "name": "laravel/tinker", + "version": "v1.0.8", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "cafbf598a90acde68985660e79b2b03c5609a405" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/cafbf598a90acde68985660e79b2b03c5609a405", + "reference": "cafbf598a90acde68985660e79b2b03c5609a405", + "shasum": "" + }, + "require": { + "illuminate/console": "~5.1", + "illuminate/contracts": "~5.1", + "illuminate/support": "~5.1", + "php": ">=5.5.9", + "psy/psysh": "0.7.*|0.8.*|0.9.*", + "symfony/var-dumper": "~3.0|~4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (~5.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "time": "2018-10-12T19:39:35+00:00" + }, { "name": "league/flysystem", "version": "1.0.49", @@ -923,6 +1107,57 @@ ], "time": "2018-11-22T18:23:02+00:00" }, + { + "name": "nikic/php-parser", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2018-02-28T20:30:58+00:00" + }, { "name": "paragonie/random_compat", "version": "v2.0.17", @@ -1278,31 +1513,55 @@ }, { "name": "phpmailer/phpmailer", - "version": "v5.2.4", + "version": "v5.2.27", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "adb0197c106fad05c1fab28dbed5437133836c44" + "reference": "dde1db116511aa4956389d75546c5be4c2beb2a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/adb0197c106fad05c1fab28dbed5437133836c44", - "reference": "adb0197c106fad05c1fab28dbed5437133836c44", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/dde1db116511aa4956389d75546c5be4c2beb2a6", + "reference": "dde1db116511aa4956389d75546c5be4c2beb2a6", "shasum": "" }, "require": { + "ext-ctype": "*", "php": ">=5.0.0" }, "require-dev": { - "phpdocumentor/phpdocumentor": "*", - "phpunit/phpunit": "*" + "doctrine/annotations": "1.2.*", + "jms/serializer": "0.16.*", + "phpdocumentor/phpdocumentor": "2.*", + "phpunit/phpunit": "4.8.*", + "symfony/debug": "2.8.*", + "symfony/filesystem": "2.8.*", + "symfony/translation": "2.8.*", + "symfony/yaml": "2.8.*", + "zendframework/zend-cache": "2.5.1", + "zendframework/zend-config": "2.5.1", + "zendframework/zend-eventmanager": "2.5.1", + "zendframework/zend-filter": "2.5.1", + "zendframework/zend-i18n": "2.5.1", + "zendframework/zend-json": "2.5.1", + "zendframework/zend-math": "2.5.1", + "zendframework/zend-serializer": "2.5.*", + "zendframework/zend-servicemanager": "2.5.*", + "zendframework/zend-stdlib": "2.5.1" + }, + "suggest": { + "league/oauth2-google": "Needed for Google XOAUTH2 authentication" }, "type": "library", "autoload": { "classmap": [ "class.phpmailer.php", + "class.phpmaileroauth.php", + "class.phpmaileroauthgoogle.php", + "class.smtp.php", "class.pop3.php", - "class.smtp.php" + "extras/EasyPeasyICS.php", + "extras/ntlm_sasl_client.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1327,7 +1586,7 @@ } ], "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "time": "2013-02-21T11:44:28+00:00" + "time": "2017-11-04T09:26:05+00:00" }, { "name": "psr/container", @@ -1416,6 +1675,80 @@ ], "time": "2012-12-21T11:40:51+00:00" }, + { + "name": "psy/psysh", + "version": "v0.9.9", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "9aaf29575bb8293206bb0420c1e1c87ff2ffa94e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/9aaf29575bb8293206bb0420c1e1c87ff2ffa94e", + "reference": "9aaf29575bb8293206bb0420c1e1c87ff2ffa94e", + "shasum": "" + }, + "require": { + "dnoegel/php-xdg-base-dir": "0.1", + "ext-json": "*", + "ext-tokenizer": "*", + "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*", + "nikic/php-parser": "~1.3|~2.0|~3.0|~4.0", + "php": ">=5.4.0", + "symfony/console": "~2.3.10|^2.4.2|~3.0|~4.0", + "symfony/var-dumper": "~2.7|~3.0|~4.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "hoa/console": "~2.15|~3.16", + "phpunit/phpunit": "~4.8.35|~5.0|~6.0|~7.0" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", + "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "0.9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "time": "2018-10-13T15:16:03+00:00" + }, { "name": "ralouphie/getallheaders", "version": "2.0.5", @@ -4935,6 +5268,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2017-06-30T09:13:00+00:00" }, { @@ -5875,6 +6209,7 @@ "phpcs", "standards" ], + "abandoned": "phpcompatibility/php-compatibility", "time": "2018-10-07T17:38:02+00:00" } ], diff --git a/config/app.php b/config/app.php index 1c94fab88..bd0116a86 100644 --- a/config/app.php +++ b/config/app.php @@ -23,7 +23,9 @@ return [ Illuminate\Queue\QueueServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, - ViewServiceProvider::class + Laravel\Tinker\TinkerServiceProvider::class, + ViewServiceProvider::class, + ], 'aliases' => [ 'Crypt' => Illuminate\Support\Facades\Crypt::class diff --git a/database/factories/AppAssignSelfServiceValueFactory.php b/database/factories/AppAssignSelfServiceValueFactory.php new file mode 100644 index 000000000..f1b7360c1 --- /dev/null +++ b/database/factories/AppAssignSelfServiceValueFactory.php @@ -0,0 +1,18 @@ +define(\ProcessMaker\Model\AppAssignSelfServiceValue::class, function(Faker $faker) { + return [ + 'ID' => $faker->unique()->numberBetween(1, 2000), + 'APP_UID' => G::generateUniqueID(), + 'DEL_INDEX' => 2, + 'PRO_UID' => G::generateUniqueID(), + 'TAS_UID' => G::generateUniqueID(), + 'TAS_ID' => $faker->unique()->numberBetween(1, 2000), + 'GRP_UID' => G::generateUniqueID(), + ]; +}); + diff --git a/database/factories/AppAssignSelfServiceValueGroupFactory.php b/database/factories/AppAssignSelfServiceValueGroupFactory.php new file mode 100644 index 000000000..72057cb5c --- /dev/null +++ b/database/factories/AppAssignSelfServiceValueGroupFactory.php @@ -0,0 +1,15 @@ +define(\ProcessMaker\Model\AppAssignSelfServiceValueGroup::class, function(Faker $faker) { + return [ + 'ID' => $faker->unique()->numberBetween(1, 2000), + 'GRP_UID' => G::generateUniqueID(), + 'ASSIGNEE_ID' => $faker->unique()->numberBetween(1, 2000), + 'ASSIGNEE_TYPE' => $faker->unique()->numberBetween(1, 2000), + ]; +}); + diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php new file mode 100644 index 000000000..76728224a --- /dev/null +++ b/database/factories/ApplicationFactory.php @@ -0,0 +1,26 @@ +define(\ProcessMaker\Model\Application::class, function(Faker $faker) { + $process = \ProcessMaker\Model\Process::all()->random(); + $statuses = ['DRAFT', 'TO_DO']; + $status = $faker->randomElement($statuses); + $statusId = array_search($status, $statuses) + 1; + return [ + 'APP_UID' => G::generateUniqueID(), + 'APP_TITLE' => G::generateUniqueID(), + 'APP_NUMBER' => $faker->unique()->numberBetween(1000), + 'APP_STATUS' => $status, + 'APP_STATUS_ID' => $statusId, + 'PRO_UID' => $process->PRO_UID, + 'APP_PARALLEL' => 'N', + 'APP_INIT_USER' => \ProcessMaker\Model\User::all()->random()->USR_UID, + 'APP_CUR_USER' => \ProcessMaker\Model\User::all()->random()->USR_UID, + 'APP_PIN' => G::generateUniqueID(), + 'APP_CREATE_DATE' => $faker->dateTime(), + 'APP_UPDATE_DATE' => $faker->dateTime(), + 'APP_INIT_DATE' => $faker->dateTime(), + 'APP_DATA' => serialize(['APP_NUMBER' => 12]) + ]; +}); \ No newline at end of file diff --git a/database/factories/DelegationFactory.php b/database/factories/DelegationFactory.php new file mode 100644 index 000000000..aa43fa7d6 --- /dev/null +++ b/database/factories/DelegationFactory.php @@ -0,0 +1,76 @@ +define(\ProcessMaker\Model\Delegation::class, function(Faker $faker) { + $app = factory(\ProcessMaker\Model\Application::class)->create(); + $process = \ProcessMaker\Model\Process::where('PRO_UID', $app->PRO_UID)->first(); + $task = $process->tasks->first(); + + // Grab a user if random + $users = \ProcessMaker\Model\User::all(); + if(!count($users)) { + $user = factory(\ProcessMaker\Model\User::class)->create(); + } else{ + $user = $users->random(); + } + + // Return with default values + return [ + 'APP_UID' => $app->APP_UID, + 'DEL_INDEX' => 1, + 'APP_NUMBER' => $app->APP_NUMBER, + 'DEL_PREVIOUS' => 0, + 'PRO_UID' => $app->PRO_UID, + 'TAS_UID' => $task->TAS_UID, + 'USR_UID' => $user->USR_UID, + 'DEL_TYPE' => 'NORMAL', + 'DEL_THREAD' => 1, + 'DEL_THREAD_STATUS' => 'OPEN', + 'DEL_PRIORITY' => 3, + 'DEL_DELEGATE_DATE' => $faker->dateTime(), + 'DEL_INIT_DATE' => $faker->dateTime(), + 'DEL_TASK_DUE_DATE' => $faker->dateTime(), + 'DEL_RISK_DATE' => $faker->dateTime(), + 'USR_ID' => $user->USR_ID, + 'PRO_ID' => $process->PRO_ID, + 'TAS_ID' => $task->TAS_ID, + 'DEL_DATA' => '' + ]; +}); + +// Create a open delegation +$factory->state(\ProcessMaker\Model\Delegation::class, 'open', function (Faker $faker) { + // Create dates with sense + $delegateDate = $faker->dateTime(); + $initDate = $faker->dateTimeInInterval($delegateDate, '+30 minutes'); + $riskDate = $faker->dateTimeInInterval($initDate, '+1 day'); + $taskDueDate = $faker->dateTimeInInterval($riskDate, '+1 day'); + + return [ + 'DEL_THREAD_STATUS' => 'OPEN', + 'DEL_DELEGATE_DATE' => $delegateDate, + 'DEL_INIT_DATE' => $initDate, + 'DEL_RISK_DATE' => $riskDate, + 'DEL_TASK_DUE_DATE' => $taskDueDate, + 'DEL_FINISH_DATE' => null + ]; +}); + +// Create a closed delegation +$factory->state(\ProcessMaker\Model\Delegation::class, 'closed', function (Faker $faker) { + // Create dates with sense + $delegateDate = $faker->dateTime(); + $initDate = $faker->dateTimeInInterval($delegateDate, '+30 minutes'); + $riskDate = $faker->dateTimeInInterval($initDate, '+1 day'); + $taskDueDate = $faker->dateTimeInInterval($riskDate, '+1 day'); + $finishDate = $faker->dateTimeInInterval($initDate, '+10 days'); + + return [ + 'DEL_THREAD_STATUS' => 'CLOSED', + 'DEL_DELEGATE_DATE' => $delegateDate, + 'DEL_INIT_DATE' => $initDate, + 'DEL_RISK_DATE' => $riskDate, + 'DEL_TASK_DUE_DATE' => $taskDueDate, + 'DEL_FINISH_DATE' => $finishDate + ]; +}); diff --git a/database/factories/DynaformFactory.php b/database/factories/DynaformFactory.php new file mode 100644 index 000000000..c790e5fda --- /dev/null +++ b/database/factories/DynaformFactory.php @@ -0,0 +1,26 @@ +define(\ProcessMaker\Model\Dynaform::class, function(Faker $faker) { + $date = $faker->dateTime(); + return [ + 'DYN_UID' => G::generateUniqueID(), + 'DYN_ID' => '', + 'DYN_TITLE' => '', + 'DYN_DESCRIPTION' => '', + 'PRO_UID' => function() { + $process = factory(\ProcessMaker\Model\Process::class)->create(); + return $process->PRO_UID; + }, + 'DYN_TYPE' => 'xmlform', + 'DYN_FILENAME' => '', + 'DYN_CONTENT' => '', + 'DYN_LABEL' => '', + 'DYN_VERSION' => 2, + 'DYN_UPDATE_DATE' => $date->format('Y-m-d H:i:s'), + ]; +}); diff --git a/database/factories/GroupUserFactory.php b/database/factories/GroupUserFactory.php new file mode 100644 index 000000000..d7f51ee31 --- /dev/null +++ b/database/factories/GroupUserFactory.php @@ -0,0 +1,14 @@ +define(\ProcessMaker\Model\GroupUser::class, function(Faker $faker) { + return [ + 'GRP_UID' => G::generateUniqueID(), + 'GRP_ID' => $faker->unique()->numberBetween(1, 2000), + 'USR_UID' => G::generateUniqueID() + ]; +}); + diff --git a/database/factories/GroupwfFactory.php b/database/factories/GroupwfFactory.php new file mode 100644 index 000000000..7ab6ca271 --- /dev/null +++ b/database/factories/GroupwfFactory.php @@ -0,0 +1,17 @@ +define(\ProcessMaker\Model\Groupwf::class, function(Faker $faker) { + return [ + 'GRP_UID' => G::generateUniqueID(), + 'GRP_ID' => $faker->unique()->numberBetween(1, 2000), + 'GRP_TITLE' => $faker->sentence(2), + 'GRP_STATUS' => 'ACTIVE', + 'GRP_LDAP_DN' => '', + 'GRP_UX' => 'NORMAL', + ]; +}); + diff --git a/database/factories/ListUnassignedFactory.php b/database/factories/ListUnassignedFactory.php new file mode 100644 index 000000000..afbc01949 --- /dev/null +++ b/database/factories/ListUnassignedFactory.php @@ -0,0 +1,40 @@ +define(\ProcessMaker\Model\ListUnassigned::class, function (Faker $faker) { + $app = factory(\ProcessMaker\Model\Application::class)->create(); + $process = \ProcessMaker\Model\Process::where('PRO_UID', $app->PRO_UID)->first(); + $task = $process->tasks->first(); + // Grab a user if random + $users = \ProcessMaker\Model\User::all(); + if(!count($users)) { + $user = factory(\ProcessMaker\Model\User::class)->create(); + } else{ + $user = $users->random(); + } + + return [ + 'APP_UID' => $app->APP_UID, + 'DEL_INDEX' => 1, + 'TAS_UID' => $task->TAS_UID, + 'PRO_UID' => $app->PRO_UID, + 'APP_NUMBER' => $app->APP_NUMBER, + 'APP_TITLE' => $app->APP_TITLE, + 'APP_PRO_TITLE' => $process->PRO_TITLE, + 'APP_TAS_TITLE' => $task->TAS_TITLE, + 'DEL_PREVIOUS_USR_USERNAME' => $user->USR_USERNAME, + 'DEL_PREVIOUS_USR_FIRSTNAME' => $user->USR_FIRSTNAME, + 'DEL_PREVIOUS_USR_LASTNAME' => $user->USR_LASTNAME, + 'APP_UPDATE_DATE' => $faker->dateTime(), + 'DEL_PREVIOUS_USR_UID' => G::generateUniqueID(), + 'DEL_DELEGATE_DATE' => $faker->dateTime(), + 'DEL_DUE_DATE' => $faker->dateTime(), + 'DEL_PRIORITY' => 3, + 'PRO_ID' => $process->PRO_ID, + 'TAS_ID' => $task->TAS_ID, + ]; +}); + diff --git a/database/factories/ProcessCategoryFactory.php b/database/factories/ProcessCategoryFactory.php new file mode 100644 index 000000000..0f8f19776 --- /dev/null +++ b/database/factories/ProcessCategoryFactory.php @@ -0,0 +1,14 @@ +define(\ProcessMaker\Model\ProcessCategory::class, function (Faker $faker) { + return [ + 'CATEGORY_UID' => G::generateUniqueID(), + 'CATEGORY_PARENT' => '', + 'CATEGORY_NAME' => $faker->sentence(5), + 'CATEGORY_ICON' => '', + ]; +}); diff --git a/database/factories/ProcessFactory.php b/database/factories/ProcessFactory.php index d8a60d371..3bfa727ed 100644 --- a/database/factories/ProcessFactory.php +++ b/database/factories/ProcessFactory.php @@ -5,13 +5,57 @@ use Faker\Generator as Faker; $factory->define(\ProcessMaker\Model\Process::class, function(Faker $faker) { - /** * @todo Determine if we need more base columns populated */ - return [ + $process = [ 'PRO_UID' => G::generateUniqueID(), 'PRO_TITLE' => $faker->sentence(3), - 'PRO_DESCRIPTION' => $faker->paragraph(3) + 'PRO_DESCRIPTION' => $faker->paragraph(3), + 'PRO_CREATE_USER' => '00000000000000000000000000000001', + 'PRO_DYNAFORMS' => '', + 'PRO_ITEE' => 1, + 'PRO_STATUS' => 'ACTIVE' ]; + + $task1 = factory(\ProcessMaker\Model\Task::class) + ->create([ + 'PRO_UID' => $process['PRO_UID'], + 'TAS_START'=>'TRUE' + ]); + + $task2 = factory(\ProcessMaker\Model\Task::class) + ->create([ + 'PRO_UID' => $process['PRO_UID'], + ]); + + //routes + factory(\ProcessMaker\Model\Route::class) + ->create([ + 'PRO_UID' => $process['PRO_UID'], + 'TAS_UID' => $task2['TAS_UID'], + 'ROU_NEXT_TASK' => '-1', + ]); + + factory(\ProcessMaker\Model\Route::class) + ->create([ + 'PRO_UID' => $process['PRO_UID'], + 'TAS_UID' => $task1['TAS_UID'], + 'ROU_NEXT_TASK' => $task2['TAS_UID'] + ]); + + //User assignments + factory(\ProcessMaker\Model\TaskUser::class) + ->create([ + 'TAS_UID' => $task1['TAS_UID'], + 'USR_UID' => \ProcessMaker\Model\User::all()->random()->USR_UID + ]); + + factory(\ProcessMaker\Model\TaskUser::class) + ->create([ + 'TAS_UID' => $task2['TAS_UID'], + 'USR_UID' => \ProcessMaker\Model\User::all()->random()->USR_UID + ]); + + return $process; }); \ No newline at end of file diff --git a/database/factories/RouteFactory.php b/database/factories/RouteFactory.php new file mode 100644 index 000000000..18a3e2f57 --- /dev/null +++ b/database/factories/RouteFactory.php @@ -0,0 +1,22 @@ +define(\ProcessMaker\Model\Route::class, function(Faker $faker) { + return [ + 'PRO_UID' => function() { + $process = factory(\ProcessMaker\Model\Process::class)->create(); + return $process->PRO_UID; + }, + 'TAS_UID' => function() { + $task = factory(\ProcessMaker\Model\Task::class)->create(); + return $task->TAS_UID; + }, + 'ROU_UID' => G::generateUniqueID(), + 'ROU_PARENT' => 0, + 'ROU_CASE' => 1, + 'ROU_TYPE' => 'SEQUENTIAL' + ]; +}); \ No newline at end of file diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php new file mode 100644 index 000000000..13dd74abf --- /dev/null +++ b/database/factories/TaskFactory.php @@ -0,0 +1,35 @@ +define(\ProcessMaker\Model\Task::class, function(Faker $faker) { + return [ + 'PRO_UID' => function() { + $process = factory(\ProcessMaker\Model\Process::class)->create(); + return $process->PRO_UID; + }, + 'TAS_UID' => G::generateUniqueID(), + 'TAS_ID' => $faker->unique()->numberBetween(1, 100000), + 'TAS_TITLE' => $faker->sentence(2), + 'TAS_TYPE' => 'NORMAL', + 'TAS_TYPE_DAY' => 1, + 'TAS_DURATION' => 1, + 'TAS_ASSIGN_TYPE' => 'BALANCED', + 'TAS_ASSIGN_VARIABLE' => '@@SYS_NEXT_USER_TO_BE_ASSIGNED', + 'TAS_MI_INSTANCE_VARIABLE' => '@@SYS_VAR_TOTAL_INSTANCE', + 'TAS_MI_COMPLETE_VARIABLE' => '@@SYS_VAR_TOTAL_INSTANCES_COMPLETE', + 'TAS_ASSIGN_LOCATION' => 'FALSE', + 'TAS_ASSIGN_LOCATION_ADHOC' => 'FALSE', + 'TAS_TRANSFER_FLY' => 'FALSE', + 'TAS_LAST_ASSIGNED' => 0, + 'TAS_USER' => 0, + 'TAS_CAN_UPLOAD' => 'FALSE', + 'TAS_CAN_CANCEL' => 'FALSE', + 'TAS_OWNER_APP' => 'FALSE', + 'TAS_CAN_SEND_MESSAGE' => 'FALSE', + 'TAS_SEND_LAST_EMAIL' => 'FALSE', + ]; +}); diff --git a/database/factories/TaskUserFactory.php b/database/factories/TaskUserFactory.php new file mode 100644 index 000000000..a4c0a5e7d --- /dev/null +++ b/database/factories/TaskUserFactory.php @@ -0,0 +1,16 @@ +define(\ProcessMaker\Model\TaskUser::class, function(Faker $faker) { + return [ + 'TAS_UID' => function() { + $task = factory(\ProcessMaker\Model\Task::class)->create(); + return $task->TAS_UID; + }, + 'TU_TYPE' => 1, + 'TU_RELATION' => 1 + ]; +}); \ No newline at end of file diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 000000000..1885c421e --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,19 @@ +define(\ProcessMaker\Model\User::class, function(Faker $faker) { + return [ + 'USR_UID' => G::generateUniqueID(), + 'USR_USERNAME' => $faker->unique()->userName, + 'USR_PASSWORD' => $faker->password, + 'USR_FIRSTNAME' => $faker->firstName, + 'USR_LASTNAME' => $faker->lastName, + 'USR_EMAIL' => $faker->unique()->email, + 'USR_DUE_DATE' => new \Carbon\Carbon(2030,1,1), + 'USR_STATUS' => 'ACTIVE', + 'USR_ROLE' => $faker->randomElement(['PROCESSMAKER_ADMIN', 'PROCESSMAKER_OPERATOR']), + 'USR_UX' => 'NORMAL', + 'USR_TIME_ZONE' => 'America/Anguilla', + 'USR_DEFAULT_LANG' => 'en', + ]; +}); \ No newline at end of file diff --git a/gulliver/system/class.bootstrap.php b/gulliver/system/class.bootstrap.php index 9eb567d9c..a192f0b27 100644 --- a/gulliver/system/class.bootstrap.php +++ b/gulliver/system/class.bootstrap.php @@ -1348,8 +1348,7 @@ class Bootstrap * * @return void */ - // _Internal: Remove recursion in result array - public function _del_p(&$ary) + public static function _del_p(&$ary) { foreach ($ary as $k => $v) { if ($k === '_p') { diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index 60207b1be..a00bdc624 100644 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -1636,13 +1636,16 @@ class G /** * Escapes special characters in a string for use in a SQL statement - * @param string $sqlString The string to be escaped - * @param string $DBEngine Target DBMS - */ - public function sqlEscape($sqlString, $DBEngine = DB_ADAPTER) + * @param string $sqlString The string to be escaped + * @param string $dbEngine Target DBMS + * + * @return string + */ + public static function sqlEscape($sqlString, $dbEngine = DB_ADAPTER) { - $DBEngine = DB_ADAPTER; - switch ($DBEngine) { + // @todo: Research why always this value is set with the same constant? + $dbEngine = DB_ADAPTER; + switch ($dbEngine) { case 'mysql': $con = Propel::getConnection('workflow'); return mysqli_real_escape_string($con->getResource(), stripslashes($sqlString)); @@ -1689,9 +1692,15 @@ class G * @# Non-quoted parameter * @! Evaluate string : Replace the parameters in value and then in the sql string * @fn() Evaluate string with the function "fn" - * @author David Callizaya + * + * @param string $sqlString + * @param array $result + * @param string $dbEngine + * @param bool $applyHtmlEntities + * + * @return string */ - public static function replaceDataField($sqlString, $result, $DBEngine = 'mysql') + public static function replaceDataField($sqlString, $result, $dbEngine = 'mysql', $applyHtmlEntities = false) { if (!is_array($result)) { $result = array(); @@ -1710,7 +1719,12 @@ class G $u = $match[0][$r][1] + strlen($match[0][$r][0]); //Mysql quotes scape if (($match[1][$r][0] == '@') && (isset($result[$match[2][$r][0]]))) { - $__textoEval .= "\"" . G::sqlEscape($result[$match[2][$r][0]], $DBEngine) . "\""; + $text = ($applyHtmlEntities && !stringIsValidHtml($result[$match[2][$r][0]])) ? + htmlentities(G::unhtmlentities($result[$match[2][$r][0]]), ENT_COMPAT, 'UTF-8') : + $result[$match[2][$r][0]]; + // Replenish the tag
because is valid + $text = str_replace('<br />', '
', $text); + $__textoEval .= "\"" . G::sqlEscape($text, $dbEngine) . "\""; continue; } //URL encode @@ -1730,7 +1744,7 @@ class G } //Substring (Sub replaceDataField) if (($match[1][$r][0] == '!') && (isset($result[$match[2][$r][0]]))) { - $__textoEval .= G::replaceDataField($result[$match[2][$r][0]], $result); + $__textoEval .= G::replaceDataField($result[$match[2][$r][0]], $result, $dbEngine, $applyHtmlEntities); continue; } //Call function @@ -1748,18 +1762,33 @@ class G } //Non-quoted if (($match[1][$r][0] == '#') && (isset($result[$match[2][$r][0]]))) { - $__textoEval .= G::replaceDataField($result[$match[2][$r][0]], $result); + $text = ($applyHtmlEntities && !stringIsValidHtml($result[$match[2][$r][0]]) && $match[2][$r][0] !== '__ABE__') ? + htmlentities(G::unhtmlentities($result[$match[2][$r][0]]), ENT_COMPAT, 'UTF-8') : + $result[$match[2][$r][0]]; + // Replenish the tag
because is valid + $text = str_replace('<br />', '
', $text); + $__textoEval .= G::replaceDataField($text, $result); continue; } //Non-quoted = if (($match[1][$r][0] == '=') && (isset($result[$match[2][$r][0]]))) { - $__textoEval .= G::replaceDataField($result[$match[2][$r][0]], $result); + $text = ($applyHtmlEntities && !stringIsValidHtml($result[$match[2][$r][0]]) && $match[2][$r][0] !== '__ABE__') ? + htmlentities(G::unhtmlentities($result[$match[2][$r][0]]), ENT_COMPAT, 'UTF-8') : + $result[$match[2][$r][0]]; + // Replenish the tag
because is valid + $text = str_replace('<br />', '
', $text); + $__textoEval .= G::replaceDataField($text, $result); continue; } //Objects attributes if (($match[1][$r][0] == '&') && (isset($result[$match[2][$r][0]]))) { if (isset($result[$match[2][$r][0]]->{$match[6][$r][0]})) { - $__textoEval .= $result[$match[2][$r][0]]->{$match[6][$r][0]}; + $text = ($applyHtmlEntities && !stringIsValidHtml($result[$match[2][$r][0]]->{$match[6][$r][0]})) ? + htmlentities(G::unhtmlentities($result[$match[2][$r][0]]->{$match[6][$r][0]}), ENT_COMPAT, 'UTF-8') : + $result[$match[2][$r][0]]->{$match[6][$r][0]}; + // Replenish the tag
because is valid + $text = str_replace('<br />', '
', $text); + $__textoEval .= $text; } continue; } @@ -1771,27 +1800,35 @@ class G } /** - * Replace Grid Values - * The tag @>GRID-NAME to open the grid and @GRID-NAME to open the grid and @sendMessage() + * @see \WsBase->sendMessage() + * @see \OutputDocument->generate() + * @see \ProcessMaker\BusinessModel\Cases\OutputDocument->generate() + */ + public static function replaceDataGridField($content, $fields, $nl2brRecursive = true, $applyHtmlEntities = false) { $nrt = array("\n", "\r", "\t"); $nrthtml = array("(n /)", "(r /)", "(t /)"); - $sContent = G::unhtmlentities($sContent); - $strContentAux = str_replace($nrt, $nrthtml, $sContent); + $strContentAux = str_replace($nrt, $nrthtml, $content); - $iOcurrences = preg_match_all('/\@(?:([\>])([a-zA-Z\_]\w*)|([a-zA-Z\_][\w\-\>\:]*)\(((?:[^\\\\\)]*(?:[\\\\][\w\W])?)*)\))((?:\s*\[[\'"]?\w+[\'"]?\])+)?/', $strContentAux, $arrayMatch1, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE); + $occurrences = preg_match_all('/\@(?:([\>])([a-zA-Z\_]\w*)|([a-zA-Z\_][\w\-\>\:]*)\(((?:[^\\\\\)]*(?:[\\\\][\w\W])?)*)\))((?:\s*\[[\'"]?\w+[\'"]?\])+)?/', + $strContentAux, $arrayMatch1, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE); - if ($iOcurrences) { + if ($occurrences) { $arrayGrid = array(); - for ($i = 0; $i <= $iOcurrences - 1; $i++) { + for ($i = 0; $i <= $occurrences - 1; $i++) { $arrayGrid[] = $arrayMatch1[2][$i][0]; } @@ -1817,16 +1854,16 @@ class G while (preg_match($ereg, $strContentAux1, $arrayMatch2)) { $strData = null; - if (isset($aFields[$grdName]) && is_array($aFields[$grdName])) { - foreach ($aFields[$grdName] as $aRow) { + if (isset($fields[$grdName]) && is_array($fields[$grdName])) { + foreach ($fields[$grdName] as $aRow) { if ($nl2brRecursive) { - foreach ($aRow as $sKey => $vValue) { - if (!is_array($vValue)) { - $aRow[$sKey] = str_replace($nrt, $nrthtml, nl2br($aRow[$sKey])); + foreach ($aRow as $key => $item) { + if (!is_array($item)) { + $aRow[$key] = str_replace($nrt, $nrthtml, nl2br($aRow[$key])); } } } - $strData = $strData . G::replaceDataField($arrayMatch2[2], $aRow); + $strData = $strData . G::replaceDataField($arrayMatch2[2], $aRow, 'mysql', $applyHtmlEntities); } } @@ -1841,19 +1878,19 @@ class G $strContentAux = str_replace($nrthtml, $nrt, $strContentAux); - $sContent = $strContentAux; + $content = $strContentAux; if ($nl2brRecursive) { - foreach ($aFields as $sKey => $vValue) { - if (!is_array($vValue) && !is_object($vValue)) { - $aFields[$sKey] = nl2br($aFields[$sKey]); + foreach ($fields as $key => $item) { + if (!is_array($item) && !is_object($item)) { + $fields[$key] = nl2br($fields[$key]); } } } - $sContent = G::replaceDataField($sContent, $aFields); + $content = G::replaceDataField($content, $fields, 'mysql', $applyHtmlEntities); - return $sContent; + return $content; } /** diff --git a/phpunit.xml b/phpunit.xml index e59678301..91b213c61 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,9 +15,9 @@ ./tests/workflow/engine/src/ - ./tests/Unit + ./tests/Unit - + @@ -33,31 +33,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 70cb1973e..fdee3691a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,4 +1,5 @@ make(Kernel::class)->bootstrap(); // Setup our testexternal database config(['database.connections.testexternal' => [ - 'driver' => 'mysql', - 'host' => env('DB_HOST', '127.0.0.1'), - 'database' => env('DB_TESTEXTERNAL_DB', 'testexternal'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', - 'strict' => true, - 'engine' => null, + 'driver' => 'mysql', + 'host' => env('DB_HOST', '127.0.0.1'), + 'database' => env('DB_DATABASE', 'testexternal'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', 'password'), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'strict' => true, + 'engine' => null ]]); // Now, drop all test tables and repopulate with schema @@ -52,13 +61,13 @@ DB::connection('testexternal')->table('test')->insert([ ]); // Only do if we are supporting MSSql tests -if(env('RUN_MSSQL_TESTS')) { +if (env('RUN_MSSQL_TESTS')) { config(['database.connections.mssql' => [ - 'driver' => 'sqlsrv', - 'host' => env('MSSQL_HOST', '127.0.0.1'), - 'database' => env('MSSQL_DATABASE', 'testexternal'), - 'username' => env('MSSQL_USERNAME', 'root'), - 'password' => env('MSSQL_PASSWORD', ''), + 'driver' => 'sqlsrv', + 'host' => env('MSSQL_HOST', '127.0.0.1'), + 'database' => env('MSSQL_DATABASE', 'testexternal'), + 'username' => env('MSSQL_USERNAME', 'root'), + 'password' => env('MSSQL_PASSWORD', 'password'), ]]); Schema::connection('mssql')->dropIfExists('test'); @@ -73,26 +82,25 @@ if(env('RUN_MSSQL_TESTS')) { // THIS IS FOR STANDARD PROCESSMAKER TABLES - // Now, drop all test tables and repopulate with schema DB::unprepared('SET FOREIGN_KEY_CHECKS = 0'); $colname = 'Tables_in_' . env('DB_DATABASE'); $tables = DB::select('SHOW TABLES'); $drop = []; -foreach($tables as $table) { +foreach ($tables as $table) { $drop[] = $table->$colname; } -if(count($drop)) { +if (count($drop)) { $drop = implode(',', $drop); DB::statement("DROP TABLE $drop"); DB::unprepared('SET FOREIGN_KEY_CHECKS = 1'); } // Repopulate with schema and standard inserts -DB::unprepared(file_get_contents(PATH_CORE.'data/mysql/schema.sql')); -DB::unprepared(file_get_contents(PATH_RBAC_CORE.'data/mysql/schema.sql')); -DB::unprepared(file_get_contents(PATH_CORE.'data/mysql/insert.sql')); -DB::unprepared(file_get_contents(PATH_RBAC_CORE.'data/mysql/insert.sql')); +DB::unprepared(file_get_contents(PATH_CORE . 'data/mysql/schema.sql')); +DB::unprepared(file_get_contents(PATH_RBAC_CORE . 'data/mysql/schema.sql')); +DB::unprepared(file_get_contents(PATH_CORE . 'data/mysql/insert.sql')); +DB::unprepared(file_get_contents(PATH_RBAC_CORE . 'data/mysql/insert.sql')); // Set our APP_SEQUENCE val DB::table('APP_SEQUENCE')->insert([ @@ -106,7 +114,7 @@ DB::table('OAUTH_CLIENTS')->insert([ 'CLIENT_NAME' => 'PM Web Designer', 'CLIENT_DESCRIPTION' => 'ProcessMaker Web Designer App', 'CLIENT_WEBSITE' => 'www.processmaker.com', - 'REDIRECT_URI' => config('app.url') . '/sys' . config('system.workspace').'/en/neoclassic/oauth2/grant', + 'REDIRECT_URI' => config('app.url') . '/sys' . config('system.workspace') . '/en/neoclassic/oauth2/grant', 'USR_UID' => '00000000000000000000000000000001' ]); DB::table('OAUTH_ACCESS_TOKENS')->insert([ @@ -133,4 +141,4 @@ Propel::initConfiguration([ 'adapter' => 'mysql' ] ] -]); \ No newline at end of file +]); diff --git a/tests/unit/gulliver/system/ReplaceDataFieldTest.php b/tests/unit/gulliver/system/ReplaceDataFieldTest.php new file mode 100644 index 000000000..c1081304e --- /dev/null +++ b/tests/unit/gulliver/system/ReplaceDataFieldTest.php @@ -0,0 +1,280 @@ +value'; + $dbEngine = 'mysql'; // This only affects the way to escape the variables with "@@" prefix + $applyEntities = true; // If a value to replace is a not valid HTML and have HTML reserved characters, entities should be applied + + // Initializing variables to test the assertions, entities should be applied in variable with @@ + $var4 = new stdClass(); + $var4->value = $faker->words(1, true); + $valuesToReplace = [ + 'var1' => 'Java < PHP & Python', + 'var2' => $faker->words(1, true), + 'var3' => $faker->words(1, true), + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/</', $stringToCheck); + $this->assertRegExp('/&/', $stringToCheck); + + // Initializing variables to test the assertions, entities should be applied in variable with @# + $var4 = new stdClass(); + $var4->value = $faker->words(1, true); + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'var2' => 'Java < PHP & Python', + 'var3' => $faker->words(1, true), + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/</', $stringToCheck); + $this->assertRegExp('/&/', $stringToCheck); + + // Initializing variables to test the assertions, entities should be applied in variable with @= + $var4 = new stdClass(); + $var4->value = $faker->words(1, true); + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'var2' => $faker->words(1, true), + 'var3' => 'Java < PHP & Python', + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/</', $stringToCheck); + $this->assertRegExp('/&/', $stringToCheck); + + // Initializing variables to test the assertions, entities should be applied in variable with @& + $var4 = new stdClass(); + $var4->value = 'Java < PHP & Python'; + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'var2' => $faker->words(1, true), + 'var3' => $faker->words(1, true), + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/</', $stringToCheck); + $this->assertRegExp('/&/', $stringToCheck); + } + + /** + * This checks that strings with HTML reserved characters are NOT replaced with entities + * @test + * @covers G::replaceDataField + */ + public function it_should_no_replace_entities() + { + // Initializing Faker instance + $faker = Faker\Factory::create(); + + // Initializing variables to use that will not change + $stringWithVariablesToReplace = 'Hello @@var1 the @#var2 is @=var3 not @&var4->value'; + $dbEngine = 'mysql'; // This only affects the way to escape the variables with "@@" prefix + $applyEntities = false; // The values should not be replaced with entities + + // Initializing variables to test the assertions, entities should be applied in variable with @@ + $var4 = new stdClass(); + $var4->value = $faker->words(1, true); + $valuesToReplace = [ + 'var1' => 'Java < PHP & Python', + 'var2' => $faker->words(1, true), + 'var3' => $faker->words(1, true), + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/assertRegExp('/&/', $stringToCheck); + + // Initializing variables to test the assertions, entities should be applied in variable with @# + $var4 = new stdClass(); + $var4->value = $faker->words(1, true); + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'var2' => 'Java < PHP & Python', + 'var3' => $faker->words(1, true), + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/assertRegExp('/&/', $stringToCheck); + + // Initializing variables to test the assertions, entities should be applied in variable with @= + $var4 = new stdClass(); + $var4->value = $faker->words(1, true); + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'var2' => $faker->words(1, true), + 'var3' => 'Java < PHP & Python', + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/assertRegExp('/&/', $stringToCheck); + + // Initializing variables to test the assertions, entities should be applied in variable with @& + $var4 = new stdClass(); + $var4->value = 'Java < PHP & Python'; + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'var2' => $faker->words(1, true), + 'var3' => $faker->words(1, true), + 'var4' => $var4 + ]; + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('/assertRegExp('/&/', $stringToCheck); + } + + /** + * This checks that strings with HTML reserved characters are NOT replaced with entities if is a valid HTML, because + * PS team sometimes build a HTML string to insert in templates (output documents or emails), Ex.- A table to list + * users or results from a query + * @test + * @covers G::replaceDataField + */ + public function it_should_no_replace_entities_if_exists_valid_html() + { + // Initializing Faker instance + $faker = Faker\Factory::create(); + + // Initializing variables to use + $stringWithVariablesToReplace = 'bla @#var1 bla @=listHtml bla @@var2 bla'; + $valuesToReplace = [ + 'var1' => $faker->words(1, true), + 'listHtml' => ' + + + + + + + + + + + + + + + + +
t1t2t3t4t5t6
c1c2c3c4c5c6
', + 'var2' => $faker->words(1, true) + ]; + $dbEngine = 'mysql'; // This only affects the way to escape the variables with "@@" prefix + $applyEntities = true; // Is true because the string will b used in a output document or a email template + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithVariablesToReplace, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp('//', $stringToCheck); + $this->assertRegExp('//', $stringToCheck); + $this->assertRegExp('/
/', $stringToCheck); + $this->assertRegExp('//', $stringToCheck); + } + + /** + * This checks that strings with tag
should not be replaced, because is a valid tag + * @test + * @covers G::replaceDataField + */ + public function it_should_no_replace_tag_br() + { + // Initializing variables to use + $stringWithTagBr = nl2br("prospection auprès d'entreprises de CA < 10 M euros +test +a +&a +\"a +'a +¢a +£a +¥a +€a +©a +®a +test"); + $valuesToReplace = []; + $dbEngine = 'mysql'; // This only affects the way to escape the variables with "@@" prefix + $applyEntities = true; // Is true because the string will be used in a output document or a email template + + // Replace variables in the string + $stringToCheck = G::replaceDataField($stringWithTagBr, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp("/
/", $stringToCheck); + } + + /** + * Check that the value for the System variable "__ABE__" should not be replaced never + * @test + * @covers G::replaceDataField + */ + public function it_should_no_replace_entities_for_var_abe() + { + // Initializing variables to use + $string = "bla @#__ABE__ bla @#anotherVar bla"; + $valuesToReplace = [// Add a value for reserved system variable "__ABE__" used in Actions By Email feature + '__ABE__' => 'Java < PHP', // The value for System variable "__ABE__" shouldn't be changed never + 'anotherVar' => '.NET < Java' // The value for another variables should be validated/replaced normally + ]; + $dbEngine = 'mysql'; // This only affects the way to escape the variables with "@@" prefix + $applyEntities = true; // Is true because the string will be used in a output document or a email template + + // Replace variables in the string + $stringToCheck = G::replaceDataField($string, $valuesToReplace, $dbEngine, $applyEntities); + + // Assertions + $this->assertRegExp("/Java < PHP/", $stringToCheck); + $this->assertRegExp("/.NET < Java/", $stringToCheck); + } +} diff --git a/tests/unit/workflow/engine/classes/PmDynaformTest.php b/tests/unit/workflow/engine/classes/PmDynaformTest.php new file mode 100644 index 000000000..d6c033209 --- /dev/null +++ b/tests/unit/workflow/engine/classes/PmDynaformTest.php @@ -0,0 +1,535 @@ +createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 6, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(); + $result = $pmDynaform->getDynaform(); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaform() method returning null if parameters is empty. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_null_if_parameters_is_empty() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 5, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform([]); + $result = $pmDynaform->getDynaform(); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaform() method returning null if current dynaform not exist. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_null_if_current_dynaform_not_exist() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 5, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => G::generateUniqueID()]); + $result = $pmDynaform->getDynaform(); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaform() method returning null if parameters is not empty. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_null_if_parameters_is_not_empty() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 5, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(["APP_DATA" => []]); + $result = $pmDynaform->getDynaform(); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaform() method returning null if parameter is a string. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_null_if_parameter_is_a_string() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 5, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(""); + + $result = $pmDynaform->getDynaform(); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaform() method returning null if parameter is a integer. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_null_if_parameter_is_a_integer() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 5, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(1); + + $result = $pmDynaform->getDynaform(); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaform() method returning record property. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_record_property_if_record_is_not_null() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 4, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + $expected = (array) $dynaform->first()->toArray(); + unset($expected['id']); //This is removed because is aggregate from factory. + //first execution in constructor + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => $arrayForm['items'][0]['id']]); + //second execution + $pmDynaform->getDynaform(); + //third execution + $pmDynaform->getDynaform(); + + $this->assertEquals($expected, $pmDynaform->record); + } + + /** + * Check if the getDynaform() method setting langs property in null if current dynaform not exist. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_langs_property_in_null_if_current_dynaform_not_exist() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 6, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => G::generateUniqueID()]); + $pmDynaform->getDynaform(); + + $this->assertEquals(null, $pmDynaform->langs); + } + + /** + * Check if the getDynaform() method returning null if dynaform not exist in the process. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_null_if_dynaform_not_exist_in_the_process() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 5, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => G::generateUniqueID()]); + $pmDynaform->getDynaform(); + + $this->assertEquals(null, $pmDynaform->record); + } + + /** + * Check if the getDynaform() method returning fields. + * @covers PmDynaform::getDynaform + * @test + */ + public function it_should_return_a_dynaform_in_array_format() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 3, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + $expected = (array) $dynaform->first()->toArray(); + unset($expected['id']); //This is removed because is aggregate from factory. + + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => $arrayForm['items'][0]['id']]); + $result = $pmDynaform->getDynaform(); + $this->assertEquals($expected, $result); + } + + /** + * Check if the getDynaforms() method returning null when not exist dynaform. + * @covers PmDynaform::getDynaforms + * @test + */ + public function it_should_return_null_when_not_exist_dynaform() + { + $process = factory(Process::class, 1)->create(); + + $arrayForm = $this->createArrayDynaform(); + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 7, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(); + $result = $pmDynaform->getDynaforms(['CURRENT_DYNAFORM' => G::generateUniqueID()]); + + $this->assertEquals(null, $result); + } + + /** + * Check if the getDynaforms() method returning null when record is null. + * @covers PmDynaform::getDynaforms + * @test + */ + public function it_should_return_null_when_record_is_null() + { + $pmDynaform = new PmDynaform(); + $pmDynaform->getDynaforms(); + + $this->assertEquals(null, $pmDynaform->record); + } + + /** + * Check if the getDynaforms() method returning record property. + * @covers PmDynaform::getDynaforms + * @test + */ + public function it_should_return_array_dynaforms_except_current_dynaform_in_second_execution() + { + $process = factory(Process::class, 1)->create(); + + $arrayForm = $this->createArrayDynaform(); + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 7, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $arrayForm2 = $this->createArrayDynaform(); + $dynaform2 = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 9, + 'DYN_UID' => $arrayForm2['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm2) + ]); + + $expected = (array) $dynaform2->first()->toArray(); + unset($expected['id']); //This is removed because is aggregate from factory. + + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => $arrayForm['items'][0]['id']]); + $pmDynaform->getDynaforms(); + + $this->assertEquals([$expected], $pmDynaform->records); + } + + /** + * Check if the getDynaforms() method returning arrays dynaforms except current dynaform. + * @covers PmDynaform::getDynaforms + * @test + */ + public function it_should_return_array_dynaforms_except_current_dynaform() + { + $process = factory(Process::class, 1)->create(); + + $arrayForm = $this->createArrayDynaform(); + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 7, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $arrayForm2 = $this->createArrayDynaform(); + $dynaform2 = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 9, + 'DYN_UID' => $arrayForm2['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm2) + ]); + + $expected = (array) $dynaform2->first()->toArray(); + unset($expected['id']); //This is removed because is aggregate from factory. + + $pmDynaform = new PmDynaform(['CURRENT_DYNAFORM' => $arrayForm['items'][0]['id']]); + $result = $pmDynaform->getDynaforms(); + + $this->assertEquals([$expected], $result); + } + + /** + * Check if the isUsed() method is returning false when not exist data related to process id. + * @covers PmDynaform::isUsed + * @test + */ + public function it_should_return_false_when_not_exist_data_related_to_id_process() + { + $processId = G::generateUniqueID(); + + $pmDynaform = new PmDynaform(); + $result = $pmDynaform->isUsed($processId, 'var1'); + + $this->assertEquals(false, $result); + } + + /** + * Check if the isUsed() method is returning the ID of the dynaform in case + * the variable is part of the dynaform. + * @covers PmDynaform::isUsed + * @test + */ + public function it_should_return_id_of_dynaform_when_is_used_variable() + { + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 1, + 'DYN_UID' => $arrayForm['items'][0]['id'], + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(); + $result = $pmDynaform->isUsed($process[0]->PRO_UID, $arrayForm['items'][0]['variables'][0]); + + $this->assertEquals($dynaform[0]->DYN_UID, $result); + } + + /** + * Check if the isUsed() method is returning false in case the variable is + * not part of the dynaform. + * @covers PmDynaform::isUsed + * @test + */ + public function it_should_return_false_when_not_used_variable() + { + $arrayVariable = $this->createArrayVariable('var10'); + + $arrayForm = $this->createArrayDynaform(); + + $process = factory(Process::class, 1)->create(); + + $dynaform = factory(Dynaform::class, 1)->create([ + 'DYN_ID' => 2, + 'PRO_UID' => $process[0]->PRO_UID, + 'DYN_CONTENT' => G::json_encode($arrayForm) + ]); + + $pmDynaform = new PmDynaform(); + $result = $pmDynaform->isUsed($process[0]->PRO_UID, $arrayVariable); + + $this->assertEquals(false, $result); + } + + /** + * Return an object that represents the structure of a process variable. + * @return array + */ + private function createArrayVariable($varName) + { + return [ + "var_uid" => G::generateUniqueID(), + "prj_uid" => G::generateUniqueID(), + "var_name" => $varName, + "var_field_type" => "string", + "var_field_size" => 10, + "var_label" => "string", + "var_dbconnection" => "workflow", + "var_dbconnection_label" => "PM Database", + "var_sql" => "", + "var_null" => 0, + "var_default" => "", + "var_accepted_values" => "[]", + "inp_doc_uid" => "" + ]; + } + + /** + * Returns an object that represents the structure of a control. + * @return array + */ + private function createArrayControl($varUid, $varName) + { + return [ + "type" => "textarea", + "variable" => $varName, + "var_uid" => $varUid, + "dataType" => "string", + "protectedValue" => false, + "id" => "textareaVar001", + "name" => "textareaVar001", + "label" => "textarea_1", + "defaultValue" => "", + "placeholder" => "", + "hint" => "", + "required" => false, + "requiredFieldErrorMessage" => "", + "validate" => "", + "validateMessage" => "", + "mode" => "parent", + "dbConnection" => "workflow", + "dbConnectionLabel" => "PM Database", + "sql" => "", + "rows" => "5", + "var_name" => "textareaVar001", + "colSpan" => 12 + ]; + } + + /** + * Returns an object that represents the structure of a dynaform. + * @return array + */ + private function createArrayDynaform() + { + $var1 = $this->createArrayVariable('var1'); + $control1 = $this->createArrayControl($var1['var_uid'], $var1['var_name']); + + $var2 = $this->createArrayVariable('var2'); + $control2 = $this->createArrayControl($var2['var_uid'], $var2['var_name']); + + return [ + "name" => "subform", + "description" => "", + "items" => [ + [ + "type" => "form", + "variable" => "", + "var_uid" => "", + "dataType" => "", + "id" => G::generateUniqueID(), + "name" => "subform", + "description" => "", + "mode" => "edit", + "script" => "", + "language" => "en", + "externalLibs" => "", + "printable" => false, + "items" => [ + [$control1], + [$control2] + ], + "variables" => [ + $var1, + $var2 + ] + ] + ] + ]; + } +} diff --git a/tests/unit/workflow/engine/classes/ReportTablesTest.php b/tests/unit/workflow/engine/classes/ReportTablesTest.php new file mode 100644 index 000000000..c48ed8191 --- /dev/null +++ b/tests/unit/workflow/engine/classes/ReportTablesTest.php @@ -0,0 +1,733 @@ +prepareData($tableName, 1); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = $result->fields; + $proUid = $result->processUid; + $grid = ''; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid, $grid); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals($expected, $actual); + } + + /** + * Check if the "populateTable" function returns an array value if entered all + * parameters and type and grid are correct values. + * @test + * @covers ReportTables::populateTable + */ + public function it_should_populating_data_with_all_parameters_with_type_is_grid() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 18, true); + $connectionShortName = 'wf'; + $type = 'GRID'; + $fields = $result->fields; + $proUid = $result->processUid; + $grid = 'var_Grid1'; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid, $grid); + + $indexRow = 1; + $expected = $result->appData[$grid]; + foreach ($expected as &$row) { + $row['APP_UID'] = $result->applicationUid; + $row['APP_NUMBER'] = $result->applicationNumber; + $row['ROW'] = (string) ($indexRow++); + } + $expected = array_values($expected); + + $actual = DB::table($tableName) + ->select() + ->get(); + $actual->transform(function ($item, $key) { + return (array) $item; + }); + $actual = $actual->toArray(); + + $this->assertEquals($expected, $actual); + } + + /** + * Check if the "populateTable" function returns an array value if entered all + * parameters and type and grid are incorrect values. + * @test + * @covers ReportTables::populateTable + */ + public function it_should_populating_data_with_all_parameters_with_type_is_grid_null() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 19, true); + $connectionShortName = 'wf'; + $type = 'GRID'; + $fields = $result->fields; + $proUid = $result->processUid; + $grid = null; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid, $grid); + + $actual = DB::table($tableName) + ->select() + ->get(); + $actual->transform(function ($item, $key) { + return (array) $item; + }); + $actual = $actual->toArray(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if only the + * name of the report table has been entered. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_only_with_the_mandatory_parameter_tableName() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 2); + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable and the name of the + * connection. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_connectionShortName_parameter() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 3); + $connectionShortName = 'wf'; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable and the name of the + * connection is null. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_connectionShortName_parameter_is_null() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 8); + $connectionShortName = null; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable and the name of the + * connection is incorrect value. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_connectionShortName_parameter_is_incorrect_value() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 9); + $connectionShortName = G::generateUniqueID(); + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection and type. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 4); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection and type is grid. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_is_grid() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 11); + $connectionShortName = 'wf'; + $type = 'GRID'; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection and type is null. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_is_null() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 10); + $connectionShortName = 'wf'; + $type = null; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type and fields. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 5); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = $result->fields; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type and fields is null. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_is_null() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 12); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = null; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type and fields is empty array. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_is_empty_array() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 13); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = []; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type and fields is incorrect value. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_is_incorrect_value() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 14); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = ""; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an array value if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type, the fields and process identifier. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_proUid() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 6); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = $result->fields; + $proUid = $result->processUid; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals($expected, $actual); + } + + /** + * Check if the "populateTable" function returns an empty array if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type, the fields and process identifier is null. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_proUid_is_null() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 15); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = $result->fields; + $proUid = null; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals([], $actual); + } + + /** + * Check if the "populateTable" function returns an array value if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type, the fields, the process identifier and grid name. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_proUid_grid() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 7); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = $result->fields; + $proUid = $result->processUid; + $grid = ''; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid, $grid); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals($expected, $actual); + } + + /** + * Check if the "populateTable" function returns an array value if you have + * entered the name of the table of the reportTable, the name of the + * connection, the type, the fields, the process identifier and grid name if null. + * @test + * @covers ReportTables::populateTable + */ + public function this_should_populate_the_reports_table_with_the_parameters_connectionShortName_type_fields_proUid_grid_if_null() + { + $tableName = 'TestReportTable'; + $result = $this->prepareData($tableName, 16); + $connectionShortName = 'wf'; + $type = 'NORMAL'; + $fields = $result->fields; + $proUid = $result->processUid; + $grid = null; + + $reportTables = new ReportTables(); + $reportTables->populateTable($tableName, $connectionShortName, $type, $fields, $proUid, $grid); + + $expected = $result->dataFields; + $expected['APP_UID'] = $result->applicationUid; + $expected['APP_NUMBER'] = $result->applicationNumber; + + $actual = (array) DB::table($tableName) + ->select() + ->first(); + + $this->assertEquals($expected, $actual); + } + + /** + * Get mapping fields supported by report table. + * @return array + */ + private function getMapFields() + { + return [ + [ + 'sFieldName' => 'var_Text1', + 'sType' => 'char' + ], + [ + 'sFieldName' => 'var_Textarea1', + 'sType' => 'text' + ], + [ + 'sFieldName' => 'var_Dropdown1', + 'sType' => 'char' + ], + [ + 'sFieldName' => 'var_Suggest1', + 'sType' => 'char' + ], + [ + 'sFieldName' => 'var_DateTime1', + 'sType' => 'date' + ], + [ + 'sFieldName' => 'var_String1', + 'sType' => 'char' + ], + [ + 'sFieldName' => 'var_Integer1', + 'sType' => 'number' + ], + [ + 'sFieldName' => 'var_Boolean1', + 'sType' => 'boolean' + ], + [ + 'sFieldName' => 'var_Array1', + 'sType' => 'array' + ] + ]; + } + + /** + * Create fields data by type supported. + * @param array $types + * @return array + */ + private function createFieldsByType($types = []) + { + $fields = []; + $mapping = []; + $faker = Faker\Factory::create(); + $date = $faker->dateTime(); + $mapFields = $this->getMapFields(); + foreach ($mapFields as $key => $value) { + if (!in_array($value['sType'], $types)) { + continue; + } + switch ($value['sType']) { + case 'number': + $mapping[] = $value; + $fields[$value['sFieldName']] = (string) random_int(0, 100); + break; + case 'char': + $mapping[] = $value; + $fields[$value['sFieldName']] = G::generateUniqueID(); + break; + case 'text': + $mapping[] = $value; + $fields[$value['sFieldName']] = G::generateUniqueID(); + break; + case 'date': + $mapping[] = $value; + $fields[$value['sFieldName']] = $date->format('Y-m-d H:i:s'); + break; + case 'boolean': + $mapping[] = $value; + $fields[$value['sFieldName']] = ['0' => 0]; + break; + } + } + return [ + 'data' => $fields, + 'mapping' => $mapping + ]; + } + + /** + * Prepare data initial for test, the grid parameter is optional if you want + * to create a grid type field. + * + * @param string $tableName + * @param integer $applicationNumber + * @param boolean $grid + * @return object + */ + private function prepareData($tableName, $applicationNumber, $grid = null) + { + $faker = Faker\Factory::create(); + $date = $faker->dateTime(); + + $userUid = G::generateUniqueID(); + $processUid = G::generateUniqueID(); + $taskUid = G::generateUniqueID(); + $applicationUid = G::generateUniqueID(); + + $structure = $this->createFieldsByType(['number', 'char', 'text', 'date']); + $fields = $structure['mapping']; + $dataFields = $structure['data']; + $appData = [ + 'SYS_LANG' => 'en', + 'SYS_SKIN' => 'neoclassic', + 'SYS_SYS' => 'workflow', + 'APPLICATION' => G::generateUniqueID(), + 'PROCESS' => G::generateUniqueID(), + 'TASK' => '', + 'INDEX' => 2, + 'USER_LOGGED' => $userUid, + 'USR_USERNAME' => 'admin', + 'APP_NUMBER' => $applicationNumber, + 'PIN' => '97ZN' + ]; + $appData = array_merge($appData, $dataFields); + if ($grid === true) { + $gridFields = [ + 'var_Grid1' => [ + '1' => $dataFields, + '2' => $dataFields, + ] + ]; + $appData = array_merge($appData, $gridFields); + } + + $user = factory(User::class)->create([ + 'USR_UID' => $userUid + ]); + + $process = factory(Process::class)->create([ + 'PRO_UID' => $processUid + ]); + + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID + ]); + + $application = factory(Application::class)->create([ + 'PRO_UID' => $process->PRO_UID, + 'APP_UID' => $applicationUid, + 'APP_NUMBER' => $applicationNumber, + 'APP_DATA' => serialize($appData) + ]); + + Schema::dropIfExists($tableName); + Schema::create($tableName, function ($table) use ($dataFields, $grid) { + $table->string('APP_UID'); + $table->string('APP_NUMBER'); + if ($grid === true) { + $table->string('ROW'); + } + foreach ($dataFields as $key => $value) { + $table->string($key); + } + }); + $result = new stdClass(); + $result->userUid = $userUid; + $result->processUid = $processUid; + $result->taskUid = $taskUid; + $result->applicationUid = $applicationUid; + $result->applicationNumber = $applicationNumber; + $result->fields = $fields; + $result->dataFields = $dataFields; + $result->appData = $appData; + $result->user = $user; + $result->process = $process; + $result->task = $task; + $result->application = $application; + return $result; + } +} diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php new file mode 100644 index 000000000..686e857e9 --- /dev/null +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -0,0 +1,1139 @@ +create(); + factory(Process::class, 10)->create(); + factory(Delegation::class, 51)->create(); + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure pagination is working properly + * @test + */ + public function it_should_return_pages_of_data_unassigned() + { + factory(User::class, 100)->create(); + factory(Process::class, 10)->create(); + factory(Delegation::class, 50)->create(); + factory(Delegation::class)->create([ + 'USR_ID' => 0 // A self service delegation + ]); + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure filter by process is working properly + * @test + */ + public function it_should_return_process_of_data() + { + factory(User::class, 100)->create(); + $process = factory(Process::class)->create(); + factory(Delegation::class, 51)->create([ + 'PRO_ID' => $process->id + ]); + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, $process->id); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, null, $process->id); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, null, $process->id); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure filter by status is working properly + * Review status filter by a specific status, such as Draft + * @test + */ + public function it_should_return_status_draft_of_data() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 1, + 'APP_STATUS' => 'DRAFT' + ]); + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Review the filter by status DRAFT + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure filter by status is working properly + * Review status filter by a specific status, such as To Do + * @test + */ + public function it_should_return_status_todo_of_data() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 2, + 'APP_STATUS' => 'TO_DO' + ]); + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Review the filter by status TO_DO + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure filter by status is working properly + * Review status filter by a specific status, such as Completed + * @test + */ + public function it_should_return_status_completed_of_data() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 3, + 'APP_STATUS' => 'COMPLETED', + ]); + + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'DEL_LAST_INDEX' => 1 + ]); + // Review the filter by status COMPLETED + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure filter by status is working properly + * Review status filter by a specific status, such as Cancelled + * @test + */ + public function it_should_return_status_cancelled_of_data() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 4, + 'APP_STATUS' => 'CANCELLED' + ]); + + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'DEL_LAST_INDEX' => 1 + ]); + // Review the filter by status CANCELLED + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, null, null, $application->APP_STATUS_ID); + $this->assertCount(1, $results['data']); + } + + /** + * This ensures searching for a valid user works + * @test + */ + public function it_should_return_one_result_for_specified_user() + { + factory(User::class, 100)->create(); + factory(Process::class, 10)->create(); + // Create our unique user, with a unique username + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'testcaseuser' + ]); + // Create a new delegation, but for this specific user + factory(Delegation::class)->create([ + 'USR_UID' => $user->USR_UID, + 'USR_ID' => $user->id + ]); + // Now fetch results, and assume delegation count is 1 and the user points to our user + $results = Delegation::search($user->id); + $this->assertCount(1, $results['data']); + $this->assertEquals('testcaseuser', $results['data'][0]['USRCR_USR_USERNAME']); + } + + /** + * This ensures searching by case number and review the page + * @test + */ + public function it_should_search_by_case_id_and_pages_of_data() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2010 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2011 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2012 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2013 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2014 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2015 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Get first page, the major case id + $results = Delegation::search(null, 0, 10, 1, null, null, 'DESC', + 'APP_NUMBER', null, null, null, 'APP_NUMBER'); + $this->assertCount(7, $results['data']); + $this->assertEquals(2015, $results['data'][0]['APP_NUMBER']); + // Get first page, the minor case id + $results = Delegation::search(null, 0, 10, 1, null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_NUMBER'); + $this->assertCount(7, $results['data']); + $this->assertEquals(2001, $results['data'][0]['APP_NUMBER']); + //Check the pagination + $results = Delegation::search(null, 0, 5, 1, null, null, 'DESC', + 'APP_NUMBER', null, null, null, 'APP_NUMBER'); + $this->assertCount(5, $results['data']); + $results = Delegation::search(null, 5, 2, 1, null, null, 'DESC', + 'APP_NUMBER', null, null, null, 'APP_NUMBER'); + $this->assertCount(2, $results['data']); + } + + /** + * This ensures searching by case title and review the page + * case title contain the case number, ex: APP_TITLE = 'Request # @=APP_NUMBER' + * @test + */ + public function it_should_search_by_case_title_and_pages_of_data_app_number_matches_case_title() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 3001, + 'APP_TITLE' => 'Request # 3001' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 3010, + 'APP_TITLE' => 'Request # 3010' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 3011, + 'APP_TITLE' => 'Request # 3011' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 3012, + 'APP_TITLE' => 'Request # 3012' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 3013, + 'APP_TITLE' => 'Request # 3013' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_TITLE' => 3014, + 'APP_TITLE' => 'Request # 3014' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + + // Get first page, the major case id + $results = Delegation::search(null, 0, 10, '1', null, null, 'DESC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(6, $results['data']); + $this->assertEquals('Request # 3014', $results['data'][0]['APP_TITLE']); + + // Get first page, the minor case id + $results = Delegation::search(null, 0, 10, '1', null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(6, $results['data']); + $this->assertEquals(3001, $results['data'][0]['APP_NUMBER']); + $this->assertEquals('Request # 3001', $results['data'][0]['APP_TITLE']); + //Check the pagination + $results = Delegation::search(null, 0, 5, '1', null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(5, $results['data']); + $results = Delegation::search(null, 5, 2, '1', null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(1, $results['data']); + } + + /** + * This ensures searching by task title and review the page + * @test + */ + public function it_should_search_by_task_title_and_pages_of_data() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $task = factory(Task::class)->create([ + 'TAS_ID' => 1, + 'TAS_TITLE' => 'Request task' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task->TAS_ID + ]); + $task = factory(Task::class)->create([ + 'TAS_ID' => 2, + 'TAS_TITLE' => 'Account task' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task->TAS_ID + ]); + // Get first page, the order taskTitle + $results = Delegation::search(null, 0, 6, 'task', null, null, 'ASC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertCount(6, $results['data']); + $this->assertEquals('Account task', $results['data'][0]['APP_TAS_TITLE']); + $results = Delegation::search(null, 6, 6, 'task', null, null, 'ASC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertEquals('Request task', $results['data'][0]['APP_TAS_TITLE']); + + // Get first page, the order taskTitle + $results = Delegation::search(null, 0, 6, 'task', null, null, 'DESC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertCount(6, $results['data']); + $this->assertEquals('Request task', $results['data'][0]['APP_TAS_TITLE']); + $results = Delegation::search(null, 6, 6, 'task', null, null, 'DESC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertEquals('Account task', $results['data'][0]['APP_TAS_TITLE']); + //Check the pagination + $results = Delegation::search(null, 0, 6, 'task', null, null, 'DESC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertCount(6, $results['data']); + $results = Delegation::search(null, 6, 6, 'task', null, null, 'DESC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertCount(4, $results['data']); + } + + /** + * This ensures searching by case title and review the page + * case title does not match with case number (hertland use case) + * @test + */ + public function it_should_search_by_case_title_and_pages_of_data_app_number_no_matches_case_title() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001, + 'APP_TITLE' => 'Request from Abigail check nro 25001' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2010, + 'APP_TITLE' => 'Request from Abigail check nro 12' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2011, + 'APP_TITLE' => 'Request from Abigail check nro 1000' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2012, + 'APP_TITLE' => 'Request from Abigail check nro 11000' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2013, + 'APP_TITLE' => 'Request from Abigail check nro 12000' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_TITLE' => 2014, + 'APP_TITLE' => 'Request from Abigail check nro 111' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Get first page, the major case title + $results = Delegation::search(null, 0, 10, '1', null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(6, $results['data']); + $this->assertEquals(2001, $results['data'][0]['APP_NUMBER']); + $this->assertEquals('Request from Abigail check nro 25001', $results['data'][0]['APP_TITLE']); + + //Check the pagination + $results = Delegation::search(null, 0, 5, '1', null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(5, $results['data']); + $results = Delegation::search(null, 5, 2, '1', null, null, 'ASC', + 'APP_NUMBER', null, null, null, 'APP_TITLE'); + $this->assertCount(1, $results['data']); + } + + /** + * This ensures ordering ascending and descending works by case number APP_NUMBER + * @test + */ + public function it_should_sort_by_case_id() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 30002 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Get first page, the minor case id + $results = Delegation::search(null, 0, 25, null, null, null, 'ASC', 'APP_NUMBER'); + $this->assertCount(2, $results['data']); + $this->assertEquals(2001, $results['data'][0]['APP_NUMBER']); + $this->assertEquals(30002, $results['data'][1]['APP_NUMBER']); + // Get first page, the major case id + $results = Delegation::search(null, 0, 25, null, null, null, 'DESC', 'APP_NUMBER'); + $this->assertCount(2, $results['data']); + $this->assertEquals(30002, $results['data'][0]['APP_NUMBER']); + $this->assertEquals(2001, $results['data'][1]['APP_NUMBER']); + } + + /** + * This ensures ordering ascending and descending works by case title APP_TITLE + * @test + */ + public function it_should_sort_by_case_title() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 2001, + 'APP_TITLE' => 'Request by Thomas' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_NUMBER' => 30002, + 'APP_TITLE' => 'Request by Ariel' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Get first page, the minor case id + $results = Delegation::search(null, 0, 25, null, null, null, 'ASC', 'APP_TITLE'); + $this->assertCount(2, $results['data']); + $this->assertEquals('Request by Ariel', $results['data'][0]['APP_TITLE']); + $this->assertEquals('Request by Thomas', $results['data'][1]['APP_TITLE']); + // Get first page, the major case id + $results = Delegation::search(null, 0, 25, null, null, null, 'DESC', 'APP_TITLE'); + $this->assertCount(2, $results['data']); + $this->assertEquals('Request by Thomas', $results['data'][0]['APP_TITLE']); + $this->assertEquals('Request by Ariel', $results['data'][1]['APP_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by case title APP_PRO_TITLE + * @test + */ + public function it_should_sort_by_process() + { + factory(User::class, 100)->create(); + $process = factory(Process::class)->create([ + 'PRO_ID' => 2, + 'PRO_TITLE' => 'Egypt Supplier Payment Proposal' + ]); + factory(Delegation::class)->create([ + 'PRO_ID' => $process->id + ]); + $process = factory(Process::class)->create([ + 'PRO_ID' => 1, + 'PRO_TITLE' => 'China Supplier Payment Proposal' + ]); + factory(Delegation::class)->create([ + 'PRO_ID' => $process->id + ]); + $process = factory(Process::class)->create([ + 'PRO_ID' => 3, + 'PRO_TITLE' => 'Russia Supplier Payment Proposal' + ]); + factory(Delegation::class)->create([ + 'PRO_ID' => $process->id + ]); + // Get first page, all process ordering ascending + $results = Delegation::search(null, 0, 3, null, null, null, 'ASC', 'APP_PRO_TITLE'); + $this->assertCount(3, $results['data']); + $this->assertEquals('China Supplier Payment Proposal', $results['data'][0]['APP_PRO_TITLE']); + $this->assertEquals('Egypt Supplier Payment Proposal', $results['data'][1]['APP_PRO_TITLE']); + $this->assertEquals('Russia Supplier Payment Proposal', $results['data'][2]['APP_PRO_TITLE']); + // Get first page, all process ordering descending + $results = Delegation::search(null, 0, 3, null, null, null, 'DESC', 'APP_PRO_TITLE'); + $this->assertCount(3, $results['data']); + $this->assertEquals('Russia Supplier Payment Proposal', $results['data'][0]['APP_PRO_TITLE']); + $this->assertEquals('Egypt Supplier Payment Proposal', $results['data'][1]['APP_PRO_TITLE']); + $this->assertEquals('China Supplier Payment Proposal', $results['data'][2]['APP_PRO_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by task title APP_TAS_TITLE + * @test + */ + public function it_should_sort_by_task_title() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $task = factory(Task::class)->create([ + 'TAS_ID' => 1000, + 'TAS_TITLE' => 'Initiate Request' + ]); + factory(Delegation::class)->create([ + 'TAS_ID' => $task->TAS_ID + ]); + $task = factory(Task::class)->create([ + 'TAS_ID' => 4000, + 'TAS_TITLE' => 'Waiting for AP Manager Validation' + ]); + factory(Delegation::class)->create([ + 'TAS_ID' => $task->TAS_ID + ]); + + $results = Delegation::search(null, 0, 25, null, null, null, 'ASC', 'APP_TAS_TITLE'); + $this->assertCount(2, $results['data']); + $this->assertEquals('Initiate Request', $results['data'][0]['APP_TAS_TITLE']); + $this->assertEquals('Waiting for AP Manager Validation', $results['data'][1]['APP_TAS_TITLE']); + + $results = Delegation::search(null, 0, 25, null, null, null, 'DESC', 'APP_TAS_TITLE'); + $this->assertCount(2, $results['data']); + $this->assertEquals('Waiting for AP Manager Validation', $results['data'][0]['APP_TAS_TITLE']); + $this->assertEquals('Initiate Request', $results['data'][1]['APP_TAS_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by current user + * @test + */ + public function it_should_sort_by_user() + { + factory(User::class, 100)->create(); + factory(Process::class, 10)->create(); + // Create our unique user, with a unique username + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'gary', + 'USR_LASTNAME' => 'Gary', + 'USR_FIRSTNAME' => 'Bailey', + ]); + // Create a new delegation, but for this specific user + factory(Delegation::class)->create([ + 'USR_UID' => $user->USR_UID, + 'USR_ID' => $user->id + ]); + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'paul', + 'USR_LASTNAME' => 'Paul', + 'USR_FIRSTNAME' => 'Griffis', + ]); + // Create a new delegation, but for this specific user + factory(Delegation::class)->create([ + 'USR_UID' => $user->USR_UID, + 'USR_ID' => $user->id + ]); + // Now fetch results, and assume delegation count is 2 and the ordering ascending return Gary + $results = Delegation::search(null, 0, 25, null, null, null, 'ASC', 'APP_CURRENT_USER'); + $this->assertCount(2, $results['data']); + $this->assertEquals('Gary Bailey', $results['data'][0]['APP_CURRENT_USER']); + + // Now fetch results, and assume delegation count is 2 and the ordering descending return Gary + $results = Delegation::search(null, 0, 25, null, null, null, 'DESC', 'APP_CURRENT_USER'); + $this->assertCount(2, $results['data']); + $this->assertEquals('Paul Griffis', $results['data'][0]['APP_CURRENT_USER']); + } + + /** + * This ensures ordering ordering ascending and descending works by last modified APP_UPDATE_DATE + * @test + */ + public function it_should_sort_by_last_modified() + { + factory(User::class,100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_UPDATE_DATE' => '2019-01-02 00:00:00' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_UPDATE_DATE' => '2019-01-03 00:00:00' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_UPDATE_DATE' => '2019-01-04 00:00:00' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Get first page, the minor last modified + $results = Delegation::search(null, 0, 1, null, null, null, 'ASC', 'APP_UPDATE_DATE'); + $this->assertCount(1, $results['data']); + $this->assertEquals('2019-01-02 00:00:00', $results['data'][0]['APP_UPDATE_DATE']); + + $results = Delegation::search(null, 1, 1, null, null, null, 'ASC', 'APP_UPDATE_DATE'); + $this->assertCount(1, $results['data']); + $this->assertEquals('2019-01-03 00:00:00', $results['data'][0]['APP_UPDATE_DATE']); + + $results = Delegation::search(null, 2, 1, null, null, null, 'ASC', 'APP_UPDATE_DATE'); + $this->assertCount(1, $results['data']); + $this->assertEquals('2019-01-04 00:00:00', $results['data'][0]['APP_UPDATE_DATE']); + + $results = Delegation::search(null, 0, 1, null, null, null, 'DESC', 'APP_UPDATE_DATE'); + $this->assertCount(1, $results['data']); + $this->assertEquals('2019-01-04 00:00:00', $results['data'][0]['APP_UPDATE_DATE']); + + $results = Delegation::search(null, 1, 1, null, null, null, 'DESC', 'APP_UPDATE_DATE'); + $this->assertCount(1, $results['data']); + $this->assertEquals('2019-01-03 00:00:00', $results['data'][0]['APP_UPDATE_DATE']); + + $results = Delegation::search(null, 2, 1, null, null, null, 'DESC', 'APP_UPDATE_DATE'); + $this->assertCount(1, $results['data']); + $this->assertEquals('2019-01-02 00:00:00', $results['data'][0]['APP_UPDATE_DATE']); + } + + /** + * This ensures ordering ascending and descending works by due date DEL_TASK_DUE_DATE + * @test + */ + public function it_should_sort_by_due_date() + { + factory(User::class,100)->create(); + factory(Process::class)->create(); + factory(Delegation::class, 10)->create([ + 'DEL_TASK_DUE_DATE' => '2019-01-02 00:00:00' + ]); + factory(Delegation::class, 10)->create([ + 'DEL_TASK_DUE_DATE' => '2019-01-03 00:00:00' + ]); + factory(Delegation::class, 9)->create([ + 'DEL_TASK_DUE_DATE' => '2019-01-04 00:00:00' + ]); + // Get first page, the minor last modified + $results = Delegation::search(null, 0, 10, null, null, null, 'ASC', 'DEL_TASK_DUE_DATE'); + $this->assertCount(10, $results['data']); + $this->assertEquals('2019-01-02 00:00:00', $results['data'][0]['DEL_TASK_DUE_DATE']); + + $results = Delegation::search(null, 10, 10, null, null, null, 'ASC', 'DEL_TASK_DUE_DATE'); + $this->assertCount(10, $results['data']); + $this->assertEquals('2019-01-03 00:00:00', $results['data'][0]['DEL_TASK_DUE_DATE']); + + $results = Delegation::search(null, 20, 10, null, null, null, 'ASC', 'DEL_TASK_DUE_DATE'); + $this->assertCount(9, $results['data']); + $this->assertEquals('2019-01-04 00:00:00', $results['data'][0]['DEL_TASK_DUE_DATE']); + + $results = Delegation::search(null, 0, 10, null, null, null, 'DESC', 'DEL_TASK_DUE_DATE'); + $this->assertCount(10, $results['data']); + $this->assertEquals('2019-01-04 00:00:00', $results['data'][0]['DEL_TASK_DUE_DATE']); + + $results = Delegation::search(null, 10, 10, null, null, null, 'DESC', 'DEL_TASK_DUE_DATE'); + $this->assertCount(10, $results['data']); + $this->assertEquals('2019-01-03 00:00:00', $results['data'][0]['DEL_TASK_DUE_DATE']); + + $results = Delegation::search(null, 20, 10, null, null, null, 'DESC', 'DEL_TASK_DUE_DATE'); + $this->assertCount(9, $results['data']); + $this->assertEquals('2019-01-02 00:00:00', $results['data'][0]['DEL_TASK_DUE_DATE']); + } + + /** + * This ensures ordering ascending and descending works by status APP_STATUS + * @test + */ + public function it_should_sort_by_status() + { + factory(User::class,100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_STATUS' => 'DRAFT' + ]); + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_STATUS' => 'TO_DO' + ]); + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_STATUS' => 'COMPLETED' + ]); + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + $application = factory(Application::class)->create([ + 'APP_STATUS' => 'CANCELLED' + ]); + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + + // Get first page, the minor status label + $results = Delegation::search(null, 0, 25, null, null, null, 'ASC', 'APP_STATUS'); + $this->assertEquals('CANCELLED', $results['data'][0]['APP_STATUS']); + $results = Delegation::search(null, 25, 25, null, null, null, 'ASC', 'APP_STATUS'); + $this->assertEquals('COMPLETED', $results['data'][0]['APP_STATUS']); + $results = Delegation::search(null, 50, 25, null, null, null, 'ASC', 'APP_STATUS'); + $this->assertEquals('DRAFT', $results['data'][0]['APP_STATUS']); + $results = Delegation::search(null, 75, 25, null, null, null, 'ASC', 'APP_STATUS'); + $this->assertEquals('TO_DO', $results['data'][0]['APP_STATUS']); + // Get first page, the major status label + $results = Delegation::search(null, 0, 25, null, null, null, 'DESC', 'APP_STATUS'); + $this->assertEquals('TO_DO', $results['data'][0]['APP_STATUS']); + $results = Delegation::search(null, 25, 25, null, null, null, 'DESC', 'APP_STATUS'); + $this->assertEquals('DRAFT', $results['data'][0]['APP_STATUS']); + $results = Delegation::search(null, 50, 25, null, null, null, 'DESC', 'APP_STATUS'); + $this->assertEquals('COMPLETED', $results['data'][0]['APP_STATUS']); + $results = Delegation::search(null, 75, 25, null, null, null, 'DESC', 'APP_STATUS'); + $this->assertEquals('CANCELLED', $results['data'][0]['APP_STATUS']); + } + + /** + * This checks to make sure filter by category is working properly + * @test + */ + public function it_should_return_data_filtered_by_process_category() + { + factory(User::class, 100)->create(); + // Dummy Processes + factory(ProcessCategory::class, 4)->create(); + factory(Process::class, 4)->create([ + 'PRO_CATEGORY' => ProcessCategory::all()->random()->CATEGORY_UID + ]); + // Dummy Delegations + factory(Delegation::class, 100)->create([ + 'PRO_ID' => Process::all()->random()->PRO_ID + ]); + // Process with the category to search + $processCategorySearch = factory(ProcessCategory::class, 1)->create(); + $categoryUid = $processCategorySearch[0]->CATEGORY_UID; + $processSearch = factory(Process::class, 1)->create([ + 'PRO_ID' => 5, + 'PRO_CATEGORY' => $categoryUid + ]); + // Delegations to found + factory(Delegation::class, 51)->create([ + 'PRO_ID' => $processSearch[0]->id + ]); + + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, null, null, null, null, $categoryUid); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, null, null, null, null, null, $categoryUid); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, null, null, null, null, null, $categoryUid); + $this->assertCount(1, $results['data']); + } + + /** + * This ensure the result is right when you search between two given dates + * @test + */ + public function it_should_return_right_data_between_two_dates() + { + factory(User::class, 10)->create(); + factory(Process::class, 10)->create(); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-02 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-03 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-04 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-05 00:00:00']); + $results = Delegation::search(null, 0, 25, null, null, null, null, null, null, '2019-01-02 00:00:00', + '2019-01-03 00:00:00'); + $this->assertCount(20, $results['data']); + foreach ($results['data'] as $value) { + $this->assertGreaterThanOrEqual('2019-01-02 00:00:00', $value['DEL_DELEGATE_DATE']); + $this->assertLessThanOrEqual('2019-01-03 00:00:00', $value['DEL_DELEGATE_DATE']); + $this->assertRegExp('(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ', $value['DEL_DELEGATE_DATE']); + } + } + + /** + * This ensure the result is right when you search from a given date + * @test + */ + public function it_should_return_right_data_when_you_send_only_dateFrom_parameter() + { + factory(User::class, 10)->create(); + factory(Process::class, 10)->create(); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-02 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-03 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-04 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-05 00:00:00']); + $results = Delegation::search(null, 0, 50, null, null, null, null, null, null, '2019-01-02 00:00:00', + null); + $this->assertCount(40, $results['data']); + foreach ($results['data'] as $value) { + $this->assertGreaterThanOrEqual('2019-01-02 00:00:00', $value['DEL_DELEGATE_DATE']); + $this->assertRegExp('(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ', $value['DEL_DELEGATE_DATE']); + } + } + + /** + * This ensure the result is right when you search to a given date + * @test + */ + public function it_should_return_right_data_when_you_send_only_dateTo_parameter() + { + factory(User::class, 10)->create(); + factory(Process::class, 10)->create(); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-02 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-03 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-04 00:00:00']); + factory(Delegation::class, 10)->create(['DEL_DELEGATE_DATE' => '2019-01-05 00:00:00']); + $results = Delegation::search(null, 0, 50, null, null, null, null, null, null, null, + '2019-01-04 00:00:00'); + $this->assertCount(30, $results['data']); + foreach ($results['data'] as $value) { + $this->assertLessThanOrEqual('2019-01-04 00:00:00', $value['DEL_DELEGATE_DATE']); + $this->assertRegExp('(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ', $value['DEL_DELEGATE_DATE']); + } + } + + /** + * This ensures return the correct data by sequential + * @test + */ + public function it_should_return_by_sequential_tasks_pages_of_data() + { + factory(User::class)->create(); + // Create a process + $process = factory(Process::class)->create(); + $application = factory(Application::class)->create(); + // Create task + $taskNormal = factory(Task::class)->create([ + 'TAS_TYPE' => 'NORMAL' + ]); + // Create a thread with the user, process and application defined before + factory(Delegation::class)->create([ + 'PRO_ID' => $process->id, + 'TAS_ID' => $taskNormal->TAS_ID, + 'APP_NUMBER' => $application->APP_NUMBER, + 'DEL_THREAD_STATUS' => 'CLOSED', + 'DEL_INDEX' => 1 + ]); + // Define a dummy task + $taskDummy = factory(Task::class)->create([ + 'TAS_TYPE' => 'INTERMEDIATE-THROW' + ]); + // Create a thread with the dummy task this does not need a user + factory(Delegation::class)->create([ + 'PRO_ID' => $process->id, + 'USR_ID' => 0, + 'TAS_ID' => $taskDummy->TAS_ID, + 'APP_NUMBER' => $application->APP_NUMBER, + 'DEL_INDEX' => 2 + ]); + // Create a thread with the user, process and application defined before + $res = factory(Delegation::class)->create([ + 'PRO_ID' => $process->id, + 'TAS_ID' => $taskNormal->TAS_ID, + 'APP_NUMBER' => $application->APP_NUMBER, + 'DEL_THREAD_STATUS' => 'OPEN', + 'DEL_INDEX' => 3 + ]); + // Review if the thread OPEN is showed + $results = Delegation::search(null, 0, 10); + $this->assertCount(1, $results['data']); + $this->assertEquals('OPEN', $results['data'][0]['DEL_THREAD_STATUS']); + } + + /** + * This ensures return the correct data by parallel task all threads CLOSED + * @test + */ + public function it_should_return_by_parallel_tasks_threads_closed() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $task = factory(Task::class)->create([ + 'TAS_TITLE' => 'Parallel task 1' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task->TAS_ID, + 'DEL_THREAD_STATUS' => 'CLOSED' + ]); + $task = factory(Task::class)->create([ + 'TAS_TITLE' => 'Parallel task 2' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task->TAS_ID, + 'DEL_THREAD_STATUS' => 'CLOSED' + ]); + // Get first page, the order taskTitle + $results = Delegation::search(null, 0, 2, null, null, null, 'ASC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertCount(0, $results['data']); + + // Get first page, the order taskTitle + $results = Delegation::search(null, 0, 2, null, null, null, 'DESC', + 'TAS_TITLE', null, null, null, 'TAS_TITLE'); + $this->assertCount(0, $results['data']); + } + + /** + * This ensures return the correct data by parallel task all threads OPEN + * @test + */ + public function it_should_return_by_parallel_tasks_threads_open() + { + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); + //Create the threads + factory(Delegation::class, 5)->create([ + 'DEL_THREAD_STATUS' => 'OPEN' + ]); + // Get first page, all the open status + $results = Delegation::search(null, 0, 5, null, null, null); + $this->assertCount(5, $results['data']); + $this->assertEquals('OPEN', $results['data'][0]['DEL_THREAD_STATUS']); + $this->assertEquals('OPEN', $results['data'][4]['DEL_THREAD_STATUS']); + } + + /** + * Review when the status is empty + * @test + */ + public function it_should_return_status_empty() + { + factory(User::class, 100)->create(); + factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_STATUS' => '' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER + ]); + // Review the filter by status empty + $results = Delegation::search(null, 0, 25); + $this->assertEmpty($results['data'][0]['APP_STATUS']); + } + + /** + * Review when filter when the process and category does not have a relation + * @test + */ + public function it_should_return_process_and_category_does_not_have_a_relation() + { + //Create a category + $category = factory(ProcessCategory::class, 2)->create(); + //Define a process with category + $processWithCat = factory(Process::class)->create([ + 'PRO_CATEGORY' => $category[0]->CATEGORY_UID + ]); + //Define a process without category + $processNoCategory = factory(Process::class)->create([ + 'PRO_CATEGORY' => '' + ]); + //Create a delegation related with the process with category + factory(Delegation::class)->create([ + 'PRO_ID' => $processWithCat->id + ]); + //Create a delegation related with the process without category + factory(Delegation::class)->create([ + 'PRO_ID' => $processNoCategory->id + ]); + //Search the cases related to the category and process does not have relation + $results = Delegation::search(null, 0, 25, null, $processWithCat->id, null, null, null, $category[1]->CATEGORY_UID); + $this->assertCount(0, $results['data']); + //Search the cases related to the category and process does not have relation + $results = Delegation::search(null, 0, 25, null, $processNoCategory->id, null, null, null, $category[0]->CATEGORY_UID); + $this->assertCount(0, $results['data']); + } + + /** + * Review when filter when the process and category does have a relation + * @test + */ + public function it_should_return_process_and_category_does_have_a_relation() + { + //Create a category + $category = factory(ProcessCategory::class)->create(); + //Define a process related with he previous category + $processWithCat = factory(Process::class)->create([ + 'PRO_CATEGORY' => $category->CATEGORY_UID + ]); + //Create a delegation related to this process + factory(Delegation::class)->create([ + 'PRO_ID' => $processWithCat->id + ]); + //Define a process related with he previous category + $process = factory(Process::class)->create([ + 'PRO_CATEGORY' => '' + ]); + //Create a delegation related to other process + factory(Delegation::class, 5)->create([ + 'PRO_ID' => $process->id, + ]); + //Search the cases when the category and process does have relation with a category + $results = Delegation::search(null, 0, 25, null, $processWithCat->id, null, null, null, $category->CATEGORY_UID); + $this->assertCount(1, $results['data']); + //Search the cases when the category and process does have relation with category empty + $results = Delegation::search(null, 0, 25, null, $process->id, null, null, null, ''); + $this->assertCount(5, $results['data']); + } + + /** + * Check if return participation information + * @test + */ + public function it_should_return_participation_info() + { + // Creating one application with two delegations + factory(User::class, 100)->create(); + $process = factory(Process::class)->create(); + $application = factory(Application::class)->create([ + 'APP_UID' => G::generateUniqueID() + ]); + factory(Delegation::class)->states('closed')->create([ + 'APP_UID' => $application->APP_UID + ]); + factory(Delegation::class)->states('open')->create([ + 'APP_UID' => $application->APP_UID, + 'DEL_INDEX' => 2 + ]); + + // Check the information returned + $results = Delegation::getParticipatedInfo($application->APP_UID); + $this->assertEquals('PARTICIPATED', $results['APP_STATUS']); + $this->assertCount(2, $results['DEL_INDEX']); + $this->assertEquals($process->PRO_UID, $results['PRO_UID']); + } + + /** + * Check if return an empty participation information + * @test + */ + public function it_should_return_empty_participation_info() + { + // Try to get the participation information from a case that not exists + $results = Delegation::getParticipatedInfo(G::generateUniqueID()); + + // Check the information returned + $this->assertEmpty($results); + } +} \ No newline at end of file diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/ListUnassignedTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/ListUnassignedTest.php new file mode 100644 index 000000000..6574caef7 --- /dev/null +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/ListUnassignedTest.php @@ -0,0 +1,834 @@ +create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $timeEnd = microtime(true); + $this->assertEquals(15, $result); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_user_assigned took [15]--->' . $time); + } + + /** + * This checks the counters is working properly in self-service-value-based when the variable has a value related with the USR_UID + * When the value assigned in the variable @@ARRAY_OF_USERS = [USR_UID] + * @covers ListUnassigned::doCount + * @test + */ + public function it_should_count_cases_by_user_with_self_service_value_based_usr_uid() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create a case + $application = factory(Application::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task self service value based + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '@@ARRAY_OF_USERS', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Create the relation for the value assigned in the TAS_GROUP_VARIABLE + $appSelfValue = factory(AppAssignSelfServiceValue::class, 1)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => 2, + 'TAS_ID' => $task[0]->TAS_ID + ]); + factory(AppAssignSelfServiceValueGroup::class, 1)->create([ + 'ID' => $appSelfValue[0]->ID, + 'GRP_UID' => $user[0]->USR_UID, + 'ASSIGNEE_ID' => $user[0]->USR_ID, //The usrId or grpId + 'ASSIGNEE_TYPE' => 1 //Related to the user=1 related to the group=2 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 10)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => $appSelfValue[0]->DEL_INDEX, + 'TAS_ID' => $task[0]->TAS_ID, + ]); + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $this->assertEquals(10, $result); + $timeEnd = microtime(true); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_value_based_usr_uid took [10]--->' . $time); + } + + /** + * This checks the counters is working properly in self-service and self-service value based + * @covers ListUnassigned::doCount + * @test + */ + public function it_should_count_cases_by_user_with_self_service_mixed_with_self_service_value_based() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create a case + $application = factory(Application::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in self service + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + //Create a task self service value based + $task1 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '@@ARRAY_OF_USERS', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Create the relation for the value assigned in the TAS_GROUP_VARIABLE + $appSelfValue = factory(AppAssignSelfServiceValue::class, 1)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => 2, + 'TAS_ID' => $task1[0]->TAS_ID + ]); + factory(AppAssignSelfServiceValueGroup::class, 1)->create([ + 'ID' => $appSelfValue[0]->ID, + 'GRP_UID' => $user[0]->USR_UID, + 'ASSIGNEE_ID' => $user[0]->USR_ID, //The usrId or grpId + 'ASSIGNEE_TYPE' => 1 //Related to the user=1 related to the group=2 + ]); + //Create the register in self service value based + factory(ListUnassigned::class, 10)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => $appSelfValue[0]->DEL_INDEX, + 'TAS_ID' => $task[0]->TAS_ID, + ]); + + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $timeEnd = microtime(true); + $this->assertEquals(25, $result); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_mixed_with_self_service_value_based took [25]--->' . $time); + } + + /** + * This checks the counters is working properly in self-service group assigned + * @covers ListUnassigned::doCount + * @test + */ + public function it_should_count_cases_by_user_with_self_service_group_assigned() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create group + $group = factory(Groupwf::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Assign a user in the group + factory(GroupUser::class, 1)->create([ + 'GRP_UID' => $group[0]->GRP_UID, + 'GRP_ID' => $group[0]->GRP_ID, + 'USR_UID' => $user[0]->USR_UID + ]); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 2, //Related to the group + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $timeEnd = microtime(true); + $this->assertEquals(15, $result); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_group_assigned took [15]--->' . $time); + } + + /** + * This checks the counters is working properly in self-service-value-based when the variable has a value related with the GRP_UID + * When the value assigned in the variable @@ARRAY_OF_USERS = [GRP_UID] + * @covers ListUnassigned::doCount + * @test + */ + public function it_should_count_cases_by_user_with_self_service_value_based_grp_uid() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create a task self service value based + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '@@ARRAY_OF_USERS', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Create a case + $application = factory(Application::class, 1)->create(); + //Create group + $group = factory(Groupwf::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create([ + 'USR_USERNAME' => 'gary', + 'USR_LASTNAME' => 'Gary', + 'USR_FIRSTNAME' => 'Bailey', + ]); + //Assign a user in the group + factory(GroupUser::class, 1)->create([ + 'GRP_UID' => $group[0]->GRP_UID, + 'GRP_ID' => $group[0]->GRP_ID, + 'USR_UID' => $user[0]->USR_UID, + ]); + //Create the relation for the value assigned in the TAS_GROUP_VARIABLE + $appSelfValue = factory(AppAssignSelfServiceValue::class, 1)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'APP_UID' => $application[0]->APP_UID, + 'DEL_INDEX' => 2, + 'TAS_ID' => $task[0]->TAS_ID + ]); + factory(AppAssignSelfServiceValueGroup::class, 1)->create([ + 'ID' => $appSelfValue[0]->ID, + 'GRP_UID' => $group[0]->GRP_UID, + 'ASSIGNEE_ID' => $group[0]->GRP_ID, //The usrId or grpId + 'ASSIGNEE_TYPE' => 2 //Related to the user=1 related to the group=2 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 10)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => 2, + 'TAS_ID' => $task[0]->TAS_ID, + ]); + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $this->assertEquals(10, $result); + $timeEnd = microtime(true); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_value_based_grp_uid took [10]--->' . $time); + } + + /** + * This checks the counters is working properly in self-service user and group assigned in parallel task + * @covers ListUnassigned::doCount + * @test + */ + public function it_should_count_cases_by_user_with_self_service_user_and_group_assigned_parallel_task() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create group + $group = factory(Groupwf::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Assign a user in the group + factory(GroupUser::class, 1)->create([ + 'GRP_UID' => $group[0]->GRP_UID, + 'GRP_ID' => $group[0]->GRP_ID, + 'USR_UID' => $user[0]->USR_UID + ]); + //Create a task self service + $task1 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task1 + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task1[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create a task self service + $task2 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task2 + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task2[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create a task self service + $task3 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task3[0]->TAS_UID, + 'USR_UID' => $group[0]->GRP_UID, + 'TU_RELATION' => 2, //Related to the group + 'TU_TYPE' => 1 + ]); + //Create a task self service + $task4 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task4[0]->TAS_UID, + 'USR_UID' => $group[0]->GRP_UID, + 'TU_RELATION' => 2, //Related to the group + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned related to the task1 + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task1[0]->TAS_ID + ]); + //Create the register in list unassigned related to the task2 + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task2[0]->TAS_ID + ]); + //Create the register in list unassigned related to the task3 + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task3[0]->TAS_ID + ]); + //Create the register in list unassigned related to the task4 + factory(ListUnassigned::class, 15)->create([ + 'TAS_ID' => $task4[0]->TAS_ID + ]); + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $timeEnd = microtime(true); + $this->assertEquals(60, $result); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_user_and_group_assigned_parallel_task took [60]--->' . $time); + } + + /** + * This checks the counters is working properly in self-service-value-based with GRP_UID and USR_UID in parallel task + * When the value assigned in the variable @@ARRAY_OF_USERS = [GRP_UID, USR_UID] + * @covers ListUnassigned::doCount + * @test + */ + public function it_should_count_cases_by_user_with_self_service_value_based_usr_uid_and_grp_uid() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create a case + $application = factory(Application::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task1 self service value based + $task1 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '@@ARRAY_OF_USERS', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Create the relation for the value assigned in the TAS_GROUP_VARIABLE + $appSelfValue = factory(AppAssignSelfServiceValue::class, 1)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'TAS_ID' => $task1[0]->TAS_ID + ]); + factory(AppAssignSelfServiceValueGroup::class, 1)->create([ + 'ID' => $appSelfValue[0]->ID, + 'GRP_UID' => $user[0]->USR_UID, + 'ASSIGNEE_ID' => $user[0]->USR_ID, //The usrId or grpId + 'ASSIGNEE_TYPE' => 1 //Related to the user=1 related to the group=2 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 10)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => $appSelfValue[0]->DEL_INDEX, + 'TAS_ID' => $task1[0]->TAS_ID, + ]); + //Create a task2 self service value based + $task2 = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '@@ARRAY_OF_USERS', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Create the relation for the value assigned in the TAS_GROUP_VARIABLE + $appSelfValue = factory(AppAssignSelfServiceValue::class, 1)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'TAS_ID' => $task2[0]->TAS_ID + ]); + factory(AppAssignSelfServiceValueGroup::class, 1)->create([ + 'ID' => $appSelfValue[0]->ID, + 'GRP_UID' => $user[0]->USR_UID, + 'ASSIGNEE_ID' => $user[0]->USR_ID, //The usrId or grpId + 'ASSIGNEE_TYPE' => 1 //Related to the user=1 related to the group=2 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 10)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_INDEX' => $appSelfValue[0]->DEL_INDEX, + 'TAS_ID' => $task2[0]->TAS_ID, + ]); + $timeStart = microtime(true); + $result = ListUnassigned::doCount($user[0]->USR_UID); + $this->assertEquals(20, $result); + $timeEnd = microtime(true); + $time = $timeEnd - $timeStart; + error_log('it_should_count_cases_by_user_with_self_service_value_based_usr_uid_and_grp_uid took [20]--->' . $time); + } + + /** + * This checks to make sure pagination is working properly + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_return_pages_of_data() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 51)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + //Define the filters + $filters = ['start' => 0, 'limit' => 25]; + //Get data first page + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(25, $result); + //Get data second page + $filters = ['start' => 25, 'limit' => 25]; + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(25, $result); + //Get data third page + $filters = ['start' => 50, 'limit' => 25]; + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(1, $result); + } + + /** + * This ensures ordering ascending and descending works by case number APP_NUMBER + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_sort_by_case_number() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create a case + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3000 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + //Create a case + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2000 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + //Define the filters + $filters = ['sort' => 'APP_NUMBER', 'dir' => 'ASC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the minor case number first + $this->assertEquals(2000, $result[0]['APP_NUMBER']); + //Get the major case number second + $this->assertEquals(3000, $result[1]['APP_NUMBER']); + //Define the filters + $filters = ['sort' => 'APP_NUMBER', 'dir' => 'DESC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the major case number first + $this->assertEquals(3000, $result[0]['APP_NUMBER']); + //Get the minor case number second + $this->assertEquals(2000, $result[1]['APP_NUMBER']); + } + + /** + * This ensures ordering ascending and descending works by case number APP_TITLE + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_sort_by_case_title() + { + //Create process + $process = factory(Process::class, 1)->create(); + //Create user + $user = factory(User::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create a case + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3001 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'APP_TITLE' => 'Request nro ' . $application[0]->APP_NUMBER, + ]); + //Create a case + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'APP_TITLE' => 'Request nro ' . $application[0]->APP_NUMBER, + ]); + //Define the filters + $filters = ['sort' => 'APP_TITLE', 'dir' => 'ASC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the minor case title first + $this->assertEquals('Request nro 2001', $result[0]['APP_TITLE']); + //Get the major case title second + $this->assertEquals('Request nro 3001', $result[1]['APP_TITLE']); + //Define the filters + $filters = ['sort' => 'APP_TITLE', 'dir' => 'DESC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the major case title first + $this->assertEquals('Request nro 3001', $result[0]['APP_TITLE']); + //Get the minor case title second + $this->assertEquals('Request nro 2001', $result[1]['APP_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by case number APP_PRO_TITLE + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_sort_by_process() + { + //Create user + $user = factory(User::class, 1)->create(); + //Create process + $process = factory(Process::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_PRO_TITLE' => 'Egypt Supplier Payment Proposal', + ]); + + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_PRO_TITLE' => 'Russia Supplier Payment Proposal', + ]); + //Define the filters + $filters = ['sort' => 'APP_PRO_TITLE', 'dir' => 'ASC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the minor process name first + $this->assertEquals('Egypt Supplier Payment Proposal', $result[0]['APP_PRO_TITLE']); + //Get the major process name second + $this->assertEquals('Russia Supplier Payment Proposal', $result[1]['APP_PRO_TITLE']); + //Define the filters + $filters = ['sort' => 'APP_PRO_TITLE', 'dir' => 'DESC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the major process name first + $this->assertEquals('Russia Supplier Payment Proposal', $result[0]['APP_PRO_TITLE']); + //Get the minor process name second + $this->assertEquals('Egypt Supplier Payment Proposal', $result[1]['APP_PRO_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by case number APP_TAS_TITLE + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_sort_by_task() + { + //Create user + $user = factory(User::class, 1)->create(); + //Create process + $process = factory(Process::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_TAS_TITLE' => 'Initiate Request', + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_TAS_TITLE' => 'Waiting for AP Manager Validation', + ]); + //Define the filters + $filters = ['sort' => 'APP_TAS_TITLE', 'dir' => 'ASC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the minor task name first + $this->assertEquals('Initiate Request', $result[0]['APP_TAS_TITLE']); + //Get the major task name second + $this->assertEquals('Waiting for AP Manager Validation', $result[1]['APP_TAS_TITLE']); + //Define the filters + $filters = ['sort' => 'APP_TAS_TITLE', 'dir' => 'DESC']; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the major task name first + $this->assertEquals('Waiting for AP Manager Validation', $result[0]['APP_TAS_TITLE']); + //Get the minor task namesecond + $this->assertEquals('Initiate Request', $result[1]['APP_TAS_TITLE']); + } + + /** + * This checks to make sure filter by category is working properly + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_return_data_filtered_by_process_category() + { + //Create user + $user = factory(User::class, 1)->create(); + //Create a category + $category = factory(ProcessCategory::class, 1)->create(); + //Create process + $process = factory(Process::class, 1)->create([ + 'PRO_CATEGORY' => $category[0]->CATEGORY_UID + ]); + //Create a category + $category1 = factory(ProcessCategory::class, 1)->create(); + //Create process + $process1 = factory(Process::class, 1)->create([ + 'PRO_CATEGORY' => $category1[0]->CATEGORY_UID + ]); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 2)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'PRO_UID' => $process[0]->PRO_UID, + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 5)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'PRO_UID' => $process1[0]->PRO_UID, + ]); + //Get all data + $result = ListUnassigned::loadList($user[0]->USR_UID); + $this->assertCount(7, $result); + //Define the filters + $filters = ['category' => $category[0]->CATEGORY_UID]; + //Get data + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Get the minor case number first + $this->assertEquals($category[0]->CATEGORY_UID, $result[0]['PRO_CATEGORY']); + //Get the major case number second + $this->assertEquals($category[0]->CATEGORY_UID, $result[1]['PRO_CATEGORY']); + } + + /** + * This checks to make sure filter by category is working properly + * @covers ListUnassigned::loadList + * @test + */ + public function it_should_return_data_filtered_by_generic_search() + { + //Create user + $user = factory(User::class, 1)->create(); + //Create process + $process = factory(Process::class, 1)->create(); + //Create a task self service + $task = factory(Task::class, 1)->create([ + 'TAS_ASSIGN_TYPE' => 'SELF_SERVICE', + 'TAS_GROUP_VARIABLE' => '', + 'PRO_UID' => $process[0]->PRO_UID + ]); + //Assign a user in the task + factory(TaskUser::class, 1)->create([ + 'TAS_UID' => $task[0]->TAS_UID, + 'USR_UID' => $user[0]->USR_UID, + 'TU_RELATION' => 1, //Related to the user + 'TU_TYPE' => 1 + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 2)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_TITLE' => 'This is a case name', + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 2)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_PRO_TITLE' => 'This is a process name', + ]); + //Create the register in list unassigned + factory(ListUnassigned::class, 2)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'APP_TAS_TITLE' => 'This is a task name', + ]); + //Create other registers + factory(ListUnassigned::class, 4)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + //Define the filters + $filters = ['search' => 'case name']; + //Get data related to the search + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Define the filters + $filters = ['search' => 'process name']; + //Get data related to the search + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + //Define the filters + $filters = ['search' => 'task name']; + //Get data related to the search + $result = ListUnassigned::loadList($user[0]->USR_UID, $filters); + $this->assertCount(2, $result); + } +} + diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/GetDiffBetweenModifiedVariablesTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/GetDiffBetweenModifiedVariablesTest.php new file mode 100644 index 000000000..db6ca1ce3 --- /dev/null +++ b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/GetDiffBetweenModifiedVariablesTest.php @@ -0,0 +1,588 @@ + 'b', + ]; + $source = [ + 'a' => 'b', + ]; + $this->assertEquals([], getDiffBetweenModifiedVariables($change, $source)); + } + + /** + * Test to make sure a diff contains nothing if the source has additional properties but the change does not have them + * Reviewing: string + * @test + */ + public function it_should_return_empty_diff_with_source_having_extra_properties() + { + // Create two of the same associative arrays + $change = [ + 'a' => 'b', + ]; + $source = [ + 'a' => 'b', + 'c' => 'd', + ]; + $this->assertEquals([], getDiffBetweenModifiedVariables($change, $source)); + } + + /** + * Test to make sure a diff contains an extra property if the change has it but source does not + * Reviewing: string + * @test + */ + public function it_should_return_simple_diff_with_change_having_extra_property() + { + // Create two of the same associative arrays + $change = [ + 'a' => 'b', + 'c' => 'd', + ]; + $source = [ + 'a' => 'b', + ]; + $expected = [ + 'c' => 'd', + ]; + $this->assertEquals($expected, getDiffBetweenModifiedVariables($change, $source)); + } + + /** + * Test to make sure the diff includes a property that is in a nested deep array and object property + * @test + */ + public function it_should_return_diff_with_nested_difference() + { + $object1 = new stdClass(); + $object1->h = 'i'; + $object1->j = 'Goodbye'; + $change = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => 'Goodbye', + ], + 'g' => $object1 + ]; + $object2 = new stdClass(); + $object2->h = 'i'; + $object2->j = 'Hello'; + $source = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => 'Hello', + ], + 'g' => $object2 + ]; + $object1 = new stdClass(); + $object1->h = 'i'; + $object1->j = 'Goodbye'; + $expected = [ + 'c' => [ + 'd' => 'e', + 'f' => 'Goodbye', + ], + 'g' => $object1 + ]; + $this->assertEquals($expected, getDiffBetweenModifiedVariables($change, $source)); + } + + /** + * Test to make sure the diff includes changes in a nested array + * @test + */ + public function it_should_return_a_diff_with_array_changes() + { + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'] + ] + ]; + $expected = [ + 'var2' => 'X', + 'grid1' => [ + // Note, the entire array will be updated + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + $this->assertEquals($expected, getDiffBetweenModifiedVariables($change, $source)); + } + + /** + * Ensure that the diff provided can be merged with source array to show added rows with proper indexes + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_added_rows_to_array() + { + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'] + ] + ]; + // Now, let's make sure that when we array replace recursive, it properly has all rows and changes + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Test to make sure it provides a diff that can be used with replace_recursive to modify an array in place + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_modifying_rows_in_array() + { + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'CC'], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ] + ]; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ] + ]; + // Now, let's make sure that when we array replace recursive, it properly has all rows and changes + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + // Note the changed record at 2 + 2 => ['field1' => 'AA', 'field2' => 'CC'], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ] + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Ensure that the diff can be applied to source to REMOVE records from the array + * @note This will absolutely fail, because you can't apply the diff to remove something, it's not possible in this way + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_removed_rows_from_array() + { + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + ] + ]; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + // Now, let's make sure that when we array replace recursive, it properly has all rows and changes + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'] + ] + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Utilize new behavior using laravel collections to map and filter to create the desired merged array + * @test + */ + public function it_should_utilize_laravel_collections_to_map_and_filter_for_desired_final_array() + { + $change = [ + 'var1' => 'A', + // Note the changed var 2 + 'var2' => 'X', + // Note the missing var3 element + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + // Note the missing record at index 2 + ] + ]; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'var3' => 'C', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + // Now, let's make sure that when we array replace recursive, it properly has all rows and changes + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + // Note we don't have var3 + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'] + // And we should not have index 2 + ] + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $merged = array_replace_recursive($source, $diff); + // Now collect and map + $final = collect($merged)->filter(function ($value, $key) use ($change) { + // Only accept properties that exist in the change + return array_key_exists($key, $change); + })->map(function ($item, $key) use ($change) { + // We aren't recursively calling, but that's not needed for our situation, so we'll + // Check if it's an array, if so, create a collection, filter, then return the array + if (is_array($item)) { + return collect($item)->filter(function ($value, $subkey) use ($change, $key) { + return array_key_exists($subkey, $change[$key]); + })->all(); + } + + // Otherwise, just return item + return $item; + })->all(); + $this->assertEquals($expected, $final); + } + + /** + * Test to make sure the diff includes changes in a nested array + * @test + */ + public function it_should_return_a_diff_with_object_changes() + { + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person1 + ]; + + $person2 = new stdClass(); + $person2->firstName = 'Corine'; + $person2->lastName = 'Erler'; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'varPerson' => $person2 + ]; + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $expected = [ + 'var2' => 'X', + 'varPerson' => $person1 + ]; + $this->assertEquals($expected, getDiffBetweenModifiedVariables($change, $source)); + } + + /** + * Ensure that the diff provided can be merged with source object to show added rows with proper indexes + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_added_rows_to_object() + { + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $person1->address = '2789 Parkview Drive'; + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person1 + ]; + $person2 = new stdClass(); + $person2->firstName = 'Corine'; + $person2->lastName = 'Erler'; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'varPerson' => $person2 + ]; + // Now, let's make sure that when we array replace recursive, it properly has all rows and changes + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $person1->address = '2789 Parkview Drive'; + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person1 + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Test to make sure it provides a diff that can be used with replace_recursive to modify an object in place + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_modifying_rows_in_object() + { + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $person1->address = '2789 Parkview Drive'; + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person1 + ]; + $person2 = new stdClass(); + $person2->firstName = 'Corine'; + $person2->lastName = 'Martell'; + $person2->address = '2789 Parkview Drive'; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'varPerson' => $person2 + ]; + // Now, let's make sure that when we array replace recursive, it properly has all rows and changes + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $person1->address = '2789 Parkview Drive'; + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person1 + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Ensure that the diff can be applied to source to REMOVE records from the object + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_removed_rows_from_object() + { + $person1 = new stdClass(); + $person1->lastName = 'Gail'; + $person1->address = '3607 Sycamore Road'; + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'varPerson' => $person1], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ], + 'varPerson' => $person1 + ]; + $person2 = new stdClass(); + $person2->firstName = 'Corine'; + $person2->lastName = 'Martell'; + $person2->address = '2789 Parkview Drive'; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'varPerson' => $person2], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ], + 'varPerson' => $person2 + ]; + // Now, let's make sure that when we has object variable we need to replace with the last change over the object + $person1 = new stdClass(); + $person1->lastName = 'Gail'; + $person1->address = '3607 Sycamore Road'; + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'varPerson' => $person1], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ], + 'varPerson' => $person1 + ]; + + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Ensure that the diff can be applied to source to REMOVE records from the object + * @test + */ + public function it_should_provide_diff_with_merge_in_the_same_object() + { + $person2 = new stdClass(); + $person2->firstName = 'Corine'; + $person2->lastName = 'Martell'; + $person2->address = '2789 Parkview Drive'; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'varPerson' => $person2 + ]; + + $person2->lastName = 'Gail'; + $person2->email = 'corine.gail@email.com'; + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person2 + ]; + + // Now, let's make sure that when we has object variable we need to replace with the last change over the object + $person1 = new stdClass(); + $person1->firstName = 'Corine'; + $person1->lastName = 'Martell'; + $person1->address = '2789 Parkview Drive'; + $person1->lastName = 'Gail'; + $person1->email = 'corine.gail@email.com'; + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'varPerson' => $person1 + ]; + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } + + /** + * Ensure that the diff can be applied to source to REMOVE records from nested object + * @test + */ + public function it_should_provide_diff_with_merge_that_supports_rows_from_nested_object() + { + $person1 = new stdClass(); + $person1->firstName = 'Gail'; + $person1->lastName = 'Martell'; + $person1->address = '3607 Sycamore Road'; + $email1 = new stdClass(); + $email1->personal = 'gail@personal.com'; + $email1->coorporative = 'gail@coorporative.com'; + $person1->email = $email1; + $change = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'varPerson' => $person1], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ], + 'varPerson' => $person1 + ]; + $person2 = new stdClass(); + $person2->firstName = 'Corine'; + $person2->lastName = 'Martell'; + $email2 = new stdClass(); + $email2->new = 'GailJMartell@email.com'; + $person2->email = $email2; + $source = [ + 'var1' => 'A', + 'var2' => 'B', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'varPerson' => $person2], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ], + 'varPerson' => $person2 + ]; + // Now, let's make sure that when we has object variable we need to replace with the last change over the object + $person1 = new stdClass(); + $person1->firstName = 'Gail'; + $person1->lastName = 'Martell'; + $person1->address = '3607 Sycamore Road'; + $email1 = new stdClass(); + $email1->personal = 'gail@personal.com'; + $email1->coorporative = 'gail@coorporative.com'; + $person1->email = $email1; + $expected = [ + 'var1' => 'A', + 'var2' => 'X', + 'grid1' => [ + 1 => ['field1' => 'A', 'field2' => 'B'], + 2 => ['field1' => 'AA', 'varPerson' => $person1], + 3 => ['field1' => 'AAA', 'field2' => 'BBB'] + ], + 'varPerson' => $person1 + ]; + + $diff = getDiffBetweenModifiedVariables($change, $source); + $collection = collect($source); + $merged = $collection->merge($diff); + $merged = $merged->all(); + $this->assertEquals($expected, $merged); + } +} + diff --git a/thirdparty/creole/drivers/mssql/MSSQLConnection.php b/thirdparty/creole/drivers/mssql/MSSQLConnection.php index cfcf30722..0135300a2 100644 --- a/thirdparty/creole/drivers/mssql/MSSQLConnection.php +++ b/thirdparty/creole/drivers/mssql/MSSQLConnection.php @@ -83,7 +83,8 @@ class MSSQLConnection extends ConnectionCommon implements Connection $opt = [ 'UID' => $user, 'PWD' => $pw, - 'Database' => $dsninfo['database'] + 'Database' => $dsninfo['database'], + 'CharacterSet' => 'UTF-8' ]; // SQLSrv is persistent always $conn = sqlsrv_connect($dbhost, $opt); diff --git a/thirdparty/phpmailer/class.phpmailer.php b/thirdparty/phpmailer/class.phpmailer.php deleted file mode 100644 index 0bf7d9eb8..000000000 --- a/thirdparty/phpmailer/class.phpmailer.php +++ /dev/null @@ -1,2862 +0,0 @@ -UseSendmailOptions) { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header); - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params); - } - return $rt; - } - - /** - * Outputs debugging info via user-defined method - * @param string $str - */ - private function edebug($str) { - if ($this->Debugoutput == "error_log") { - error_log($str); - } else { - error_log($str); - } - } - - /** - * Constructor - * @param boolean $exceptions Should we throw external exceptions? - */ - public function __construct($exceptions = false) { - $this->exceptions = ($exceptions == true); - } - - /** - * Sets message type to HTML. - * @param bool $ishtml - * @return void - */ - public function IsHTML($ishtml = true) { - if ($ishtml) { - $this->ContentType = 'text/html'; - } else { - $this->ContentType = 'text/plain'; - } - } - - /** - * Sets Mailer to send message using SMTP. - * @return void - */ - public function IsSMTP() { - $this->Mailer = 'smtp'; - } - - /** - * Sets Mailer to send message using PHP mail() function. - * @return void - */ - public function IsMail() { - $this->Mailer = 'mail'; - } - - /** - * Sets Mailer to send message using the $Sendmail program. - * @return void - */ - public function IsSendmail() { - if (!stristr(ini_get('sendmail_path'), 'sendmail')) { - $this->Sendmail = '/var/qmail/bin/sendmail'; - } - $this->Mailer = 'sendmail'; - } - - /** - * Sets Mailer to send message using the qmail MTA. - * @return void - */ - public function IsQmail() { - if (stristr(ini_get('sendmail_path'), 'qmail')) { - $this->Sendmail = '/var/qmail/bin/sendmail'; - } - $this->Mailer = 'sendmail'; - } - - ///////////////////////////////////////////////// - // METHODS, RECIPIENTS - ///////////////////////////////////////////////// - - /** - * Adds a "To" address. - * @param string $address - * @param string $name - * @return boolean true on success, false if address already used - */ - public function AddAddress($address, $name = '') { - return $this->AddAnAddress('to', $address, $name); - } - - /** - * Adds a "Cc" address. - * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address - * @param string $name - * @return boolean true on success, false if address already used - */ - public function AddCC($address, $name = '') { - return $this->AddAnAddress('cc', $address, $name); - } - - /** - * Adds a "Bcc" address. - * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address - * @param string $name - * @return boolean true on success, false if address already used - */ - public function AddBCC($address, $name = '') { - return $this->AddAnAddress('bcc', $address, $name); - } - - /** - * Adds a "Reply-to" address. - * @param string $address - * @param string $name - * @return boolean - */ - public function AddReplyTo($address, $name = '') { - return $this->AddAnAddress('Reply-To', $address, $name); - } - - /** - * Adds an address to one of the recipient arrays - * Addresses that have been added already return false, but do not throw exceptions - * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' - * @param string $address The email address to send to - * @param string $name - * @throws phpmailerException - * @return boolean true on success, false if address already used or invalid in some way - * @access protected - */ - protected function AddAnAddress($kind, $address, $name = '') { - if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { - $this->SetError($this->Lang('Invalid recipient array').': '.$kind); - if ($this->exceptions) { - throw new phpmailerException('Invalid recipient array: ' . $kind); - } - if ($this->SMTPDebug) { - $this->edebug($this->Lang('Invalid recipient array').': '.$kind); - } - return false; - } - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!$this->ValidateAddress($address)) { - $this->SetError($this->Lang('invalid_address').': '. $address); - if ($this->exceptions) { - throw new phpmailerException($this->Lang('invalid_address').': '.$address); - } - if ($this->SMTPDebug) { - $this->edebug($this->Lang('invalid_address').': '.$address); - } - return false; - } - if ($kind != 'Reply-To') { - if (!isset($this->all_recipients[strtolower($address)])) { - array_push($this->$kind, array($address, $name)); - $this->all_recipients[strtolower($address)] = true; - return true; - } - } else { - if (!array_key_exists(strtolower($address), $this->ReplyTo)) { - $this->ReplyTo[strtolower($address)] = array($address, $name); - return true; - } - } - return false; -} - -/** - * Set the From and FromName properties - * @param string $address - * @param string $name - * @param int $auto Also set Reply-To and Sender - * @throws phpmailerException - * @return boolean - */ - public function SetFrom($address, $name = '', $auto = 1) { - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!$this->ValidateAddress($address)) { - $this->SetError($this->Lang('invalid_address').': '. $address); - if ($this->exceptions) { - throw new phpmailerException($this->Lang('invalid_address').': '.$address); - } - if ($this->SMTPDebug) { - $this->edebug($this->Lang('invalid_address').': '.$address); - } - return false; - } - $this->From = $address; - $this->FromName = $name; - if ($auto) { - if (empty($this->ReplyTo)) { - $this->AddAnAddress('Reply-To', $address, $name); - } - if (empty($this->Sender)) { - $this->Sender = $address; - } - } - return true; - } - - /** - * Check that a string looks roughly like an email address should - * Static so it can be used without instantiation, public so people can overload - * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is - * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to - * not allow a@b type valid addresses :( - * Some Versions of PHP break on the regex though, likely due to PCRE, so use - * the older validation method for those users. (http://php.net/manual/en/pcre.installation.php) - * @link http://squiloople.com/2009/12/20/email-address-validation/ - * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice. - * @param string $address The email address to check - * @return boolean - * @static - * @access public - */ - public static function ValidateAddress($address) { - if ((defined('PCRE_VERSION')) && (version_compare(PCRE_VERSION, '8.0') >= 0)) { - return preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[ ])+|(?>[ ]*\x0D\x0A)?[ ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){7,})((?6)(?>:(?6)){0,5})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){5,})(?8)?::(?>((?6)(?>:(?6)){0,3}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); - } elseif (function_exists('filter_var')) { //Introduced in PHP 5.2 - if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { - return false; - } else { - return true; - } - } else { - return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); - } - } - - ///////////////////////////////////////////////// - // METHODS, MAIL SENDING - ///////////////////////////////////////////////// - - /** - * Creates message and assigns Mailer. If the message is - * not sent successfully then it returns false. Use the ErrorInfo - * variable to view description of the error. - * @throws phpmailerException - * @return bool - */ - public function Send() { - try { - if(!$this->PreSend()) return false; - return $this->PostSend(); - } catch (phpmailerException $e) { - $this->mailHeader = ''; - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - return false; - } - } - - /** - * Prep mail by constructing all message entities - * @throws phpmailerException - * @return bool - */ - public function PreSend() { - try { - $this->mailHeader = ""; - if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { - throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); - } - - // Set whether the message is multipart/alternative - if(!empty($this->AltBody)) { - $this->ContentType = 'multipart/alternative'; - } - - $this->error_count = 0; // reset errors - $this->SetMessageType(); - //Refuse to send an empty message - if (empty($this->Body)) { - throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); - } - - $this->MIMEHeader = $this->CreateHeader(); - $this->MIMEBody = $this->CreateBody(); - - // To capture the complete message when using mail(), create - // an extra header list which CreateHeader() doesn't fold in - if ($this->Mailer == 'mail') { - if (count($this->to) > 0) { - $this->mailHeader .= $this->AddrAppend("To", $this->to); - } else { - $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); - } - $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); - // if(count($this->cc) > 0) { - // $this->mailHeader .= $this->AddrAppend("Cc", $this->cc); - // } - } - - // digitally sign with DKIM if enabled - if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) { - $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); - $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; - } - - return true; - - } catch (phpmailerException $e) { - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - return false; - } - } - - /** - * Actual Email transport function - * Send the email via the selected mechanism - * @throws phpmailerException - * @return bool - */ - public function PostSend() { - try { - // Choose the mailer and send through it - switch($this->Mailer) { - case 'sendmail': - return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); - case 'smtp': - return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); - case 'mail': - return $this->MailSend($this->MIMEHeader, $this->MIMEBody); - default: - return $this->MailSend($this->MIMEHeader, $this->MIMEBody); - } - } catch (phpmailerException $e) { - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - if ($this->SMTPDebug) { - $this->edebug($e->getMessage()."\n"); - } - } - return false; - } - - /** - * Sends mail using the $Sendmail program. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @access protected - * @return bool - */ - protected function SendmailSend($header, $body) { - if ($this->Sender != '') { - $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); - } else { - $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); - } - if ($this->SingleTo === true) { - foreach ($this->SingleToArray as $val) { - if(!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, "To: " . $val . "\n"); - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - // implement call back function if it exists - $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - if($result != 0) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - } - } else { - if(!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - // implement call back function if it exists - $isSent = ($result == 0) ? 1 : 0; - $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); - if($result != 0) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - } - return true; - } - - /** - * Sends mail using the PHP mail() function. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @access protected - * @return bool - */ - protected function MailSend($header, $body) - { - $toArr = array(); - foreach ($this->to as $t) { - $toArr[] = $this->AddrFormat($t); - } - $to = implode(', ', $toArr); - - if (empty($this->Sender)) { - $params = "-oi "; - } else { - $params = sprintf("-oi -f%s", $this->Sender); - } - if ($this->Sender != '') { - $old_from = ini_get('sendmail_from'); - ini_set('sendmail_from', $this->Sender); - } - $rt = false; - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $val) { - $rt = $this->mail_passthru($val, $this->Subject, $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - } - } else { - $rt = $this->mail_passthru($to, $this->Subject, $body, $header, $params); - // implement call back function if it exists - $isSent = ($rt == 1) ? 1 : 0; - $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); - } - if (isset($old_from)) { - ini_set('sendmail_from', $old_from); - } - if (!$rt) { - throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); - } - return true; - } - - /** - * Sends mail via SMTP using PhpSMTP - * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @uses SMTP - * @access protected - * @return bool - */ - protected function SmtpSend($header, $body) { - require_once $this->PluginDir . 'class.smtp.php'; - $bad_rcpt = array(); - - if(!$this->SmtpConnect()) { - throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL); - } - $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; - if(!$this->smtp->Mail($smtp_from)) { - $this->SetError($this->Lang('from_failed') . $smtp_from . " : " . implode(",",$this->smtp->getError())) ; - throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); - } - - // Attempt to send attach all recipients - foreach($this->to as $to) { - if (!$this->smtp->Recipient($to[0])) { - $bad_rcpt[] = $to[0]; - // implement call back function if it exists - $isSent = 0; - $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); - } else { - // implement call back function if it exists - $isSent = 1; - $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); - } - } - foreach($this->cc as $cc) { - if (!$this->smtp->Recipient($cc[0])) { - $bad_rcpt[] = $cc[0]; - // implement call back function if it exists - $isSent = 0; - $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); - } else { - // implement call back function if it exists - $isSent = 1; - $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); - } - } - foreach($this->bcc as $bcc) { - if (!$this->smtp->Recipient($bcc[0])) { - $bad_rcpt[] = $bcc[0]; - // implement call back function if it exists - $isSent = 0; - $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); - } else { - // implement call back function if it exists - $isSent = 1; - $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); - } - } - - - if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses - $badaddresses = implode(', ', $bad_rcpt); - throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); - } - if(!$this->smtp->Data($header . $body)) { - throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); - } - if($this->SMTPKeepAlive == true) { - $this->smtp->Reset(); - } else { - $this->smtp->Quit(); - $this->smtp->Close(); - } - return true; - } - - /** - * Initiates a connection to an SMTP server. - * Returns false if the operation failed. - * @uses SMTP - * @access public - * @throws phpmailerException - * @return bool - */ - public function SmtpConnect() { - if(is_null($this->smtp)) { - $this->smtp = new SMTP; - } - - $this->smtp->Timeout = $this->Timeout; - $this->smtp->do_debug = $this->SMTPDebug; - $hosts = explode(';', $this->Host); - $index = 0; - $connection = $this->smtp->Connected(); - - // Retry while there is no connection - try { - while($index < count($hosts) && !$connection) { - $hostinfo = array(); - if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } else { - $host = $hosts[$index]; - $port = $this->Port; - } - - $tls = ($this->SMTPSecure == 'tls'); - $ssl = ($this->SMTPSecure == 'ssl'); - - if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { - - $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); - $this->smtp->Hello($hello); - - if ($tls) { - if (!$this->smtp->StartTLS()) { - throw new phpmailerException($this->Lang('connect_host')); - } - - //We must resend HELO after tls negotiation - $this->smtp->Hello($hello); - } - - $connection = true; - if ($this->SMTPAuth) { - if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, - $this->Realm, $this->Workstation)) { - throw new phpmailerException($this->Lang('authenticate')); - } - } - } - $index++; - if (!$connection) { - throw new phpmailerException($this->Lang('connect_host')); - } - } - } catch (phpmailerException $e) { - $this->smtp->Reset(); - if ($this->exceptions) { - throw $e; - } - } - return true; - } - - /** - * Closes the active SMTP session if one exists. - * @return void - */ - public function SmtpClose() { - if ($this->smtp !== null) { - if($this->smtp->Connected()) { - $this->smtp->Quit(); - $this->smtp->Close(); - } - } - } - - /** - * Sets the language for all class error messages. - * Returns false if it cannot load the language file. The default language is English. - * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory - * @return bool - * @access public - */ - function SetLanguage($langcode = 'en', $lang_path = 'language/') { - //Define full set of translatable strings - $PHPMAILER_LANG = array( - 'authenticate' => 'SMTP Error: Could not authenticate.', - 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', - 'data_not_accepted' => 'SMTP Error: Data not accepted.', - 'empty_message' => 'Message body empty', - 'encoding' => 'Unknown encoding: ', - 'execute' => 'Could not execute: ', - 'file_access' => 'Could not access file: ', - 'file_open' => 'File Error: Could not open file: ', - 'from_failed' => 'The following From address failed: ', - 'instantiate' => 'Could not instantiate mail function.', - 'invalid_address' => 'Invalid address', - 'mailer_not_supported' => ' mailer is not supported.', - 'provide_address' => 'You must provide at least one recipient email address.', - 'recipients_failed' => 'SMTP Error: The following recipients failed: ', - 'signing' => 'Signing Error: ', - 'smtp_connect_failed' => 'SMTP Connect() failed.', - 'smtp_error' => 'SMTP server error: ', - 'variable_set' => 'Cannot set or reset variable: ' - ); - //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! - $l = true; - if ($langcode != 'en') { //There is no English translation file - if (substr($langcode, 0, 2) == 'pt') { - $langcode = 'br'; - } - $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; - } - $this->language = $PHPMAILER_LANG; - return ($l == true); //Returns false if language not found - } - - /** - * Return the current array of language strings - * @return array - */ - public function GetTranslations() { - return $this->language; - } - - ///////////////////////////////////////////////// - // METHODS, MESSAGE CREATION - ///////////////////////////////////////////////// - - /** - * Creates recipient headers. - * @access public - * @param string $type - * @param array $addr - * @return string - */ - public function AddrAppend($type, $addr) { - $addr_str = $type . ': '; - $addresses = array(); - foreach ($addr as $a) { - $addresses[] = $this->AddrFormat($a); - } - $addr_str .= implode(', ', $addresses); - $addr_str .= $this->LE; - - return $addr_str; - } - - /** - * Formats an address correctly. - * @access public - * @param string $addr - * @return string - */ - public function AddrFormat($addr) { - if (empty($addr[1])) { - return $this->SecureHeader($addr[0]); - } else { - return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; - } - } - - /** - * Wraps message for use with mailers that do not - * automatically perform wrapping and for quoted-printable. - * Original written by philippe. - * @param string $message The message to wrap - * @param integer $length The line length to wrap to - * @param boolean $qp_mode Whether to run in Quoted-Printable mode - * @access public - * @return string - */ - public function WrapText($message, $length, $qp_mode = false) { - $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; - // If utf-8 encoding is used, we will need to make sure we don't - // split multibyte characters when we wrap - $is_utf8 = (strtolower($this->CharSet) == "utf-8"); - $lelen = strlen($this->LE); - $crlflen = strlen(self::CRLF); - - $message = $this->FixEOL($message); - if (substr($message, -$lelen) == $this->LE) { - $message = substr($message, 0, -$lelen); - } - - $line = explode($this->LE, $message); // Magic. We know FixEOL uses $LE - $message = ''; - for ($i = 0 ;$i < count($line); $i++) { - $line_part = explode(' ', $line[$i]); - $buf = ''; - for ($e = 0; $e $length)) { - $space_left = $length - strlen($buf) - $crlflen; - if ($e != 0) { - if ($space_left > 20) { - $len = $space_left; - if ($is_utf8) { - $len = $this->UTF8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == "=") { - $len--; - } elseif (substr($word, $len - 2, 1) == "=") { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - $buf .= ' ' . $part; - $message .= $buf . sprintf("=%s", self::CRLF); - } else { - $message .= $buf . $soft_break; - } - $buf = ''; - } - while (strlen($word) > 0) { - $len = $length; - if ($is_utf8) { - $len = $this->UTF8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == "=") { - $len--; - } elseif (substr($word, $len - 2, 1) == "=") { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - - if (strlen($word) > 0) { - $message .= $part . sprintf("=%s", self::CRLF); - } else { - $buf = $part; - } - } - } else { - $buf_o = $buf; - $buf .= ($e == 0) ? $word : (' ' . $word); - - if (strlen($buf) > $length and $buf_o != '') { - $message .= $buf_o . $soft_break; - $buf = $word; - } - } - } - $message .= $buf . self::CRLF; - } - - return $message; - } - - /** - * Finds last character boundary prior to maxLength in a utf-8 - * quoted (printable) encoded string. - * Original written by Colin Brown. - * @access public - * @param string $encodedText utf-8 QP text - * @param int $maxLength find last character boundary prior to this length - * @return int - */ - public function UTF8CharBoundary($encodedText, $maxLength) { - $foundSplitPos = false; - $lookBack = 3; - while (!$foundSplitPos) { - $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); - $encodedCharPos = strpos($lastChunk, "="); - if ($encodedCharPos !== false) { - // Found start of encoded character byte within $lookBack block. - // Check the encoded byte value (the 2 chars after the '=') - $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); - $dec = hexdec($hex); - if ($dec < 128) { // Single byte character. - // If the encoded char was found at pos 0, it will fit - // otherwise reduce maxLength to start of the encoded char - $maxLength = ($encodedCharPos == 0) ? $maxLength : - $maxLength - ($lookBack - $encodedCharPos); - $foundSplitPos = true; - } elseif ($dec >= 192) { // First byte of a multi byte character - // Reduce maxLength to split at start of character - $maxLength = $maxLength - ($lookBack - $encodedCharPos); - $foundSplitPos = true; - } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back - $lookBack += 3; - } - } else { - // No encoded character found - $foundSplitPos = true; - } - } - return $maxLength; - } - - - /** - * Set the body wrapping. - * @access public - * @return void - */ - public function SetWordWrap() { - if($this->WordWrap < 1) { - return; - } - - switch($this->message_type) { - case 'alt': - case 'alt_inline': - case 'alt_attach': - case 'alt_inline_attach': - $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); - break; - default: - $this->Body = $this->WrapText($this->Body, $this->WordWrap); - break; - } - } - - /** - * Assembles message header. - * @access public - * @return string The assembled header - */ - public function CreateHeader() { - $result = ''; - - if(!class_exists('G')){ - $realdocuroot = str_replace( '\\', '/', $_SERVER['DOCUMENT_ROOT'] ); - $docuroot = explode( '/', $realdocuroot ); - array_pop( $docuroot ); - $pathhome = implode( '/', $docuroot ) . '/'; - array_pop( $docuroot ); - $pathTrunk = implode( '/', $docuroot ) . '/'; - require_once($pathTrunk.'gulliver/system/class.g.php'); - } - // Set the boundaries - $uniq_id = G::encryptOld(uniqid(time())); - $this->boundary[1] = 'b1_' . $uniq_id; - $this->boundary[2] = 'b2_' . $uniq_id; - $this->boundary[3] = 'b3_' . $uniq_id; - - if ($this->MessageDate == '') { - $result .= $this->HeaderLine('Date', self::RFCDate()); - } else { - $result .= $this->HeaderLine('Date', $this->MessageDate); - } - - if ($this->ReturnPath) { - $result .= $this->HeaderLine('Return-Path', trim($this->ReturnPath)); - } elseif ($this->Sender == '') { - $result .= $this->HeaderLine('Return-Path', trim($this->From)); - } else { - $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); - } - - // To be created automatically by mail() - if($this->Mailer != 'mail') { - if ($this->SingleTo === true) { - foreach($this->to as $t) { - $this->SingleToArray[] = $this->AddrFormat($t); - } - } else { - if(count($this->to) > 0) { - $result .= $this->AddrAppend('To', $this->to); - } elseif (count($this->cc) == 0) { - $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); - } - } - } - - $from = array(); - $from[0][0] = trim($this->From); - $from[0][1] = $this->FromName; - $result .= $this->AddrAppend('From', $from); - - // sendmail and mail() extract Cc from the header before sending - if(count($this->cc) > 0) { - $result .= $this->AddrAppend('Cc', $this->cc); - } - - // sendmail and mail() extract Bcc from the header before sending - if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { - $result .= $this->AddrAppend('Bcc', $this->bcc); - } - - if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); - } - - // mail() sets the subject itself - if($this->Mailer != 'mail') { - $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); - } - - if($this->MessageID != '') { - $result .= $this->HeaderLine('Message-ID', $this->MessageID); - } else { - $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); - } - $result .= $this->HeaderLine('X-Priority', $this->Priority); - if ($this->XMailer == '') { - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); - } else { - $myXmailer = trim($this->XMailer); - if ($myXmailer) { - $result .= $this->HeaderLine('X-Mailer', $myXmailer); - } - } - - if($this->ConfirmReadingTo != '') { - $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); - } - - // Add custom headers - for($index = 0; $index < count($this->CustomHeader); $index++) { - $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); - } - if (!$this->sign_key_file) { - $result .= $this->HeaderLine('MIME-Version', '1.0'); - $result .= $this->GetMailMIME(); - } - - return $result; - } - - /** - * Returns the message MIME. - * @access public - * @return string - */ - public function GetMailMIME() { - $result = ''; - switch($this->message_type) { - case 'inline': - $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - case 'attach': - case 'inline_attach': - case 'alt_attach': - case 'alt_inline_attach': - $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - case 'alt': - case 'alt_inline': - $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - default: - // Catches case 'plain': and case '': - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); - $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet); - break; - } - - if($this->Mailer != 'mail') { - $result .= $this->LE; - } - - return $result; - } - - /** - * Returns the MIME message (headers and body). Only really valid post PreSend(). - * @access public - * @return string - */ - public function GetSentMIMEMessage() { - return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; - } - - - /** - * Assembles the message body. Returns an empty string on failure. - * @access public - * @throws phpmailerException - * @return string The assembled message body - */ - public function CreateBody() { - $body = ''; - - if ($this->sign_key_file) { - $body .= $this->GetMailMIME().$this->LE; - } - - $this->SetWordWrap(); - - switch($this->message_type) { - case 'inline': - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[1]); - break; - case 'attach': - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); - break; - case 'inline_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[2]); - $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); - break; - case 'alt': - $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->EndBoundary($this->boundary[1]); - break; - case 'alt_inline': - $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->TextLine("--" . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[2]); - $body .= $this->LE; - $body .= $this->EndBoundary($this->boundary[1]); - break; - case 'alt_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->EndBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); - break; - case 'alt_inline_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); - $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); - $body .= $this->EncodeString($this->AltBody, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->TextLine("--" . $this->boundary[2]); - $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); - $body .= $this->LE; - $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[3]); - $body .= $this->LE; - $body .= $this->EndBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); - break; - default: - // catch case 'plain' and case '' - $body .= $this->EncodeString($this->Body, $this->Encoding); - break; - } - - if ($this->IsError()) { - $body = ''; - } elseif ($this->sign_key_file) { - try { - $file = tempnam('', 'mail'); - file_put_contents($file, $body); //TODO check this worked - $signed = tempnam("", "signed"); - if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { - @unlink($file); - $body = file_get_contents($signed); - @unlink($signed); - } else { - @unlink($file); - @unlink($signed); - throw new phpmailerException($this->Lang("signing").openssl_error_string()); - } - } catch (phpmailerException $e) { - $body = ''; - if ($this->exceptions) { - throw $e; - } - } - } - - return $body; - } - - /** - * Returns the start of a message boundary. - * @access protected - * @param string $boundary - * @param string $charSet - * @param string $contentType - * @param string $encoding - * @return string - */ - protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { - $result = ''; - if($charSet == '') { - $charSet = $this->CharSet; - } - if($contentType == '') { - $contentType = $this->ContentType; - } - if($encoding == '') { - $encoding = $this->Encoding; - } - $result .= $this->TextLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); - $result .= $this->LE; - $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); - $result .= $this->LE; - - return $result; - } - - /** - * Returns the end of a message boundary. - * @access protected - * @param string $boundary - * @return string - */ - protected function EndBoundary($boundary) { - return $this->LE . '--' . $boundary . '--' . $this->LE; - } - - /** - * Sets the message type. - * @access protected - * @return void - */ - protected function SetMessageType() { - $this->message_type = array(); - if($this->AlternativeExists()) $this->message_type[] = "alt"; - if($this->InlineImageExists()) $this->message_type[] = "inline"; - if($this->AttachmentExists()) $this->message_type[] = "attach"; - $this->message_type = implode("_", $this->message_type); - if($this->message_type == "") $this->message_type = "plain"; - } - - /** - * Returns a formatted header line. - * @access public - * @param string $name - * @param string $value - * @return string - */ - public function HeaderLine($name, $value) { - return $name . ': ' . $value . $this->LE; - } - - /** - * Returns a formatted mail line. - * @access public - * @param string $value - * @return string - */ - public function TextLine($value) { - return $value . $this->LE; - } - - ///////////////////////////////////////////////// - // CLASS METHODS, ATTACHMENTS - ///////////////////////////////////////////////// - - /** - * Adds an attachment from a path on the filesystem. - * Returns false if the file could not be found - * or accessed. - * @param string $path Path to the attachment. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @throws phpmailerException - * @return bool - */ - public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - try { - if ( !@is_file($path) ) { - throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); - } - $filename = basename($path); - if ( $name == '' ) { - $name = $filename; - } - - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => 'attachment', - 7 => 0 - ); - - } catch (phpmailerException $e) { - $this->SetError($e->getMessage()); - if ($this->exceptions) { - throw $e; - } - if ($this->SMTPDebug) { - $this->edebug($e->getMessage()."\n"); - } - if ( $e->getCode() == self::STOP_CRITICAL ) { - return false; - } - } - return true; - } - - /** - * Return the current array of attachments - * @return array - */ - public function GetAttachments() { - return $this->attachment; - } - - /** - * Attaches all fs, string, and binary attachments to the message. - * Returns an empty string on failure. - * @access protected - * @param string $disposition_type - * @param string $boundary - * @return string - */ - protected function AttachAll($disposition_type, $boundary) { - // Return text of body - $mime = array(); - $cidUniq = array(); - $incl = array(); - if(!class_exists('G')){ - $realdocuroot = str_replace( '\\', '/', $_SERVER['DOCUMENT_ROOT'] ); - $docuroot = explode( '/', $realdocuroot ); - array_pop( $docuroot ); - $pathhome = implode( '/', $docuroot ) . '/'; - array_pop( $docuroot ); - $pathTrunk = implode( '/', $docuroot ) . '/'; - require_once($pathTrunk.'gulliver/system/class.g.php'); - } - - // Add all attachments - foreach ($this->attachment as $attachment) { - // CHECK IF IT IS A VALID DISPOSITION_FILTER - if($attachment[6] == $disposition_type) { - // Check for string attachment - $string = ''; - $path = ''; - $bString = $attachment[5]; - if ($bString) { - $string = $attachment[0]; - } else { - $path = $attachment[0]; - } - - $inclhash = G::encryptOld(serialize($attachment)); - if (in_array($inclhash, $incl)) { continue; } - $incl[] = $inclhash; - $filename = $attachment[1]; - $name = $attachment[2]; - $encoding = $attachment[3]; - $type = $attachment[4]; - $disposition = $attachment[6]; - $cid = $attachment[7]; - if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } - $cidUniq[$cid] = true; - - $mime[] = sprintf("--%s%s", $boundary, $this->LE); - $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); - - if($disposition == 'inline') { - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); - } - - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); - - // Encode as string attachment - if($bString) { - $mime[] = $this->EncodeString($string, $encoding); - if($this->IsError()) { - return ''; - } - $mime[] = $this->LE.$this->LE; - } else { - $mime[] = $this->EncodeFile($path, $encoding); - if($this->IsError()) { - return ''; - } - $mime[] = $this->LE.$this->LE; - } - } - } - - $mime[] = sprintf("--%s--%s", $boundary, $this->LE); - - return implode("", $mime); - } - - /** - * Encodes attachment in requested format. - * Returns an empty string on failure. - * @param string $path The full path to the file - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @throws phpmailerException - * @see EncodeFile() - * @access protected - * @return string - */ - protected function EncodeFile($path, $encoding = 'base64') { - try { - if (!is_readable($path)) { - throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); - } - // if (!function_exists('get_magic_quotes')) { - // function get_magic_quotes() { - // return false; - // } - // } - $magic_quotes = get_magic_quotes_runtime(); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime(0); - } else { - ini_set('magic_quotes_runtime', 0); - } - } - $file_buffer = file_get_contents($path); - $file_buffer = $this->EncodeString($file_buffer, $encoding); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime($magic_quotes); - } else { - ini_set('magic_quotes_runtime', $magic_quotes); - } - } - return $file_buffer; - } catch (Exception $e) { - $this->SetError($e->getMessage()); - return ''; - } - } - - /** - * Encodes string to requested format. - * Returns an empty string on failure. - * @param string $str The text to encode - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @access public - * @return string - */ - public function EncodeString($str, $encoding = 'base64') { - $encoded = ''; - switch(strtolower($encoding)) { - case 'base64': - $encoded = chunk_split(base64_encode($str), 76, $this->LE); - break; - case '7bit': - case '8bit': - $encoded = $this->FixEOL($str); - //Make sure it ends with a line break - if (substr($encoded, -(strlen($this->LE))) != $this->LE) - $encoded .= $this->LE; - break; - case 'binary': - $encoded = $str; - break; - case 'quoted-printable': - $encoded = $this->EncodeQP($str); - break; - default: - $this->SetError($this->Lang('encoding') . $encoding); - break; - } - return $encoded; - } - - /** - * Encode a header string to best (shortest) of Q, B, quoted or none. - * @access public - * @param string $str - * @param string $position - * @return string - */ - public function EncodeHeader($str, $position = 'text') { - $x = 0; - - switch (strtolower($position)) { - case 'phrase': - if (!preg_match('/[\200-\377]/', $str)) { - // Can't use addslashes as we don't know what value has magic_quotes_sybase - $encoded = addcslashes($str, "\0..\37\177\\\""); - if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { - return ($encoded); - } else { - return ("\"$encoded\""); - } - } - $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); - break; - case 'comment': - $x = preg_match_all('/[()"]/', $str, $matches); - // Fall-through - case 'text': - default: - $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); - break; - } - - if ($x == 0) { - return ($str); - } - - $maxlen = 75 - 7 - strlen($this->CharSet); - // Try to select the encoding which should produce the shortest output - if (strlen($str)/3 < $x) { - $encoding = 'B'; - if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { - // Use a custom function which correctly encodes and wraps long - // multibyte strings without breaking lines within a character - $encoded = $this->Base64EncodeWrapMB($str, "\n"); - } else { - $encoded = base64_encode($str); - $maxlen -= $maxlen % 4; - $encoded = trim(chunk_split($encoded, $maxlen, "\n")); - } - } else { - $encoding = 'Q'; - $encoded = $this->EncodeQ($str, $position); - $encoded = $this->WrapText($encoded, $maxlen, true); - $encoded = str_replace('='.self::CRLF, "\n", trim($encoded)); - } - - $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); - $encoded = trim(str_replace("\n", $this->LE, $encoded)); - - return $encoded; - } - - /** - * Checks if a string contains multibyte characters. - * @access public - * @param string $str multi-byte text to wrap encode - * @return bool - */ - public function HasMultiBytes($str) { - if (function_exists('mb_strlen')) { - return (strlen($str) > mb_strlen($str, $this->CharSet)); - } else { // Assume no multibytes (we can't handle without mbstring functions anyway) - return false; - } - } - - /** - * Correctly encodes and wraps long multibyte strings for mail headers - * without breaking lines within a character. - * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php - * @access public - * @param string $str multi-byte text to wrap encode - * @param string $lf string to use as linefeed/end-of-line - * @return string - */ - public function Base64EncodeWrapMB($str, $lf=null) { - $start = "=?".$this->CharSet."?B?"; - $end = "?="; - $encoded = ""; - if ($lf === null) { - $lf = $this->LE; - } - - $mb_length = mb_strlen($str, $this->CharSet); - // Each line must have length <= 75, including $start and $end - $length = 75 - strlen($start) - strlen($end); - // Average multi-byte ratio - $ratio = $mb_length / strlen($str); - // Base64 has a 4:3 ratio - $offset = $avgLength = floor($length * $ratio * .75); - - for ($i = 0; $i < $mb_length; $i += $offset) { - $lookBack = 0; - - do { - $offset = $avgLength - $lookBack; - $chunk = mb_substr($str, $i, $offset, $this->CharSet); - $chunk = base64_encode($chunk); - $lookBack++; - } - while (strlen($chunk) > $length); - - $encoded .= $chunk . $lf; - } - - // Chomp the last linefeed - $encoded = substr($encoded, 0, -strlen($lf)); - return $encoded; - } - - /** - * Encode string to quoted-printable. - * Only uses standard PHP, slow, but will always work - * @access public - * @param string $input - * @param integer $line_max Number of chars allowed on a line before wrapping - * @param bool $space_conv - * @internal param string $string the text to encode - * @return string - */ - public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { - $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); - $lines = preg_split('/(?:\r\n|\r|\n)/', $input); - $eol = "\r\n"; - $escape = '='; - $output = ''; - while( list(, $line) = each($lines) ) { - $linlen = strlen($line); - $newline = ''; - for($i = 0; $i < $linlen; $i++) { - $c = substr( $line, $i, 1 ); - $dec = ord( $c ); - if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E - $c = '=2E'; - } - if ( $dec == 32 ) { - if ( $i == ( $linlen - 1 ) ) { // convert space at eol only - $c = '=20'; - } else if ( $space_conv ) { - $c = '=20'; - } - } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required - $h2 = (integer)floor($dec/16); - $h1 = (integer)floor($dec%16); - $c = $escape.$hex[$h2].$hex[$h1]; - } - if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted - $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay - $newline = ''; - // check if newline first character will be point or not - if ( $dec == 46 ) { - $c = '=2E'; - } - } - $newline .= $c; - } // end of for - $output .= $newline.$eol; - } // end of while - return $output; - } - - /** - * Encode string to RFC2045 (6.7) quoted-printable format - * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version - * Also results in same content as you started with after decoding - * @see EncodeQPphp() - * @access public - * @param string $string the text to encode - * @param integer $line_max Number of chars allowed on a line before wrapping - * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function - * @return string - * @author Marcus Bointon - */ - public function EncodeQP($string, $line_max = 76, $space_conv = false) { - if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) - return quoted_printable_encode($string); - } - $filters = stream_get_filters(); - if (!in_array('convert.*', $filters)) { //Got convert stream filter? - return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation - } - $fp = fopen('php://temp/', 'r+'); - $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks - $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); - $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); - fputs($fp, $string); - rewind($fp); - $out = stream_get_contents($fp); - stream_filter_remove($s); - $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange - fclose($fp); - return $out; - } - - /** - * Encode string to q encoding. - * @link http://tools.ietf.org/html/rfc2047 - * @param string $str the text to encode - * @param string $position Where the text is going to be used, see the RFC for what that means - * @access public - * @return string - */ - public function EncodeQ($str, $position = 'text') { - //There should not be any EOL in the string - $pattern=""; - $encoded = str_replace(array("\r", "\n"), '', $str); - switch (strtolower($position)) { - case 'phrase': - $pattern = '^A-Za-z0-9!*+\/ -'; - break; - - case 'comment': - $pattern = '\(\)"'; - //note that we dont break here! - //for this reason we build the $pattern withoud including delimiters and [] - - case 'text': - default: - //Replace every high ascii, control =, ? and _ characters - //We put \075 (=) as first value to make sure it's the first one in being converted, preventing double encode - $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern; - break; - } - - if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { - foreach (array_unique($matches[0]) as $char) { - $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); - } - } - - //Replace every spaces to _ (more readable than =20) - return str_replace(' ', '_', $encoded); -} - - - /** - * Adds a string or binary attachment (non-filesystem) to the list. - * This method can be used to attach ascii or binary data, - * such as a BLOB record from a database. - * @param string $string String attachment data. - * @param string $filename Name of the attachment. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return void - */ - public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $filename, - 2 => basename($filename), - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => 'attachment', - 7 => 0 - ); - } - - /** - * Adds an embedded attachment. This can include images, sounds, and - * just about any other document. Make sure to set the $type to an - * image type. For JPEG images use "image/jpeg" and for GIF images - * use "image/gif". - * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment. Use this to identify - * the Id for accessing the image in an HTML form. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool - */ - public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - - if ( !@is_file($path) ) { - $this->SetError($this->Lang('file_access') . $path); - return false; - } - - $filename = basename($path); - if ( $name == '' ) { - $name = $filename; - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => 'inline', - 7 => $cid - ); - - return true; - } - - /** - * Adds an embedded stringified attachment. This can include images, sounds, and - * just about any other document. Make sure to set the $type to an - * image type. For JPEG images use "image/jpeg" and for GIF images - * use "image/gif". - * @param string $string The attachment. - * @param string $cid Content ID of the attachment. Use this to identify - * the Id for accessing the image in an HTML form. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool - */ - public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $name, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => 'inline', - 7 => $cid - ); - } - - /** - * Returns true if an inline attachment is present. - * @access public - * @return bool - */ - public function InlineImageExists() { - foreach($this->attachment as $attachment) { - if ($attachment[6] == 'inline') { - return true; - } - } - return false; - } - - /** - * Returns true if an attachment (non-inline) is present. - * @return bool - */ - public function AttachmentExists() { - foreach($this->attachment as $attachment) { - if ($attachment[6] == 'attachment') { - return true; - } - } - return false; - } - - /** - * Does this message have an alternative body set? - * @return bool - */ - public function AlternativeExists() { - return !empty($this->AltBody); - } - - ///////////////////////////////////////////////// - // CLASS METHODS, MESSAGE RESET - ///////////////////////////////////////////////// - - /** - * Clears all recipients assigned in the TO array. Returns void. - * @return void - */ - public function ClearAddresses() { - foreach($this->to as $to) { - unset($this->all_recipients[strtolower($to[0])]); - } - $this->to = array(); - } - - /** - * Clears all recipients assigned in the CC array. Returns void. - * @return void - */ - public function ClearCCs() { - foreach($this->cc as $cc) { - unset($this->all_recipients[strtolower($cc[0])]); - } - $this->cc = array(); - } - - /** - * Clears all recipients assigned in the BCC array. Returns void. - * @return void - */ - public function ClearBCCs() { - foreach($this->bcc as $bcc) { - unset($this->all_recipients[strtolower($bcc[0])]); - } - $this->bcc = array(); - } - - /** - * Clears all recipients assigned in the ReplyTo array. Returns void. - * @return void - */ - public function ClearReplyTos() { - $this->ReplyTo = array(); - } - - /** - * Clears all recipients assigned in the TO, CC and BCC - * array. Returns void. - * @return void - */ - public function ClearAllRecipients() { - $this->to = array(); - $this->cc = array(); - $this->bcc = array(); - $this->all_recipients = array(); - } - - /** - * Clears all previously set filesystem, string, and binary - * attachments. Returns void. - * @return void - */ - public function ClearAttachments() { - $this->attachment = array(); - } - - /** - * Clears all custom headers. Returns void. - * @return void - */ - public function ClearCustomHeaders() { - $this->CustomHeader = array(); - } - - ///////////////////////////////////////////////// - // CLASS METHODS, MISCELLANEOUS - ///////////////////////////////////////////////// - - /** - * Adds the error message to the error container. - * @access protected - * @param string $msg - * @return void - */ - protected function SetError($msg) { - $this->error_count++; - if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { - $lasterror = $this->smtp->getError(); - if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { - $msg .= '

' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "

\n"; - } - } - $this->ErrorInfo = $msg; - } - - /** - * Returns the proper RFC 822 formatted date. - * @access public - * @return string - * @static - */ - public static function RFCDate() { - $tz = date('Z'); - $tzs = ($tz < 0) ? '-' : '+'; - $tz = abs($tz); - $tz = (int)($tz/3600)*100 + ($tz%3600)/60; - $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); - - return $result; - } - - /** - * Returns the server hostname or 'localhost.localdomain' if unknown. - * @access protected - * @return string - */ - protected function ServerHostname() { - if (!empty($this->Hostname)) { - $result = $this->Hostname; - } elseif (isset($_SERVER['SERVER_NAME'])) { - $result = $_SERVER['SERVER_NAME']; - } else { - $result = 'localhost.localdomain'; - } - - return $result; - } - - /** - * Returns a message in the appropriate language. - * @access protected - * @param string $key - * @return string - */ - protected function Lang($key) { - $lang = 'en'; - if(count($this->language) < 1) { - if (defined('SYS_LANG')) { - $lang = SYS_LANG; - } - $this->SetLanguage($lang); - } - - if(isset($this->language[$key])) { - return $this->language[$key]; - } else { - return 'Language string failed to load: ' . $key; - } - } - - /** - * Returns true if an error occurred. - * @access public - * @return bool - */ - public function IsError() { - return ($this->error_count > 0); - } - - /** - * Changes every end of line from CRLF, CR or LF to $this->LE. - * @access public - * @param string $str String to FixEOL - * @return string - */ - public function FixEOL($str) { - // condense down to \n - $nstr = str_replace(array("\r\n", "\r"), "\n", $str); - // Now convert LE as needed - if ($this->LE !== "\n") { - $nstr = str_replace("\n", $this->LE, $nstr); - } - return $nstr; - } - - /** - * Adds a custom header. $name value can be overloaded to contain - * both header name and value (name:value) - * @access public - * @param string $name custom header name - * @param string $value header value - * @return void - */ - public function AddCustomHeader($name, $value=null) { - if ($value === null) { - // Value passed in as name:value - $this->CustomHeader[] = explode(':', $name, 2); - } else { - $this->CustomHeader[] = array($name, $value); - } - } - - /** - * Evaluates the message and returns modifications for inline images and backgrounds - * @access public - * @param string $message Text to be HTML modified - * @param string $basedir baseline directory for path - * @return string $message - */ - public function MsgHTML($message, $basedir = '') { - preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); - if(!class_exists('G')){ - $realdocuroot = str_replace( '\\', '/', $_SERVER['DOCUMENT_ROOT'] ); - $docuroot = explode( '/', $realdocuroot ); - array_pop( $docuroot ); - $pathhome = implode( '/', $docuroot ) . '/'; - array_pop( $docuroot ); - $pathTrunk = implode( '/', $docuroot ) . '/'; - require_once($pathTrunk.'gulliver/system/class.g.php'); - } - if(isset($images[2])) { - foreach($images[2] as $i => $url) { - // do not change urls for absolute images (thanks to corvuscorax) - if (!preg_match('#^[A-z]+://#', $url)) { - $filename = basename($url); - $directory = dirname($url); - if ($directory == '.') { - $directory = ''; - } - $cid = 'cid:' . G::encryptOld($url); - $ext = pathinfo($filename, PATHINFO_EXTENSION); - $mimeType = self::_mime_types($ext); - if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } - if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } - if ( $this->AddEmbeddedImage($basedir.$directory.$filename, G::encryptOld($url), $filename, 'base64', $mimeType) ) { - $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message); - } - } - } - } - $this->IsHTML(true); - $this->Body = $message; - if (empty($this->AltBody)) { - $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); - if (!empty($textMsg)) { - $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet); - } - } - if (empty($this->AltBody)) { - $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; - } - return $message; - } - - /** - * Gets the MIME type of the embedded or inline image - * @param string $ext File extension - * @access public - * @return string MIME type of ext - * @static - */ - public static function _mime_types($ext = '') { - $mimes = array( - 'xl' => 'application/excel', - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'bin' => 'application/macbinary', - 'doc' => 'application/msword', - 'word' => 'application/msword', - 'class' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'psd' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'so' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'php3' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => 'application/x-javascript', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => 'application/x-tar', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'zip' => 'application/zip', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mpga' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'png' => 'image/png', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'eml' => 'message/rfc822', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', - 'shtml' => 'text/html', - 'log' => 'text/plain', - 'text' => 'text/plain', - 'txt' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mov' => 'video/quicktime', - 'qt' => 'video/quicktime', - 'rv' => 'video/vnd.rn-realvideo', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie' - ); - return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; - } - - /** - * Set (or reset) Class Objects (variables) - * - * Usage Example: - * $page->set('X-Priority', '3'); - * - * @access public - * @param string $name Parameter Name - * @param mixed $value Parameter Value - * NOTE: will not work with arrays, there are no arrays to set/reset - * @throws phpmailerException - * @return bool - * @todo Should this not be using __set() magic function? - */ - public function set($name, $value = '') { - try { - if (isset($this->$name) ) { - $this->$name = $value; - } else { - throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); - } - } catch (Exception $e) { - $this->SetError($e->getMessage()); - if ($e->getCode() == self::STOP_CRITICAL) { - return false; - } - } - return true; - } - - /** - * Strips newlines to prevent header injection. - * @access public - * @param string $str String - * @return string - */ - public function SecureHeader($str) { - return trim(str_replace(array("\r", "\n"), '', $str)); - } - - /** - * Set the private key file and password to sign the message. - * - * @access public - * @param $cert_filename - * @param string $key_filename Parameter File Name - * @param string $key_pass Password for private key - */ - public function Sign($cert_filename, $key_filename, $key_pass) { - $this->sign_cert_file = $cert_filename; - $this->sign_key_file = $key_filename; - $this->sign_key_pass = $key_pass; - } - - /** - * Set the private key file and password to sign the message. - * - * @access public - * @param string $txt - * @return string - */ - public function DKIM_QP($txt) { - $line = ''; - for ($i = 0; $i < strlen($txt); $i++) { - $ord = ord($txt[$i]); - if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) { - $line .= $txt[$i]; - } else { - $line .= "=".sprintf("%02X", $ord); - } - } - return $line; - } - - /** - * Generate DKIM signature - * - * @access public - * @param string $s Header - * @return string - */ - public function DKIM_Sign($s) { - $privKeyStr = file_get_contents($this->DKIM_private); - if ($this->DKIM_passphrase != '') { - $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); - } else { - $privKey = $privKeyStr; - } - if (openssl_sign($s, $signature, $privKey)) { - return base64_encode($signature); - } - return ''; - } - - /** - * Generate DKIM Canonicalization Header - * - * @access public - * @param string $s Header - * @return string - */ - public function DKIM_HeaderC($s) { - $s = preg_replace("/\r\n\s+/", " ", $s); - $lines = explode("\r\n", $s); - foreach ($lines as $key => $line) { - list($heading, $value) = explode(":", $line, 2); - $heading = strtolower($heading); - $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces - $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value - } - $s = implode("\r\n", $lines); - return $s; - } - - /** - * Generate DKIM Canonicalization Body - * - * @access public - * @param string $body Message Body - * @return string - */ - public function DKIM_BodyC($body) { - if ($body == '') return "\r\n"; - // stabilize line endings - $body = str_replace("\r\n", "\n", $body); - $body = str_replace("\n", "\r\n", $body); - // END stabilize line endings - while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { - $body = substr($body, 0, strlen($body) - 2); - } - return $body; - } - - /** - * Create the DKIM header, body, as new header - * - * @access public - * @param string $headers_line Header lines - * @param string $subject Subject - * @param string $body Body - * @return string - */ - public function DKIM_Add($headers_line, $subject, $body) { - $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms - $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body - $DKIMquery = 'dns/txt'; // Query method - $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) - $subject_header = "Subject: $subject"; - $headers = explode($this->LE, $headers_line); - $from_header = ""; - $to_header = ""; - foreach($headers as $header) { - if (strpos($header, 'From:') === 0) { - $from_header = $header; - } elseif (strpos($header, 'To:') === 0) { - $to_header = $header; - } - } - $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); - $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); - $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable - $body = $this->DKIM_BodyC($body); - $DKIMlen = strlen($body) ; // Length of body - $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body - $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";"; - $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n". - "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n". - "\th=From:To:Subject;\r\n". - "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n". - "\tz=$from\r\n". - "\t|$to\r\n". - "\t|$subject;\r\n". - "\tbh=" . $DKIMb64 . ";\r\n". - "\tb="; - $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); - $signed = $this->DKIM_Sign($toSign); - return "X-PHPMAILER-DKIM: code.google.com/a/apache-extras.org/p/phpmailer/\r\n".$dkimhdrs.$signed."\r\n"; - } - - /** - * Perform callback - * @param boolean $isSent - * @param string $to - * @param string $cc - * @param string $bcc - * @param string $subject - * @param string $body - * @param string $from - */ - protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from=null) { - if (!empty($this->action_function) && is_callable($this->action_function)) { - $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); - call_user_func_array($this->action_function, $params); - } - } -} - -/** - * Exception handler for PHPMailer - * @package PHPMailer - */ -class phpmailerException extends Exception { - /** - * Prettify error message output - * @return string - */ - public function errorMessage() { - $errorMsg = '' . $this->getMessage() . "
\n"; - return $errorMsg; - } -} -?> diff --git a/thirdparty/phpmailer/class.pop3.php b/thirdparty/phpmailer/class.pop3.php deleted file mode 100644 index 506b59994..000000000 --- a/thirdparty/phpmailer/class.pop3.php +++ /dev/null @@ -1,418 +0,0 @@ - - * @author Andy Prevost - * @author Jim Jagielski - */ - -class POP3 { - /** - * Default POP3 port - * @var int - */ - public $POP3_PORT = 110; - - /** - * Default Timeout - * @var int - */ - public $POP3_TIMEOUT = 30; - - /** - * POP3 Carriage Return + Line Feed - * @var string - */ - public $CRLF = "\r\n"; - - /** - * Displaying Debug warnings? (0 = now, 1+ = yes) - * @var int - */ - public $do_debug = 2; - - /** - * POP3 Mail Server - * @var string - */ - public $host; - - /** - * POP3 Port - * @var int - */ - public $port; - - /** - * POP3 Timeout Value - * @var int - */ - public $tval; - - /** - * POP3 Username - * @var string - */ - public $username; - - /** - * POP3 Password - * @var string - */ - public $password; - - /** - * Sets the POP3 PHPMailer Version number - * @var string - */ - public $Version = '5.2.4'; - - ///////////////////////////////////////////////// - // PROPERTIES, PRIVATE AND PROTECTED - ///////////////////////////////////////////////// - - /** - * @var resource Resource handle for the POP connection socket - */ - private $pop_conn; - /** - * @var boolean Are we connected? - */ - private $connected; - /** - * @var array Error container - */ - private $error; // Error log array - - /** - * Constructor, sets the initial values - * @access public - * @return POP3 - */ - public function __construct() { - $this->pop_conn = 0; - $this->connected = false; - $this->error = null; - } - - /** - * Combination of public events - connect, login, disconnect - * @access public - * @param string $host - * @param bool|int $port - * @param bool|int $tval - * @param string $username - * @param string $password - * @param int $debug_level - * @return bool - */ - public function Authorise ($host, $port = false, $tval = false, $username, $password, $debug_level = 0) { - $this->host = $host; - - // If no port value is passed, retrieve it - if ($port == false) { - $this->port = $this->POP3_PORT; - } else { - $this->port = $port; - } - - // If no port value is passed, retrieve it - if ($tval == false) { - $this->tval = $this->POP3_TIMEOUT; - } else { - $this->tval = $tval; - } - - $this->do_debug = $debug_level; - $this->username = $username; - $this->password = $password; - - // Refresh the error log - $this->error = null; - - // Connect - $result = $this->Connect($this->host, $this->port, $this->tval); - - if ($result) { - $login_result = $this->Login($this->username, $this->password); - - if ($login_result) { - $this->Disconnect(); - - return true; - } - - } - - // We need to disconnect regardless if the login succeeded - $this->Disconnect(); - - return false; - } - - /** - * Connect to the POP3 server - * @access public - * @param string $host - * @param bool|int $port - * @param integer $tval - * @return boolean - */ - public function Connect ($host, $port = false, $tval = 30) { - // Are we already connected? - if ($this->connected) { - return true; - } - - /* - On Windows this will raise a PHP Warning error if the hostname doesn't exist. - Rather than supress it with @fsockopen, let's capture it cleanly instead - */ - - set_error_handler(array(&$this, 'catchWarning')); - - // Connect to the POP3 server - $this->pop_conn = fsockopen($host, // POP3 Host - $port, // Port # - $errno, // Error Number - $errstr, // Error Message - $tval); // Timeout (seconds) - - // Restore the error handler - restore_error_handler(); - - // Does the Error Log now contain anything? - if ($this->error && $this->do_debug >= 1) { - $this->displayErrors(); - } - - // Did we connect? - if ($this->pop_conn == false) { - // It would appear not... - $this->error = array( - 'error' => "Failed to connect to server $host on port $port", - 'errno' => $errno, - 'errstr' => $errstr - ); - - if ($this->do_debug >= 1) { - $this->displayErrors(); - } - - return false; - } - - // Increase the stream time-out - - // Check for PHP 4.3.0 or later - if (version_compare(phpversion(), '5.0.0', 'ge')) { - stream_set_timeout($this->pop_conn, $tval, 0); - } else { - // Does not work on Windows - if (substr(PHP_OS, 0, 3) !== 'WIN') { - socket_set_timeout($this->pop_conn, $tval, 0); - } - } - - // Get the POP3 server response - $pop3_response = $this->getResponse(); - - // Check for the +OK - if ($this->checkResponse($pop3_response)) { - // The connection is established and the POP3 server is talking - $this->connected = true; - return true; - } - return false; - } - - /** - * Login to the POP3 server (does not support APOP yet) - * @access public - * @param string $username - * @param string $password - * @return boolean - */ - public function Login ($username = '', $password = '') { - if ($this->connected == false) { - $this->error = 'Not connected to POP3 server'; - - if ($this->do_debug >= 1) { - $this->displayErrors(); - } - } - - if (empty($username)) { - $username = $this->username; - } - - if (empty($password)) { - $password = $this->password; - } - - $pop_username = "USER $username" . $this->CRLF; - $pop_password = "PASS $password" . $this->CRLF; - - // Send the Username - $this->sendString($pop_username); - $pop3_response = $this->getResponse(); - - if ($this->checkResponse($pop3_response)) { - // Send the Password - $this->sendString($pop_password); - $pop3_response = $this->getResponse(); - - if ($this->checkResponse($pop3_response)) { - return true; - } - } - return false; - } - - /** - * Disconnect from the POP3 server - * @access public - */ - public function Disconnect () { - $this->sendString('QUIT'); - - fclose($this->pop_conn); - } - - ///////////////////////////////////////////////// - // Private Methods - ///////////////////////////////////////////////// - - /** - * Get the socket response back. - * $size is the maximum number of bytes to retrieve - * @access private - * @param integer $size - * @return string - */ - private function getResponse ($size = 128) { - $pop3_response = fgets($this->pop_conn, $size); - - return $pop3_response; - } - - /** - * Send a string down the open socket connection to the POP3 server - * @access private - * @param string $string - * @return integer - */ - private function sendString ($string) { - $bytes_sent = fwrite($this->pop_conn, $string, strlen($string)); - - return $bytes_sent; - } - - /** - * Checks the POP3 server response for +OK or -ERR - * @access private - * @param string $string - * @return boolean - */ - private function checkResponse ($string) { - if (substr($string, 0, 3) !== '+OK') { - $this->error = array( - 'error' => "Server reported an error: $string", - 'errno' => 0, - 'errstr' => '' - ); - - if ($this->do_debug >= 1) { - $this->displayErrors(); - } - - return false; - } else { - return true; - } - - } - - /** - * If debug is enabled, display the error message array - * @access private - */ - private function displayErrors () { - echo '
';
-
-    foreach ($this->error as $single_error) {
-      print_r($single_error);
-    }
-
-    echo '
'; - } - - /** - * Takes over from PHP for the socket warning handler - * @access private - * @param integer $errno - * @param string $errstr - * @param string $errfile - * @param integer $errline - */ - private function catchWarning ($errno, $errstr, $errfile, $errline) { - $this->error[] = array( - 'error' => "Connecting to the POP3 server raised a PHP warning: ", - 'errno' => $errno, - 'errstr' => $errstr - ); - } - - // End of class -} -?> diff --git a/thirdparty/phpmailer/class.smtp.php b/thirdparty/phpmailer/class.smtp.php deleted file mode 100644 index 45abc8694..000000000 --- a/thirdparty/phpmailer/class.smtp.php +++ /dev/null @@ -1,1002 +0,0 @@ -Debugoutput == "error_log") { - error_log($str); - } else { - echo $str; - } - } - - /** - * Initialize the class so that the data is in a known state. - * @access public - * @return SMTP - */ - public function __construct() { - $this->smtp_conn = 0; - $this->error = null; - $this->helo_rply = null; - - $this->do_debug = 0; - } - - ///////////////////////////////////////////////// - // CONNECTION FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Connect to the server specified on the port specified. - * If the port is not specified use the default SMTP_PORT. - * If tval is specified then a connection will try and be - * established with the server for that number of seconds. - * If tval is not specified the default is 30 seconds to - * try on the connection. - * - * SMTP CODE SUCCESS: 220 - * SMTP CODE FAILURE: 421 - * @access public - * @param string $host - * @param int $port - * @param int $tval - * @return bool - */ - public function Connect($host, $port = 0, $tval = 30) { - // set the error val to null so there is no confusion - $this->error = null; - - // make sure we are __not__ connected - if($this->connected()) { - // already connected, generate error - $this->error = array("error" => G::LoadTranslation( 'ID_SMTP_ALREADY_CONNECTED' )); - return false; - } - - if(empty($port)) { - $port = $this->SMTP_PORT; - } - - // connect to the smtp server - $this->smtp_conn = @fsockopen($host, // the host of the server - $port, // the port to use - $errno, // error number if any - $errstr, // error message if any - $tval); // give up after ? secs - // verify we connected properly - if(empty($this->smtp_conn)) { - $this->error = array("error" => G::LoadTranslation( 'ID_SMTP_FAILED_CONNECT_SERVER' ), - "errno" => $errno, - "errstr" => $errstr); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'); - } - return false; - } - - // SMTP server can take longer to respond, give longer timeout for first read - // Windows does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != "WIN") { - $max = ini_get('max_execution_time'); - if ($max != 0 && $tval > $max) { // don't bother if unlimited - @set_time_limit($tval); - } - stream_set_timeout($this->smtp_conn, $tval, 0); - } - - // get any announcement - $announce = $this->get_lines(); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'); - } - - return true; - } - - /** - * Initiate a TLS communication with the server. - * - * SMTP CODE 220 Ready to start TLS - * SMTP CODE 501 Syntax error (no parameters allowed) - * SMTP CODE 454 TLS not available due to temporary reason - * @access public - * @return bool success - */ - public function StartTLS() { - $this->error = null; # to avoid confusion - - if(!$this->connected()) { - $this->error = array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_START_TLS' )); - return false; - } - - fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 220) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_START_TLS_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - - // Begin encrypted connection - if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { - return false; - } - - return true; - } - - /** - * Performs SMTP authentication. Must be run after running the - * Hello() method. Returns true if successfully authenticated. - * @access public - * @param string $username - * @param string $password - * @param string $authtype - * @param string $realm - * @param string $workstation - * @return bool - */ - public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') { - if (empty($authtype)) { - $authtype = 'LOGIN'; - } - - switch ($authtype) { - case 'PLAIN': - // Start authentication - fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_AUTH_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - // Send encoded username and password - fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_AUTH_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - break; - case 'LOGIN': - // Start authentication - @fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_AUTH_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - - // Send encoded username - fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_USERNAME_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - - // Send encoded password - fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_PASSWORD_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - break; - case 'NTLM': - /* - * ntlm_sasl_client.php - ** Bundled with Permission - ** - ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx - ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication - */ - require_once('extras/ntlm_sasl_client.php'); - $temp = new stdClass(); - $ntlm_client = new ntlm_sasl_client_class; - if(! $ntlm_client->Initialize($temp)){//let's test if every function its available - $this->error = array("error" => $temp->error); - if($this->do_debug >= 1) { - $this->edebug(G::LoadTranslation( 'ID_SMTP_ERROR_ENABLE_PHPINI' ) . ": " . $this->error["error"] . $this->CRLF); - } - return false; - } - $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1 - - fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - - if($code != 334) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_AUTH_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); - } - return false; - } - - $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2 - $challange = base64_decode($challange); - $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password); - $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3 - // Send encoded username - fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_COULD_NOT_AUTH' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); - } - return false; - } - break; - } - return true; - } - - /** - * Returns true if connected to a server otherwise false - * @access public - * @return bool - */ - public function Connected() { - if(!empty($this->smtp_conn)) { - $sock_status = socket_get_status($this->smtp_conn); - if($sock_status["eof"]) { - // the socket is valid but we are not connected - if($this->do_debug >= 1) { - $this->edebug("SMTP -> NOTICE:" . $this->CRLF . G::LoadTranslation( 'ID_SMTP_ERROR_EOF_CAUGHT' )); - } - $this->Close(); - return false; - } - return true; // everything looks good - } - return false; - } - - /** - * Closes the socket and cleans up the state of the class. - * It is not considered good to use this function without - * first trying to use QUIT. - * @access public - * @return void - */ - public function Close() { - $this->error = null; // so there is no confusion - $this->helo_rply = null; - if(!empty($this->smtp_conn)) { - // close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = 0; - } - } - - ///////////////////////////////////////////////// - // SMTP COMMANDS - ///////////////////////////////////////////////// - - /** - * Issues a data command and sends the msg_data to the server - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a with the message headers - * and the message body being seperated by and additional . - * - * Implements rfc 821: DATA - * - * SMTP CODE INTERMEDIATE: 354 - * [data] - * . - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 552,554,451,452 - * SMTP CODE FAILURE: 451,554 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @param string $msg_data - * @return bool - */ - public function Data($msg_data) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_DATA_WITHOUT' )); - return false; - } - - fputs($this->smtp_conn,"DATA" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 354) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_DATA_COM_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - - /* the server is ready to accept data! - * according to rfc 821 we should not send more than 1000 - * including the CRLF - * characters on a single line so we will break the data up - * into lines by \r and/or \n then if needed we will break - * each of those into smaller lines to fit within the limit. - * in addition we will be looking for lines that start with - * a period '.' and append and additional period '.' to that - * line. NOTE: this does not count towards limit. - */ - - // normalize the line breaks so we know the explode works - $msg_data = str_replace("\r\n","\n",$msg_data); - $msg_data = str_replace("\r","\n",$msg_data); - $lines = explode("\n",$msg_data); - - /* we need to find a good way to determine is headers are - * in the msg_data or if it is a straight msg body - * currently I am assuming rfc 822 definitions of msg headers - * and if the first field of the first line (':' sperated) - * does not contain a space then it _should_ be a header - * and we can process all lines before a blank "" line as - * headers. - */ - - $field = substr($lines[0],0,strpos($lines[0],":")); - $in_headers = false; - if(!empty($field) && !strstr($field," ")) { - $in_headers = true; - } - - $max_line_length = 998; // used below; set here for ease in change - - while(list(,$line) = @each($lines)) { - $lines_out = null; - if($line == "" && $in_headers) { - $in_headers = false; - } - // ok we need to break this line up into several smaller lines - while(strlen($line) > $max_line_length) { - $pos = strrpos(substr($line,0,$max_line_length)," "); - - // Patch to fix DOS attack - if(!$pos) { - $pos = $max_line_length - 1; - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos); - } else { - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos + 1); - } - - /* if processing headers add a LWSP-char to the front of new line - * rfc 822 on long msg headers - */ - if($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - // send the lines to the server - while(list(,$line_out) = @each($lines_out)) { - if(strlen($line_out) > 0) - { - if(substr($line_out, 0, 1) == ".") { - $line_out = "." . $line_out; - } - } - fputs($this->smtp_conn,$line_out . $this->CRLF); - } - } - - // message data has been sent - fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 250) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_DATA_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - return true; - } - - /** - * Sends the HELO command to the smtp server. - * This makes sure that we and the server are in - * the same known state. - * - * Implements from rfc 821: HELO - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 501, 504, 421 - * @access public - * @param string $host - * @return bool - */ - public function Hello($host = '') { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_HELLO_WITHOUT' )); - return false; - } - - // if hostname for HELO was not specified send default - if(empty($host)) { - // determine appropriate default to send to server - $host = "localhost"; - } - - // Send extended hello first (RFC 2821) - if(!$this->SendHello("EHLO", $host)) { - if(!$this->SendHello("HELO", $host)) { - return false; - } - } - - return true; - } - - /** - * Sends a HELO/EHLO command. - * @access private - * @param string $hello - * @param string $host - * @return bool - */ - private function SendHello($hello, $host) { - fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'); - } - - if($code != 250) { - $this->error = - array("error" => $hello . ' ' . G::LoadTranslation( 'ID_SMTP_ERROR_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - - $this->helo_rply = $rply; - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. - * - * Implements rfc 821: MAIL FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,421 - * @access public - * @param string $from - * @return bool - */ - public function Mail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_MAIL_WITHOUT' )); - return false; - } - - $useVerp = ($this->do_verp ? " XVERP" : ""); - fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 250) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_MAIL_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - return true; - } - - /** - * Sends the quit command to the server and then closes the socket - * if there is no error or the $close_on_error argument is true. - * - * Implements from rfc 821: QUIT - * - * SMTP CODE SUCCESS: 221 - * SMTP CODE ERROR : 500 - * @access public - * @param bool $close_on_error - * @return bool - */ - public function Quit($close_on_error = true) { - $this->error = null; // so there is no confusion - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_QUIT_WITHOUT' )); - return false; - } - - // send the quit command to the server - fputs($this->smtp_conn,"quit" . $this->CRLF); - - // get any good-bye messages - $byemsg = $this->get_lines(); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'); - } - - $rval = true; - $e = null; - - $code = substr($byemsg,0,3); - if($code != 221) { - // use e as a tmp var cause Close will overwrite $this->error - $e = array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_SERVER_REJECTED' ), - "smtp_code" => $code, - "smtp_rply" => substr($byemsg,4)); - $rval = false; - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'); - } - } - - if(empty($e) || $close_on_error) { - $this->Close(); - } - - return $rval; - } - - /** - * Sends the command RCPT to the SMTP server with the TO: argument of $to. - * Returns true if the recipient was accepted false if it was rejected. - * - * Implements from rfc 821: RCPT TO: - * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,552,553,450,451,452 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @param string $to - * @return bool - */ - public function Recipient($to) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_RECIPIENT_WITHOUT' )); - return false; - } - - fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 250 && $code != 251) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_RCPT_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - return true; - } - - /** - * Sends the RSET command to abort and transaction that is - * currently in progress. Returns true if successful false - * otherwise. - * - * Implements rfc 821: RSET - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500,501,504,421 - * @access public - * @return bool - */ - public function Reset() { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_RESET_WITHOUT' )); - return false; - } - - fputs($this->smtp_conn,"RSET" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 250) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_RSET_FAILED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * - * Implements rfc 821: SAML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @param string $from - * @return bool - */ - public function SendAndMail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => G::LoadTranslation( 'ID_SMTP_ERROR_CALLED_SENDANDMAIL_WITHOUT' )); - return false; - } - - fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'); - } - - if($code != 250) { - $this->error = - array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_SAML_NOT_ACCEPTED' ), - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'); - } - return false; - } - return true; - } - - /** - * This is an optional command for SMTP that this class does not - * support. This method is here to make the RFC821 Definition - * complete for this class and __may__ be implimented in the future - * - * Implements from rfc 821: TURN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 502 - * SMTP CODE ERROR : 500, 503 - * @access public - * @return bool - */ - public function Turn() { - $this->error = array("error" => G::LoadTranslation( 'ID_SMTP_ERROR_MET_TURN_SMTP_NOT_IMPLEMENTED' )); - if($this->do_debug >= 1) { - $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'); - } - return false; - } - - /** - * Get the current error - * @access public - * @return array - */ - public function getError() { - return $this->error; - } - - ///////////////////////////////////////////////// - // INTERNAL FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Read in as many lines as possible - * either before eof or socket timeout occurs on the operation. - * With SMTP we can tell if we have more lines to read if the - * 4th character is '-' symbol. If it is a space then we don't - * need to read anything else. - * @access private - * @return string - */ - private function get_lines() { - $data = ""; - $endtime = 0; - /* If for some reason the fp is bad, don't inf loop */ - if (!is_resource($this->smtp_conn)) { - return $data; - } - stream_set_timeout($this->smtp_conn, $this->Timeout); - if ($this->Timelimit > 0) { - $endtime = time() + $this->Timelimit; - } - while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { - $str = @fgets($this->smtp_conn,515); - if($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'); - $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'); - } - $data .= $str; - if($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'); - } - // if 4th character is a space, we are done reading, break the loop - if(substr($str,3,1) == " ") { break; } - // Timed-out? Log and break - $info = stream_get_meta_data($this->smtp_conn); - if ($info['timed_out']) { - if($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds)
"); - } - break; - } - // Now check if reads took too long - if ($endtime) { - if (time() > $endtime) { - if($this->do_debug >= 4) { - $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds)
"); - } - break; - } - } - } - return $data; - } - -} -?> diff --git a/thirdparty/phpmailer/extras/class.html2text.inc b/thirdparty/phpmailer/extras/class.html2text.inc deleted file mode 100644 index 56c486ca7..000000000 --- a/thirdparty/phpmailer/extras/class.html2text.inc +++ /dev/null @@ -1,489 +0,0 @@ - * - * All rights reserved. * - * * - * This script is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * The GNU General Public License can be found at * - * http://www.gnu.org/copyleft/gpl.html. * - * * - * This script is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * Author(s): Jon Abernathy * - * * - * Last modified: 08/08/07 * - * * - *************************************************************************/ - - -/** - * Takes HTML and converts it to formatted, plain text. - * - * Thanks to Alexander Krug (http://www.krugar.de/) to pointing out and - * correcting an error in the regexp search array. Fixed 7/30/03. - * - * Updated set_html() function's file reading mechanism, 9/25/03. - * - * Thanks to Joss Sanglier (http://www.dancingbear.co.uk/) for adding - * several more HTML entity codes to the $search and $replace arrays. - * Updated 11/7/03. - * - * Thanks to Darius Kasperavicius (http://www.dar.dar.lt/) for - * suggesting the addition of $allowed_tags and its supporting function - * (which I slightly modified). Updated 3/12/04. - * - * Thanks to Justin Dearing for pointing out that a replacement for the - *
tag was missing, and suggesting an appropriate fix. - * Updated 8/25/04. - * - * Thanks to Mathieu Collas (http://www.myefarm.com/) for finding a - * display/formatting bug in the _build_link_list() function: email - * readers would show the left bracket and number ("[1") as part of the - * rendered email address. - * Updated 12/16/04. - * - * Thanks to Wojciech Bajon (http://histeria.pl/) for submitting code - * to handle relative links, which I hadn't considered. I modified his - * code a bit to handle normal HTTP links and MAILTO links. Also for - * suggesting three additional HTML entity codes to search for. - * Updated 03/02/05. - * - * Thanks to Jacob Chandler for pointing out another link condition - * for the _build_link_list() function: "https". - * Updated 04/06/05. - * - * Thanks to Marc Bertrand (http://www.dresdensky.com/) for - * suggesting a revision to the word wrapping functionality; if you - * specify a $width of 0 or less, word wrapping will be ignored. - * Updated 11/02/06. - * - * *** Big housecleaning updates below: - * - * Thanks to Colin Brown (http://www.sparkdriver.co.uk/) for - * suggesting the fix to handle and blank lines (whitespace). - * Christian Basedau (http://www.movetheweb.de/) also suggested the - * blank lines fix. - * - * Special thanks to Marcus Bointon (http://www.synchromedia.co.uk/), - * Christian Basedau, Norbert Laposa (http://ln5.co.uk/), - * Bas van de Weijer, and Marijn van Butselaar - * for pointing out my glaring error in the handling. Marcus also - * supplied a host of fixes. - * - * Thanks to Jeffrey Silverman (http://www.newtnotes.com/) for pointing - * out that extra spaces should be compressed--a problem addressed with - * Marcus Bointon's fixes but that I had not yet incorporated. - * - * Thanks to Daniel Schledermann (http://www.typoconsult.dk/) for - * suggesting a valuable fix with tag handling. - * - * Thanks to Wojciech Bajon (again!) for suggesting fixes and additions, - * including the tag handling that Daniel Schledermann pointed - * out but that I had not yet incorporated. I haven't (yet) - * incorporated all of Wojciech's changes, though I may at some - * future time. - * - * *** End of the housecleaning updates. Updated 08/08/07. - * - * @author Jon Abernathy - * @version 1.0.0 - * @since PHP 4.0.2 - */ -class html2text -{ - - /** - * Contains the HTML content to convert. - * - * @var string $html - * @access public - */ - var $html; - - /** - * Contains the converted, formatted text. - * - * @var string $text - * @access public - */ - var $text; - - /** - * Maximum width of the formatted text, in columns. - * - * Set this value to 0 (or less) to ignore word wrapping - * and not constrain text to a fixed-width column. - * - * @var integer $width - * @access public - */ - var $width = 70; - - /** - * List of preg* regular expression patterns to search for, - * used in conjunction with $replace. - * - * @var array $search - * @access public - * @see $replace - */ - var $search = array( - "/\r/", // Non-legal carriage return - "/[\n\t]+/", // Newlines and tabs - '/[ ]{2,}/', // Runs of spaces, pre-handling - '/]*>.*?<\/script>/i', //