From 5c7fcdd4177365d9d6af863d0b1f0b8824a9c21b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 14 Nov 2017 14:24:50 +0200 Subject: [PATCH 1/9] Fixed Postmark --- composer.json | 2 +- composer.lock | 164 +++++++++++++++++++++++++++----------------------- 2 files changed, 90 insertions(+), 76 deletions(-) diff --git a/composer.json b/composer.json index 3f235974ef10..d4b3dad2fac5 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "webpatser/laravel-countries": "dev-master", "websight/l5-google-cloud-storage": "dev-master", "wepay/php-sdk": "^0.2", - "wildbit/laravel-postmark-provider": "3.0" + "wildbit/laravel-postmark-provider": "dev-master#134f359" }, "require-dev": { "symfony/dom-crawler": "~3.1", diff --git a/composer.lock b/composer.lock index 97946ba44fff..82604aefd251 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "4e21badbba68d5feb34f05eaa12ccfe1", - "content-hash": "2d294b7828cf14adaf1deeb66426acc3", + "hash": "f2043c8ce923c8af3c99ba7c5bcf5d5e", + "content-hash": "dbc2b358c79e0abe0a58d2bd02310966", "packages": [ { "name": "abdala/omnipay-pagseguro", @@ -1747,21 +1747,21 @@ }, { "name": "doctrine/annotations", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5beebb01b025c94e93686b7a0ed3edae81fe3e7f", + "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "require-dev": { "doctrine/cache": "1.*", @@ -1770,7 +1770,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.5.x-dev" } }, "autoload": { @@ -1811,37 +1811,41 @@ "docblock", "parser" ], - "time": "2017-02-24 16:22:25" + "time": "2017-07-22 10:58:02" }, { "name": "doctrine/cache", - "version": "v1.6.2", + "version": "v1.7.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b" + "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b", - "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b3217d58609e9c8e661cd41357a54d926c4a2a1a", + "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a", "shasum": "" }, "require": { - "php": "~5.5|~7.0" + "php": "~7.1" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "phpunit/phpunit": "~4.8|~5.0", - "predis/predis": "~1.0", - "satooshi/php-coveralls": "~0.6" + "alcaeus/mongo-php-adapter": "^1.1", + "mongodb/mongodb": "^1.1", + "phpunit/phpunit": "^5.7", + "predis/predis": "~1.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.7.x-dev" } }, "autoload": { @@ -1881,24 +1885,24 @@ "cache", "caching" ], - "time": "2017-07-22 12:49:21" + "time": "2017-08-25 07:02:50" }, { "name": "doctrine/collections", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", - "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", + "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "require-dev": { "doctrine/coding-standard": "~0.1@dev", @@ -1948,7 +1952,7 @@ "collections", "iterator" ], - "time": "2017-01-03 10:49:41" + "time": "2017-07-22 10:37:32" }, { "name": "doctrine/common", @@ -2539,7 +2543,7 @@ "shasum": null }, "type": "library", - "time": "2016-05-12 12:00:45" + "time": "2017-11-13 12:00:28" }, { "name": "gocardless/gocardless-pro", @@ -2654,16 +2658,16 @@ }, { "name": "google/apiclient-services", - "version": "v0.33", + "version": "v0.34", "source": { "type": "git", "url": "https://github.com/google/google-api-php-client-services.git", - "reference": "6399eb82c725aef79124cd6b543e1984cd13d207" + "reference": "4d0d438e323929579cabf0b4d420177a96835c6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/google/google-api-php-client-services/zipball/6399eb82c725aef79124cd6b543e1984cd13d207", - "reference": "6399eb82c725aef79124cd6b543e1984cd13d207", + "url": "https://api.github.com/repos/google/google-api-php-client-services/zipball/4d0d438e323929579cabf0b4d420177a96835c6d", + "reference": "4d0d438e323929579cabf0b4d420177a96835c6d", "shasum": "" }, "require": { @@ -2687,7 +2691,7 @@ "keywords": [ "google" ], - "time": "2017-11-06 00:24:21" + "time": "2017-11-11 00:27:24" }, { "name": "google/auth", @@ -2941,16 +2945,16 @@ }, { "name": "google/protobuf", - "version": "v3.4.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/google/protobuf.git", - "reference": "b04e5cba356212e4e8c66c61bbe0c3a20537c5b9" + "reference": "2761122b810fe8861004ae785cc3ab39f384d342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/google/protobuf/zipball/b04e5cba356212e4e8c66c61bbe0c3a20537c5b9", - "reference": "b04e5cba356212e4e8c66c61bbe0c3a20537c5b9", + "url": "https://api.github.com/repos/google/protobuf/zipball/2761122b810fe8861004ae785cc3ab39f384d342", + "reference": "2761122b810fe8861004ae785cc3ab39f384d342", "shasum": "" }, "require": { @@ -2978,7 +2982,7 @@ "keywords": [ "proto" ], - "time": "2017-09-14 19:24:28" + "time": "2017-11-13 18:47:29" }, { "name": "grpc/grpc", @@ -3533,23 +3537,32 @@ }, { "name": "jakoch/phantomjs-installer", - "version": "2.1.1", + "version": "2.1.1-p08", "source": { "type": "git", "url": "https://github.com/jakoch/phantomjs-installer.git", - "reference": "b8ee2aac9b95f9a9ee30a05a4df4a0984a8a8b85" + "reference": "7775820cf5eae8962cf68ab44757238e4d3136cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jakoch/phantomjs-installer/zipball/b8ee2aac9b95f9a9ee30a05a4df4a0984a8a8b85", - "reference": "b8ee2aac9b95f9a9ee30a05a4df4a0984a8a8b85", + "url": "https://api.github.com/repos/jakoch/phantomjs-installer/zipball/7775820cf5eae8962cf68ab44757238e4d3136cc", + "reference": "7775820cf5eae8962cf68ab44757238e4d3136cc", "shasum": "" }, "require": { - "ext-openssl": "*" + "ext-bz2": "*", + "ext-openssl": "*", + "php": ">5.3" + }, + "require-dev": { + "composer/composer": "^1.2", + "phpunit/phpunit": "^4.8" }, "type": "custom-installer", "autoload": { + "psr-4": { + "PhantomInstaller\\Test\\": "tests/" + }, "psr-0": { "PhantomInstaller\\": "src/" } @@ -3570,7 +3583,7 @@ "headless", "phantomjs" ], - "time": "2016-01-25 16:30:30" + "time": "2017-01-10 09:56:38" }, { "name": "jakub-onderka/php-console-color", @@ -3877,20 +3890,20 @@ }, { "name": "jonnyw/php-phantomjs", - "version": "v4.5.1", + "version": "v4.6.0", "source": { "type": "git", "url": "https://github.com/jonnnnyw/php-phantomjs.git", - "reference": "cf8d9a221f4c624aa1537c55a2e181f4b50367d7" + "reference": "b3c3e20857caf9a36eb90fb1a7cf1e1f1ec4b672" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jonnnnyw/php-phantomjs/zipball/cf8d9a221f4c624aa1537c55a2e181f4b50367d7", - "reference": "cf8d9a221f4c624aa1537c55a2e181f4b50367d7", + "url": "https://api.github.com/repos/jonnnnyw/php-phantomjs/zipball/b3c3e20857caf9a36eb90fb1a7cf1e1f1ec4b672", + "reference": "b3c3e20857caf9a36eb90fb1a7cf1e1f1ec4b672", "shasum": "" }, "require": { - "jakoch/phantomjs-installer": "2.1.1", + "jakoch/phantomjs-installer": "2.1.1-p08", "php": ">=5.3.0", "symfony/config": "~2.3|~3.0", "symfony/dependency-injection": "~2.3|~3.0", @@ -3929,7 +3942,7 @@ "phantomjs", "testing" ], - "time": "2016-06-28 16:00:15" + "time": "2017-04-22 21:24:02" }, { "name": "justinbusschau/omnipay-secpay", @@ -8325,12 +8338,12 @@ "source": { "type": "git", "url": "https://github.com/simshaun/recurr.git", - "reference": "7d0e8942cb1d00d4bc11d5eb87fa990d77b0de61" + "reference": "7679f92be8e6046c40668b34dbf87000e4ab430f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simshaun/recurr/zipball/7d0e8942cb1d00d4bc11d5eb87fa990d77b0de61", - "reference": "7d0e8942cb1d00d4bc11d5eb87fa990d77b0de61", + "url": "https://api.github.com/repos/simshaun/recurr/zipball/7679f92be8e6046c40668b34dbf87000e4ab430f", + "reference": "7679f92be8e6046c40668b34dbf87000e4ab430f", "shasum": "" }, "require": { @@ -8371,7 +8384,7 @@ "recurring", "rrule" ], - "time": "2017-08-03 23:25:58" + "time": "2017-11-13 18:18:52" }, { "name": "softcommerce/omnipay-paytrace", @@ -8481,7 +8494,7 @@ }, { "name": "symfony/class-loader", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", @@ -8654,7 +8667,7 @@ }, { "name": "symfony/css-selector", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -8827,7 +8840,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.29", + "version": "v2.8.30", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -8887,7 +8900,7 @@ }, { "name": "symfony/filesystem", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -9120,7 +9133,7 @@ }, { "name": "symfony/options-resolver", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -9592,7 +9605,7 @@ }, { "name": "symfony/yaml", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -10220,16 +10233,16 @@ }, { "name": "wildbit/laravel-postmark-provider", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/wildbit/laravel-postmark-provider.git", - "reference": "b80815602f618abe24030ea6d3f117da49a72885" + "reference": "134f359" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wildbit/laravel-postmark-provider/zipball/b80815602f618abe24030ea6d3f117da49a72885", - "reference": "b80815602f618abe24030ea6d3f117da49a72885", + "url": "https://api.github.com/repos/wildbit/laravel-postmark-provider/zipball/134f359", + "reference": "134f359", "shasum": "" }, "require": { @@ -10247,7 +10260,7 @@ "MIT" ], "description": "An officially supported mail provider to send mail from Laravel through Postmark, see instructions for integrating it here: https://github.com/wildbit/laravel-postmark-provider/blob/master/README.md", - "time": "2016-02-10 14:15:58" + "time": "2017-01-19 19:52:38" }, { "name": "wildbit/swiftmailer-postmark", @@ -11007,32 +11020,32 @@ }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -11057,7 +11070,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "time": "2017-07-22 11:58:36" }, { "name": "facebook/webdriver", @@ -12227,7 +12240,7 @@ }, { "name": "symfony/browser-kit", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -12284,7 +12297,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v3.3.11", + "version": "v3.3.12", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -12412,7 +12425,8 @@ "omnipay/authorizenet": 20, "simshaun/recurr": 20, "webpatser/laravel-countries": 20, - "websight/l5-google-cloud-storage": 20 + "websight/l5-google-cloud-storage": 20, + "wildbit/laravel-postmark-provider": 20 }, "prefer-stable": true, "prefer-lowest": false, From 0f5f1b6d19e12ae35504b3dfff7dedf87400d002 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 11:25:41 +0200 Subject: [PATCH 2/9] working on password reset --- app/Http/Controllers/Auth/ResetPasswordController.php | 9 +++++++-- .../Controllers/ClientAuth/ResetPasswordController.php | 8 ++++++-- resources/views/auth/passwords/reset.blade.php | 4 ++-- resources/views/clientauth/passwords/reset.blade.php | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 4303afa40a04..b19a40cde50f 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -3,6 +3,8 @@ namespace App\Http\Controllers\Auth; use Event; +use Illuminate\Http\Request; +use App\Models\PasswordReset; use App\Events\UserLoggedIn; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ResetsPasswords; @@ -58,10 +60,13 @@ class ResetPasswordController extends Controller public function showResetForm(Request $request, $token = null) { $passwordReset = PasswordReset::whereToken($token)->first(); - $email = $passwordReset ? $passwordReset->email : ''; + + if (! $passwordReset) { + return redirect('login')->withMessage(trans('texts.invalid_code')); + } return view('auth.passwords.reset')->with( - ['token' => $token, 'email' => $email] + ['token' => $token, 'email' => $passwordReset->email] ); } } diff --git a/app/Http/Controllers/ClientAuth/ResetPasswordController.php b/app/Http/Controllers/ClientAuth/ResetPasswordController.php index a2b379a4191d..33b8f85e38ba 100644 --- a/app/Http/Controllers/ClientAuth/ResetPasswordController.php +++ b/app/Http/Controllers/ClientAuth/ResetPasswordController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\ClientAuth; use Password; use Config; +use App\Models\PasswordReset; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Http\Request; @@ -56,10 +57,13 @@ class ResetPasswordController extends Controller public function showResetForm(Request $request, $token = null) { $passwordReset = PasswordReset::whereToken($token)->first(); - $email = $passwordReset ? $passwordReset->email : ''; + + if (! $passwordReset) { + return redirect('login')->withMessage(trans('texts.invalid_code')); + } return view('clientauth.passwords.reset')->with( - ['token' => $token, 'email' => $email] + ['token' => $token, 'email' => $passwordReset->email] ); } diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php index 8d9df7cb83f2..46ff102143bf 100644 --- a/resources/views/auth/passwords/reset.blade.php +++ b/resources/views/auth/passwords/reset.blade.php @@ -40,7 +40,7 @@
- {!! Former::text('email')->placeholder(trans('texts.password'))->value($email)->raw() !!} + {!! Former::text('email')->placeholder(trans('texts.email'))->value($email)->readonly(true)->raw() !!} {!! Former::password('password')->placeholder(trans('texts.password'))->autocomplete('new-password')->raw() !!} {!! Former::password('password_confirmation')->placeholder(trans('texts.confirm_password'))->autocomplete('new-password')->raw() !!}
@@ -52,7 +52,7 @@ diff --git a/resources/views/clientauth/passwords/reset.blade.php b/resources/views/clientauth/passwords/reset.blade.php index 0c2a50005e8a..f0a0b05aaf39 100644 --- a/resources/views/clientauth/passwords/reset.blade.php +++ b/resources/views/clientauth/passwords/reset.blade.php @@ -40,7 +40,7 @@
- {!! Former::text('email')->placeholder(trans('texts.password'))->value($email)->raw() !!} + {!! Former::text('email')->placeholder(trans('texts.email'))->value($email)->readonly(true)->raw() !!} {!! Former::password('password')->placeholder(trans('texts.password'))->autocomplete('new-password')->raw() !!} {!! Former::password('password_confirmation')->placeholder(trans('texts.confirm_password'))->autocomplete('new-password')->raw() !!}
From 9010938f1b143f6083cd7caaa2134290512faa9e Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 11:54:23 +0200 Subject: [PATCH 3/9] Update min php to 7 --- README.md | 2 +- app/Http/Controllers/AppController.php | 6 +++ app/Http/Controllers/Auth/LoginController.php | 15 +++++++ .../ClientAuth/LoginController.php | 15 +++++++ app/Http/Middleware/StartupCheck.php | 4 +- composer.json | 4 +- composer.lock | 44 ++++++++++--------- docs/install.rst | 2 +- docs/update.rst | 8 +++- resources/views/setup.blade.php | 4 +- 10 files changed, 75 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0649db5d86bd..5b8a581004a2 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The self-host zip includes all third party libraries whereas downloading the cod ## Requirements -* PHP >= 5.5.9 +* PHP 7 * MySQL ## Recommended Providers diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index ba0dc4cea421..f7240d317def 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -272,6 +272,12 @@ class AppController extends BaseController try { set_time_limit(60 * 5); $this->checkInnoDB(); + + $cacheCompiled = base_path('bootstrap/cache/compiled.php'); + if (file_exists($cacheCompiled)) { unlink ($cacheCompiled); } + $cacheServices = base_path('bootstrap/cache/services.json'); + if (file_exists($cacheServices)) { unlink ($cacheServices); } + Artisan::call('clear-compiled'); Artisan::call('cache:clear'); Artisan::call('debugbar:clear'); diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index aff9502a31f9..e1840d57fb80 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -110,6 +110,21 @@ class LoginController extends Controller return $response; } + /** + * Get the failed login response instance. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ + protected function sendFailedLoginResponse(Request $request) + { + return redirect()->back() + ->withInput($request->only($this->username(), 'remember')) + ->withErrors([ + $this->username() => trans('texts.invalid_credentials'), + ]); + } + /** * Send the post-authentication response. * diff --git a/app/Http/Controllers/ClientAuth/LoginController.php b/app/Http/Controllers/ClientAuth/LoginController.php index befc498cfa57..becb723d593d 100644 --- a/app/Http/Controllers/ClientAuth/LoginController.php +++ b/app/Http/Controllers/ClientAuth/LoginController.php @@ -88,6 +88,21 @@ class LoginController extends Controller return $credentials; } + /** + * Get the failed login response instance. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ + protected function sendFailedLoginResponse(Request $request) + { + return redirect()->back() + ->withInput($request->only($this->username(), 'remember')) + ->withErrors([ + $this->username() => trans('texts.invalid_credentials'), + ]); + } + /** * Validate the user login request - don't require the email * diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index 5db83b89b0c0..9c82abad036c 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -55,8 +55,8 @@ class StartupCheck $file = storage_path() . '/version.txt'; $version = @file_get_contents($file); if ($version != NINJA_VERSION) { - if (version_compare(phpversion(), '5.5.9', '<')) { - dd('Please update PHP to >= 5.5.9'); + if (version_compare(phpversion(), '7.0.0', '<')) { + dd('Please update PHP to >= 7.0.0'); } $handle = fopen($file, 'w'); fwrite($handle, NINJA_VERSION); diff --git a/composer.json b/composer.json index d4b3dad2fac5..5946f95f3717 100644 --- a/composer.json +++ b/composer.json @@ -13,10 +13,10 @@ } ], "require": { - "php": ">=5.5.9", + "php": ">=7.0.0", "ext-gd": "*", "ext-gmp": "*", - "anahkiasen/former": "4.0.*@dev", + "anahkiasen/former": "4.*", "asgrim/ofxparser": "^1.1", "bacon/bacon-qr-code": "^1.0", "barracudanetworks/archivestream-php": "^1.0", diff --git a/composer.lock b/composer.lock index 82604aefd251..77dbcbae5c05 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "f2043c8ce923c8af3c99ba7c5bcf5d5e", - "content-hash": "dbc2b358c79e0abe0a58d2bd02310966", + "hash": "303fbf170906bca5a5354e7bbb92c94d", + "content-hash": "a1aaf83c53c00ce8dbb3ec13d70a42b3", "packages": [ { "name": "abdala/omnipay-pagseguro", @@ -169,16 +169,16 @@ }, { "name": "anahkiasen/former", - "version": "4.0.6", + "version": "4.1.1", "source": { "type": "git", "url": "https://github.com/formers/former.git", - "reference": "1ad9b332e8d8f5b23159aabbca89084276938382" + "reference": "56fd10035d7e7c34e2761e0458b92ca8890e6a9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/formers/former/zipball/1ad9b332e8d8f5b23159aabbca89084276938382", - "reference": "1ad9b332e8d8f5b23159aabbca89084276938382", + "url": "https://api.github.com/repos/formers/former/zipball/56fd10035d7e7c34e2761e0458b92ca8890e6a9a", + "reference": "56fd10035d7e7c34e2761e0458b92ca8890e6a9a", "shasum": "" }, "require": { @@ -197,6 +197,11 @@ "phpunit/phpunit": "~4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.5.x-dev" + } + }, "autoload": { "psr-4": { "Former\\": [ @@ -217,14 +222,14 @@ } ], "description": "A powerful form builder", - "homepage": "http://anahkiasen.github.com/former/", + "homepage": "http://formers.github.io/former/", "keywords": [ "bootstrap", "form", "foundation", "laravel" ], - "time": "2016-07-28 19:36:11" + "time": "2017-05-30 18:43:09" }, { "name": "anahkiasen/html-object", @@ -380,16 +385,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.38.1", + "version": "3.38.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "9f704274f4748d2039a16d45b3388ed8dde74e89" + "reference": "7b13de0b39f29833a977176ce58eed990e1487ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9f704274f4748d2039a16d45b3388ed8dde74e89", - "reference": "9f704274f4748d2039a16d45b3388ed8dde74e89", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7b13de0b39f29833a977176ce58eed990e1487ba", + "reference": "7b13de0b39f29833a977176ce58eed990e1487ba", "shasum": "" }, "require": { @@ -456,7 +461,7 @@ "s3", "sdk" ], - "time": "2017-11-09 19:15:59" + "time": "2017-11-14 23:47:41" }, { "name": "bacon/bacon-qr-code", @@ -2547,16 +2552,16 @@ }, { "name": "gocardless/gocardless-pro", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/gocardless/gocardless-pro-php.git", - "reference": "d2d4adb7cde53722858f1e7e5508697fefdb5390" + "reference": "16ac38c2531e08c15e54b4a82d44854349cbfcf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/gocardless/gocardless-pro-php/zipball/d2d4adb7cde53722858f1e7e5508697fefdb5390", - "reference": "d2d4adb7cde53722858f1e7e5508697fefdb5390", + "url": "https://api.github.com/repos/gocardless/gocardless-pro-php/zipball/16ac38c2531e08c15e54b4a82d44854349cbfcf6", + "reference": "16ac38c2531e08c15e54b4a82d44854349cbfcf6", "shasum": "" }, "require": { @@ -2595,7 +2600,7 @@ "direct debit", "gocardless" ], - "time": "2017-09-18 15:08:13" + "time": "2017-11-14 15:46:50" }, { "name": "google/apiclient", @@ -12412,7 +12417,6 @@ ], "minimum-stability": "dev", "stability-flags": { - "anahkiasen/former": 20, "chumper/datatable": 20, "codedge/laravel-selfupdater": 20, "collizo4sky/omnipay-wepay": 20, @@ -12431,7 +12435,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=5.5.9", + "php": ">=7.0.0", "ext-gd": "*", "ext-gmp": "*" }, diff --git a/docs/install.rst b/docs/install.rst index 6cd6b3e5487b..cf4955fded75 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -3,7 +3,7 @@ Install Thanks for taking the time to setup Invoice Ninja. -.. Note:: The applications requires PHP >= 5.5.9 and MySQL. +.. Note:: The applications requires PHP >= 7.0.0 and MySQL. Detailed Guides ^^^^^^^^^^^^^^^ diff --git a/docs/update.rst b/docs/update.rst index c8af0f37d3d7..ecc2797d53be 100644 --- a/docs/update.rst +++ b/docs/update.rst @@ -24,6 +24,11 @@ If the auto-update fails you can manually run the update with the following comm .. TIP:: You can see the detailed changes for each release on our `GitHub release notes `_. +Version 4.0 +""""""""""""" + +The minimum PHP version is now 7.0.0 + Version 3.2 """"""""""" @@ -36,7 +41,8 @@ Make sure the .env file includes ``APP_CIPHER=rijndael-128`` Version 2.5.1 """"""""""""" -Minimum PHP version is now 5.5.9 + +The minimum PHP version is now 5.5.9 Version 2.0 """"""""""" diff --git a/resources/views/setup.blade.php b/resources/views/setup.blade.php index b4a4969e78e6..b1520c233eca 100644 --- a/resources/views/setup.blade.php +++ b/resources/views/setup.blade.php @@ -26,8 +26,8 @@

Invoice Ninja Setup

- @if (version_compare(phpversion(), '5.5.9', '<')) -
Warning: The application requires PHP >= 5.5.9
+ @if (version_compare(phpversion(), '7.0.0', '<')) +
Warning: The application requires PHP >= 7.0.0
@endif @if (!function_exists('proc_open'))
Warning: proc_open must be enabled.
From 63f8a299d354dc83707329781dbc3612f2ec62d3 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 12:07:10 +0200 Subject: [PATCH 4/9] Update readme --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5b8a581004a2..0652ef89939f 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,11 @@ All Pro and Enterprise features from the hosted app are included in the open-sou The self-host zip includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies. ## Affiliates Programs -* Referral program (we pay you): $100 per sign up paid over 3 years - [Learn more](https://www.invoiceninja.com/referral-program/) -* White-label reseller (you pay us): $500 sign up fee and either 10% of revenue or $1 per user per month +* Referral program (we pay you) + * $100 per sign up paid over 3 years - [Learn more](https://www.invoiceninja.com/referral-program/) +* White-label reseller (you pay us) + * Hosted: $500 sign up fee and either 10% of revenue or $1/user/month + * Self-Hosted: Contact us for volume license pricing ### Installation Options * [Self-Host Zip](http://docs.invoiceninja.com/en/latest/install.html) From 817908e9cef54b0e5c23d92fd950bba38491edef Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Wed, 15 Nov 2017 12:11:34 +0100 Subject: [PATCH 5/9] Explicit version number for jsoneditor v5.10.1 --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index bc99469934a8..9bf2abf3a244 100644 --- a/bower.json +++ b/bower.json @@ -21,7 +21,7 @@ "handsontable": "*", "pdfmake": "0.1.31", "moment": "*", - "jsoneditor": "*", + "jsoneditor": "5.10.1", "moment-timezone": "~0.4.0", "quill": "~0.20.0", "datetimepicker": "~2.4.5", From 851064b75b5b6f57e3f90a24ee2700acd12d3263 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 13:16:59 +0200 Subject: [PATCH 6/9] Update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0652ef89939f..8249bb6e1ef7 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ All Pro and Enterprise features from the hosted app are included in the open-sou The self-host zip includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies. ## Affiliates Programs -* Referral program (we pay you) +* Referral Program (we pay you) * $100 per sign up paid over 3 years - [Learn more](https://www.invoiceninja.com/referral-program/) -* White-label reseller (you pay us) +* White-Label Reseller (you pay us) * Hosted: $500 sign up fee and either 10% of revenue or $1/user/month * Self-Hosted: Contact us for volume license pricing From 5ff37e70e0a42fea775e9ebc5066f3e2b8d1a921 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 13:39:24 +0200 Subject: [PATCH 7/9] Use new route files --- app/Http/Kernel.php | 54 ++-- app/Http/Middleware/EncryptCookies.php | 17 ++ app/Http/routes.php | 8 +- app/Providers/RouteServiceProvider.php | 55 +++- routes/api.php | 36 +++ routes/web.php | 368 +++++++++++++++++++++++++ 6 files changed, 506 insertions(+), 32 deletions(-) create mode 100644 app/Http/Middleware/EncryptCookies.php create mode 100644 routes/api.php create mode 100644 routes/web.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c19fce2245e5..5762a895c229 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -9,33 +9,53 @@ class Kernel extends HttpKernel /** * The application's global HTTP middleware stack. * + * These middleware are run during every request to your application. + * * @var array */ protected $middleware = [ - 'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode', - 'Illuminate\Cookie\Middleware\EncryptCookies', - 'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse', - 'Illuminate\Session\Middleware\StartSession', - 'Illuminate\View\Middleware\ShareErrorsFromSession', - 'App\Http\Middleware\VerifyCsrfToken', - 'App\Http\Middleware\DuplicateSubmissionCheck', - 'App\Http\Middleware\QueryLogging', - 'App\Http\Middleware\StartupCheck', + \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array + */ + protected $middlewareGroups = [ + 'web' => [ + \App\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \App\Http\Middleware\VerifyCsrfToken::class, + //\Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\DuplicateSubmissionCheck::class, + \App\Http\Middleware\QueryLogging::class, + \App\Http\Middleware\StartupCheck::class, + ], + 'api' => [ + 'throttle:60,1', + 'bindings', + ], ]; /** * The application's route middleware. * + * These middleware may be assigned to groups or used individually. + * * @var array */ protected $routeMiddleware = [ - 'lookup' => 'App\Http\Middleware\DatabaseLookup', - 'auth' => 'App\Http\Middleware\Authenticate', - 'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth', - 'permissions.required' => 'App\Http\Middleware\PermissionsRequired', - 'guest' => 'App\Http\Middleware\RedirectIfAuthenticated', - 'api' => 'App\Http\Middleware\ApiCheck', - 'cors' => '\Barryvdh\Cors\HandleCors', - 'throttle' => 'Illuminate\Routing\Middleware\ThrottleRequests', + 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'lookup' => \App\Http\Middleware\DatabaseLookup::class, + 'permissions.required' => \App\Http\Middleware\PermissionsRequired::class, + 'api' => \App\Http\Middleware\ApiCheck::class, ]; } diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 000000000000..3aa15f8dd91d --- /dev/null +++ b/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,17 @@ +mapApiRoutes(); + + $this->mapWebRoutes(); + + // + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. * * @return void */ - public function map(Router $router) + protected function mapWebRoutes() { - $router->group(['namespace' => $this->namespace], function ($router) { - require app_path('Http/routes.php'); + Route::group([ + 'middleware' => 'web', + 'namespace' => $this->namespace, + ], function ($router) { + require base_path('routes/web.php'); + }); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() + { + Route::group([ + 'middleware' => 'api', + 'namespace' => $this->namespace, + 'prefix' => 'api', + ], function ($router) { + require base_path('routes/api.php'); }); } } diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 000000000000..492eb892562b --- /dev/null +++ b/routes/api.php @@ -0,0 +1,36 @@ + ['lookup:api', 'api'], 'prefix' => 'api/v1'], function () { + Route::get('ping', 'AccountApiController@ping'); + Route::post('login', 'AccountApiController@login'); + Route::post('oauth_login', 'AccountApiController@oauthLogin'); + Route::post('register', 'AccountApiController@register'); + Route::get('static', 'AccountApiController@getStaticData'); + Route::get('accounts', 'AccountApiController@show'); + Route::put('accounts', 'AccountApiController@update'); + Route::resource('clients', 'ClientApiController'); + Route::resource('contacts', 'ContactApiController'); + Route::get('quotes', 'QuoteApiController@index'); + Route::get('download/{invoice_id}', 'InvoiceApiController@download'); + Route::resource('invoices', 'InvoiceApiController'); + Route::resource('payments', 'PaymentApiController'); + Route::resource('tasks', 'TaskApiController'); + Route::resource('credits', 'CreditApiController'); + Route::post('hooks', 'IntegrationController@subscribe'); + Route::post('email_invoice', 'InvoiceApiController@emailInvoice'); + Route::get('user_accounts', 'AccountApiController@getUserAccounts'); + Route::resource('products', 'ProductApiController'); + Route::resource('projects', 'ProjectApiController'); + Route::resource('tax_rates', 'TaxRateApiController'); + Route::resource('users', 'UserApiController'); + Route::resource('expenses', 'ExpenseApiController'); + Route::post('add_token', 'AccountApiController@addDeviceToken'); + Route::post('remove_token', 'AccountApiController@removeDeviceToken'); + Route::post('update_notifications', 'AccountApiController@updatePushNotifications'); + Route::get('dashboard', 'DashboardApiController@index'); + Route::resource('documents', 'DocumentAPIController'); + Route::resource('vendors', 'VendorApiController'); + Route::resource('expense_categories', 'ExpenseCategoryApiController'); + Route::post('ios_subscription_status', 'AccountApiController@iosSubscriptionStatus'); +}); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 000000000000..ffc39e757199 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,368 @@ + ['lookup:contact', 'auth:client']], function () { + Route::get('view/{invitation_key}', 'ClientPortalController@view'); + Route::get('download/{invitation_key}', 'ClientPortalController@download'); + Route::put('sign/{invitation_key}', 'ClientPortalController@sign'); + Route::get('view', 'HomeController@viewLogo'); + Route::get('approve/{invitation_key}', 'QuoteController@approve'); + Route::get('payment/{invitation_key}/{gateway_type?}/{source_id?}', 'OnlinePaymentController@showPayment'); + Route::post('payment/{invitation_key}', 'OnlinePaymentController@doPayment'); + Route::get('complete_source/{invitation_key}/{gateway_type}', 'OnlinePaymentController@completeSource'); + Route::match(['GET', 'POST'], 'complete/{invitation_key?}/{gateway_type?}', 'OnlinePaymentController@offsitePayment'); + Route::get('bank/{routing_number}', 'OnlinePaymentController@getBankInfo'); + Route::get('client/payment_methods', 'ClientPortalController@paymentMethods'); + Route::post('client/payment_methods/verify', 'ClientPortalController@verifyPaymentMethod'); + Route::post('client/payment_methods/default', 'ClientPortalController@setDefaultPaymentMethod'); + Route::post('client/payment_methods/{source_id}/remove', 'ClientPortalController@removePaymentMethod'); + Route::get('client/quotes', 'ClientPortalController@quoteIndex'); + Route::get('client/credits', 'ClientPortalController@creditIndex'); + Route::get('client/invoices', 'ClientPortalController@invoiceIndex'); + Route::get('client/invoices/recurring', 'ClientPortalController@recurringInvoiceIndex'); + Route::post('client/invoices/auto_bill', 'ClientPortalController@setAutoBill'); + Route::get('client/documents', 'ClientPortalController@documentIndex'); + Route::get('client/payments', 'ClientPortalController@paymentIndex'); + Route::get('client/dashboard/{contact_key?}', 'ClientPortalController@dashboard'); + Route::get('client/documents/js/{documents}/{filename}', 'ClientPortalController@getDocumentVFSJS'); + Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'ClientPortalController@getDocument'); + Route::get('client/documents/{invitation_key}/{filename?}', 'ClientPortalController@getInvoiceDocumentsZip'); + + Route::get('api/client.quotes', ['as' => 'api.client.quotes', 'uses' => 'ClientPortalController@quoteDatatable']); + Route::get('api/client.credits', ['as' => 'api.client.credits', 'uses' => 'ClientPortalController@creditDatatable']); + Route::get('api/client.invoices', ['as' => 'api.client.invoices', 'uses' => 'ClientPortalController@invoiceDatatable']); + Route::get('api/client.recurring_invoices', ['as' => 'api.client.recurring_invoices', 'uses' => 'ClientPortalController@recurringInvoiceDatatable']); + Route::get('api/client.documents', ['as' => 'api.client.documents', 'uses' => 'ClientPortalController@documentDatatable']); + Route::get('api/client.payments', ['as' => 'api.client.payments', 'uses' => 'ClientPortalController@paymentDatatable']); + Route::get('api/client.activity', ['as' => 'api.client.activity', 'uses' => 'ClientPortalController@activityDatatable']); +}); + +Route::group(['middleware' => 'lookup:license'], function () { + Route::get('license', 'NinjaController@show_license_payment'); + Route::post('license', 'NinjaController@do_license_payment'); + Route::get('claim_license', 'NinjaController@claim_license'); + if (Utils::isNinja()) { + Route::post('/signup/register', 'AccountController@doRegister'); + Route::get('/news_feed/{user_type}/{version}/', 'HomeController@newsFeed'); + } +}); + +Route::group(['middleware' => 'lookup:postmark'], function () { + Route::post('/hook/email_bounced', 'AppController@emailBounced'); + Route::post('/hook/email_opened', 'AppController@emailOpened'); +}); + +Route::group(['middleware' => 'lookup:account'], function () { + Route::post('/payment_hook/{account_key}/{gateway_id}', 'OnlinePaymentController@handlePaymentWebhook'); + Route::match(['GET', 'POST', 'OPTIONS'], '/buy_now/{gateway_type?}', 'OnlinePaymentController@handleBuyNow'); + Route::get('validate_two_factor/{account_key}', 'Auth\LoginController@getValidateToken'); + Route::post('validate_two_factor/{account_key}', ['middleware' => 'throttle:5', 'uses' => 'Auth\LoginController@postValidateToken']); +}); + +//Route::post('/hook/bot/{platform?}', 'BotController@handleMessage'); + +// Laravel auth routes +Route::get('/login', ['as' => 'login', 'uses' => 'Auth\LoginController@getLoginWrapper']); +Route::get('/logout', ['as' => 'logout', 'uses' => 'Auth\LoginController@getLogoutWrapper']); +Route::get('/recover_password', ['as' => 'forgot', 'uses' => 'Auth\ForgotPasswordController@showLinkRequestForm']); +Route::get('/password/reset/{token}', ['as' => 'forgot', 'uses' => 'Auth\ResetPasswordController@showResetForm']); +Route::get('/auth/{provider}', 'Auth\AuthController@oauthLogin'); + +Route::group(['middleware' => ['lookup:user']], function () { + Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm'); + Route::post('/login', ['as' => 'login', 'uses' => 'Auth\LoginController@postLoginWrapper']); + Route::post('/recover_password', ['as' => 'forgot', 'uses' => 'Auth\ForgotPasswordController@sendResetLinkEmail']); + Route::post('/password/reset', ['as' => 'forgot', 'uses' => 'Auth\ResetPasswordController@reset']); +}); + +// Client auth +Route::get('/client/login', ['as' => 'login', 'uses' => 'ClientAuth\LoginController@showLoginForm']); +Route::get('/client/logout', ['as' => 'logout', 'uses' => 'ClientAuth\LoginController@getLogout']); +Route::get('/client/session_expired', ['as' => 'logout', 'uses' => 'ClientAuth\LoginController@getSessionExpired']); +Route::get('/client/recover_password', ['as' => 'forgot', 'uses' => 'ClientAuth\ForgotPasswordController@showLinkRequestForm']); +Route::get('/client/password/reset/{token}', ['as' => 'forgot', 'uses' => 'ClientAuth\ResetPasswordController@showResetForm']); + +Route::group(['middleware' => ['lookup:contact']], function () { + Route::post('/client/login', ['as' => 'login', 'uses' => 'ClientAuth\LoginController@login']); + Route::post('/client/recover_password', ['as' => 'forgot', 'uses' => 'ClientAuth\ForgotPasswordController@sendResetLinkEmail']); + Route::post('/client/password/reset', ['as' => 'forgot', 'uses' => 'ClientAuth\ResetPasswordController@reset']); +}); + +if (Utils::isReseller()) { + Route::post('/reseller_stats', 'AppController@stats'); +} + +if (Utils::isTravis()) { + Route::get('/check_data', 'AppController@checkData'); +} + +Route::group(['middleware' => ['lookup:user', 'auth:user']], function () { + Route::get('logged_in', 'HomeController@loggedIn'); + Route::get('dashboard', 'DashboardController@index'); + Route::get('dashboard_chart_data/{group_by}/{start_date}/{end_date}/{currency_id}/{include_expenses}', 'DashboardController@chartData'); + Route::get('set_entity_filter/{entity_type}/{filter?}', 'AccountController@setEntityFilter'); + Route::get('hide_message', 'HomeController@hideMessage'); + Route::get('force_inline_pdf', 'UserController@forcePDFJS'); + Route::get('account/get_search_data', ['as' => 'get_search_data', 'uses' => 'AccountController@getSearchData']); + Route::get('check_invoice_number/{invoice_id?}', 'InvoiceController@checkInvoiceNumber'); + Route::post('save_sidebar_state', 'UserController@saveSidebarState'); + Route::post('contact_us', 'HomeController@contactUs'); + Route::post('handle_command', 'BotController@handleCommand'); + + Route::post('signup/validate', 'AccountController@checkEmail'); + Route::post('signup/submit', 'AccountController@submitSignup'); + Route::get('auth_unlink', 'Auth\AuthController@oauthUnlink'); + + Route::get('settings/user_details', 'AccountController@showUserDetails'); + Route::post('settings/user_details', 'AccountController@saveUserDetails'); + Route::post('settings/payment_gateway_limits', 'AccountGatewayController@savePaymentGatewayLimits'); + Route::post('users/change_password', 'UserController@changePassword'); + Route::get('settings/enable_two_factor', 'TwoFactorController@setupTwoFactor'); + Route::post('settings/enable_two_factor', 'TwoFactorController@enableTwoFactor'); + + Route::resource('clients', 'ClientController'); + Route::get('api/clients', 'ClientController@getDatatable'); + Route::get('api/activities/{client_id?}', 'ActivityController@getDatatable'); + Route::post('clients/bulk', 'ClientController@bulk'); + Route::get('clients/statement/{client_id}/{status_id?}/{start_date?}/{end_date?}', 'ClientController@statement'); + + Route::get('time_tracker', 'TimeTrackerController@index'); + Route::resource('tasks', 'TaskController'); + Route::get('api/tasks/{client_id?}', 'TaskController@getDatatable'); + Route::get('tasks/create/{client_id?}/{project_id?}', 'TaskController@create'); + Route::post('tasks/bulk', 'TaskController@bulk'); + Route::get('projects', 'ProjectController@index'); + Route::get('api/projects', 'ProjectController@getDatatable'); + Route::get('projects/create/{client_id?}', 'ProjectController@create'); + Route::post('projects', 'ProjectController@store'); + Route::put('projects/{projects}', 'ProjectController@update'); + Route::get('projects/{projects}/edit', 'ProjectController@edit'); + Route::get('projects/{projects}', 'ProjectController@edit'); + Route::post('projects/bulk', 'ProjectController@bulk'); + + Route::get('api/recurring_invoices/{client_id?}', 'InvoiceController@getRecurringDatatable'); + + Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory'); + Route::get('quotes/quote_history/{invoice_id}', 'InvoiceController@invoiceHistory'); + + Route::resource('invoices', 'InvoiceController'); + Route::get('api/invoices/{client_id?}', 'InvoiceController@getDatatable'); + Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); + Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring'); + Route::get('recurring_invoices', 'RecurringInvoiceController@index'); + Route::get('recurring_invoices/{invoices}/edit', 'InvoiceController@edit'); + Route::get('recurring_invoices/{invoices}', 'InvoiceController@edit'); + Route::get('invoices/{invoices}/clone', 'InvoiceController@cloneInvoice'); + Route::post('invoices/bulk', 'InvoiceController@bulk'); + Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); + + Route::get('recurring_expenses', 'RecurringExpenseController@index'); + Route::get('api/recurring_expenses', 'RecurringExpenseController@getDatatable'); + Route::get('recurring_expenses/create/{vendor_id?}/{client_id?}/{category_id?}', 'RecurringExpenseController@create'); + Route::post('recurring_expenses', 'RecurringExpenseController@store'); + Route::put('recurring_expenses/{recurring_expenses}', 'RecurringExpenseController@update'); + Route::get('recurring_expenses/{recurring_expenses}/edit', 'RecurringExpenseController@edit'); + Route::get('recurring_expenses/{recurring_expenses}', 'RecurringExpenseController@edit'); + Route::post('recurring_expenses/bulk', 'RecurringExpenseController@bulk'); + + Route::get('documents/{documents}/{filename?}', 'DocumentController@get'); + Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS'); + Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview'); + Route::post('documents', 'DocumentController@postUpload'); + Route::delete('documents/{documents}', 'DocumentController@delete'); + + Route::get('quotes/create/{client_id?}', 'QuoteController@create'); + Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneQuote'); + Route::get('quotes/{invoices}/edit', 'InvoiceController@edit'); + Route::put('quotes/{invoices}', 'InvoiceController@update'); + Route::get('quotes/{invoices}', 'InvoiceController@edit'); + Route::post('quotes', 'InvoiceController@store'); + Route::get('quotes', 'QuoteController@index'); + Route::get('api/quotes/{client_id?}', 'QuoteController@getDatatable'); + Route::post('quotes/bulk', 'QuoteController@bulk'); + + Route::resource('payments', 'PaymentController'); + Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create'); + Route::get('api/payments/{client_id?}', 'PaymentController@getDatatable'); + Route::post('payments/bulk', 'PaymentController@bulk'); + + Route::resource('credits', 'CreditController'); + Route::get('credits/create/{client_id?}/{invoice_id?}', 'CreditController@create'); + Route::get('api/credits/{client_id?}', 'CreditController@getDatatable'); + Route::post('credits/bulk', 'CreditController@bulk'); + + Route::get('api/products', 'ProductController@getDatatable'); + Route::resource('products', 'ProductController'); + Route::post('products/bulk', 'ProductController@bulk'); + + Route::get('/resend_confirmation', 'AccountController@resendConfirmation'); + Route::post('/update_setup', 'AppController@updateSetup'); + + // vendor + Route::resource('vendors', 'VendorController'); + Route::get('api/vendors', 'VendorController@getDatatable'); + Route::post('vendors/bulk', 'VendorController@bulk'); + + // Expense + Route::resource('expenses', 'ExpenseController'); + Route::get('expenses/create/{vendor_id?}/{client_id?}/{category_id?}', 'ExpenseController@create'); + Route::get('expenses/{expenses}/clone', 'ExpenseController@cloneExpense'); + Route::get('api/expenses', 'ExpenseController@getDatatable'); + Route::get('api/expenses/{id}', 'ExpenseController@getDatatableVendor'); + Route::post('expenses/bulk', 'ExpenseController@bulk'); + Route::get('expense_categories', 'ExpenseCategoryController@index'); + Route::get('api/expense_categories', 'ExpenseCategoryController@getDatatable'); + Route::get('expense_categories/create', 'ExpenseCategoryController@create'); + Route::post('expense_categories', 'ExpenseCategoryController@store'); + Route::put('expense_categories/{expense_categories}', 'ExpenseCategoryController@update'); + Route::get('expense_categories/{expense_categories}/edit', 'ExpenseCategoryController@edit'); + Route::post('expense_categories/bulk', 'ExpenseCategoryController@bulk'); + + // BlueVine + Route::post('bluevine/signup', 'BlueVineController@signup'); + Route::get('bluevine/hide_message', 'BlueVineController@hideMessage'); + Route::get('bluevine/completed', 'BlueVineController@handleCompleted'); + + Route::get('white_label/hide_message', 'NinjaController@hideWhiteLabelMessage'); + Route::get('white_label/purchase', 'NinjaController@purchaseWhiteLabel'); + + Route::get('reports', 'ReportController@showReports'); + Route::post('reports', 'ReportController@showReports'); + Route::get('calendar', 'CalendarController@showCalendar'); + Route::get('calendar_events', 'CalendarController@loadEvents'); +}); + +Route::group([ + 'middleware' => ['lookup:user', 'auth:user', 'permissions.required'], + 'permissions' => 'admin', +], function () { + Route::get('api/users', 'UserController@getDatatable'); + Route::resource('users', 'UserController'); + Route::post('users/bulk', 'UserController@bulk'); + Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); + Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); + Route::get('/account/{account_key}', 'UserController@viewAccountByKey'); + Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount'); + Route::get('/manage_companies', 'UserController@manageCompanies'); + Route::get('/errors', 'AppController@errors'); + + Route::get('api/tokens', 'TokenController@getDatatable'); + Route::resource('tokens', 'TokenController'); + Route::post('tokens/bulk', 'TokenController@bulk'); + + Route::get('api/tax_rates', 'TaxRateController@getDatatable'); + Route::resource('tax_rates', 'TaxRateController'); + Route::post('tax_rates/bulk', 'TaxRateController@bulk'); + + Route::get('settings/email_preview', 'AccountController@previewEmail'); + Route::post('settings/client_portal', 'AccountController@saveClientPortalSettings'); + Route::post('settings/email_settings', 'AccountController@saveEmailSettings'); + Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy'); + Route::get('settings/data_visualizations', 'ReportController@d3'); + + Route::post('settings/change_plan', 'AccountController@changePlan'); + Route::post('settings/cancel_account', 'AccountController@cancelAccount'); + Route::post('settings/purge_data', 'AccountController@purgeData'); + Route::post('settings/company_details', 'AccountController@updateDetails'); + Route::post('settings/{section?}', 'AccountController@doSection'); + + Route::post('user/setTheme', 'UserController@setTheme'); + Route::post('remove_logo', 'AccountController@removeLogo'); + + Route::post('/export', 'ExportController@doExport'); + Route::post('/import', 'ImportController@doImport'); + Route::get('/cancel_import', 'ImportController@cancelImport'); + Route::post('/import_csv', 'ImportController@doImportCSV'); + + Route::get('gateways/create/{show_wepay?}', 'AccountGatewayController@create'); + Route::resource('gateways', 'AccountGatewayController'); + Route::get('gateways/{public_id}/resend_confirmation', 'AccountGatewayController@resendConfirmation'); + Route::get('api/gateways', 'AccountGatewayController@getDatatable'); + Route::post('account_gateways/bulk', 'AccountGatewayController@bulk'); + + Route::get('payment_terms', 'PaymentTermController@index'); + Route::get('api/payment_terms', 'PaymentTermController@getDatatable'); + Route::get('payment_terms/create', 'PaymentTermController@create'); + Route::post('payment_terms', 'PaymentTermController@store'); + Route::put('payment_terms/{payment_terms}', 'PaymentTermController@update'); + Route::get('payment_terms/{payment_terms}/edit', 'PaymentTermController@edit'); + Route::post('payment_terms/bulk', 'PaymentTermController@bulk'); + + Route::get('bank_accounts/import_ofx', 'BankAccountController@showImportOFX'); + Route::post('bank_accounts/import_ofx', 'BankAccountController@doImportOFX'); + Route::resource('bank_accounts', 'BankAccountController'); + Route::get('api/bank_accounts', 'BankAccountController@getDatatable'); + Route::post('bank_accounts/bulk', 'BankAccountController@bulk'); + Route::post('bank_accounts/validate', 'BankAccountController@validateAccount'); + Route::post('bank_accounts/import_expenses/{bank_id}', 'BankAccountController@importExpenses'); + Route::get('self-update', 'SelfUpdateController@index'); + Route::post('self-update', 'SelfUpdateController@update'); + Route::get('self-update/download', 'SelfUpdateController@download'); +}); + +Route::group(['middleware' => ['lookup:user', 'auth:user']], function () { + Route::get('settings/{section?}', 'AccountController@showSection'); +}); + +// Redirects for legacy links +Route::get('/rocksteady', function () { + return Redirect::to(NINJA_WEB_URL, 301); +}); +Route::get('/about', function () { + return Redirect::to(NINJA_WEB_URL, 301); +}); +Route::get('/contact', function () { + return Redirect::to(NINJA_WEB_URL.'/contact', 301); +}); +Route::get('/plans', function () { + return Redirect::to(NINJA_WEB_URL.'/pricing', 301); +}); +Route::get('/faq', function () { + return Redirect::to(NINJA_WEB_URL.'/how-it-works', 301); +}); +Route::get('/features', function () { + return Redirect::to(NINJA_WEB_URL.'/features', 301); +}); +Route::get('/testimonials', function () { + return Redirect::to(NINJA_WEB_URL, 301); +}); +Route::get('/compare-online-invoicing{sites?}', function () { + return Redirect::to(NINJA_WEB_URL, 301); +}); +Route::get('/forgot', function () { + return Redirect::to(NINJA_APP_URL.'/recover_password', 301); +}); +Route::get('/feed', function () { + return Redirect::to(NINJA_WEB_URL.'/feed', 301); +}); +Route::get('/comments/feed', function () { + return Redirect::to(NINJA_WEB_URL.'/comments/feed', 301); +}); +Route::get('/terms', function () { + return Redirect::to(NINJA_WEB_URL.'/terms', 301); +}); + +/* +if (Utils::isNinjaDev()) +{ + //ini_set('memory_limit','1024M'); + //set_time_limit(0); + Auth::loginUsingId(1); +} +*/ + +// Include static app constants +require_once app_path() . '/Constants.php'; From 3439aa9a06e00094067461667bde6c8b23bb52a5 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 14:51:02 +0200 Subject: [PATCH 8/9] Check subdomain is uniuqe across dbs --- app/Console/Commands/InitLookup.php | 34 +++++++++++++++++-- app/Http/Controllers/AccountController.php | 13 +++++-- app/Models/LookupAccount.php | 22 ++++++++++++ ..._11_15_114422_add_subdomain_to_lookups.php | 32 +++++++++++++++++ resources/lang/en/texts.php | 1 + 5 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php diff --git a/app/Console/Commands/InitLookup.php b/app/Console/Commands/InitLookup.php index aa52ec4f4a35..f37de078be2f 100644 --- a/app/Console/Commands/InitLookup.php +++ b/app/Console/Commands/InitLookup.php @@ -21,7 +21,7 @@ class InitLookup extends Command * * @var string */ - protected $signature = 'ninja:init-lookup {--truncate=} {--validate=} {--update=} {--company_id=} {--page_size=100} {--database=db-ninja-1}'; + protected $signature = 'ninja:init-lookup {--truncate=} {--subdomain} {--validate=} {--update=} {--company_id=} {--page_size=100} {--database=db-ninja-1}'; /** * The console command description. @@ -57,9 +57,12 @@ class InitLookup extends Command $database = $this->option('database'); $dbServer = DbServer::whereName($database)->first(); - if ($this->option('truncate')) { + if ($this->option('subdomain')) { + $this->logMessage('Updating subdomains...'); + $this->popuplateSubdomains(); + } else if ($this->option('truncate')) { + $this->logMessage('Truncating data...'); $this->truncateTables(); - $this->logMessage('Truncated'); } else { config(['database.default' => $this->option('database')]); @@ -87,6 +90,30 @@ class InitLookup extends Command } } + private function popuplateSubdomains() + { + $data = []; + + config(['database.default' => $this->option('database')]); + + $accounts = DB::table('accounts') + ->orderBy('id') + ->where('subdomain', '!=', '') + ->get(['account_key', 'subdomain']); + foreach ($accounts as $account) { + $data[$account->account_key] = $account->subdomain; + } + + config(['database.default' => DB_NINJA_LOOKUP]); + + $validate = $this->option('validate'); + $update = $this->option('update'); + + foreach ($data as $accountKey => $subdomain) { + LookupAccount::whereAccountKey($accountKey)->update(['subdomain' => $subdomain]); + } + } + private function initCompanies($dbServerId, $offset = 0) { $data = []; @@ -340,6 +367,7 @@ class InitLookup extends Command protected function getOptions() { return [ + ['subdomain', null, InputOption::VALUE_OPTIONAL, 'Subdomain', null], ['truncate', null, InputOption::VALUE_OPTIONAL, 'Truncate', null], ['company_id', null, InputOption::VALUE_OPTIONAL, 'Company Id', null], ['page_size', null, InputOption::VALUE_OPTIONAL, 'Page Size', null], diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index f41b9e4cadf3..64b553e58732 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -769,11 +769,20 @@ class AccountController extends BaseController */ public function saveClientPortalSettings(SaveClientPortalSettings $request) { - $account = $request->user()->account; - if($account->subdomain !== $request->subdomain) + // check subdomain is unique in the lookup tables + if (request()->subdomain) { + if (! \App\Models\LookupAccount::validateField('subdomain', request()->subdomain, $account)) { + return Redirect::to('settings/' . ACCOUNT_CLIENT_PORTAL) + ->withError(trans('texts.subdomain_taken')) + ->withInput(); + } + } + + if ($account->subdomain !== $request->subdomain) { event(new SubdomainWasUpdated($account)); + } $account->fill($request->all()); $account->client_view_css = $request->client_view_css; diff --git a/app/Models/LookupAccount.php b/app/Models/LookupAccount.php index 7d5cd8e8c12d..605769de00c6 100644 --- a/app/Models/LookupAccount.php +++ b/app/Models/LookupAccount.php @@ -55,4 +55,26 @@ class LookupAccount extends LookupModel return $this->lookupCompany->dbServer->name; } + public static function validateField($field, $value, $account = false) + { + if (! env('MULTI_DB_ENABLED')) { + return true; + } + + $current = config('database.default'); + + config(['database.default' => DB_NINJA_LOOKUP]); + + $lookupAccount = LookupAccount::where($field, '=', $value)->first(); + + if ($account) { + $isValid = ! $lookupAccount || ($lookupAccount->account_key == $account->account_key); + } else { + $isValid = ! $lookupAccount; + } + + config(['database.default' => $current]); + + return $isValid; + } } diff --git a/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php b/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php new file mode 100644 index 000000000000..ce88a26b08e4 --- /dev/null +++ b/database/migrations/2017_11_15_114422_add_subdomain_to_lookups.php @@ -0,0 +1,32 @@ +string('subdomain')->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('lookup_accounts', function ($table) { + $table->dropColumn('subdomain'); + }); + } +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 315cbc56786c..cda9b7ff7225 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -2522,6 +2522,7 @@ $LANG = array( 'set_self_hoat_url' => 'Self-Host URL', 'local_storage_required' => 'Error: local storage is not available.', 'your_password_reset_link' => 'Your Password Reset Link', + 'subdomain_taken' => 'The subdomain is already in use', ); From 82286a23394fc64f6d4740995526c4329d6ccee7 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 15 Nov 2017 14:59:20 +0200 Subject: [PATCH 9/9] Check subdomain is uniuqe across dbs --- .../Requests/SaveClientPortalSettings.php | 2 +- app/Models/Account.php | 7 +++++++ app/Models/LookupAccount.php | 20 ++++++++++++++++++- .../views/accounts/client_portal.blade.php | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/Http/Requests/SaveClientPortalSettings.php b/app/Http/Requests/SaveClientPortalSettings.php index cd81bf7d31dd..00a239c590b7 100644 --- a/app/Http/Requests/SaveClientPortalSettings.php +++ b/app/Http/Requests/SaveClientPortalSettings.php @@ -44,7 +44,7 @@ class SaveClientPortalSettings extends Request if (Utils::isNinja()) { if ($this->custom_link == 'subdomain') { $subdomain = substr(strtolower($input['subdomain']), 0, MAX_SUBDOMAIN_LENGTH); - $input['subdomain'] = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $subdomain); + $input['subdomain'] = preg_replace('/[^a-zA-Z0-9\-\.]/', '', $subdomain); $input['iframe_url'] = null; } else { $iframeURL = substr(strtolower($input['iframe_url']), 0, MAX_IFRAME_URL_LENGTH); diff --git a/app/Models/Account.php b/app/Models/Account.php index 61643b68df36..118dedc22880 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -1711,6 +1711,13 @@ Account::creating(function ($account) LookupAccount::createAccount($account->account_key, $account->company_id); }); +Account::updating(function ($account) { + $dirty = $account->getDirty(); + if (array_key_exists('subdomain', $dirty)) { + LookupAccount::updateAccount($account->account_key, $account); + } +}); + Account::updated(function ($account) { // prevent firing event if the invoice/quote counter was changed // TODO: remove once counters are moved to separate table diff --git a/app/Models/LookupAccount.php b/app/Models/LookupAccount.php index 605769de00c6..65c45360786c 100644 --- a/app/Models/LookupAccount.php +++ b/app/Models/LookupAccount.php @@ -55,6 +55,24 @@ class LookupAccount extends LookupModel return $this->lookupCompany->dbServer->name; } + public static function updateAccount($accountKey, $account) + { + if (! env('MULTI_DB_ENABLED')) { + return; + } + + $current = config('database.default'); + config(['database.default' => DB_NINJA_LOOKUP]); + + $lookupAccount = LookupAccount::whereAccountKey($accountKey) + ->firstOrFail(); + + $lookupAccount->subdomain = $account->subdomain; + $lookupAccount->save(); + + config(['database.default' => $current]); + } + public static function validateField($field, $value, $account = false) { if (! env('MULTI_DB_ENABLED')) { @@ -62,7 +80,7 @@ class LookupAccount extends LookupModel } $current = config('database.default'); - + config(['database.default' => DB_NINJA_LOOKUP]); $lookupAccount = LookupAccount::where($field, '=', $value)->first(); diff --git a/resources/views/accounts/client_portal.blade.php b/resources/views/accounts/client_portal.blade.php index 26a1e5d86803..ce767f19a826 100644 --- a/resources/views/accounts/client_portal.blade.php +++ b/resources/views/accounts/client_portal.blade.php @@ -467,7 +467,7 @@ iframe.src = '{{ rtrim(SITE_URL ,'/') }}/view/' var input = $('#subdomain'); var val = input.val(); if (!val) return; - val = val.replace(/[^a-zA-Z0-9_\-]/g, '').toLowerCase().substring(0, {{ MAX_SUBDOMAIN_LENGTH }}); + val = val.replace(/[^a-zA-Z0-9\-]/g, '').toLowerCase().substring(0, {{ MAX_SUBDOMAIN_LENGTH }}); input.val(val); }