From 22383ec53b65afb789f6f1727a01509adccbf0c0 Mon Sep 17 00:00:00 2001 From: Roly Rudy Gutierrez Pinto Date: Fri, 3 May 2019 12:07:11 -0400 Subject: [PATCH 01/57] PMC-761 --- .gitignore | 1 + phpunit.xml | 57 +++++++++++++++++++++++---------------------- tests/bootstrap.php | 53 ++++++++++++++++++++--------------------- 3 files changed, 57 insertions(+), 54 deletions(-) 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/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..ed5442c3f 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 +54,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 +75,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 +107,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 +134,4 @@ Propel::initConfiguration([ 'adapter' => 'mysql' ] ] -]); \ No newline at end of file +]); From 6b8065a98f1dd63d1f322c642cc20db8d30403f7 Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 13:47:21 -0700 Subject: [PATCH 02/57] Add initial app models. Add tinker as a dependency to allow use with artisan. --- app/Models/Application.php | 10 ++ app/Models/Delegation.php | 42 +++++ app/Models/Process.php | 17 ++ app/Models/Task.php | 10 ++ app/Models/User.php | 19 +++ composer.json | 4 +- composer.lock | 315 ++++++++++++++++++++++++++++++++++++- config/app.php | 2 + 8 files changed, 416 insertions(+), 3 deletions(-) create mode 100644 app/Models/Application.php create mode 100644 app/Models/Delegation.php create mode 100644 app/Models/Process.php create mode 100644 app/Models/Task.php create mode 100644 app/Models/User.php diff --git a/app/Models/Application.php b/app/Models/Application.php new file mode 100644 index 000000000..6afa38919 --- /dev/null +++ b/app/Models/Application.php @@ -0,0 +1,10 @@ +belongsTo(Application::class, 'APP_UID', 'APP_UID'); + } + + /** + * Returns the user this delegation belongs to + */ + public function user() + { + return $this->belongsTo(User::class, 'USR_ID', 'USR_ID'); + } + + /** + * Return the process task this belongs to + */ + public function task() + { + return $this->belongsTo(Task::class, 'TAS_ID', 'TAS_ID'); + } + + /** + * Return the process this delegation belongs to + */ + public function process() + { + return $this->belongsTo(Process::class, 'PRO_ID', 'PRO_ID'); + } +} diff --git a/app/Models/Process.php b/app/Models/Process.php new file mode 100644 index 000000000..e45241287 --- /dev/null +++ b/app/Models/Process.php @@ -0,0 +1,17 @@ +hasMany(Application::class, 'PRO_ID', 'PRO_ID'); + + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 000000000..a79ef4a9c --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,10 @@ +hasMany(Delegation::class, 'USR_ID', 'USR_ID'); + + } +} diff --git a/composer.json b/composer.json index 6e2eec268..b52a58582 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,9 @@ "ralouphie/getallheaders": "^2.0", "smarty/smarty": "2.6.30", "pdepend/pdepend": "@stable", - "chumper/zipper": "^1.0" + "chumper/zipper": "^1.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 527736ee1..510c23ba6 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", @@ -1364,6 +1599,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", @@ -4883,6 +5192,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2017-06-30T09:13:00+00:00" }, { @@ -5823,6 +6133,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 fad6a83a2..035380c52 100644 --- a/config/app.php +++ b/config/app.php @@ -22,6 +22,8 @@ return [ Illuminate\Queue\QueueServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, + Laravel\Tinker\TinkerServiceProvider::class, + ], 'aliases' => [ From 9aec7285098b47ca01c1e7b8ee5164766c58412e Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 13:54:39 -0700 Subject: [PATCH 03/57] Move models to proper ProcessMaker Model namespace. --- app/Models/Process.php | 17 ---------------- app/Models/Task.php | 10 ---------- .../src/ProcessMaker/Model}/Application.php | 2 +- .../src/ProcessMaker/Model}/Delegation.php | 2 +- .../engine/src/ProcessMaker/Model/Process.php | 8 ++++++++ .../engine/src/ProcessMaker/Model/Task.php | 20 +++++++++++++++++++ .../engine/src/ProcessMaker/Model}/User.php | 2 +- 7 files changed, 31 insertions(+), 30 deletions(-) delete mode 100644 app/Models/Process.php delete mode 100644 app/Models/Task.php rename {app/Models => workflow/engine/src/ProcessMaker/Model}/Application.php (80%) rename {app/Models => workflow/engine/src/ProcessMaker/Model}/Delegation.php (96%) create mode 100644 workflow/engine/src/ProcessMaker/Model/Task.php rename {app/Models => workflow/engine/src/ProcessMaker/Model}/User.php (90%) diff --git a/app/Models/Process.php b/app/Models/Process.php deleted file mode 100644 index e45241287..000000000 --- a/app/Models/Process.php +++ /dev/null @@ -1,17 +0,0 @@ -hasMany(Application::class, 'PRO_ID', 'PRO_ID'); - - } -} diff --git a/app/Models/Task.php b/app/Models/Task.php deleted file mode 100644 index a79ef4a9c..000000000 --- a/app/Models/Task.php +++ /dev/null @@ -1,10 +0,0 @@ -hasMany(Application::class, 'PRO_ID', 'PRO_ID'); + } } \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/Task.php b/workflow/engine/src/ProcessMaker/Model/Task.php new file mode 100644 index 000000000..ad2812698 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Model/Task.php @@ -0,0 +1,20 @@ +belongsTo(Process::class, 'PRO_UID', 'PRO_UID'); + } + + public function delegations() + { + return $this->hasMany(Delegation::class, 'TAS_ID', 'TAS_ID'); + } +} diff --git a/app/Models/User.php b/workflow/engine/src/ProcessMaker/Model/User.php similarity index 90% rename from app/Models/User.php rename to workflow/engine/src/ProcessMaker/Model/User.php index 2c7442d51..f8c992bc1 100644 --- a/app/Models/User.php +++ b/workflow/engine/src/ProcessMaker/Model/User.php @@ -1,6 +1,6 @@ Date: Thu, 25 Apr 2019 14:15:41 -0700 Subject: [PATCH 04/57] Update models to have more relationships and timestamp column definitions. --- .../src/ProcessMaker/Model/Application.php | 19 +++++++++++++++++++ .../src/ProcessMaker/Model/Delegation.php | 3 +++ .../engine/src/ProcessMaker/Model/Process.php | 9 +++++++-- .../engine/src/ProcessMaker/Model/Task.php | 2 ++ .../engine/src/ProcessMaker/Model/User.php | 3 +++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Application.php b/workflow/engine/src/ProcessMaker/Model/Application.php index d0dd1f479..2ca974844 100644 --- a/workflow/engine/src/ProcessMaker/Model/Application.php +++ b/workflow/engine/src/ProcessMaker/Model/Application.php @@ -7,4 +7,23 @@ use Illuminate\Database\Eloquent\Model; class Application extends Model { protected $table = "APPLICATION"; + // Our custom timestamp columns + const CREATED_AT = 'APP_CREATE_DATE'; + const UPDATED_AT = 'APP_UPDATE_DATE'; + + public function delegations() + { + return $this->hasMany(Delegation::class, 'APP_UID', 'APP_UID'); + } + + public function parent() + { + return $this->hasOne(Application::class, 'APP_PARENT', 'APP_UID'); + } + + public function currentUser() + { + return $this->hasOne(User::class, 'APP_CUR_USER', 'USR_UID'); + } + } diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index c33dd4e32..366666c1c 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -8,6 +8,9 @@ class Delegation extends Model { protected $table = "APP_DELEGATION"; + // We don't have our standard timestamp columns + protected $timestamps = false; + /** * Returns the application this delegation belongs to */ diff --git a/workflow/engine/src/ProcessMaker/Model/Process.php b/workflow/engine/src/ProcessMaker/Model/Process.php index e9b3cb972..abaac4e7a 100644 --- a/workflow/engine/src/ProcessMaker/Model/Process.php +++ b/workflow/engine/src/ProcessMaker/Model/Process.php @@ -14,9 +14,9 @@ class Process extends Model { // Set our table name protected $table = 'PROCESS'; - // We do have a created at, but we don't store an updated at + // Our custom timestamp columns const CREATED_AT = 'PRO_CREATE_DATE'; - const UPDATED_AT = null; + const UPDATED_AT = 'PRO_UPDATE_DATE'; /** * Retrieve all applications that belong to this process @@ -26,4 +26,9 @@ class Process extends Model return $this->hasMany(Application::class, 'PRO_ID', 'PRO_ID'); } + + public function creator() + { + return $this->hasOne(User::class, 'PRO_CREATE_USER', 'USR_UID'); + } } \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/Task.php b/workflow/engine/src/ProcessMaker/Model/Task.php index ad2812698..75c8e2fee 100644 --- a/workflow/engine/src/ProcessMaker/Model/Task.php +++ b/workflow/engine/src/ProcessMaker/Model/Task.php @@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Model; class Task extends Model { protected $table = 'TASK'; + // We do not have create/update timestamps for this table + protected $timestamps = false; public function process() { diff --git a/workflow/engine/src/ProcessMaker/Model/User.php b/workflow/engine/src/ProcessMaker/Model/User.php index f8c992bc1..40fc1be55 100644 --- a/workflow/engine/src/ProcessMaker/Model/User.php +++ b/workflow/engine/src/ProcessMaker/Model/User.php @@ -7,6 +7,9 @@ use Illuminate\Database\Eloquent\Model; class User extends Model { protected $table = "USERS"; + // Our custom timestamp columns + const CREATED_AT = 'USR_CREATE_DATE'; + const UPDATED_AT = 'USR_UPDATE_DATE'; /** * Returns the delegations this user has (all of them) From 2bf175ac85d3c46be7a65af0324f7996517892df Mon Sep 17 00:00:00 2001 From: dante Date: Thu, 25 Apr 2019 17:46:02 -0400 Subject: [PATCH 05/57] timestamps attribute visibility set to public --- workflow/engine/src/ProcessMaker/Model/Task.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Task.php b/workflow/engine/src/ProcessMaker/Model/Task.php index 75c8e2fee..de66e26a0 100644 --- a/workflow/engine/src/ProcessMaker/Model/Task.php +++ b/workflow/engine/src/ProcessMaker/Model/Task.php @@ -8,7 +8,7 @@ class Task extends Model { protected $table = 'TASK'; // We do not have create/update timestamps for this table - protected $timestamps = false; + public $timestamps = false; public function process() { From e79503f590e27af31cd3d6fe1f5da129da7b08dd Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 15:32:23 -0700 Subject: [PATCH 06/57] Include initial transform to transform results to desired necessity. Change proxyCaseList to utilize Delegation search. --- .../engine/methods/cases/proxyCasesList.php | 47 +++++- .../src/ProcessMaker/Model/Delegation.php | 136 +++++++++++++++++- 2 files changed, 175 insertions(+), 8 deletions(-) diff --git a/workflow/engine/methods/cases/proxyCasesList.php b/workflow/engine/methods/cases/proxyCasesList.php index bfec0df59..a5b0b7779 100644 --- a/workflow/engine/methods/cases/proxyCasesList.php +++ b/workflow/engine/methods/cases/proxyCasesList.php @@ -1,4 +1,9 @@ error = G::LoadTranslation('ID_LOGIN_AGAIN'); @@ -9,31 +14,59 @@ if (!isset($_SESSION['USER_LOGGED'])) { } +/** + * Do input filtering, although filtering should be done on the frontend rendering, not here + */ $filter = new InputFilter(); $_GET = $filter->xssFilterHard($_GET); $_REQUEST = $filter->xssFilterHard($_REQUEST); $_SESSION['USER_LOGGED'] = $filter->xssFilterHard($_SESSION['USER_LOGGED']); -//Getting the extJs parameters +// Callback in the UI to utilize $callback = isset($_REQUEST["callback"]) ? $_REQUEST["callback"] : "stcCallback1001"; -//This default value was defined in casesList.js -$dir = isset($_REQUEST["dir"]) ? $_REQUEST["dir"] : "DESC"; -//This default value was defined in casesList.js + +// Sort column $sort = isset($_REQUEST["sort"]) ? $_REQUEST["sort"] : "APP_NUMBER"; +// Sort direction +$dir = isset($_REQUEST["dir"]) ? $_REQUEST["dir"] : "DESC"; + +// Pagination control $start = !empty($_REQUEST["start"]) ? $_REQUEST["start"] : 0; $limit = !empty($_REQUEST["limit"]) ? $_REQUEST["limit"] : 25; + +// Our search filter $filter = isset($_REQUEST["filter"]) ? $_REQUEST["filter"] : ""; + +// What process $process = isset($_REQUEST["process"]) ? $_REQUEST["process"] : ""; + +// What category $category = isset($_REQUEST["category"]) ? $_REQUEST["category"] : ""; + +// What status $status = isset($_REQUEST["status"]) ? strtoupper($_REQUEST["status"]) : ""; $filterStatus = isset($_REQUEST["filterStatus"]) ? strtoupper($_REQUEST["filterStatus"]) : ""; + +// What user $user = isset($_REQUEST["user"]) ? $_REQUEST["user"] : ""; + +// What search??? $search = isset($_REQUEST["search"]) ? $_REQUEST["search"] : ""; + +// What kind of action $action = isset($_GET["action"]) ? $_GET["action"] : (isset($_REQUEST["action"]) ? $_REQUEST["action"] : "todo"); + +// What kind of search $type = isset($_GET["type"]) ? $_GET["type"] : (isset($_REQUEST["type"]) ? $_REQUEST["type"] : "extjs"); + +// Date ranges $dateFrom = isset($_REQUEST["dateFrom"]) ? substr($_REQUEST["dateFrom"], 0, 10) : ""; $dateTo = isset($_REQUEST["dateTo"]) ? substr($_REQUEST["dateTo"], 0, 10) : ""; + +// First? No idea $first = isset($_REQUEST["first"]) ? true : false; + + $openApplicationUid = (isset($_REQUEST['openApplicationUid']) && $_REQUEST['openApplicationUid'] != '') ? $_REQUEST['openApplicationUid'] : null; $search = (!is_null($openApplicationUid)) ? $openApplicationUid : $search; @@ -67,10 +100,10 @@ try { break; } - $apps = new Applications(); + //$apps = new Applications(); if ($action == 'search') { - $data = $apps->searchAll( + $data = Delegation::search( $userUid, $start, $limit, @@ -85,7 +118,7 @@ try { $columnSearch ); } else { - $data = $apps->getAll( + $data = Delegation::search( $userUid, $start, $limit, diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 366666c1c..332822c1f 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -3,13 +3,14 @@ namespace ProcessMaker\Model; use Illuminate\Database\Eloquent\Model; +use G; class Delegation extends Model { protected $table = "APP_DELEGATION"; // We don't have our standard timestamp columns - protected $timestamps = false; + public $timestamps = false; /** * Returns the application this delegation belongs to @@ -42,4 +43,137 @@ class Delegation extends Model { return $this->belongsTo(Process::class, 'PRO_ID', 'PRO_ID'); } + + /** + * Searches for delegations which match certain criteria + * + * The query is related to advanced search with different filters + * We can search by process, status of case, category of process, users, delegate date from and to + * + * @param string $userUid + * @param integer $start for the pagination + * @param integer $limit for the pagination + * @param string $search + * @param integer $process the pro_id + * @param integer $status of the case + * @param string $dir if the order is DESC or ASC + * @param string $sort name of column by sort, can be: + * [APP_NUMBER, APP_TITLE, APP_PRO_TITLE, APP_TAS_TITLE, APP_CURRENT_USER, APP_UPDATE_DATE, DEL_DELEGATE_DATE, DEL_TASK_DUE_DATE, APP_STATUS_LABEL] + * @param string $category uid for the process + * @param date $dateFrom + * @param date $dateTo + * @param string $filterBy name of column for a specific search, can be: [APP_NUMBER, APP_TITLE, TAS_TITLE] + * @return array $result result of the query + */ + + public static function search( + $userUid, + $start = null, + $limit = null, + $search = null, + $process = null, + $status = null, + $dir = null, + $sort = null, + $category = null, + $dateFrom = null, + $dateTo = null, + $filterBy = 'APP_TITLE' + ) + { + // Default pagination values + $start = $start ? $start : 0; + $limit = $limit ? $limit : 25; + + // Start the query builder + $query = self::query(); + + // Add pagination to the query + $query = $query->offset($start) + ->limit($limit); + + // Fetch results and transform to a laravel collection + $results = collect($query->get()); + + // Transform with additional data + $priorities = ['1' => 'VL','2' => 'L','3' => 'N','4' => 'H','5' => 'VH']; + $results->transform(function($item, $key) use($priorities) { + // Grab related records + $application = Application::where('APP_UID', $item['APP_UID'])->first(); + if(!$application) { + // Application wasn't found, return null + return null; + } + $task = Task::where('TAS_ID', $item['TAS_ID'])->first(); + if(!$task) { + // Task not found, return null + return null; + } + $user = User::where('USR_ID', $item['USR_ID'])->first(); + if(!$user) { + // User not found, return null + return null; + } + $process = Process::where('PRO_ID', $item['PRO_ID'])->first();; + if(!$process) { + // Process not found, return null + return null; + } + + // Rewrite priority string + if($item['DEL_PRIORITY']) { + $item['DEL_PRIORITY'] = G::LoadTranslation( "ID_PRIORITY_{$priorities[$item['DEL_PRIORITY']]}" ); + } + + // Merge in desired application data + $item['APP_STATUS'] = $application->APP_STATUS; + if($item['APP_STATUS']) { + $item['APP_STATUS_LABEL'] = G::LoadTranslation( "ID_${item['APP_STATUS']}"); + } else { + $item['APP_STATUS_LABEL'] = $application->APP_STATUS; + } + $item['APP_CREATE_DATE'] = $application->APP_CREATE_DATE; + $item['APP_FINISH_DATE'] = $application->APP_FINISH_DATE; + $item['APP_UPDATE_DATE'] = $application->APP_UPDATE_DATE; + $item['APP_TITLE'] = $application->APP_TITLE; + + // Merge in desired process data + $item['APP_PRO_TITLE'] = $process->PRO_TITLE; + + // Merge in desired task data + $item['APP_TAS_TITLE'] = $task->TAS_TITLE; + $item['APP_TAS_TYPE'] = $task->TAS_TYPE; + + // Merge in desired user data + $item['USR_LASTNAME'] = $user->USR_LASTNAME; + $item['USR_FIRSTNAME'] = $user->USR_FIRSTNAME; + $item['USR_USERNAME'] = $user->USR_USERNAME; + + //@todo: this section needs to use 'User Name Display Format', currently in the extJs is defined this + $item["APP_CURRENT_USER"] = $item["USR_LASTNAME"].' '.$item["USR_FIRSTNAME"]; + + $item["APPDELCR_APP_TAS_TITLE"] = ''; + + $item["USRCR_USR_UID"] = $item["USR_UID"]; + $item["USRCR_USR_FIRSTNAME"] = $item["USR_FIRSTNAME"]; + $item["USRCR_USR_LASTNAME"] = $item["USR_LASTNAME"]; + $item["USRCR_USR_USERNAME"] = $item["USR_USERNAME"]; + $item["APP_OVERDUE_PERCENTAGE"] = ''; + + return $item; + }); + + // Remove any empty erroenous data + $results = $results->filter(); + + // Bundle into response array + $response = [ + // Fake totalCount to show pagination + 'totalCount' => $start + $limit + 1, + 'data' => $results->toArray() + ]; + + return $response; + } + } From 141e358b6b22647e78beddc25f387ba3b329a8ee Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 15:58:26 -0700 Subject: [PATCH 07/57] Remove commented line in proxyCasesList. Add filtering support for task title and application only when requested. --- .../engine/methods/cases/proxyCasesList.php | 2 -- .../src/ProcessMaker/Model/Delegation.php | 27 ++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/workflow/engine/methods/cases/proxyCasesList.php b/workflow/engine/methods/cases/proxyCasesList.php index a5b0b7779..fbe9bd03e 100644 --- a/workflow/engine/methods/cases/proxyCasesList.php +++ b/workflow/engine/methods/cases/proxyCasesList.php @@ -100,8 +100,6 @@ try { break; } - //$apps = new Applications(); - if ($action == 'search') { $data = Delegation::search( $userUid, diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 332822c1f..0ce42fecf 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -88,6 +88,31 @@ class Delegation extends Model // Start the query builder $query = self::query(); + // Add join for task, filtering for task title if needed + // It doesn't make sense for us to search for any delegations that match tasks that are events or web entry + $query->join('TASK', function($join) use($filterBy, $search) { + $join->on('APP_DELEGATION.TAS_ID', '=', 'TASK.TAS_ID') + ->whereNotIn('TASK.TAS_TYPE', [ + 'WEBENTRYEVENT', + 'END-MESSAGE-EVENT', + 'START-MESSAGE-EVENT', + 'INTERMEDIATE-THROW' + ]); + if($filterBy == 'TAS_TITLE') { + $join->where('TASK.TAS_TITLE', 'LIKE', "%${search}%"); + } + }); + + // Add join for application, but only for certain scenarios of app title search or sorting by app title + if($filterBy == 'APP_TITLE' || $sort == 'APP_TITLE') { + $query->join('APPLICATION', function($join) use($filterBy, $search) { + $join->on('APP_DELEGATION.APP_UID', '=', 'APPLICATION.APP_UID'); + if($filterBy == 'APP_TITLE') { + $join->where('APPLICATION.APP_TITLE', 'LIKE', "%${search}%"); + } + }); + } + // Add pagination to the query $query = $query->offset($start) ->limit($limit); @@ -99,7 +124,7 @@ class Delegation extends Model $priorities = ['1' => 'VL','2' => 'L','3' => 'N','4' => 'H','5' => 'VH']; $results->transform(function($item, $key) use($priorities) { // Grab related records - $application = Application::where('APP_UID', $item['APP_UID'])->first(); + $application = Application::where('APP_NUMBER', $item['APP_NUMBER'])->first(); if(!$application) { // Application wasn't found, return null return null; From c1a6bf9c77bd5616a58bd19f2ac832bebdca68de Mon Sep 17 00:00:00 2001 From: dante Date: Thu, 25 Apr 2019 19:00:41 -0400 Subject: [PATCH 08/57] Classic Processes Factory --- database/factories/ProcessFactory.php | 36 +++++++++++++++++++++++++-- database/factories/RouteFactory.php | 23 +++++++++++++++++ database/factories/TaskFactory.php | 32 ++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 database/factories/RouteFactory.php create mode 100644 database/factories/TaskFactory.php diff --git a/database/factories/ProcessFactory.php b/database/factories/ProcessFactory.php index d8a60d371..ad3f557c9 100644 --- a/database/factories/ProcessFactory.php +++ b/database/factories/ProcessFactory.php @@ -9,9 +9,41 @@ $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, ]; + + $task1 = factory(\ProcessMaker\Model\Task::class) + ->create([ + 'PRO_UID' => $process['PRO_UID'], + 'TAS_START'=>'TRUE', + 'TAS_TITLE'=>'Task A' + ]); + + $task2 = factory(\ProcessMaker\Model\Task::class) + ->create([ + 'PRO_UID' => $process['PRO_UID'], + 'TAS_TITLE' => 'Task B' + ]); + + 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'] + ]); + + 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..7e649a31f --- /dev/null +++ b/database/factories/RouteFactory.php @@ -0,0 +1,23 @@ +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..4ebc5a1d6 --- /dev/null +++ b/database/factories/TaskFactory.php @@ -0,0 +1,32 @@ +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_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' => 'FALSE', + 'TAS_CAN_UPLOAD' => 'FALSE', + 'TAS_CAN_CANCEL' => 'FALSE', + 'TAS_OWNER_APP' => 'FALSE', + ]; +}); \ No newline at end of file From ea6b0301931d071fb838cf5cedb6e663303ea11a Mon Sep 17 00:00:00 2001 From: dante Date: Thu, 25 Apr 2019 19:02:23 -0400 Subject: [PATCH 09/57] Model for ROUTE table --- .../engine/src/ProcessMaker/Model/Route.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 workflow/engine/src/ProcessMaker/Model/Route.php diff --git a/workflow/engine/src/ProcessMaker/Model/Route.php b/workflow/engine/src/ProcessMaker/Model/Route.php new file mode 100644 index 000000000..6be408ac0 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Model/Route.php @@ -0,0 +1,19 @@ + Date: Thu, 25 Apr 2019 16:10:12 -0700 Subject: [PATCH 10/57] Add some sort functionality and app title, case number and task title searching working properly. --- .../src/ProcessMaker/Model/Delegation.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 0ce42fecf..e443e092a 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -81,6 +81,8 @@ class Delegation extends Model $filterBy = 'APP_TITLE' ) { + $search = trim($search); + // Default pagination values $start = $start ? $start : 0; $limit = $limit ? $limit : 25; @@ -104,7 +106,7 @@ class Delegation extends Model }); // Add join for application, but only for certain scenarios of app title search or sorting by app title - if($filterBy == 'APP_TITLE' || $sort == 'APP_TITLE') { + if(($filterBy == 'APP_TITLE' && $search) || $sort == 'APP_TITLE') { $query->join('APPLICATION', function($join) use($filterBy, $search) { $join->on('APP_DELEGATION.APP_UID', '=', 'APPLICATION.APP_UID'); if($filterBy == 'APP_TITLE') { @@ -113,6 +115,19 @@ class Delegation extends Model }); } + if($filterBy == 'APP_NUMBER') { + $query->where('APP_DELEGATION.APP_NUMBER', 'LIKE', "%${search}%"); + } + + // Add any sort if needed + if($sort) { + // Clean up any specific sort parameters + if($sort == 'APP_NUMBER') { + $sort = 'APP_DELEGATION.APP_NUMBER'; + } + $query->orderBy($sort, $dir); + } + // Add pagination to the query $query = $query->offset($start) ->limit($limit); @@ -195,6 +210,7 @@ class Delegation extends Model $response = [ // Fake totalCount to show pagination 'totalCount' => $start + $limit + 1, + 'sql' => $query->toSql(), 'data' => $results->toArray() ]; From d3a944c2f62c5758020c9358651ed5219c96791f Mon Sep 17 00:00:00 2001 From: dante Date: Thu, 25 Apr 2019 19:11:37 -0400 Subject: [PATCH 11/57] User assignment for created tasks --- database/factories/ProcessFactory.php | 14 ++++++++++++++ database/factories/TaskFactory.php | 2 +- .../engine/src/ProcessMaker/Model/TaskUser.php | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 workflow/engine/src/ProcessMaker/Model/TaskUser.php diff --git a/database/factories/ProcessFactory.php b/database/factories/ProcessFactory.php index ad3f557c9..12e60c998 100644 --- a/database/factories/ProcessFactory.php +++ b/database/factories/ProcessFactory.php @@ -31,6 +31,7 @@ $factory->define(\ProcessMaker\Model\Process::class, function(Faker $faker) { 'TAS_TITLE' => 'Task B' ]); + //routes factory(\ProcessMaker\Model\Route::class) ->create([ 'PRO_UID' => $process['PRO_UID'], @@ -45,5 +46,18 @@ $factory->define(\ProcessMaker\Model\Process::class, function(Faker $faker) { '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/TaskFactory.php b/database/factories/TaskFactory.php index 4ebc5a1d6..bf196690f 100644 --- a/database/factories/TaskFactory.php +++ b/database/factories/TaskFactory.php @@ -24,7 +24,7 @@ $factory->define(\ProcessMaker\Model\Task::class, function(Faker $faker) { 'TAS_ASSIGN_LOCATION_ADHOC' => 'FALSE', 'TAS_TRANSFER_FLY' => 'FALSE', 'TAS_LAST_ASSIGNED' => 0, - 'TAS_USER' => 'FALSE', + 'TAS_USER' => 0, 'TAS_CAN_UPLOAD' => 'FALSE', 'TAS_CAN_CANCEL' => 'FALSE', 'TAS_OWNER_APP' => 'FALSE', diff --git a/workflow/engine/src/ProcessMaker/Model/TaskUser.php b/workflow/engine/src/ProcessMaker/Model/TaskUser.php new file mode 100644 index 000000000..67549792b --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Model/TaskUser.php @@ -0,0 +1,12 @@ + Date: Thu, 25 Apr 2019 19:23:54 -0400 Subject: [PATCH 12/57] User factory --- database/factories/TaskUserFactory.php | 17 +++++++++++++++++ database/factories/UserFactory.php | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 database/factories/TaskUserFactory.php create mode 100644 database/factories/UserFactory.php diff --git a/database/factories/TaskUserFactory.php b/database/factories/TaskUserFactory.php new file mode 100644 index 000000000..751c491f7 --- /dev/null +++ b/database/factories/TaskUserFactory.php @@ -0,0 +1,17 @@ +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..0eff02f51 --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,20 @@ +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 From 0331e65626630628f933df97789f49904764aa4c Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 16:50:52 -0700 Subject: [PATCH 13/57] Make sure all sorting works. Verify process category selection. Use date range selection. --- .../src/ProcessMaker/Model/Application.php | 5 +- .../src/ProcessMaker/Model/Delegation.php | 74 ++++++++++++++++--- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Application.php b/workflow/engine/src/ProcessMaker/Model/Application.php index 2ca974844..38e30c69c 100644 --- a/workflow/engine/src/ProcessMaker/Model/Application.php +++ b/workflow/engine/src/ProcessMaker/Model/Application.php @@ -7,9 +7,8 @@ use Illuminate\Database\Eloquent\Model; class Application extends Model { protected $table = "APPLICATION"; - // Our custom timestamp columns - const CREATED_AT = 'APP_CREATE_DATE'; - const UPDATED_AT = 'APP_UPDATE_DATE'; + // No timestamps + public $timestamps = false; public function delegations() { diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index e443e092a..c5e7a86f5 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -100,32 +100,83 @@ class Delegation extends Model 'START-MESSAGE-EVENT', 'INTERMEDIATE-THROW' ]); - if($filterBy == 'TAS_TITLE') { + if($filterBy == 'TAS_TITLE' && $search) { $join->where('TASK.TAS_TITLE', 'LIKE', "%${search}%"); } }); - // Add join for application, but only for certain scenarios of app title search or sorting by app title - if(($filterBy == 'APP_TITLE' && $search) || $sort == 'APP_TITLE') { + // Add join for application, but only for certain scenarios of app title search or sorting by app title, update_date or status + if(($filterBy == 'APP_TITLE' && $search) || $sort == 'APP_TITLE' || $sort == 'APP_UPDATE_DATE' || $sort == 'APP_STATUS') { $query->join('APPLICATION', function($join) use($filterBy, $search) { $join->on('APP_DELEGATION.APP_UID', '=', 'APPLICATION.APP_UID'); - if($filterBy == 'APP_TITLE') { + if($filterBy == 'APP_TITLE' && $search) { $join->where('APPLICATION.APP_TITLE', 'LIKE', "%${search}%"); } }); } - if($filterBy == 'APP_NUMBER') { + // Add join for process, but only for certain scenarios such as category or process + if(($category && !$process) || $sort == 'APP_PRO_TITLE') { + $query->join('PROCESS', function($join) use ($category) { + $join->on('APP_DELEGATION.PRO_ID', '=', 'PROCESS.PRO_ID'); + if($category) { + $join->where('PROCESS.PRO_CATEGORY', $category); + } + }); + } + + // Add join for user, but only for certain scenarios as sorting + if($sort == 'APP_CURRENT_USER') { + $query->join('USERS', function($join) use ($userUid) { + $join->on('APP_DELEGATION.USR_ID', '=', 'USERS.USR_ID'); + }); + } + + // Search for specified user + if($userUid) { + $query->where('APP_DELEGATION.USR_ID', $userUid); + } + + // Search for specified process + if($process) { + $query->where('APP_DELEGATION.PRO_ID', $process); + } + + // Search for an app/case number + if($filterBy == 'APP_NUMBER' && $search) { $query->where('APP_DELEGATION.APP_NUMBER', 'LIKE', "%${search}%"); } + // Date range filter + if (!empty($dateFrom)) { + $query->where('APP_DELEGATION.DEL_DELEGATE_DATE', '>=', $dateFrom); + } + if (!empty($dateTo)) { + $dateTo = $dateTo . " 23:59:59"; + // This is inclusive + $query->where('APP_DELEGATION.DEL_DELEGATE_DATE', '<=', $dateTo); + } + + // Add any sort if needed - if($sort) { - // Clean up any specific sort parameters - if($sort == 'APP_NUMBER') { - $sort = 'APP_DELEGATION.APP_NUMBER'; - } - $query->orderBy($sort, $dir); + switch($sort) { + case 'APP_NUMBER': + $query->orderBy('APP_DELEGATION.APP_NUMBER', $dir); + break; + case 'APP_PRO_TITLE': + // We can do this because we joined the process table if sorting by it + $query->orderBy('PROCESS.PRO_TITLE', $dir); + break; + case 'APP_TAS_TITLE': + $query->orderBy('TASK.TAS_TITLE', $dir); + break; + case 'APP_CURRENT_USER': + // We can do this because we joined the user table if sorting by it + $query->orderBy('USERS.USR_LASTNAME', $dir); + $query->orderBy('USERS.USR_FIRSTNAME', $dir); + break; + default: + $query->orderBy($sort, $dir); } // Add pagination to the query @@ -211,6 +262,7 @@ class Delegation extends Model // Fake totalCount to show pagination 'totalCount' => $start + $limit + 1, 'sql' => $query->toSql(), + 'bindings' => $query->getBindings(), 'data' => $results->toArray() ]; From 52a877bb943d7edbb3c7fb763a38f54905c4e426 Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 17:28:15 -0700 Subject: [PATCH 14/57] Add status filtering. Status always requires us joining on application. --- .../src/ProcessMaker/Model/Delegation.php | 124 ++++++++++++------ 1 file changed, 86 insertions(+), 38 deletions(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index c5e7a86f5..5023d173a 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -2,8 +2,8 @@ namespace ProcessMaker\Model; -use Illuminate\Database\Eloquent\Model; use G; +use Illuminate\Database\Eloquent\Model; class Delegation extends Model { @@ -79,8 +79,7 @@ class Delegation extends Model $dateFrom = null, $dateTo = null, $filterBy = 'APP_TITLE' - ) - { + ) { $search = trim($search); // Default pagination values @@ -92,58 +91,76 @@ class Delegation extends Model // Add join for task, filtering for task title if needed // It doesn't make sense for us to search for any delegations that match tasks that are events or web entry - $query->join('TASK', function($join) use($filterBy, $search) { + $query->join('TASK', function ($join) use ($filterBy, $search) { $join->on('APP_DELEGATION.TAS_ID', '=', 'TASK.TAS_ID') ->whereNotIn('TASK.TAS_TYPE', [ 'WEBENTRYEVENT', 'END-MESSAGE-EVENT', 'START-MESSAGE-EVENT', - 'INTERMEDIATE-THROW' + 'INTERMEDIATE-THROW', ]); - if($filterBy == 'TAS_TITLE' && $search) { + if ($filterBy == 'TAS_TITLE' && $search) { $join->where('TASK.TAS_TITLE', 'LIKE', "%${search}%"); } }); - // Add join for application, but only for certain scenarios of app title search or sorting by app title, update_date or status - if(($filterBy == 'APP_TITLE' && $search) || $sort == 'APP_TITLE' || $sort == 'APP_UPDATE_DATE' || $sort == 'APP_STATUS') { - $query->join('APPLICATION', function($join) use($filterBy, $search) { - $join->on('APP_DELEGATION.APP_UID', '=', 'APPLICATION.APP_UID'); - if($filterBy == 'APP_TITLE' && $search) { - $join->where('APPLICATION.APP_TITLE', 'LIKE', "%${search}%"); - } - }); - } + // Add join for application, taking care of status and filtering if necessary + $query->join('APPLICATION', function ($join) use ($filterBy, $search, $status, $query) { + $join->on('APP_DELEGATION.APP_UID', '=', 'APPLICATION.APP_UID'); + if ($filterBy == 'APP_TITLE' && $search) { + $join->where('APPLICATION.APP_TITLE', 'LIKE', "%${search}%"); + } + // Based on the below, we can further limit the join so that we have a smaller data set based on join criteria + switch ($status) { + case 1: //DRAFT + $join->where('APPLICATION.APP_STATUS_ID', 1); + break; + case 2: //TO_DO + $join->where('APPLICATION.APP_STATUS_ID', 2); + break; + case 3: //COMPLETED + $join->where('APPLICATION.APP_STATUS_ID', 3); + break; + case 4: //CANCELLED + $join->where('APPLICATION.APP_STATUS_ID', 4); + break; + case "PAUSED": + $join->where('APPLICATION.APP_STATUS', 'TO_DO'); + break; + default: //All status + // Don't do anything here, we'll need to do the more advanced where below + } + }); // Add join for process, but only for certain scenarios such as category or process - if(($category && !$process) || $sort == 'APP_PRO_TITLE') { - $query->join('PROCESS', function($join) use ($category) { + if (($category && !$process) || $sort == 'APP_PRO_TITLE') { + $query->join('PROCESS', function ($join) use ($category) { $join->on('APP_DELEGATION.PRO_ID', '=', 'PROCESS.PRO_ID'); - if($category) { + if ($category) { $join->where('PROCESS.PRO_CATEGORY', $category); } }); } // Add join for user, but only for certain scenarios as sorting - if($sort == 'APP_CURRENT_USER') { - $query->join('USERS', function($join) use ($userUid) { + if ($sort == 'APP_CURRENT_USER') { + $query->join('USERS', function ($join) use ($userUid) { $join->on('APP_DELEGATION.USR_ID', '=', 'USERS.USR_ID'); }); } // Search for specified user - if($userUid) { + if ($userUid) { $query->where('APP_DELEGATION.USR_ID', $userUid); } // Search for specified process - if($process) { + if ($process) { $query->where('APP_DELEGATION.PRO_ID', $process); } // Search for an app/case number - if($filterBy == 'APP_NUMBER' && $search) { + if ($filterBy == 'APP_NUMBER' && $search) { $query->where('APP_DELEGATION.APP_NUMBER', 'LIKE', "%${search}%"); } @@ -157,9 +174,40 @@ class Delegation extends Model $query->where('APP_DELEGATION.DEL_DELEGATE_DATE', '<=', $dateTo); } + // Status Filter + // This is tricky, the below behavior is combined with the application join behavior above + switch ($status) { + case 1: //DRAFT + $query->where('APP_DELEGATION.DEL_THREAD_STATUS', 'OPEN'); + break; + case 2: //TO_DO + $query->where('APP_DELEGATION.DEL_THREAD_STATUS', 'OPEN'); + break; + case 3: //COMPLETED + $query->where('APP_DELEGATION.DEL_LAST_INDEX', 1); + break; + case 4: //CANCELLED + $query->where('APP_DELEGATION.DEL_LAST_INDEX', 1); + break; + case "PAUSED": + // Do nothing, as the app status check for TO_DO is performed in the join above + break; + default: //All statuses. + $query->where(function ($query) { + // Check to see if thread status is open + $query->where('APP_DELEGATION.DEL_THREAD_STATUS', 'OPEN') + ->orWhere(function ($query) { + // Or, we make sure if the thread is closed, and it's the last delegation, and if the app is completed or cancelled + $query->where('APP_DELEGATION.DEL_THREAD_STATUS', 'CLOSED') + ->where('APP_DELEGATION.DEL_LAST_INDEX', 1) + ->whereIn('APPLICATION.APP_STATUS_ID', [3, 4]); + }); + }); + break; + } // Add any sort if needed - switch($sort) { + switch ($sort) { case 'APP_NUMBER': $query->orderBy('APP_DELEGATION.APP_NUMBER', $dir); break; @@ -187,39 +235,39 @@ class Delegation extends Model $results = collect($query->get()); // Transform with additional data - $priorities = ['1' => 'VL','2' => 'L','3' => 'N','4' => 'H','5' => 'VH']; - $results->transform(function($item, $key) use($priorities) { + $priorities = ['1' => 'VL', '2' => 'L', '3' => 'N', '4' => 'H', '5' => 'VH']; + $results->transform(function ($item, $key) use ($priorities) { // Grab related records $application = Application::where('APP_NUMBER', $item['APP_NUMBER'])->first(); - if(!$application) { + if (!$application) { // Application wasn't found, return null return null; } $task = Task::where('TAS_ID', $item['TAS_ID'])->first(); - if(!$task) { + if (!$task) { // Task not found, return null return null; } $user = User::where('USR_ID', $item['USR_ID'])->first(); - if(!$user) { + if (!$user) { // User not found, return null return null; } $process = Process::where('PRO_ID', $item['PRO_ID'])->first();; - if(!$process) { + if (!$process) { // Process not found, return null return null; } - + // Rewrite priority string - if($item['DEL_PRIORITY']) { - $item['DEL_PRIORITY'] = G::LoadTranslation( "ID_PRIORITY_{$priorities[$item['DEL_PRIORITY']]}" ); + if ($item['DEL_PRIORITY']) { + $item['DEL_PRIORITY'] = G::LoadTranslation("ID_PRIORITY_{$priorities[$item['DEL_PRIORITY']]}"); } // Merge in desired application data $item['APP_STATUS'] = $application->APP_STATUS; - if($item['APP_STATUS']) { - $item['APP_STATUS_LABEL'] = G::LoadTranslation( "ID_${item['APP_STATUS']}"); + if ($item['APP_STATUS']) { + $item['APP_STATUS_LABEL'] = G::LoadTranslation("ID_${item['APP_STATUS']}"); } else { $item['APP_STATUS_LABEL'] = $application->APP_STATUS; } @@ -234,14 +282,14 @@ class Delegation extends Model // Merge in desired task data $item['APP_TAS_TITLE'] = $task->TAS_TITLE; $item['APP_TAS_TYPE'] = $task->TAS_TYPE; - + // Merge in desired user data $item['USR_LASTNAME'] = $user->USR_LASTNAME; $item['USR_FIRSTNAME'] = $user->USR_FIRSTNAME; $item['USR_USERNAME'] = $user->USR_USERNAME; //@todo: this section needs to use 'User Name Display Format', currently in the extJs is defined this - $item["APP_CURRENT_USER"] = $item["USR_LASTNAME"].' '.$item["USR_FIRSTNAME"]; + $item["APP_CURRENT_USER"] = $item["USR_LASTNAME"] . ' ' . $item["USR_FIRSTNAME"]; $item["APPDELCR_APP_TAS_TITLE"] = ''; @@ -263,7 +311,7 @@ class Delegation extends Model 'totalCount' => $start + $limit + 1, 'sql' => $query->toSql(), 'bindings' => $query->getBindings(), - 'data' => $results->toArray() + 'data' => $results->toArray(), ]; return $response; From 9768f07a1e16a65f1bf10fb2bc5acc21e96f9694 Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 25 Apr 2019 18:02:32 -0700 Subject: [PATCH 15/57] Add change to always bring in specific columns since we always join on task and application in search. --- .../src/ProcessMaker/Model/Delegation.php | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 5023d173a..8e272c311 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -4,6 +4,7 @@ namespace ProcessMaker\Model; use G; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\DB; class Delegation extends Model { @@ -86,8 +87,41 @@ class Delegation extends Model $start = $start ? $start : 0; $limit = $limit ? $limit : 25; - // Start the query builder - $query = self::query(); + // Start the query builder, selecting our base attributes + $selectColumns = [ + 'APPLICATION.APP_NUMBER', + 'APPLICATION.APP_UID', + 'APPLICATION.APP_STATUS', + 'APPLICATION.APP_STATUS AS APP_STATUS_LABEL', + 'APPLICATION.PRO_UID', + 'APPLICATION.APP_CREATE_DATE', + 'APPLICATION.APP_FINISH_DATE', + 'APPLICATION.APP_UPDATE_DATE', + 'APPLICATION.APP_TITLE', + 'APP_DELEGATION.USR_UID', + 'APP_DELEGATION.TAS_UID', + 'APP_DELEGATION.USR_ID', + 'APP_DELEGATION.PRO_ID', + 'APP_DELEGATION.DEL_INDEX', + 'APP_DELEGATION.DEL_LAST_INDEX', + 'APP_DELEGATION.DEL_DELEGATE_DATE', + 'APP_DELEGATION.DEL_INIT_DATE', + 'APP_DELEGATION.DEL_FINISH_DATE', + 'APP_DELEGATION.DEL_TASK_DUE_DATE', + 'APP_DELEGATION.DEL_RISK_DATE', + 'APP_DELEGATION.DEL_THREAD_STATUS', + 'APP_DELEGATION.DEL_PRIORITY', + 'APP_DELEGATION.DEL_DURATION', + 'APP_DELEGATION.DEL_QUEUE_DURATION', + 'APP_DELEGATION.DEL_STARTED', + 'APP_DELEGATION.DEL_DELAY_DURATION', + 'APP_DELEGATION.DEL_FINISHED', + 'APP_DELEGATION.DEL_DELAYED', + 'APP_DELEGATION.DEL_DELAY_DURATION', + 'TASK.TAS_TITLE AS APP_TAS_TITLE', + 'TASK.TAS_TYPE AS APP_TAS_TYPE', + ]; + $query = DB::table('APP_DELEGATION')->select(DB::raw(implode(',', $selectColumns))); // Add join for task, filtering for task title if needed // It doesn't make sense for us to search for any delegations that match tasks that are events or web entry @@ -232,22 +266,13 @@ class Delegation extends Model ->limit($limit); // Fetch results and transform to a laravel collection - $results = collect($query->get()); + $results = $query->get(); // Transform with additional data $priorities = ['1' => 'VL', '2' => 'L', '3' => 'N', '4' => 'H', '5' => 'VH']; $results->transform(function ($item, $key) use ($priorities) { - // Grab related records - $application = Application::where('APP_NUMBER', $item['APP_NUMBER'])->first(); - if (!$application) { - // Application wasn't found, return null - return null; - } - $task = Task::where('TAS_ID', $item['TAS_ID'])->first(); - if (!$task) { - // Task not found, return null - return null; - } + // Convert to an array as our results must be an array + $item = json_decode(json_encode($item), true); $user = User::where('USR_ID', $item['USR_ID'])->first(); if (!$user) { // User not found, return null @@ -265,24 +290,15 @@ class Delegation extends Model } // Merge in desired application data - $item['APP_STATUS'] = $application->APP_STATUS; if ($item['APP_STATUS']) { $item['APP_STATUS_LABEL'] = G::LoadTranslation("ID_${item['APP_STATUS']}"); } else { $item['APP_STATUS_LABEL'] = $application->APP_STATUS; } - $item['APP_CREATE_DATE'] = $application->APP_CREATE_DATE; - $item['APP_FINISH_DATE'] = $application->APP_FINISH_DATE; - $item['APP_UPDATE_DATE'] = $application->APP_UPDATE_DATE; - $item['APP_TITLE'] = $application->APP_TITLE; // Merge in desired process data $item['APP_PRO_TITLE'] = $process->PRO_TITLE; - // Merge in desired task data - $item['APP_TAS_TITLE'] = $task->TAS_TITLE; - $item['APP_TAS_TYPE'] = $task->TAS_TYPE; - // Merge in desired user data $item['USR_LASTNAME'] = $user->USR_LASTNAME; $item['USR_FIRSTNAME'] = $user->USR_FIRSTNAME; From 1936f5cf3b4ad33dc74fb8002b562706dac258e0 Mon Sep 17 00:00:00 2001 From: dante Date: Thu, 25 Apr 2019 21:15:43 -0400 Subject: [PATCH 16/57] Delegations Factory --- database/factories/ApplicationFactory.php | 24 ++++++++++++++ database/factories/DelegationFactory.php | 32 +++++++++++++++++++ database/factories/TaskFactory.php | 2 ++ .../engine/src/ProcessMaker/Model/Process.php | 7 ++++ 4 files changed, 65 insertions(+) create mode 100644 database/factories/ApplicationFactory.php create mode 100644 database/factories/DelegationFactory.php diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php new file mode 100644 index 000000000..21f92aef2 --- /dev/null +++ b/database/factories/ApplicationFactory.php @@ -0,0 +1,24 @@ +define(\ProcessMaker\Model\Application::class, function(Faker $faker) { + + $process = \ProcessMaker\Model\Process::all()->random(); + return [ + 'APP_UID' => G::generateUniqueID(), + 'APP_TITLE' => G::generateUniqueID(), + 'APP_NUMBER' => $faker->unique()->numberBetween(1000), + 'APP_STATUS' => 'TO_DO', + 'APP_STATUS_ID' => 1, + '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..d4590d275 --- /dev/null +++ b/database/factories/DelegationFactory.php @@ -0,0 +1,32 @@ +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(); + $user = \ProcessMaker\Model\User::all()->random(); + 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' => '' + ]; +}); \ No newline at end of file diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php index bf196690f..ea6548bde 100644 --- a/database/factories/TaskFactory.php +++ b/database/factories/TaskFactory.php @@ -28,5 +28,7 @@ $factory->define(\ProcessMaker\Model\Task::class, function(Faker $faker) { 'TAS_CAN_UPLOAD' => 'FALSE', 'TAS_CAN_CANCEL' => 'FALSE', 'TAS_OWNER_APP' => 'FALSE', + 'TAS_CAN_SEND_MESSAGE' => 'FALSE', + 'TAS_SEND_LAST_EMAIL' => 'FALSE', ]; }); \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/Process.php b/workflow/engine/src/ProcessMaker/Model/Process.php index abaac4e7a..e136f07a3 100644 --- a/workflow/engine/src/ProcessMaker/Model/Process.php +++ b/workflow/engine/src/ProcessMaker/Model/Process.php @@ -27,6 +27,13 @@ class Process extends Model } + + public function tasks() + { + return $this->hasMany(Task::class, 'PRO_UID', 'PRO_UID'); + + } + public function creator() { return $this->hasOne(User::class, 'PRO_CREATE_USER', 'USR_UID'); From ef6c0276d93bca013919ccdb04b44f366bada921 Mon Sep 17 00:00:00 2001 From: dante Date: Thu, 25 Apr 2019 21:40:21 -0400 Subject: [PATCH 17/57] Task names are randomly generated --- database/factories/ApplicationFactory.php | 7 +++++-- database/factories/ProcessFactory.php | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php index 21f92aef2..e497e9c38 100644 --- a/database/factories/ApplicationFactory.php +++ b/database/factories/ApplicationFactory.php @@ -5,12 +5,15 @@ use ProcessMaker\BusinessModel\Cases as BmCases; $factory->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' => 'TO_DO', - 'APP_STATUS_ID' => 1, + '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, diff --git a/database/factories/ProcessFactory.php b/database/factories/ProcessFactory.php index 12e60c998..2ddbe0d0f 100644 --- a/database/factories/ProcessFactory.php +++ b/database/factories/ProcessFactory.php @@ -21,14 +21,12 @@ $factory->define(\ProcessMaker\Model\Process::class, function(Faker $faker) { $task1 = factory(\ProcessMaker\Model\Task::class) ->create([ 'PRO_UID' => $process['PRO_UID'], - 'TAS_START'=>'TRUE', - 'TAS_TITLE'=>'Task A' + 'TAS_START'=>'TRUE' ]); $task2 = factory(\ProcessMaker\Model\Task::class) ->create([ 'PRO_UID' => $process['PRO_UID'], - 'TAS_TITLE' => 'Task B' ]); //routes From 9fa4c1750ac5d11a1e7acec51df59bae3ae98b14 Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Fri, 26 Apr 2019 08:45:38 -0700 Subject: [PATCH 18/57] Join on APP NUMBER instead of APP_UID --- workflow/engine/src/ProcessMaker/Model/Delegation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 8e272c311..d2b3e60e2 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -140,7 +140,7 @@ class Delegation extends Model // Add join for application, taking care of status and filtering if necessary $query->join('APPLICATION', function ($join) use ($filterBy, $search, $status, $query) { - $join->on('APP_DELEGATION.APP_UID', '=', 'APPLICATION.APP_UID'); + $join->on('APP_DELEGATION.APP_NUMBER', '=', 'APPLICATION.APP_NUMBER'); if ($filterBy == 'APP_TITLE' && $search) { $join->where('APPLICATION.APP_TITLE', 'LIKE', "%${search}%"); } From 17e6ef344135feffaf0b26c9663baa73b5884637 Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Wed, 1 May 2019 13:49:05 -0700 Subject: [PATCH 19/57] Add sample tests. Change userUid argument to be properly userId. Do some safe checks in search. --- database/factories/DelegationFactory.php | 9 ++- .../src/ProcessMaker/Model/DelegationTest.php | 80 +++++++++++++++++++ .../src/ProcessMaker/Model/Delegation.php | 57 +++++++------ 3 files changed, 116 insertions(+), 30 deletions(-) create mode 100644 tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php diff --git a/database/factories/DelegationFactory.php b/database/factories/DelegationFactory.php index d4590d275..4b7c6e947 100644 --- a/database/factories/DelegationFactory.php +++ b/database/factories/DelegationFactory.php @@ -7,7 +7,14 @@ $factory->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(); - $user = \ProcessMaker\Model\User::all()->random(); + + // 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, 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..06493e73a --- /dev/null +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -0,0 +1,80 @@ +create(); + factory(\ProcessMaker\Model\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 ensures searching for a valid user works + * @test + */ + public function it_should_return_one_result_for_specified_user() + { + factory(\ProcessMaker\Model\User::class,100)->create(); + factory(\ProcessMaker\Model\Process::class,10)->create(); + // Create our unique user, with a unique username + $user = factory(\ProcessMaker\Model\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']); + } + + /** + * @test + */ + public function it_should_have_data_match_certain_schema() + { + $this->markTestIncomplete(); + } + + /** + * @test + */ + public function it_should_sort_by_case_id() + { + $this->markTestIncomplete(); + } + + /** + * @test + */ + public function it_should_sort_by_user() + { + $this->markTestIncomplete(); + } +} \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index d2b3e60e2..9e157dbba 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -51,7 +51,7 @@ class Delegation extends Model * The query is related to advanced search with different filters * We can search by process, status of case, category of process, users, delegate date from and to * - * @param string $userUid + * @param integer $userId The USR_ID to search for (Note, this is no longer the USR_UID) * @param integer $start for the pagination * @param integer $limit for the pagination * @param string $search @@ -68,9 +68,10 @@ class Delegation extends Model */ public static function search( - $userUid, - $start = null, - $limit = null, + $userId = null, + // Default pagination values + $start = 0, + $limit = 25, $search = null, $process = null, $status = null, @@ -83,10 +84,6 @@ class Delegation extends Model ) { $search = trim($search); - // Default pagination values - $start = $start ? $start : 0; - $limit = $limit ? $limit : 25; - // Start the query builder, selecting our base attributes $selectColumns = [ 'APPLICATION.APP_NUMBER', @@ -178,14 +175,14 @@ class Delegation extends Model // Add join for user, but only for certain scenarios as sorting if ($sort == 'APP_CURRENT_USER') { - $query->join('USERS', function ($join) use ($userUid) { + $query->join('USERS', function ($join) use ($userId) { $join->on('APP_DELEGATION.USR_ID', '=', 'USERS.USR_ID'); }); } // Search for specified user - if ($userUid) { - $query->where('APP_DELEGATION.USR_ID', $userUid); + if ($userId) { + $query->where('APP_DELEGATION.USR_ID', $userId); } // Search for specified process @@ -241,24 +238,26 @@ class Delegation extends Model } // Add any sort if needed - switch ($sort) { - case 'APP_NUMBER': - $query->orderBy('APP_DELEGATION.APP_NUMBER', $dir); - break; - case 'APP_PRO_TITLE': - // We can do this because we joined the process table if sorting by it - $query->orderBy('PROCESS.PRO_TITLE', $dir); - break; - case 'APP_TAS_TITLE': - $query->orderBy('TASK.TAS_TITLE', $dir); - break; - case 'APP_CURRENT_USER': - // We can do this because we joined the user table if sorting by it - $query->orderBy('USERS.USR_LASTNAME', $dir); - $query->orderBy('USERS.USR_FIRSTNAME', $dir); - break; - default: - $query->orderBy($sort, $dir); + if($sort) { + switch ($sort) { + case 'APP_NUMBER': + $query->orderBy('APP_DELEGATION.APP_NUMBER', $dir); + break; + case 'APP_PRO_TITLE': + // We can do this because we joined the process table if sorting by it + $query->orderBy('PROCESS.PRO_TITLE', $dir); + break; + case 'APP_TAS_TITLE': + $query->orderBy('TASK.TAS_TITLE', $dir); + break; + case 'APP_CURRENT_USER': + // We can do this because we joined the user table if sorting by it + $query->orderBy('USERS.USR_LASTNAME', $dir); + $query->orderBy('USERS.USR_FIRSTNAME', $dir); + break; + default: + $query->orderBy($sort, $dir); + } } // Add pagination to the query From dd81f0f759a0a6755a9199f866f726e4baa4b645 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Wed, 1 May 2019 18:10:46 -0400 Subject: [PATCH 20/57] First unit test --- .../src/ProcessMaker/Model/DelegationTest.php | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 06493e73a..fb5c25dce 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -31,6 +31,95 @@ class DelegationTest extends TestCase $this->assertCount(1, $results['data']); } + /** + * This checks to make sure pagination is working properly with search by caseTitle + * @test + */ + public function it_should_return_pages_of_data_filter_default_case_title() + { + factory(\ProcessMaker\Model\User::class,100)->create(); + factory(\ProcessMaker\Model\Process::class,10)->create(); + factory(\ProcessMaker\Model\Application::class,100)->create(); + factory(Delegation::class, 51)->create(); + $search = '#'; + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, $search); + $this->assertCount(10, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25, $search); + $this->assertCount(10, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25, $search); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure pagination is working properly with search by case title + * @test + */ + public function it_should_return_pages_of_data_filter_case_title() + { + factory(\ProcessMaker\Model\User::class,100)->create(); + factory(\ProcessMaker\Model\Process::class,10)->create(); + factory(\ProcessMaker\Model\Application::class,51)->create(); + factory(Delegation::class, 51)->create(); + $search = '#'; + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, $search, null, null, null, null, null, null, null, 'APP_TITLE'); + $this->assertCount(10, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 0, 25, $search, null, null, null, null, null, null, null, 'APP_TITLE'); + $this->assertCount(10, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 0, 25, $search, null, null, null, null, null, null, null, 'APP_TITLE'); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure pagination is working properly with search by case number + * @test + */ + public function it_should_return_pages_of_data_filter_case_number() + { + factory(\ProcessMaker\Model\User::class,100)->create(); + factory(\ProcessMaker\Model\Process::class,10)->create(); + factory(\ProcessMaker\Model\Application::class,101)->create(); + factory(Delegation::class, 101)->create(); + $search = '1'; + // Get first page, which is 25 + $results = Delegation::search(null, 0, 10, $search, null, null, null, null, null, null, null, 'APP_NUMBER'); + $this->assertCount(10, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 10, 10, $search, null, null, null, null, null, null, null, 'APP_NUMBER'); + $this->assertCount(10, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 20, 10, $search, null, null, null, null, null, null, null, 'APP_NUMBER'); + $this->assertCount(1, $results['data']); + } + + /** + * This checks to make sure pagination is working properly with search by case title + * @test + */ + public function it_should_return_pages_of_data_filter_task_title() + { + factory(\ProcessMaker\Model\User::class,100)->create(); + factory(\ProcessMaker\Model\Process::class,10)->create(); + factory(\ProcessMaker\Model\Task::class,200)->create(); + factory(Delegation::class, 51)->create(); + //I need to check the Faker names + $search = 'task'; + // Get first page, which is 25 + $results = Delegation::search(null, 0, 10, $search, null, null, null, null, null, null, null, 'TAS_TITLE'); + $this->assertCount(10, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 10, 10, $search, null, null, null, null, null, null, null, 'TAS_TITLE'); + $this->assertCount(10, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 20, 10, $search, null, null, null, null, null, null, null, 'TAS_TITLE'); + $this->assertCount(1, $results['data']); + } + /** * This ensures searching for a valid user works * @test From 24368423c05dd59f363d846697c6ba96d6f3d26b Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 2 May 2019 10:53:43 -0400 Subject: [PATCH 21/57] conflicts --- .../src/ProcessMaker/Model/DelegationTest.php | 163 ++++++++++++------ .../src/ProcessMaker/Model/Delegation.php | 2 +- 2 files changed, 108 insertions(+), 57 deletions(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index fb5c25dce..7c064abef 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -2,9 +2,10 @@ namespace Tests\unit\workflow\src\ProcessMaker\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; -use ProcessMaker\Model\User; -use ProcessMaker\Model\Process; +use ProcessMaker\Model\Application; use ProcessMaker\Model\Delegation; +use ProcessMaker\Model\Process; +use ProcessMaker\Model\User; use Tests\TestCase; class DelegationTest extends TestCase @@ -17,8 +18,8 @@ class DelegationTest extends TestCase */ public function it_should_return_pages_of_data() { - factory(\ProcessMaker\Model\User::class,100)->create(); - factory(\ProcessMaker\Model\Process::class,10)->create(); + factory(User::class,100)->create(); + factory(Process::class,10)->create(); factory(Delegation::class, 51)->create(); // Get first page, which is 25 $results = Delegation::search(null, 0, 25); @@ -32,91 +33,141 @@ class DelegationTest extends TestCase } /** - * This checks to make sure pagination is working properly with search by caseTitle + * This checks to make sure filter by process is working properly * @test */ - public function it_should_return_pages_of_data_filter_default_case_title() + public function it_should_return_process_of_data() { - factory(\ProcessMaker\Model\User::class,100)->create(); - factory(\ProcessMaker\Model\Process::class,10)->create(); - factory(\ProcessMaker\Model\Application::class,100)->create(); - factory(Delegation::class, 51)->create(); - $search = '#'; + factory(User::class,100)->create(); + $process = factory(Process::class, 1)->create([ + 'PRO_ID' => 1 + ]); + factory(Delegation::class, 51)->create([ + 'PRO_ID' => $process[0]->id + ]); // Get first page, which is 25 - $results = Delegation::search(null, 0, 25, $search); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 0, 25, null, $process[0]->id); + $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25, $search); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 25, 25,null, $process[0]->id); + $this->assertCount(25, $results['data']); // Get third page, which is only 1 result - $results = Delegation::search(null, 50, 25, $search); + $results = Delegation::search(null, 50, 25,null, $process[0]->id); $this->assertCount(1, $results['data']); } /** - * This checks to make sure pagination is working properly with search by case title + * 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_pages_of_data_filter_case_title() + public function it_should_return_status_draft_of_data() { - factory(\ProcessMaker\Model\User::class,100)->create(); - factory(\ProcessMaker\Model\Process::class,10)->create(); - factory(\ProcessMaker\Model\Application::class,51)->create(); - factory(Delegation::class, 51)->create(); - $search = '#'; + factory(User::class,100)->create(); + factory(Process::class,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 1 + ]);//DRAFT + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Review the filter by status DRAFT // Get first page, which is 25 - $results = Delegation::search(null, 0, 25, $search, null, null, null, null, null, null, null, 'APP_TITLE'); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 0, 25, $search, null, null, null, null, null, null, null, 'APP_TITLE'); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); // Get third page, which is only 1 result - $results = Delegation::search(null, 0, 25, $search, null, null, null, null, null, null, null, 'APP_TITLE'); + $results = Delegation::search(null, 50, 25,null, null, $application[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } /** - * This checks to make sure pagination is working properly with search by case number + * 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_pages_of_data_filter_case_number() + public function it_should_return_status_todo_of_data() { - factory(\ProcessMaker\Model\User::class,100)->create(); - factory(\ProcessMaker\Model\Process::class,10)->create(); - factory(\ProcessMaker\Model\Application::class,101)->create(); - factory(Delegation::class, 101)->create(); - $search = '1'; + factory(User::class,100)->create(); + factory(Process::class,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 2 + ]);//DRAFT + + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Review the filter by status TO_DO // Get first page, which is 25 - $results = Delegation::search(null, 0, 10, $search, null, null, null, null, null, null, null, 'APP_NUMBER'); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 10, 10, $search, null, null, null, null, null, null, null, 'APP_NUMBER'); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); // Get third page, which is only 1 result - $results = Delegation::search(null, 20, 10, $search, null, null, null, null, null, null, null, 'APP_NUMBER'); + $results = Delegation::search(null, 50, 25,null, null, $application[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } /** - * This checks to make sure pagination is working properly with search by case title + * 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_pages_of_data_filter_task_title() + public function it_should_return_status_completed_of_data() { - factory(\ProcessMaker\Model\User::class,100)->create(); - factory(\ProcessMaker\Model\Process::class,10)->create(); - factory(\ProcessMaker\Model\Task::class,200)->create(); - factory(Delegation::class, 51)->create(); - //I need to check the Faker names - $search = 'task'; + factory(User::class,100)->create(); + factory(Process::class,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 3 + ]);//DRAFT + + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Review the filter by status DRAFT // Get first page, which is 25 - $results = Delegation::search(null, 0, 10, $search, null, null, null, null, null, null, null, 'TAS_TITLE'); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 10, 10, $search, null, null, null, null, null, null, null, 'TAS_TITLE'); - $this->assertCount(10, $results['data']); + $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); // Get third page, which is only 1 result - $results = Delegation::search(null, 20, 10, $search, null, null, null, null, null, null, null, 'TAS_TITLE'); + $results = Delegation::search(null, 50, 25,null, null, $application[0]->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,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001, + 'APP_STATUS_ID' => 4 + ]);//DRAFT + + factory(Delegation::class, 51)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Review the filter by status DRAFT + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25,null, null, $application[0]->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[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } @@ -126,10 +177,10 @@ class DelegationTest extends TestCase */ public function it_should_return_one_result_for_specified_user() { - factory(\ProcessMaker\Model\User::class,100)->create(); - factory(\ProcessMaker\Model\Process::class,10)->create(); + factory(User::class,100)->create(); + factory(Process::class,10)->create(); // Create our unique user, with a unique username - $user = factory(\ProcessMaker\Model\User::class)->create([ + $user = factory(User::class)->create([ 'USR_USERNAME' => 'testcaseuser' ]); // Create a new delegation, but for this specific user diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 9e157dbba..304081921 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -326,7 +326,7 @@ class Delegation extends Model 'totalCount' => $start + $limit + 1, 'sql' => $query->toSql(), 'bindings' => $query->getBindings(), - 'data' => $results->toArray(), + 'data' => $results->values()->toArray(), ]; return $response; From 7b96da5bc5d9801cee013c10bd8e594a7f2eb9b4 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 2 May 2019 11:43:38 -0400 Subject: [PATCH 22/57] sort by case and user --- .../src/ProcessMaker/Model/DelegationTest.php | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 7c064abef..7951c33fd 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -203,18 +203,74 @@ class DelegationTest extends TestCase } /** + * This ensures ordering ascending works by case number * @test */ public function it_should_sort_by_case_id() { - $this->markTestIncomplete(); + factory(User::class,100)->create(); + factory(Process::class,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 30002 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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 user * @test */ public function it_should_sort_by_user() { - $this->markTestIncomplete(); + 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']); } } \ No newline at end of file From a874ab23906c877c563c90cb805f7caca308f473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 2 May 2019 12:46:53 -0400 Subject: [PATCH 23/57] First commit for filter by category --- database/factories/ProcessCategoryFactory.php | 14 +++++++++++ .../src/ProcessMaker/Model/DelegationTest.php | 24 +++++++++++++++++++ .../engine/src/ProcessMaker/Model/Process.php | 9 ++++--- .../ProcessMaker/Model/ProcessCategory.php | 17 +++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 database/factories/ProcessCategoryFactory.php create mode 100644 workflow/engine/src/ProcessMaker/Model/ProcessCategory.php diff --git a/database/factories/ProcessCategoryFactory.php b/database/factories/ProcessCategoryFactory.php new file mode 100644 index 000000000..df5dc69d0 --- /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->paragraph(3), + 'CATEGORY_ICON' => '', + ]; +}); diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 7951c33fd..7c971ea26 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -273,4 +273,28 @@ class DelegationTest extends TestCase $this->assertCount(2, $results['data']); $this->assertEquals('Paul Griffis', $results['data'][0]['APP_CURRENT_USER']); } + + /** + * This checks to make sure filter by category is working properly + * @test + */ + public function it_should_return_categories_of_data() + { + /*factory(User::class,100)->create(); + $process = factory(Process::class, 1)->create([ + 'PRO_ID' => 1 + ]); + factory(Delegation::class, 51)->create([ + 'PRO_ID' => $process[0]->id + ]); + // Get first page, which is 25 + $results = Delegation::search(null, 0, 25, null, $process[0]->id); + $this->assertCount(25, $results['data']); + // Get second page, which is 25 results + $results = Delegation::search(null, 25, 25,null, $process[0]->id); + $this->assertCount(25, $results['data']); + // Get third page, which is only 1 result + $results = Delegation::search(null, 50, 25,null, $process[0]->id); + $this->assertCount(1, $results['data']);*/ + } } \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/Process.php b/workflow/engine/src/ProcessMaker/Model/Process.php index e136f07a3..52ff107e2 100644 --- a/workflow/engine/src/ProcessMaker/Model/Process.php +++ b/workflow/engine/src/ProcessMaker/Model/Process.php @@ -24,18 +24,21 @@ class Process extends Model public function applications() { return $this->hasMany(Application::class, 'PRO_ID', 'PRO_ID'); - } public function tasks() { return $this->hasMany(Task::class, 'PRO_UID', 'PRO_UID'); - } public function creator() { return $this->hasOne(User::class, 'PRO_CREATE_USER', 'USR_UID'); } -} \ No newline at end of file + + public function category() + { + return $this->hasOne(ProcessCategory::class, 'PRO_CATEGORY', 'CATEGORY_UID'); + } +} diff --git a/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php b/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php new file mode 100644 index 000000000..9d8c69bd4 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php @@ -0,0 +1,17 @@ + Date: Thu, 2 May 2019 13:23:23 -0400 Subject: [PATCH 24/57] conflicts --- .../src/ProcessMaker/Model/DelegationTest.php | 138 ++++++++++++++---- .../src/ProcessMaker/Model/Delegation.php | 4 +- 2 files changed, 115 insertions(+), 27 deletions(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 7c971ea26..8e99a843e 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -5,6 +5,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use ProcessMaker\Model\Application; use ProcessMaker\Model\Delegation; use ProcessMaker\Model\Process; +use ProcessMaker\Model\Task; use ProcessMaker\Model\User; use Tests\TestCase; @@ -67,8 +68,9 @@ class DelegationTest extends TestCase factory(Process::class,1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, - 'APP_STATUS_ID' => 1 - ]);//DRAFT + 'APP_STATUS_ID' => 1, + 'APP_STATUS' => 'DRAFT' + ]); factory(Delegation::class, 51)->create([ 'APP_NUMBER' => $application[0]->APP_NUMBER ]); @@ -95,9 +97,9 @@ class DelegationTest extends TestCase factory(Process::class,1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, - 'APP_STATUS_ID' => 2 - ]);//DRAFT - + 'APP_STATUS_ID' => 2, + 'APP_STATUS' => 'TO_DO' + ]); factory(Delegation::class, 51)->create([ 'APP_NUMBER' => $application[0]->APP_NUMBER ]); @@ -124,13 +126,15 @@ class DelegationTest extends TestCase factory(Process::class,1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, - 'APP_STATUS_ID' => 3 - ]);//DRAFT + 'APP_STATUS_ID' => 3, + 'APP_STATUS' => 'COMPLETED', + ]); factory(Delegation::class, 51)->create([ - 'APP_NUMBER' => $application[0]->APP_NUMBER + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_LAST_INDEX' => 1 ]); - // Review the filter by status DRAFT + // Review the filter by status COMPLETED // Get first page, which is 25 $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(25, $results['data']); @@ -153,13 +157,15 @@ class DelegationTest extends TestCase factory(Process::class,1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, - 'APP_STATUS_ID' => 4 - ]);//DRAFT + 'APP_STATUS_ID' => 4, + 'APP_STATUS' => 'CANCELLED' + ]); factory(Delegation::class, 51)->create([ - 'APP_NUMBER' => $application[0]->APP_NUMBER + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_LAST_INDEX' => 1 ]); - // Review the filter by status DRAFT + // Review the filter by status CANCELLED // Get first page, which is 25 $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(25, $results['data']); @@ -235,7 +241,41 @@ class DelegationTest extends TestCase } /** - * This ensures ordering ascending and descending works by user + * This ensures ordering ascending works by case title + * @test + */ + public function it_should_sort_by_case_title() + { + factory(User::class,100)->create(); + factory(Process::class,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001, + 'APP_TITLE' => 'Request by Thomas' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 30002, + 'APP_TITLE' => 'Request by Ariel' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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 current user * @test */ public function it_should_sort_by_user() @@ -280,21 +320,69 @@ class DelegationTest extends TestCase */ public function it_should_return_categories_of_data() { - /*factory(User::class,100)->create(); + + } + + /** + * This checks to make sure filter by process is working properly + * @test + */ + public function it_should_return_data_issue() + { + factory(User::class,100)->create(); + // Create a threads over the process $process = factory(Process::class, 1)->create([ 'PRO_ID' => 1 ]); - factory(Delegation::class, 51)->create([ + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 1, + 'APP_TITLE' => 'Request by Thomas', + 'APP_STATUS_ID' => 2, + 'APP_STATUS' => 'TO_DO' + ]); + // Create a user Gary in a thread + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'gary', + 'USR_LASTNAME' => 'Gary', + 'USR_FIRSTNAME' => 'Bailey', + ]); + // Create a thread with the user Gary + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id, + 'USR_ID' => $user->id, + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + + // Define a dummy task + $task = factory(Task::class, 1)->create([ + 'TAS_ID' => 1, + 'TAS_TYPE' => 'INTERMEDIATE-THROW' + ]); + // Create a thread with the dummy task this does not need a user + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id, + 'USR_ID' => 0, + 'TAS_ID' => $task[0]->id, + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Create a user Paul in a thread + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'Paul', + 'USR_LASTNAME' => 'Griffis', + 'USR_FIRSTNAME' => 'paul', + ]); + // Create a thread with the user Paul + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id, + 'USR_ID' => $user->id, + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Create others delegations + factory(Delegation::class, 24)->create([ 'PRO_ID' => $process[0]->id ]); - // Get first page, which is 25 - $results = Delegation::search(null, 0, 25, null, $process[0]->id); - $this->assertCount(25, $results['data']); - // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25,null, $process[0]->id); - $this->assertCount(25, $results['data']); - // Get third page, which is only 1 result - $results = Delegation::search(null, 50, 25,null, $process[0]->id); - $this->assertCount(1, $results['data']);*/ + // Get first page, which is 25 of 26 + $results = Delegation::search(null, 0, 10, null, $process[0]->id, null, 'ASC', 'APP_NUMBER'); + $this->assertCount(10, $results['data']); } } \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 304081921..839c7db33 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -285,12 +285,12 @@ class Delegation extends Model // Rewrite priority string if ($item['DEL_PRIORITY']) { - $item['DEL_PRIORITY'] = G::LoadTranslation("ID_PRIORITY_{$priorities[$item['DEL_PRIORITY']]}"); + $item['DEL_PRIORITY'] = G::LoadTranslation("ID_PRIORITY_{$priorities[$item['DEL_PRIORITY']]}", 'en'); } // Merge in desired application data if ($item['APP_STATUS']) { - $item['APP_STATUS_LABEL'] = G::LoadTranslation("ID_${item['APP_STATUS']}"); + $item['APP_STATUS_LABEL'] = G::LoadTranslation("ID_${item['APP_STATUS']}", 'en'); } else { $item['APP_STATUS_LABEL'] = $application->APP_STATUS; } From 5955ff6737a14dac5815ca83f3a95d689a94fe3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 2 May 2019 14:48:50 -0400 Subject: [PATCH 25/57] conflicts --- database/factories/ProcessCategoryFactory.php | 2 +- .../src/ProcessMaker/Model/DelegationTest.php | 49 +++++++++++++++---- .../ProcessMaker/Model/ProcessCategory.php | 2 + 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/database/factories/ProcessCategoryFactory.php b/database/factories/ProcessCategoryFactory.php index df5dc69d0..0f8f19776 100644 --- a/database/factories/ProcessCategoryFactory.php +++ b/database/factories/ProcessCategoryFactory.php @@ -8,7 +8,7 @@ $factory->define(\ProcessMaker\Model\ProcessCategory::class, function (Faker $fa return [ 'CATEGORY_UID' => G::generateUniqueID(), 'CATEGORY_PARENT' => '', - 'CATEGORY_NAME' => $faker->paragraph(3), + 'CATEGORY_NAME' => $faker->sentence(5), 'CATEGORY_ICON' => '', ]; }); diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 8e99a843e..63c93bad0 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -5,6 +5,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use ProcessMaker\Model\Application; use ProcessMaker\Model\Delegation; use ProcessMaker\Model\Process; +use ProcessMaker\Model\ProcessCategory; use ProcessMaker\Model\Task; use ProcessMaker\Model\User; use Tests\TestCase; @@ -314,15 +315,6 @@ class DelegationTest extends TestCase $this->assertEquals('Paul Griffis', $results['data'][0]['APP_CURRENT_USER']); } - /** - * This checks to make sure filter by category is working properly - * @test - */ - public function it_should_return_categories_of_data() - { - - } - /** * This checks to make sure filter by process is working properly * @test @@ -385,4 +377,43 @@ class DelegationTest extends TestCase $results = Delegation::search(null, 0, 10, null, $process[0]->id, null, 'ASC', 'APP_NUMBER'); $this->assertCount(10, $results['data']); } + + /** + * This checks to make sure filter by category is working properly + * @test + */ + public function it_should_return_categories_of_data() + { + factory(User::class, 100)->create(); + // Dummy Processes + factory(ProcessCategory::class, 4)->create(); + factory(Process::class, 4)->create([ + 'PRO_CATEGORY' => \ProcessMaker\Model\ProcessCategory::all()->random()->CATEGORY_UID + ]); + // Dummy Delegations + factory(Delegation::class, 100)->create([ + 'PRO_ID' => \ProcessMaker\Model\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']); + } } \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php b/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php index 9d8c69bd4..8ad9d705e 100644 --- a/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php +++ b/workflow/engine/src/ProcessMaker/Model/ProcessCategory.php @@ -14,4 +14,6 @@ class ProcessCategory extends Model { // Set our table name protected $table = 'PROCESS_CATEGORY'; + + public $timestamps = false; } From 9b2ac65d23239330bd15b0e11f7852aa950f0d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 2 May 2019 15:02:20 -0400 Subject: [PATCH 26/57] Change test name to it_should_return_data_filtered_by_process_category --- .../workflow/engine/src/ProcessMaker/Model/DelegationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 63c93bad0..57e077e69 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -382,7 +382,7 @@ class DelegationTest extends TestCase * This checks to make sure filter by category is working properly * @test */ - public function it_should_return_categories_of_data() + public function it_should_return_data_filtered_by_process_category() { factory(User::class, 100)->create(); // Dummy Processes From 8b74343026840850725493ba145087b7e9707f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 2 May 2019 16:30:07 -0400 Subject: [PATCH 27/57] Add self service delegation for it_should_return_pages_of_data --- .../engine/src/ProcessMaker/Model/DelegationTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 57e077e69..e06b1bd26 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -22,7 +22,10 @@ class DelegationTest extends TestCase { factory(User::class,100)->create(); factory(Process::class,10)->create(); - factory(Delegation::class, 51)->create(); + factory(Delegation::class, 50)->create(); + factory(Delegation::class, 1)->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']); From a2d9c857a8fc645ffa86dbcfe68a22adc1b2862b Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 2 May 2019 16:51:47 -0400 Subject: [PATCH 28/57] filter with unassigned, date from and date to --- .../src/ProcessMaker/Model/DelegationTest.php | 95 +++++++++++++++++-- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index e06b1bd26..8f62c5231 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -19,6 +19,25 @@ class DelegationTest extends TestCase * @test */ public function it_should_return_pages_of_data() + { + factory(User::class,100)->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(); @@ -204,14 +223,6 @@ class DelegationTest extends TestCase $this->assertEquals('testcaseuser', $results['data'][0]['USRCR_USR_USERNAME']); } - /** - * @test - */ - public function it_should_have_data_match_certain_schema() - { - $this->markTestIncomplete(); - } - /** * This ensures ordering ascending works by case number * @test @@ -391,11 +402,11 @@ class DelegationTest extends TestCase // Dummy Processes factory(ProcessCategory::class, 4)->create(); factory(Process::class, 4)->create([ - 'PRO_CATEGORY' => \ProcessMaker\Model\ProcessCategory::all()->random()->CATEGORY_UID + 'PRO_CATEGORY' => ProcessCategory::all()->random()->CATEGORY_UID ]); // Dummy Delegations factory(Delegation::class, 100)->create([ - 'PRO_ID' => \ProcessMaker\Model\Process::all()->random()->PRO_ID + 'PRO_ID' => Process::all()->random()->PRO_ID ]); // Process with the category to search $processCategorySearch = factory(ProcessCategory::class, 1)->create(); @@ -419,4 +430,68 @@ class DelegationTest extends TestCase $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']); + } + } } \ No newline at end of file From 3b28019fe54cb83b21cc8c791596455b1437214f Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Thu, 2 May 2019 14:06:55 -0700 Subject: [PATCH 29/57] Add check for invalid USER potentially due to unassigned. In this situation, assign blank values to result. Fixes test to check for unassigned users. --- .../src/ProcessMaker/Model/DelegationTest.php | 1 + .../src/ProcessMaker/Model/Delegation.php | 24 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 8f62c5231..d14433108 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -41,6 +41,7 @@ class DelegationTest extends TestCase { factory(User::class,100)->create(); factory(Process::class,10)->create(); + factory(Delegation::class, 50)->create(); factory(Delegation::class, 1)->create([ 'USR_ID' => 0 // A self service delegation diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 839c7db33..a5a11b08b 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -272,16 +272,13 @@ class Delegation extends Model $results->transform(function ($item, $key) use ($priorities) { // Convert to an array as our results must be an array $item = json_decode(json_encode($item), true); - $user = User::where('USR_ID', $item['USR_ID'])->first(); - if (!$user) { - // User not found, return null - return null; - } - $process = Process::where('PRO_ID', $item['PRO_ID'])->first();; - if (!$process) { - // Process not found, return null - return null; + // If it's assigned, fetch the user + if($item['USR_ID']) { + $user = User::where('USR_ID', $item['USR_ID'])->first(); + } else { + $user = null; } + $process = Process::where('PRO_ID', $item['PRO_ID'])->first(); // Rewrite priority string if ($item['DEL_PRIORITY']) { @@ -296,12 +293,13 @@ class Delegation extends Model } // Merge in desired process data - $item['APP_PRO_TITLE'] = $process->PRO_TITLE; + // Handle situation where the process might not be in the system anymore + $item['APP_PRO_TITLE'] = $process ? $process->PRO_TITLE : ''; // Merge in desired user data - $item['USR_LASTNAME'] = $user->USR_LASTNAME; - $item['USR_FIRSTNAME'] = $user->USR_FIRSTNAME; - $item['USR_USERNAME'] = $user->USR_USERNAME; + $item['USR_LASTNAME'] = $user ? $user->USR_LASTNAME : ''; + $item['USR_FIRSTNAME'] = $user ? $user->USR_FIRSTNAME : ''; + $item['USR_USERNAME'] = $user ? $user->USR_USERNAME : ''; //@todo: this section needs to use 'User Name Display Format', currently in the extJs is defined this $item["APP_CURRENT_USER"] = $item["USR_LASTNAME"] . ' ' . $item["USR_FIRSTNAME"]; From c983a67a87c665f3c4ca49c6ecdd59ddb3c1e5dc Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 2 May 2019 17:12:35 -0400 Subject: [PATCH 30/57] Filter by case number --- .../src/ProcessMaker/Model/DelegationTest.php | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index d14433108..9c570ec0f 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -224,6 +224,148 @@ class DelegationTest extends TestCase $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,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2010 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2011 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2012 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2013 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2014 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2015 + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3001, + 'APP_TITLE' => 'Request # 3001' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3010, + 'APP_TITLE' => 'Request # 3010' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3011, + 'APP_TITLE' => 'Request # 3011' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3012, + 'APP_TITLE' => 'Request # 3012' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 3013, + 'APP_TITLE' => 'Request # 3013' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_TITLE' => 3014, + 'APP_TITLE' => 'Request # 3014' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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 ordering ascending works by case number * @test From 9f844097dad23238852fb0a81feb1bd96a3d8672 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 2 May 2019 17:19:10 -0400 Subject: [PATCH 31/57] Adding user case Hertland --- .../src/ProcessMaker/Model/DelegationTest.php | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 9c570ec0f..1ad1f55a7 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -366,6 +366,73 @@ class DelegationTest extends TestCase $this->assertCount(1, $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,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2001, + 'APP_TITLE' => 'Request from Abigail check nro 25001' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2010, + 'APP_TITLE' => 'Request from Abigail check nro 12' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2011, + 'APP_TITLE' => 'Request from Abigail check nro 1000' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 2012, + 'APP_TITLE' => 'Request from Abigail check nro 11000' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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, 1)->create([ + 'APP_TITLE' => 2014, + 'APP_TITLE' => 'Request from Abigail check nro 111' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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 works by case number * @test From 60005f820db1e307b7dde614a263caca720ddd6c Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Mon, 29 Apr 2019 16:01:22 -0700 Subject: [PATCH 32/57] Add test case for array diff recursive to show function works as designed. Add tests to show the underlying problem with using this function with grids and assuming changes. --- .../Util/Helpers/ArrayDiffRecursiveTest.php | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php new file mode 100644 index 000000000..2e4978243 --- /dev/null +++ b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php @@ -0,0 +1,194 @@ + 'b', + ]; + $source = [ + 'a' => 'b', + ]; + $this->assertEquals([], arrayDiffRecursive($change, $source)); + } + + /** + * Test to make sure a diff contains nothing if the source has additional properties but the change does not have them + * @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([], arrayDiffRecursive($change, $source)); + } + + /** + * Test to make sure a diff contains an extra property if the change has it but source does not + * @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, arrayDiffRecursive($change, $source)); + } + + /** + * Test to make sure the diff includes a property that is in a nested deep array property + * @test + */ + public function it_should_return_diff_with_nested_difference() + { + $change = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => 'Goodbye', + ], + ]; + + $source = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => 'Hello', + ], + ]; + $expected = [ + 'c' => [ + 'f' => 'Goodbye', + ], + ]; + $this->assertEquals($expected, arrayDiffRecursive($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 index of 1 is not present in the expected, because the record at index 1 did not change + 2 => ['field1' => 'AA', 'field2' => 'BB'] + ] + ]; + $this->assertEquals($expected, arrayDiffRecursive($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 = arrayDiffRecursive($change, $source); + $merged = array_replace_recursive($source, $diff); + $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 = arrayDiffRecursive($change, $source); + $merged = array_replace_recursive($source, $diff); + $this->assertEquals($expected, $merged); + + } + +} + + From b6f2011342b757bac3cfb9c582e481c5e9ec5afb Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Mon, 29 Apr 2019 16:10:09 -0700 Subject: [PATCH 33/57] Add test to make sure it can modify a row in place. --- tests/bootstrap.php | 2 + .../Util/Helpers/ArrayDiffRecursiveTest.php | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ed5442c3f..2dfbf6f1e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -27,6 +27,7 @@ define('SYS_LANG', 'en'); $app = require __DIR__ . '/../bootstrap/app.php'; $app->make(Kernel::class)->bootstrap(); +/* // Setup our testexternal database config(['database.connections.testexternal' => [ 'driver' => 'mysql', @@ -72,6 +73,7 @@ if (env('RUN_MSSQL_TESTS')) { 'value' => 'testvalue' ]); } +*/ // THIS IS FOR STANDARD PROCESSMAKER TABLES diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php index 2e4978243..dee9b711f 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php @@ -153,6 +153,46 @@ class ArrayDiffRecursiveTest extends TestCase $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 = arrayDiffRecursive($change, $source); + $merged = array_replace_recursive($source, $diff); + $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 From 6d1769e8ceb5bd097d07c93d2524e84ec83199b9 Mon Sep 17 00:00:00 2001 From: Taylor Dondich Date: Mon, 29 Apr 2019 16:50:41 -0700 Subject: [PATCH 34/57] Add test to show how to utilize a diff with laravel collection filter and map to remove elements when they are no longer in the change. --- .../Util/Helpers/ArrayDiffRecursiveTest.php | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php index dee9b711f..c90eb04f2 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Util/Helpers/ArrayDiffRecursiveTest.php @@ -226,9 +226,64 @@ class ArrayDiffRecursiveTest extends TestCase $diff = arrayDiffRecursive($change, $source); $merged = array_replace_recursive($source, $diff); $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 = arrayDiffRecursive($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); + } + + } From 7f3b8b1e40ca78fa5a67e665f88c93799cac292a Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Fri, 3 May 2019 08:03:56 -0400 Subject: [PATCH 35/57] filter by task title and order by lastModified, dueDate, status, process and task --- config/app.php | 2 - database/factories/ApplicationFactory.php | 1 - database/factories/DelegationFactory.php | 1 - database/factories/ProcessFactory.php | 1 - database/factories/RouteFactory.php | 1 - database/factories/TaskFactory.php | 1 - database/factories/TaskUserFactory.php | 1 - database/factories/UserFactory.php | 1 - .../src/ProcessMaker/Model/DelegationTest.php | 510 ++++++++++++++---- .../engine/methods/cases/proxyCasesList.php | 2 +- .../src/ProcessMaker/Model/Application.php | 1 - .../engine/src/ProcessMaker/Model/Process.php | 2 - .../engine/src/ProcessMaker/Model/User.php | 1 - 13 files changed, 418 insertions(+), 107 deletions(-) diff --git a/config/app.php b/config/app.php index 035380c52..651ebfc6a 100644 --- a/config/app.php +++ b/config/app.php @@ -21,9 +21,7 @@ return [ Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, - Laravel\Tinker\TinkerServiceProvider::class, - ], 'aliases' => [ diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php index e497e9c38..76728224a 100644 --- a/database/factories/ApplicationFactory.php +++ b/database/factories/ApplicationFactory.php @@ -3,7 +3,6 @@ use Faker\Generator as Faker; use ProcessMaker\BusinessModel\Cases as BmCases; $factory->define(\ProcessMaker\Model\Application::class, function(Faker $faker) { - $process = \ProcessMaker\Model\Process::all()->random(); $statuses = ['DRAFT', 'TO_DO']; $status = $faker->randomElement($statuses); diff --git a/database/factories/DelegationFactory.php b/database/factories/DelegationFactory.php index 4b7c6e947..e5917a414 100644 --- a/database/factories/DelegationFactory.php +++ b/database/factories/DelegationFactory.php @@ -3,7 +3,6 @@ use Faker\Generator as Faker; use ProcessMaker\BusinessModel\Cases as BmCases; $factory->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(); diff --git a/database/factories/ProcessFactory.php b/database/factories/ProcessFactory.php index 2ddbe0d0f..40ae93dc3 100644 --- a/database/factories/ProcessFactory.php +++ b/database/factories/ProcessFactory.php @@ -5,7 +5,6 @@ use Faker\Generator as Faker; $factory->define(\ProcessMaker\Model\Process::class, function(Faker $faker) { - /** * @todo Determine if we need more base columns populated */ diff --git a/database/factories/RouteFactory.php b/database/factories/RouteFactory.php index 7e649a31f..18a3e2f57 100644 --- a/database/factories/RouteFactory.php +++ b/database/factories/RouteFactory.php @@ -5,7 +5,6 @@ use Faker\Generator as Faker; $factory->define(\ProcessMaker\Model\Route::class, function(Faker $faker) { - return [ 'PRO_UID' => function() { $process = factory(\ProcessMaker\Model\Process::class)->create(); diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php index ea6548bde..41f1167a9 100644 --- a/database/factories/TaskFactory.php +++ b/database/factories/TaskFactory.php @@ -5,7 +5,6 @@ use Faker\Generator as Faker; $factory->define(\ProcessMaker\Model\Task::class, function(Faker $faker) { - return [ 'PRO_UID' => function() { $process = factory(\ProcessMaker\Model\Process::class)->create(); diff --git a/database/factories/TaskUserFactory.php b/database/factories/TaskUserFactory.php index 751c491f7..a4c0a5e7d 100644 --- a/database/factories/TaskUserFactory.php +++ b/database/factories/TaskUserFactory.php @@ -5,7 +5,6 @@ use Faker\Generator as Faker; $factory->define(\ProcessMaker\Model\TaskUser::class, function(Faker $faker) { - return [ 'TAS_UID' => function() { $task = factory(\ProcessMaker\Model\Task::class)->create(); diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 0eff02f51..1885c421e 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,7 +2,6 @@ use Faker\Generator as Faker; $factory->define(\ProcessMaker\Model\User::class, function(Faker $faker) { - return [ 'USR_UID' => G::generateUniqueID(), 'USR_USERNAME' => $faker->unique()->userName, diff --git a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php index 1ad1f55a7..a584902b4 100644 --- a/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php +++ b/tests/unit/workflow/engine/src/ProcessMaker/Model/DelegationTest.php @@ -20,8 +20,8 @@ class DelegationTest extends TestCase */ public function it_should_return_pages_of_data() { - factory(User::class,100)->create(); - factory(Process::class,10)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 10)->create(); factory(Delegation::class, 51)->create(); // Get first page, which is 25 $results = Delegation::search(null, 0, 25); @@ -33,14 +33,15 @@ class DelegationTest extends TestCase $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(User::class, 100)->create(); + factory(Process::class, 10)->create(); factory(Delegation::class, 50)->create(); factory(Delegation::class, 1)->create([ @@ -63,7 +64,7 @@ class DelegationTest extends TestCase */ public function it_should_return_process_of_data() { - factory(User::class,100)->create(); + factory(User::class, 100)->create(); $process = factory(Process::class, 1)->create([ 'PRO_ID' => 1 ]); @@ -74,10 +75,10 @@ class DelegationTest extends TestCase $results = Delegation::search(null, 0, 25, null, $process[0]->id); $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25,null, $process[0]->id); + $results = Delegation::search(null, 25, 25, null, $process[0]->id); $this->assertCount(25, $results['data']); // Get third page, which is only 1 result - $results = Delegation::search(null, 50, 25,null, $process[0]->id); + $results = Delegation::search(null, 50, 25, null, $process[0]->id); $this->assertCount(1, $results['data']); } @@ -88,8 +89,8 @@ class DelegationTest extends TestCase */ public function it_should_return_status_draft_of_data() { - factory(User::class,100)->create(); - factory(Process::class,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, 'APP_STATUS_ID' => 1, @@ -103,10 +104,10 @@ class DelegationTest extends TestCase $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $results = Delegation::search(null, 25, 25, null, null, $application[0]->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[0]->APP_STATUS_ID); + $results = Delegation::search(null, 50, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } @@ -117,8 +118,8 @@ class DelegationTest extends TestCase */ public function it_should_return_status_todo_of_data() { - factory(User::class,100)->create(); - factory(Process::class,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, 'APP_STATUS_ID' => 2, @@ -132,10 +133,10 @@ class DelegationTest extends TestCase $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $results = Delegation::search(null, 25, 25, null, null, $application[0]->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[0]->APP_STATUS_ID); + $results = Delegation::search(null, 50, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } @@ -146,8 +147,8 @@ class DelegationTest extends TestCase */ public function it_should_return_status_completed_of_data() { - factory(User::class,100)->create(); - factory(Process::class,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, 'APP_STATUS_ID' => 3, @@ -163,10 +164,10 @@ class DelegationTest extends TestCase $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $results = Delegation::search(null, 25, 25, null, null, $application[0]->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[0]->APP_STATUS_ID); + $results = Delegation::search(null, 50, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } @@ -177,8 +178,8 @@ class DelegationTest extends TestCase */ public function it_should_return_status_cancelled_of_data() { - factory(User::class,100)->create(); - factory(Process::class,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, 'APP_STATUS_ID' => 4, @@ -194,10 +195,10 @@ class DelegationTest extends TestCase $results = Delegation::search(null, 0, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(25, $results['data']); // Get second page, which is 25 results - $results = Delegation::search(null, 25, 25,null, null, $application[0]->APP_STATUS_ID); + $results = Delegation::search(null, 25, 25, null, null, $application[0]->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[0]->APP_STATUS_ID); + $results = Delegation::search(null, 50, 25, null, null, $application[0]->APP_STATUS_ID); $this->assertCount(1, $results['data']); } @@ -207,8 +208,8 @@ class DelegationTest extends TestCase */ public function it_should_return_one_result_for_specified_user() { - factory(User::class,100)->create(); - factory(Process::class,10)->create(); + 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' @@ -230,8 +231,8 @@ class DelegationTest extends TestCase */ public function it_should_search_by_case_id_and_pages_of_data() { - factory(User::class,100)->create(); - factory(Process::class,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001 ]); @@ -276,7 +277,7 @@ class DelegationTest extends TestCase ]); // Get first page, the major case id $results = Delegation::search(null, 0, 10, 1, null, null, 'DESC', - 'APP_NUMBER', null, null, null,'APP_NUMBER'); + '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 @@ -300,8 +301,8 @@ class DelegationTest extends TestCase */ 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,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 3001, 'APP_TITLE' => 'Request # 3001' @@ -353,19 +354,67 @@ class DelegationTest extends TestCase // Get first page, the minor case id $results = Delegation::search(null, 0, 10, '1', null, null, 'ASC', - 'APP_NUMBER', null, null, null,'APP_TITLE'); + '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'); + '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'); + '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, 1)->create(); + $task = factory(Task::class, 1)->create([ + 'TAS_ID' => 1, + 'TAS_TITLE' => 'Request task' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + $task = factory(Task::class, 1)->create([ + 'TAS_ID' => 2, + 'TAS_TITLE' => 'Account task' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task[0]->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) @@ -373,8 +422,8 @@ class DelegationTest extends TestCase */ 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,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, 'APP_TITLE' => 'Request from Abigail check nro 25001' @@ -426,21 +475,21 @@ class DelegationTest extends TestCase //Check the pagination $results = Delegation::search(null, 0, 5, '1', null, null, 'ASC', - 'APP_NUMBER', null, null, null,'APP_TITLE'); + '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'); + 'APP_NUMBER', null, null, null, 'APP_TITLE'); $this->assertCount(1, $results['data']); } /** - * This ensures ordering ascending works by case number + * 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,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001 ]); @@ -466,13 +515,13 @@ class DelegationTest extends TestCase } /** - * This ensures ordering ascending works by case title + * 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,1)->create(); + factory(User::class, 100)->create(); + factory(Process::class, 1)->create(); $application = factory(Application::class, 1)->create([ 'APP_NUMBER' => 2001, 'APP_TITLE' => 'Request by Thomas' @@ -499,14 +548,92 @@ class DelegationTest extends TestCase $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, 1)->create([ + 'PRO_ID' => 2, + 'PRO_TITLE' => 'Egypt Supplier Payment Proposal' + ]); + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id + ]); + $process = factory(Process::class, 1)->create([ + 'PRO_ID' => 1, + 'PRO_TITLE' => 'China Supplier Payment Proposal' + ]); + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id + ]); + $process = factory(Process::class, 1)->create([ + 'PRO_ID' => 3, + 'PRO_TITLE' => 'Russia Supplier Payment Proposal' + ]); + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->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, 1)->create(); + + $task = factory(Task::class, 1)->create([ + 'TAS_ID' => 1000, + 'TAS_TITLE' => 'Initiate Request' + ]); + factory(Delegation::class, 1)->create([ + 'TAS_ID' => $task[0]->TAS_ID + ]); + + $task = factory(Task::class, 1)->create([ + 'TAS_ID' => 4000, + 'TAS_TITLE' => 'Waiting for AP Manager Validation' + ]); + factory(Delegation::class, 1)->create([ + 'TAS_ID' => $task[0]->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(); + 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', @@ -540,66 +667,151 @@ class DelegationTest extends TestCase } /** - * This checks to make sure filter by process is working properly + * This ensures ordering ordering ascending and descending works by last modified APP_UPDATE_DATE * @test */ - public function it_should_return_data_issue() + public function it_should_sort_by_last_modified() { factory(User::class,100)->create(); - // Create a threads over the process - $process = factory(Process::class, 1)->create([ - 'PRO_ID' => 1 + factory(Process::class,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_UPDATE_DATE' => '2019-01-02 00:00:00' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_UPDATE_DATE' => '2019-01-03 00:00:00' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_UPDATE_DATE' => '2019-01-04 00:00:00' + ]); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application[0]->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,1)->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,1)->create(); + $application = factory(Application::class, 1)->create([ + 'APP_STATUS' => 'DRAFT' + ]); + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER ]); $application = factory(Application::class, 1)->create([ - 'APP_NUMBER' => 1, - 'APP_TITLE' => 'Request by Thomas', - 'APP_STATUS_ID' => 2, 'APP_STATUS' => 'TO_DO' ]); - // Create a user Gary in a thread - $user = factory(User::class)->create([ - 'USR_USERNAME' => 'gary', - 'USR_LASTNAME' => 'Gary', - 'USR_FIRSTNAME' => 'Bailey', + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER ]); - // Create a thread with the user Gary - factory(Delegation::class, 1)->create([ - 'PRO_ID' => $process[0]->id, - 'USR_ID' => $user->id, + $application = factory(Application::class, 1)->create([ + 'APP_STATUS' => 'COMPLETED' + ]); + factory(Delegation::class, 25)->create([ + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + $application = factory(Application::class, 1)->create([ + 'APP_STATUS' => 'CANCELLED' + ]); + factory(Delegation::class, 25)->create([ 'APP_NUMBER' => $application[0]->APP_NUMBER ]); - // Define a dummy task - $task = factory(Task::class, 1)->create([ - 'TAS_ID' => 1, - 'TAS_TYPE' => 'INTERMEDIATE-THROW' - ]); - // Create a thread with the dummy task this does not need a user - factory(Delegation::class, 1)->create([ - 'PRO_ID' => $process[0]->id, - 'USR_ID' => 0, - 'TAS_ID' => $task[0]->id, - 'APP_NUMBER' => $application[0]->APP_NUMBER - ]); - // Create a user Paul in a thread - $user = factory(User::class)->create([ - 'USR_USERNAME' => 'Paul', - 'USR_LASTNAME' => 'Griffis', - 'USR_FIRSTNAME' => 'paul', - ]); - // Create a thread with the user Paul - factory(Delegation::class, 1)->create([ - 'PRO_ID' => $process[0]->id, - 'USR_ID' => $user->id, - 'APP_NUMBER' => $application[0]->APP_NUMBER - ]); - // Create others delegations - factory(Delegation::class, 24)->create([ - 'PRO_ID' => $process[0]->id - ]); - // Get first page, which is 25 of 26 - $results = Delegation::search(null, 0, 10, null, $process[0]->id, null, 'ASC', 'APP_NUMBER'); - $this->assertCount(10, $results['data']); + // 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']); } /** @@ -704,4 +916,116 @@ class DelegationTest extends TestCase $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, 100)->create(); + // Create a threads over the process + $process = factory(Process::class, 1)->create([ + 'PRO_ID' => 1 + ]); + $application = factory(Application::class, 1)->create([ + 'APP_NUMBER' => 1, + 'APP_TITLE' => 'Request by Thomas', + ]); + // Create a user Gary in a thread + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'gary', + 'USR_LASTNAME' => 'Bailey', + 'USR_FIRSTNAME' => 'Gary', + ]); + // Create a thread with the user Gary + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id, + 'USR_ID' => $user->id, + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_THREAD_STATUS' => 'CLOSED' + ]); + + // Define a dummy task + $task = factory(Task::class, 1)->create([ + 'TAS_TYPE' => 'INTERMEDIATE-THROW' + ]); + // Create a thread with the dummy task this does not need a user + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id, + 'USR_ID' => 0, + 'TAS_ID' => $task[0]->id, + 'APP_NUMBER' => $application[0]->APP_NUMBER + ]); + // Create a user Paul in a thread + $user = factory(User::class)->create([ + 'USR_USERNAME' => 'paul', + 'USR_LASTNAME' => 'Griffis', + 'USR_FIRSTNAME' => 'Paul', + ]); + // Create a thread with the user Paul + factory(Delegation::class, 1)->create([ + 'PRO_ID' => $process[0]->id, + 'USR_ID' => $user->id, + 'APP_NUMBER' => $application[0]->APP_NUMBER, + 'DEL_THREAD_STATUS' => 'OPEN' + ]); + // Get first page, which is 25 of 26 + $results = Delegation::search(null, 0, 10, null, $process[0]->id, null, 'ASC', 'APP_NUMBER'); + $this->assertCount(1, $results['data']); + $this->assertEquals('Griffis Paul', $results['data'][0]['APP_CURRENT_USER']); + } + + /** + * 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,1)->create(); + $task = factory(Task::class,1)->create([ + 'TAS_TITLE' => 'Parallel task 1' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task[0]->TAS_ID, + 'DEL_THREAD_STATUS' => 'CLOSED' + ]); + $task = factory(Task::class,1)->create([ + 'TAS_TITLE' => 'Parallel task 2' + ]); + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task[0]->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']); + + } } \ No newline at end of file diff --git a/workflow/engine/methods/cases/proxyCasesList.php b/workflow/engine/methods/cases/proxyCasesList.php index fbe9bd03e..0be7607f4 100644 --- a/workflow/engine/methods/cases/proxyCasesList.php +++ b/workflow/engine/methods/cases/proxyCasesList.php @@ -50,7 +50,7 @@ $filterStatus = isset($_REQUEST["filterStatus"]) ? strtoupper($_REQUEST["filterS // What user $user = isset($_REQUEST["user"]) ? $_REQUEST["user"] : ""; -// What search??? +// What keywords to search $search = isset($_REQUEST["search"]) ? $_REQUEST["search"] : ""; // What kind of action diff --git a/workflow/engine/src/ProcessMaker/Model/Application.php b/workflow/engine/src/ProcessMaker/Model/Application.php index 38e30c69c..eb367a03c 100644 --- a/workflow/engine/src/ProcessMaker/Model/Application.php +++ b/workflow/engine/src/ProcessMaker/Model/Application.php @@ -24,5 +24,4 @@ class Application extends Model { return $this->hasOne(User::class, 'APP_CUR_USER', 'USR_UID'); } - } diff --git a/workflow/engine/src/ProcessMaker/Model/Process.php b/workflow/engine/src/ProcessMaker/Model/Process.php index 52ff107e2..b32b92eb1 100644 --- a/workflow/engine/src/ProcessMaker/Model/Process.php +++ b/workflow/engine/src/ProcessMaker/Model/Process.php @@ -17,7 +17,6 @@ class Process extends Model // Our custom timestamp columns const CREATED_AT = 'PRO_CREATE_DATE'; const UPDATED_AT = 'PRO_UPDATE_DATE'; - /** * Retrieve all applications that belong to this process */ @@ -26,7 +25,6 @@ class Process extends Model return $this->hasMany(Application::class, 'PRO_ID', 'PRO_ID'); } - public function tasks() { return $this->hasMany(Task::class, 'PRO_UID', 'PRO_UID'); diff --git a/workflow/engine/src/ProcessMaker/Model/User.php b/workflow/engine/src/ProcessMaker/Model/User.php index 40fc1be55..7c6da958d 100644 --- a/workflow/engine/src/ProcessMaker/Model/User.php +++ b/workflow/engine/src/ProcessMaker/Model/User.php @@ -17,6 +17,5 @@ class User extends Model public function delegations() { return $this->hasMany(Delegation::class, 'USR_ID', 'USR_ID'); - } } From e3702dfd5eaf65f788b8f3a3a9f946a96c541bbc Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Tue, 30 Apr 2019 17:57:34 -0400 Subject: [PATCH 36/57] PMC-755 --- thirdparty/phpmailer/class.phpmailer.php | 2862 ----------------- thirdparty/phpmailer/class.pop3.php | 418 --- thirdparty/phpmailer/class.smtp.php | 1002 ------ .../phpmailer/extras/class.html2text.inc | 489 --- thirdparty/phpmailer/extras/htmlfilter.php | 861 ----- .../phpmailer/extras/ntlm_sasl_client.php | 185 -- .../phpmailer/language/phpmailer.lang-ar.php | 27 - .../phpmailer/language/phpmailer.lang-br.php | 26 - .../phpmailer/language/phpmailer.lang-ca.php | 26 - .../phpmailer/language/phpmailer.lang-ch.php | 26 - .../phpmailer/language/phpmailer.lang-cz.php | 25 - .../phpmailer/language/phpmailer.lang-de.php | 25 - .../phpmailer/language/phpmailer.lang-dk.php | 26 - .../phpmailer/language/phpmailer.lang-en.php | 23 - .../phpmailer/language/phpmailer.lang-es.php | 26 - .../phpmailer/language/phpmailer.lang-et.php | 26 - .../phpmailer/language/phpmailer.lang-fi.php | 27 - .../phpmailer/language/phpmailer.lang-fo.php | 27 - .../phpmailer/language/phpmailer.lang-fr.php | 25 - .../phpmailer/language/phpmailer.lang-hu.php | 25 - .../phpmailer/language/phpmailer.lang-it.php | 27 - .../phpmailer/language/phpmailer.lang-ja.php | 26 - .../phpmailer/language/phpmailer.lang-nl.php | 25 - .../phpmailer/language/phpmailer.lang-no.php | 25 - .../phpmailer/language/phpmailer.lang-pl.php | 25 - .../phpmailer/language/phpmailer.lang-ro.php | 27 - .../phpmailer/language/phpmailer.lang-ru.php | 26 - .../phpmailer/language/phpmailer.lang-se.php | 26 - .../phpmailer/language/phpmailer.lang-sk.php | 26 - .../phpmailer/language/phpmailer.lang-tr.php | 27 - .../phpmailer/language/phpmailer.lang-zh.php | 26 - .../language/phpmailer.lang-zh_cn.php | 26 - 32 files changed, 6489 deletions(-) delete mode 100644 thirdparty/phpmailer/class.phpmailer.php delete mode 100644 thirdparty/phpmailer/class.pop3.php delete mode 100644 thirdparty/phpmailer/class.smtp.php delete mode 100644 thirdparty/phpmailer/extras/class.html2text.inc delete mode 100644 thirdparty/phpmailer/extras/htmlfilter.php delete mode 100644 thirdparty/phpmailer/extras/ntlm_sasl_client.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-ar.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-br.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-ca.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-ch.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-cz.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-de.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-dk.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-en.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-es.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-et.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-fi.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-fo.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-fr.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-hu.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-it.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-ja.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-nl.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-no.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-pl.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-ro.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-ru.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-se.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-sk.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-tr.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-zh.php delete mode 100644 thirdparty/phpmailer/language/phpmailer.lang-zh_cn.php 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', //