diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 0c088c893802..ddf38a4a3065 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -11,7 +11,11 @@ class Kernel extends ConsoleKernel { * @var array */ protected $commands = [ - 'App\Console\Commands\Inspire', + 'App\Console\Commands\SendRecurringInvoices', + 'App\Console\Commands\CreateRandomData', + 'App\Console\Commands\ResetData', + 'App\Console\Commands\ImportTimesheetData', + 'App\Console\Commands\CheckData', ]; /** @@ -22,8 +26,8 @@ class Kernel extends ConsoleKernel { */ protected function schedule(Schedule $schedule) { - $schedule->command('inspire') - ->hourly(); + // $schedule->command('inspire') + // ->hourly(); } } diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php deleted file mode 100644 index 4ad5c58a1a24..000000000000 --- a/app/Http/Controllers/Auth/AuthController.php +++ /dev/null @@ -1,38 +0,0 @@ -auth = $auth; - $this->registrar = $registrar; - - $this->middleware('guest', ['except' => 'getLogout']); - } - -} diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php deleted file mode 100644 index 3106193591ce..000000000000 --- a/app/Http/Controllers/Auth/PasswordController.php +++ /dev/null @@ -1,38 +0,0 @@ -auth = $auth; - $this->passwords = $passwords; - - $this->middleware('guest'); - } - -} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php deleted file mode 100644 index 27b3f45272fd..000000000000 --- a/app/Http/Controllers/Controller.php +++ /dev/null @@ -1,11 +0,0 @@ -middleware('auth'); - } + public function __construct(Mailer $mailer) + { + parent::__construct(); - /** - * Show the application dashboard to the user. - * - * @return Response - */ - public function index() - { - return view('home'); - } + $this->mailer = $mailer; + } + public function showIndex() + { + if (!Utils::isDatabaseSetup()) { + return Redirect::to('/setup'); + } elseif (Account::count() == 0) { + return Redirect::to('/invoice_now'); + } else { + return Redirect::to('/login'); + } + } + + public function showTerms() + { + return View::make('public.terms', ['hideHeader' => true]); + } + + public function invoiceNow() + { + if (Auth::check()) { + return Redirect::to('invoices/create')->with('sign_up', Input::get('sign_up')); + } else { + return View::make('public.header', ['invoiceNow' => true]); + } + } + + public function newsFeed($userType, $version) + { + $response = Utils::getNewsFeedResponse($userType); + + return Response::json($response); + } + + public function hideMessage() + { + if (Auth::check() && Session::has('news_feed_id')) { + $newsFeedId = Session::get('news_feed_id'); + if ($newsFeedId != NEW_VERSION_AVAILABLE && $newsFeedId > Auth::user()->news_feed_id) { + $user = Auth::user(); + $user->news_feed_id = $newsFeedId; + $user->save(); + } + + Session::forget('news_feed_message'); + } + + return 'success'; + } + + public function logError() + { + return Utils::logError(Input::get('error'), 'JavaScript'); + } } diff --git a/app/Http/Controllers/WelcomeController.php b/app/Http/Controllers/WelcomeController.php deleted file mode 100644 index 8a5ac6dba5b9..000000000000 --- a/app/Http/Controllers/WelcomeController.php +++ /dev/null @@ -1,36 +0,0 @@ -middleware('guest'); - } - - /** - * Show the application welcome screen to the user. - * - * @return Response - */ - public function index() - { - return view('welcome'); - } - -} diff --git a/app/Http/routes.php b/app/Http/routes.php index c8a310385828..fc436e177b86 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -7,15 +7,487 @@ | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to -| and give it the controller to call when that URI is requested. +| and give it the Closure to execute when that URI is requested. | */ -Route::get('/', 'WelcomeController@index'); +//Cache::flush(); +//apc_clear_cache(); +//dd(DB::getQueryLog()); +//dd(Client::getPrivateId(1)); +//dd(new DateTime()); +//Event::fire('user.signup'); +//dd(App::environment()); +//dd(gethostname()); +//Log::error('test'); -Route::get('home', 'HomeController@index'); +// Application setup +Route::get('setup', 'AppController@showSetup'); +Route::post('setup', 'AppController@doSetup'); +Route::get('install', 'AppController@install'); +Route::get('update', 'AppController@update'); -Route::controllers([ - 'auth' => 'Auth\AuthController', - 'password' => 'Auth\PasswordController', -]); +// Public pages +Route::get('/', 'HomeController@showIndex'); +Route::get('terms', 'HomeController@showTerms'); +Route::get('log_error', 'HomeController@logError'); +Route::get('invoice_now', 'HomeController@invoiceNow'); +Route::post('get_started', 'AccountController@getStarted'); + +// Client visible pages +Route::get('view/{invitation_key}', 'InvoiceController@view'); +Route::get('approve/{invitation_key}', 'QuoteController@approve'); +Route::get('payment/{invitation_key}', 'PaymentController@show_payment'); +Route::post('payment/{invitation_key}', 'PaymentController@do_payment'); +Route::get('complete', 'PaymentController@offsite_payment'); +Route::get('client/quotes', 'QuoteController@clientIndex'); +Route::get('client/invoices', 'InvoiceController@clientIndex'); +Route::get('client/payments', 'PaymentController@clientIndex'); +Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'QuoteController@getClientDatatable')); +Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'InvoiceController@getClientDatatable')); +Route::get('api/client.payments', array('as'=>'api.client.payments', 'uses'=>'PaymentController@getClientDatatable')); + +Route::get('license', 'PaymentController@show_license_payment'); +Route::post('license', 'PaymentController@do_license_payment'); +Route::get('claim_license', 'PaymentController@claim_license'); + +Route::post('signup/validate', 'AccountController@checkEmail'); +Route::post('signup/submit', 'AccountController@submitSignup'); + +// Confide routes +Route::get('login', 'UserController@login'); +Route::post('login', 'UserController@do_login'); +Route::get('user/confirm/{code}', 'UserController@confirm'); +Route::get('forgot_password', 'UserController@forgot_password'); +Route::post('forgot_password', 'UserController@do_forgot_password'); +Route::get('user/reset/{token?}', 'UserController@reset_password'); +Route::post('user/reset', 'UserController@do_reset_password'); +Route::get('logout', 'UserController@logout'); + +if (Utils::isNinja()) { + Route::post('/signup/register', 'AccountController@doRegister'); + Route::get('/news_feed/{user_type}/{version}/', 'HomeController@newsFeed'); + Route::get('/demo', 'AccountController@demo'); +} + +Route::group(array('before' => 'auth'), function() { + Route::get('dashboard', 'DashboardController@index'); + Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible'); + Route::get('hide_message', 'HomeController@hideMessage'); + Route::get('force_inline_pdf', 'UserController@forcePDFJS'); + + Route::get('api/users', array('as'=>'api.users', 'uses'=>'UserController@getDatatable')); + Route::resource('users', 'UserController'); + Route::post('users/delete', 'UserController@delete'); + Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); + Route::get('restore_user/{user_id}', 'UserController@restoreUser'); + Route::post('users/change_password', 'UserController@changePassword'); + + Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable')); + Route::resource('tokens', 'TokenController'); + Route::post('tokens/delete', 'TokenController@delete'); + + Route::get('api/products', array('as'=>'api.products', 'uses'=>'ProductController@getDatatable')); + Route::resource('products', 'ProductController'); + Route::get('products/{product_id}/archive', 'ProductController@archive'); + + Route::get('company/advanced_settings/data_visualizations', 'ReportController@d3'); + Route::get('company/advanced_settings/chart_builder', 'ReportController@report'); + Route::post('company/advanced_settings/chart_builder', 'ReportController@report'); + + Route::post('company/cancel_account', 'AccountController@cancelAccount'); + Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); + Route::get('company/{section?}/{sub_section?}', 'AccountController@showSection'); + Route::post('company/{section?}/{sub_section?}', 'AccountController@doSection'); + Route::post('user/setTheme', 'UserController@setTheme'); + Route::post('remove_logo', 'AccountController@removeLogo'); + Route::post('account/go_pro', 'AccountController@enableProPlan'); + + Route::resource('gateways', 'AccountGatewayController'); + Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable')); + Route::post('gateways/delete', 'AccountGatewayController@delete'); + + Route::resource('clients', 'ClientController'); + Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable')); + Route::get('api/activities/{client_id?}', array('as'=>'api.activities', 'uses'=>'ActivityController@getDatatable')); + Route::post('clients/bulk', 'ClientController@bulk'); + + Route::get('recurring_invoices', 'InvoiceController@recurringIndex'); + Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'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?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); + Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); + Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice'); + Route::post('invoices/bulk', 'InvoiceController@bulk'); + + Route::get('quotes/create/{client_id?}', 'QuoteController@create'); + Route::get('quotes/{public_id}/clone', 'InvoiceController@cloneInvoice'); + Route::get('quotes/{public_id}/edit', 'InvoiceController@edit'); + Route::put('quotes/{public_id}', 'InvoiceController@update'); + Route::get('quotes/{public_id}', 'InvoiceController@edit'); + Route::post('quotes', 'InvoiceController@store'); + Route::get('quotes', 'QuoteController@index'); + Route::get('api/quotes/{client_id?}', array('as'=>'api.quotes', 'uses'=>'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?}', array('as'=>'api.payments', 'uses'=>'PaymentController@getDatatable')); + Route::post('payments/bulk', 'PaymentController@bulk'); + + Route::get('credits/{id}/edit', function() { + return View::make('header'); + }); + Route::resource('credits', 'CreditController'); + Route::get('credits/create/{client_id?}/{invoice_id?}', 'CreditController@create'); + Route::get('api/credits/{client_id?}', array('as'=>'api.credits', 'uses'=>'CreditController@getDatatable')); + Route::post('credits/bulk', 'CreditController@bulk'); + + //Route::resource('timesheets', 'TimesheetController'); +}); + +// Route group for API +Route::group(array('prefix' => 'api/v1', 'before' => ['api.access']), function() +{ + Route::resource('ping', 'ClientApiController@ping'); + Route::resource('clients', 'ClientApiController'); + Route::resource('invoices', 'InvoiceApiController'); + Route::resource('quotes', 'QuoteApiController'); + Route::resource('payments', 'PaymentApiController'); + Route::post('api/hooks', 'IntegrationController@subscribe'); + Route::post('email_invoice', 'InvoiceApiController@emailInvoice'); +}); + +define('CONTACT_EMAIL', Config::get('mail.from.address')); +define('CONTACT_NAME', Config::get('mail.from.name')); +define('SITE_URL', Config::get('app.url')); + +define('ENV_DEVELOPMENT', 'local'); +define('ENV_STAGING', 'staging'); +define('ENV_PRODUCTION', 'fortrabbit'); + +define('RECENTLY_VIEWED', 'RECENTLY_VIEWED'); +define('ENTITY_CLIENT', 'client'); +define('ENTITY_INVOICE', 'invoice'); +define('ENTITY_RECURRING_INVOICE', 'recurring_invoice'); +define('ENTITY_PAYMENT', 'payment'); +define('ENTITY_CREDIT', 'credit'); +define('ENTITY_QUOTE', 'quote'); + +define('PERSON_CONTACT', 'contact'); +define('PERSON_USER', 'user'); + +define('ACCOUNT_DETAILS', 'details'); +define('ACCOUNT_NOTIFICATIONS', 'notifications'); +define('ACCOUNT_IMPORT_EXPORT', 'import_export'); +define('ACCOUNT_PAYMENTS', 'payments'); +define('ACCOUNT_MAP', 'import_map'); +define('ACCOUNT_EXPORT', 'export'); +define('ACCOUNT_PRODUCTS', 'products'); +define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings'); +define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings'); +define('ACCOUNT_INVOICE_DESIGN', 'invoice_design'); +define('ACCOUNT_CHART_BUILDER', 'chart_builder'); +define('ACCOUNT_USER_MANAGEMENT', 'user_management'); +define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations'); +define('ACCOUNT_EMAIL_TEMPLATES', 'email_templates'); +define('ACCOUNT_TOKEN_MANAGEMENT', 'token_management'); + +define('ACTIVITY_TYPE_CREATE_CLIENT', 1); +define('ACTIVITY_TYPE_ARCHIVE_CLIENT', 2); +define('ACTIVITY_TYPE_DELETE_CLIENT', 3); + +define('ACTIVITY_TYPE_CREATE_INVOICE', 4); +define('ACTIVITY_TYPE_UPDATE_INVOICE', 5); +define('ACTIVITY_TYPE_EMAIL_INVOICE', 6); +define('ACTIVITY_TYPE_VIEW_INVOICE', 7); +define('ACTIVITY_TYPE_ARCHIVE_INVOICE', 8); +define('ACTIVITY_TYPE_DELETE_INVOICE', 9); + +define('ACTIVITY_TYPE_CREATE_PAYMENT', 10); +define('ACTIVITY_TYPE_UPDATE_PAYMENT', 11); +define('ACTIVITY_TYPE_ARCHIVE_PAYMENT', 12); +define('ACTIVITY_TYPE_DELETE_PAYMENT', 13); + +define('ACTIVITY_TYPE_CREATE_CREDIT', 14); +define('ACTIVITY_TYPE_UPDATE_CREDIT', 15); +define('ACTIVITY_TYPE_ARCHIVE_CREDIT', 16); +define('ACTIVITY_TYPE_DELETE_CREDIT', 17); + +define('ACTIVITY_TYPE_CREATE_QUOTE', 18); +define('ACTIVITY_TYPE_UPDATE_QUOTE', 19); +define('ACTIVITY_TYPE_EMAIL_QUOTE', 20); +define('ACTIVITY_TYPE_VIEW_QUOTE', 21); +define('ACTIVITY_TYPE_ARCHIVE_QUOTE', 22); +define('ACTIVITY_TYPE_DELETE_QUOTE', 23); + +define('ACTIVITY_TYPE_RESTORE_QUOTE', 24); +define('ACTIVITY_TYPE_RESTORE_INVOICE', 25); +define('ACTIVITY_TYPE_RESTORE_CLIENT', 26); +define('ACTIVITY_TYPE_RESTORE_PAYMENT', 27); +define('ACTIVITY_TYPE_RESTORE_CREDIT', 28); +define('ACTIVITY_TYPE_APPROVE_QUOTE', 29); + +define('DEFAULT_INVOICE_NUMBER', '0001'); +define('RECENTLY_VIEWED_LIMIT', 8); +define('LOGGED_ERROR_LIMIT', 100); +define('RANDOM_KEY_LENGTH', 32); +define('MAX_NUM_CLIENTS', 500); +define('MAX_NUM_CLIENTS_PRO', 20000); +define('MAX_NUM_USERS', 20); + +define('INVOICE_STATUS_DRAFT', 1); +define('INVOICE_STATUS_SENT', 2); +define('INVOICE_STATUS_VIEWED', 3); +define('INVOICE_STATUS_PARTIAL', 4); +define('INVOICE_STATUS_PAID', 5); + +define('PAYMENT_TYPE_CREDIT', 1); + +define('FREQUENCY_WEEKLY', 1); +define('FREQUENCY_TWO_WEEKS', 2); +define('FREQUENCY_FOUR_WEEKS', 3); +define('FREQUENCY_MONTHLY', 4); +define('FREQUENCY_THREE_MONTHS', 5); +define('FREQUENCY_SIX_MONTHS', 6); +define('FREQUENCY_ANNUALLY', 7); + +define('SESSION_TIMEZONE', 'timezone'); +define('SESSION_CURRENCY', 'currency'); +define('SESSION_DATE_FORMAT', 'dateFormat'); +define('SESSION_DATE_PICKER_FORMAT', 'datePickerFormat'); +define('SESSION_DATETIME_FORMAT', 'datetimeFormat'); +define('SESSION_COUNTER', 'sessionCounter'); +define('SESSION_LOCALE', 'sessionLocale'); + +define('DEFAULT_TIMEZONE', 'US/Eastern'); +define('DEFAULT_CURRENCY', 1); // US Dollar +define('DEFAULT_DATE_FORMAT', 'M j, Y'); +define('DEFAULT_DATE_PICKER_FORMAT', 'M d, yyyy'); +define('DEFAULT_DATETIME_FORMAT', 'F j, Y, g:i a'); +define('DEFAULT_QUERY_CACHE', 120); // minutes +define('DEFAULT_LOCALE', 'en'); + +define('RESULT_SUCCESS', 'success'); +define('RESULT_FAILURE', 'failure'); + + +define('PAYMENT_LIBRARY_OMNIPAY', 1); +define('PAYMENT_LIBRARY_PHP_PAYMENTS', 2); + +define('GATEWAY_AUTHORIZE_NET', 1); +define('GATEWAY_AUTHORIZE_NET_SIM', 2); +define('GATEWAY_PAYPAL_EXPRESS', 17); +define('GATEWAY_PAYPAL_PRO', 18); +define('GATEWAY_STRIPE', 23); +define('GATEWAY_TWO_CHECKOUT', 27); +define('GATEWAY_BEANSTREAM', 29); +define('GATEWAY_PSIGATE', 30); +define('GATEWAY_MOOLAH', 31); + +define('EVENT_CREATE_CLIENT', 1); +define('EVENT_CREATE_INVOICE', 2); +define('EVENT_CREATE_QUOTE', 3); +define('EVENT_CREATE_PAYMENT', 4); + +define('REQUESTED_PRO_PLAN', 'REQUESTED_PRO_PLAN'); +define('DEMO_ACCOUNT_ID', 'DEMO_ACCOUNT_ID'); +define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h'); +define('NINJA_GATEWAY_ID', GATEWAY_AUTHORIZE_NET); +define('NINJA_GATEWAY_CONFIG', '{"apiLoginId":"626vWcD5","transactionKey":"4bn26TgL9r4Br4qJ","testMode":"","developerMode":""}'); +define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); +define('NINJA_APP_URL', 'https://app.invoiceninja.com'); +define('NINJA_VERSION', '1.6.1'); +define('NINJA_DATE', '2000-01-01'); +define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); +define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/'); + +define('COUNT_FREE_DESIGNS', 4); +define('PRODUCT_ONE_CLICK_INSTALL', 1); +define('PRODUCT_INVOICE_DESIGNS', 2); +define('PRODUCT_WHITE_LABEL', 3); +define('PRODUCT_SELF_HOST', 4); +define('WHITE_LABEL_AFFILIATE_KEY', '92D2J5'); +define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74'); +define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); + +define('PRO_PLAN_PRICE', 50); +define('WHITE_LABEL_PRICE', 20); +define('INVOICE_DESIGNS_PRICE', 10); + +define('USER_TYPE_SELF_HOST', 'SELF_HOST'); +define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST'); +define('NEW_VERSION_AVAILABLE', 'NEW_VERSION_AVAILABLE'); + +define('TOKEN_BILLING_DISABLED', 1); +define('TOKEN_BILLING_OPT_IN', 2); +define('TOKEN_BILLING_OPT_OUT', 3); +define('TOKEN_BILLING_ALWAYS', 4); + +define('PAYMENT_TYPE_PAYPAL', 'PAYMENT_TYPE_PAYPAL'); +define('PAYMENT_TYPE_CREDIT_CARD', 'PAYMENT_TYPE_CREDIT_CARD'); +define('PAYMENT_TYPE_ANY', 'PAYMENT_TYPE_ANY'); + +/* +define('GATEWAY_AMAZON', 30); +define('GATEWAY_BLUEPAY', 31); +define('GATEWAY_BRAINTREE', 32); +define('GATEWAY_GOOGLE', 33); +define('GATEWAY_QUICKBOOKS', 35); +*/ + +/** + * TEST VALUES FOR THE CREDIT CARDS + * NUMBER IS FOR THE BINARY COUNT FOR WHICH IMAGES TO DISPLAY + * card IS FOR CARD IMAGE AND text IS FOR CARD NAME (TO ADD TO alt FOR IMAGE) +**/ +$creditCards = [ + 1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'], + 2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'], + 4 => ['card' => 'images/credit_cards/Test-AmericanExpress-Icon.png', 'text' => 'American Express'], + 8 => ['card' => 'images/credit_cards/Test-Diners-Icon.png', 'text' => 'Diners'], + 16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover'] + ]; + +define('CREDIT_CARDS', serialize($creditCards)); + + +HTML::macro('nav_link', function($url, $text, $url2 = '', $extra = '') { + $class = ( Request::is($url) || Request::is($url.'/*') || Request::is($url2.'/*') ) ? ' class="active"' : ''; + $title = ucwords(trans("texts.$text")) . Utils::getProLabel($text); + return ''.$title.''; +}); + +HTML::macro('tab_link', function($url, $text, $active = false) { + $class = $active ? ' class="active"' : ''; + return ''.$text.''; +}); + +HTML::macro('menu_link', function($type) { + $types = $type.'s'; + $Type = ucfirst($type); + $Types = ucfirst($types); + $class = ( Request::is($types) || Request::is('*'.$type.'*')) && !Request::is('*advanced_settings*') ? ' active' : ''; + + return ''; +}); + +HTML::macro('image_data', function($imagePath) { + return 'data:image/jpeg;base64,' . base64_encode(file_get_contents(public_path().'/'.$imagePath)); +}); + + +HTML::macro('breadcrumbs', function() { + $str = ''; +}); + +function uctrans($text) +{ + return ucwords(trans($text)); +} + +// optional trans: only return the string if it's translated +function otrans($text) +{ + $locale = Session::get(SESSION_LOCALE); + + if ($locale == 'en') { + return trans($text); + } else { + $string = trans($text); + $english = trans($text, [], 'en'); + return $string != $english ? $string : ''; + } +} + +Validator::extend('positive', function($attribute, $value, $parameters) { + return Utils::parseFloat($value) >= 0; +}); + +Validator::extend('has_credit', function($attribute, $value, $parameters) { + $publicClientId = $parameters[0]; + $amount = $parameters[1]; + + $client = Client::scope($publicClientId)->firstOrFail(); + $credit = $client->getTotalCredit(); + + return $credit >= $amount; +}); + + +/* +// Log all SQL queries to laravel.log +Event::listen('illuminate.query', function($query, $bindings, $time, $name) +{ + $data = compact('bindings', 'time', 'name'); + + // Format binding data for sql insertion + foreach ($bindings as $i => $binding) + { + if ($binding instanceof \DateTime) + { + $bindings[$i] = $binding->format('\'Y-m-d H:i:s\''); + } + else if (is_string($binding)) + { + $bindings[$i] = "'$binding'"; + } + } + + // Insert bindings into query + $query = str_replace(array('%', '?'), array('%%', '%s'), $query); + $query = vsprintf($query, $bindings); + + Log::info($query, $data); +}); +*/ + +/* +if (Auth::check() && Auth::user()->id === 1) +{ + Auth::loginUsingId(1); +} +*/ \ No newline at end of file diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index afa34c83dc8d..8d39c9fe1b0a 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -24,7 +24,266 @@ class RouteServiceProvider extends ServiceProvider { { parent::boot($router); - // + + + + App::before(function($request) + { + // Ensure all request are over HTTPS in production + if (App::environment() == ENV_PRODUCTION) + { + if (!Request::secure()) + { + return Redirect::secure(Request::getRequestUri()); + } + } + + // If the database doens't yet exist we'll skip the rest + if (!Utils::isNinja() && !Utils::isDatabaseSetup()) + { + return; + } + + // check the application is up to date and for any news feed messages + if (Auth::check()) + { + $count = Session::get(SESSION_COUNTER, 0); + Session::put(SESSION_COUNTER, ++$count); + + if (!Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) { + $data = false; + if (Utils::isNinja()) { + $data = Utils::getNewsFeedResponse(); + } else { + $file = @file_get_contents(NINJA_APP_URL . '/news_feed/' . Utils::getUserType() . '/' . NINJA_VERSION); + $data = @json_decode($file); + } + if ($data) { + if ($data->version != NINJA_VERSION) { + $params = [ + 'user_version' => NINJA_VERSION, + 'latest_version'=> $data->version, + 'releases_link' => link_to(RELEASES_URL, 'Invoice Ninja', ['target' => '_blank']) + ]; + Session::put('news_feed_id', NEW_VERSION_AVAILABLE); + Session::put('news_feed_message', trans('texts.new_version_available', $params)); + } else { + Session::put('news_feed_id', $data->id); + if ($data->message && $data->id > Auth::user()->news_feed_id) { + Session::put('news_feed_message', $data->message); + } + } + } else { + Session::put('news_feed_id', true); + } + } + } + + // Check if we're requesting to change the account's language + if (Input::has('lang')) + { + $locale = Input::get('lang'); + App::setLocale($locale); + Session::set(SESSION_LOCALE, $locale); + + if (Auth::check()) + { + if ($language = Language::whereLocale($locale)->first()) + { + $account = Auth::user()->account; + $account->language_id = $language->id; + $account->save(); + } + } + } + else if (Auth::check()) + { + $locale = Session::get(SESSION_LOCALE, DEFAULT_LOCALE); + App::setLocale($locale); + } + + // Make sure the account/user localization settings are in the session + if (Auth::check() && !Session::has(SESSION_TIMEZONE)) + { + Event::fire('user.refresh'); + } + + // Check if the user is claiming a license (ie, additional invoices, white label, etc.) + $claimingLicense = Utils::startsWith($_SERVER['REQUEST_URI'], '/claim_license'); + if (!$claimingLicense && Input::has('license_key') && Input::has('product_id')) + { + $licenseKey = Input::get('license_key'); + $productId = Input::get('product_id'); + + $data = trim(file_get_contents((Utils::isNinjaDev() ? 'http://ninja.dev' : NINJA_APP_URL) . "/claim_license?license_key={$licenseKey}&product_id={$productId}")); + + if ($productId == PRODUCT_INVOICE_DESIGNS) + { + if ($data = json_decode($data)) + { + foreach ($data as $item) + { + $design = new InvoiceDesign(); + $design->id = $item->id; + $design->name = $item->name; + $design->javascript = $item->javascript; + $design->save(); + } + + if (!Utils::isNinjaProd()) { + Cache::forget('invoice_designs_cache_' . Auth::user()->maxInvoiceDesignId()); + } + + Session::flash('message', trans('texts.bought_designs')); + } + } + else if ($productId == PRODUCT_WHITE_LABEL) + { + if ($data == 'valid') + { + $account = Auth::user()->account; + $account->pro_plan_paid = NINJA_DATE; + $account->save(); + + Session::flash('message', trans('texts.bought_white_label')); + } + } + } + }); + + /* + |-------------------------------------------------------------------------- + | Authentication Filters + |-------------------------------------------------------------------------- + | + | The following filters are used to verify that the user of the current + | session is logged into this application. The "basic" filter easily + | integrates HTTP Basic authentication for quick, simple checking. + | + */ + + $router->filter('auth', function() + { + if (Auth::guest()) + { + if (Utils::isNinja() || Account::count() == 0) + { + return Redirect::guest('/'); + } + else + { + return Redirect::guest('/login'); + } + } + }); + + + $router->filter('auth.basic', function() + { + return Auth::basic(); + }); + + $router->filter('api.access', function() + { + $headers = Utils::getApiHeaders(); + + // check for a valid token + $token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']); + + if ($token) { + Auth::loginUsingId($token->user_id); + Session::set('token_id', $token->id); + } else { + sleep(3); + return Response::make('Invalid token', 403, $headers); + } + + if (!Utils::isNinja()) { + return null; + } + + if (!Utils::isPro()) { + return Response::make('API requires pro plan', 403, $headers); + } else { + $accountId = Auth::user()->account->id; + + // http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users + $hour = 60 * 60; + $hour_limit = 100; # users are limited to 100 requests/hour + $hour_throttle = Cache::get("hour_throttle:{$accountId}", null); + $last_api_request = Cache::get("last_api_request:{$accountId}", 0); + $last_api_diff = time() - $last_api_request; + + if (is_null($hour_throttle)) { + $new_hour_throttle = 0; + } else { + $new_hour_throttle = $hour_throttle - $last_api_diff; + $new_hour_throttle = $new_hour_throttle < 0 ? 0 : $new_hour_throttle; + $new_hour_throttle += $hour / $hour_limit; + $hour_hits_remaining = floor(( $hour - $new_hour_throttle ) * $hour_limit / $hour); + $hour_hits_remaining = $hour_hits_remaining >= 0 ? $hour_hits_remaining : 0; + } + + if ($new_hour_throttle > $hour) { + $wait = ceil($new_hour_throttle - $hour); + sleep(1); + return Response::make("Please wait {$wait} second(s)", 403, $headers); + } + + Cache::put("hour_throttle:{$accountId}", $new_hour_throttle, 10); + Cache::put("last_api_request:{$accountId}", time(), 10); + } + + return null; + }); + + + + /* + |-------------------------------------------------------------------------- + | Guest Filter + |-------------------------------------------------------------------------- + | + | The "guest" filter is the counterpart of the authentication filters as + | it simply checks that the current user is not logged in. A redirect + | response will be issued if they are, which you may freely change. + | + */ + + $router->filter('guest', function() + { + if (Auth::check()) return Redirect::to('/'); + }); + + /* + |-------------------------------------------------------------------------- + | CSRF Protection Filter + |-------------------------------------------------------------------------- + | + | The CSRF filter is responsible for protecting your application against + | cross-site request forgery attacks. If this special token in a user + | session does not match the one given in this request, we'll bail. + | + */ + + $router->filter('csrf', function() + { + if ($_SERVER['REQUEST_URI'] != '/signup/register') + { + $token = Request::ajax() ? Request::header('X-CSRF-Token') : Input::get('_token'); + + if (Session::token() != $token) + { + Session::flash('warning', trans('texts.session_expired')); + + return Redirect::to('/'); + //throw new Illuminate\Session\TokenMismatchException; + } + } + }); + + + } /** diff --git a/app/User.php b/app/User.php deleted file mode 100644 index 2dae84799d11..000000000000 --- a/app/User.php +++ /dev/null @@ -1,34 +0,0 @@ - env('APP_DEBUG'), + 'debug' => true, /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php deleted file mode 100644 index 36a1db9bc339..000000000000 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ /dev/null @@ -1,36 +0,0 @@ -increments('id'); - $table->string('name'); - $table->string('email')->unique(); - $table->string('password', 60); - $table->rememberToken(); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('users'); - } - -} diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php deleted file mode 100644 index 679df38f8838..000000000000 --- a/database/migrations/2014_10_12_100000_create_password_resets_table.php +++ /dev/null @@ -1,33 +0,0 @@ -string('email')->index(); - $table->string('token')->index(); - $table->timestamp('created_at'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('password_resets'); - } - -} diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index b3c69b56e850..955e643203b9 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -1,8 +1,5 @@ command->info('Running DatabaseSeeder'); - // $this->call('UserTableSeeder'); + Eloquent::unguard(); + + $this->call('UserTableSeeder'); + $this->call('ConstantsSeeder'); + + $this->call('CountriesSeeder'); + $this->command->info('Seeded the countries!'); + + $this->call('PaymentLibrariesSeeder'); + $this->command->info('Seeded the Payment Libraries!'); } -} +} \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess index 77827ae70510..54d1cba8a46f 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -12,4 +12,8 @@ RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] + + # cp from invoice ninja + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] diff --git a/public/favicon.ico b/public/favicon.ico index e69de29bb2d1..20bc39679617 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/fonts/glyphicons-halflings-regular.eot b/public/fonts/glyphicons-halflings-regular.eot index b93a4953fff6..4a4ca865d67e 100644 Binary files a/public/fonts/glyphicons-halflings-regular.eot and b/public/fonts/glyphicons-halflings-regular.eot differ diff --git a/public/fonts/glyphicons-halflings-regular.svg b/public/fonts/glyphicons-halflings-regular.svg index 94fb5490a2ed..e3e2dc739dd8 100644 --- a/public/fonts/glyphicons-halflings-regular.svg +++ b/public/fonts/glyphicons-halflings-regular.svg @@ -6,283 +6,224 @@ - - + + + - - + + - - - - - - - - - - + + + + + + + + + - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/glyphicons-halflings-regular.ttf b/public/fonts/glyphicons-halflings-regular.ttf index 1413fc609ab6..67fa00bf8380 100644 Binary files a/public/fonts/glyphicons-halflings-regular.ttf and b/public/fonts/glyphicons-halflings-regular.ttf differ diff --git a/public/fonts/glyphicons-halflings-regular.woff b/public/fonts/glyphicons-halflings-regular.woff index 9e612858f802..8c54182aa5d4 100644 Binary files a/public/fonts/glyphicons-halflings-regular.woff and b/public/fonts/glyphicons-halflings-regular.woff differ diff --git a/public/robots.txt b/public/robots.txt index eb0536286f30..9e60f970fbd0 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,2 @@ User-agent: * -Disallow: +Disallow: diff --git a/readme.md b/readme.md index a4d8d553cb04..48fdc6d208e2 100644 --- a/readme.md +++ b/readme.md @@ -1,23 +1,118 @@ -## Laravel PHP Framework +# Invoice Ninja - Rebuilt in Laravel 5 +## Simple, Intuitive Invoicing -[![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework) -[![Total Downloads](https://poser.pugx.org/laravel/framework/downloads.svg)](https://packagist.org/packages/laravel/framework) -[![Latest Stable Version](https://poser.pugx.org/laravel/framework/v/stable.svg)](https://packagist.org/packages/laravel/framework) -[![Latest Unstable Version](https://poser.pugx.org/laravel/framework/v/unstable.svg)](https://packagist.org/packages/laravel/framework) -[![License](https://poser.pugx.org/laravel/framework/license.svg)](https://packagist.org/packages/laravel/framework) +### [https://www.invoiceninja.com](https://www.invoiceninja.com) -Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, queueing, and caching. +If you'd like to use our code to sell your own invoicing app we have an affiliate program. Get in touch for more details. -Laravel is accessible, yet powerful, providing powerful tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the tools you need to build any application with which you are tasked. +### Introduction -## Official Documentation +Most online invoicing sites are expensive. They shouldn't be. The aim of this project is to provide a free, open-source alternative. Additionally, the hope is the codebase will serve as a sample site for Laravel as well as other JavaScript technologies. -Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs). +To setup the site you can either use this [zip file](http://hillelcoren.com/invoice-ninja/self-hosting/) (easier to setup) or checkout the code from GitHub following the instructions below (easier to stay up to date). There's also a more detailed setup guide [available here](http://hillelcoren.com/invoice-ninja/laravel-ubuntu-virtualbox/). -## Contributing +For a WAMP/MAMP/LAMP setup you can one-click install using Softaculous's [AMPPS](http://www.ampps.com/). To deploy the app with [Docker](http://www.docker.com/) you can use [this project](https://github.com/rollbrettler/Dockerfiles/tree/master/invoice-ninja). -Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions). +To connect follow [@invoiceninja](https://twitter.com/invoiceninja) or join the [Facebook Group](https://www.facebook.com/invoiceninja). For discussion of the code please use the [Google Group](https://groups.google.com/d/forum/invoiceninja). -### License +If you'd like to translate the site please use [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) for the starter files. -The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) +Developed by [@hillelcoren](https://twitter.com/hillelcoren) | Designed by [kantorp-wegl.in](http://kantorp-wegl.in/). + +### Features + +* Core application built using Laravel 4.1 +* Invoice PDF generation directly in the browser +* Integrates with many payment providers +* Recurring invoices +* Tax rates and payment terms +* Multi-user support +* [Zapier](https://zapier.com/) integration +* [D3.js](http://d3js.org/) visualizations + +### Contributors + +* [Troels Liebe Bentsen](https://github.com/tlbdk) + +### Steps to setup + +If you plan on submitting changes it's best to [fork the repo](https://help.github.com/articles/fork-a-repo), otherwise you can just checkout the code. + + git clone https://github.com/hillelcoren/invoice-ninja.git ninja + cd ninja + +Install Laravel packages using Composer + +Note: you may be prompted for your Github user/pass due to their API limits. + + composer install + +Install JavaScript and HTML packages using Bower. This is optional, it's only needed if you want to modify the JavaScript. + + bower install + +Create database user and a database for ninja + + CREATE SCHEMA `ninja` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; + CREATE USER 'ninja'@'localhost' IDENTIFIED BY 'ninja'; + GRANT ALL PRIVILEGES ON `ninja`.* TO 'ninja'@'localhost'; + FLUSH PRIVILEGES; + +Add public/ to your web server root then load / to configure the application. + +### Developer Notes + +* The application requires PHP >= 5.4.0 +* If you make any changes to the JavaScript files you need to run grunt to create the built files. See Gruntfile.js for more details. +* The lookup tables are cached in memory (ie, Currencies, Timezones, Languages, etc). If you add a record to the database you need to clear the cache by uncommenting Cache::flush() in app/routes.php. +* If you run into any composer errors try running composer dump-autoload. + +### Ubuntu Notes + + # Install php-mcrypt + apt-get install php5-mcrypt + sudo php5enmod mcrypt + + # Install Composer + curl -sS https://getcomposer.org/installer | php + sudo mv composer.phar /usr/local/bin/composer + + # Install Bower + sudo apt-get install npm nodejs-legacy + sudo npm install -g bower + sudo ln -s /usr/local/lib/node_modules/bower/bin/bower /usr/local/bin/bower + + # Install Grunt (For development only) + npm install -g grunt-cli + +### Frameworks/Libraries +* [laravel/laravel](https://github.com/laravel/laravel) - A PHP Framework For Web Artisans +* [twbs/bootstrap](https://github.com/twbs/bootstrap) - Sleek, intuitive, and powerful front-end framework for faster and easier web development. +* [patricktalmadge/bootstrapper](https://github.com/patricktalmadge/bootstrapper) - Laravel Twitter Bootstrap Bundle +* [danielfarrell/bootstrap-combobox](https://github.com/danielfarrell/bootstrap-combobox) - A combobox plugin +* [jquery/jquery](https://github.com/jquery/jquery) - jQuery JavaScript Library +* [eternicode/bootstrap-datepicker](https://github.com/eternicode/bootstrap-datepicker) - A datepicker for @twitter bootstrap +* [jquery/jquery-ui](https://github.com/jquery/jquery-ui) - The official jQuery user interface library +* [knockout/knockout](https://github.com/knockout/knockout) - Knockout makes it easier to create rich, responsive UIs with JavaScript +* [rniemeyer/knockout-sortable](https://github.com/rniemeyer/knockout-sortable) - A Knockout.js binding to connect observableArrays with jQuery UI sortable functionality +* [MrRio/jsPDF](https://github.com/MrRio/jsPDF) - Generate PDF files in JavaScript. HTML5 FTW. +* [FortAwesome/Font-Awesome](https://github.com/FortAwesome/Font-Awesome) - The iconic font designed for Bootstrap that works with twitter bootstrap +* [jasonlewis/basset](https://github.com/jasonlewis/basset) - A better asset management package for Laravel +* [Zizaco/confide](https://github.com/Zizaco/confide) - Confide is a authentication solution for Laravel 4 +* [Anahkiasen/former](https://github.com/Anahkiasen/former) - A powerful form builder, for Laravel and other frameworks (stand-alone too) +* [barryvdh/laravel-debugbar](https://github.com/barryvdh/laravel-debugbar) - Laravel debugbar +* [DataTables/DataTables](https://github.com/DataTables/DataTables) - Tables plug-in for jQuery +* [Chumper/Datatable](https://github.com/Chumper/Datatable) - This is a laravel 4 package for the server and client side of datatables +* [omnipay/omnipay](https://github.com/omnipay/omnipay) - A framework agnostic, multi-gateway payment processing library for PHP 5.3+ +* [Intervention/image](https://github.com/Intervention/image) - PHP Image Manipulation +* [webpatser/laravel-countries](https://github.com/webpatser/laravel-countries) - Almost ISO 3166_2, 3166_3, currency, Capital and more for all countries +* [briannesbitt/Carbon](https://github.com/briannesbitt/Carbon) - A simple API extension for DateTime with PHP 5.3+ +* [thomaspark/bootswatch](https://github.com/thomaspark/bootswatch) - Themes for Bootstrap +* [mozilla/pdf.js](https://github.com/mozilla/pdf.js) - PDF Reader in JavaScript +* [nnnick/Chart.js](https://github.com/nnnick/Chart.js) - Simple HTML5 Charts using the canvas tag +* [josscrowcroft/accounting.js](https://github.com/josscrowcroft/accounting.js) - A lightweight JavaScript library for number, money and currency formatting +* [jashkenas/underscore](https://github.com/jashkenas/underscore) - JavaScript's utility _ belt +* [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) - List of languages ​​for Laravel4 +* [calvinfroedge/PHP-Payments](https://github.com/calvinfroedge/PHP-Payments) - A uniform payments interface for PHP +* [bgrins/spectrum](https://github.com/bgrins/spectrum) - The No Hassle JavaScript Colorpicker +* [lokesh/lightbox2](https://github.com/lokesh/lightbox2/) - The original lightbox script diff --git a/resources/lang/en/pagination.php b/resources/lang/en/pagination.php index 13b4dcb3c0f4..eb9be3baaed5 100644 --- a/resources/lang/en/pagination.php +++ b/resources/lang/en/pagination.php @@ -1,6 +1,6 @@ - '« Previous', + 'next' => 'Next »', -]; +); \ No newline at end of file diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 764f05636d27..68b39c68af2f 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -1,6 +1,6 @@ "The :attribute must be accepted.", - "active_url" => "The :attribute is not a valid URL.", - "after" => "The :attribute must be a date after :date.", - "alpha" => "The :attribute may only contain letters.", - "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", - "alpha_num" => "The :attribute may only contain letters and numbers.", - "array" => "The :attribute must be an array.", - "before" => "The :attribute must be a date before :date.", - "between" => [ - "numeric" => "The :attribute must be between :min and :max.", - "file" => "The :attribute must be between :min and :max kilobytes.", - "string" => "The :attribute must be between :min and :max characters.", - "array" => "The :attribute must have between :min and :max items.", - ], - "boolean" => "The :attribute field must be true or false.", - "confirmed" => "The :attribute confirmation does not match.", - "date" => "The :attribute is not a valid date.", - "date_format" => "The :attribute does not match the format :format.", - "different" => "The :attribute and :other must be different.", - "digits" => "The :attribute must be :digits digits.", - "digits_between" => "The :attribute must be between :min and :max digits.", - "email" => "The :attribute must be a valid email address.", - "filled" => "The :attribute field is required.", - "exists" => "The selected :attribute is invalid.", - "image" => "The :attribute must be an image.", - "in" => "The selected :attribute is invalid.", - "integer" => "The :attribute must be an integer.", - "ip" => "The :attribute must be a valid IP address.", - "max" => [ + "accepted" => "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "after" => "The :attribute must be a date after :date.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "array" => "The :attribute must be an array.", + "before" => "The :attribute must be a date before :date.", + "between" => array( + "numeric" => "The :attribute must be between :min - :max.", + "file" => "The :attribute must be between :min - :max kilobytes.", + "string" => "The :attribute must be between :min - :max characters.", + "array" => "The :attribute must have between :min - :max items.", + ), + "confirmed" => "The :attribute confirmation does not match.", + "date" => "The :attribute is not a valid date.", + "date_format" => "The :attribute does not match the format :format.", + "different" => "The :attribute and :other must be different.", + "digits" => "The :attribute must be :digits digits.", + "digits_between" => "The :attribute must be between :min and :max digits.", + "email" => "The :attribute format is invalid.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "max" => array( "numeric" => "The :attribute may not be greater than :max.", "file" => "The :attribute may not be greater than :max kilobytes.", "string" => "The :attribute may not be greater than :max characters.", "array" => "The :attribute may not have more than :max items.", - ], - "mimes" => "The :attribute must be a file of type: :values.", - "min" => [ + ), + "mimes" => "The :attribute must be a file of type: :values.", + "min" => array( "numeric" => "The :attribute must be at least :min.", "file" => "The :attribute must be at least :min kilobytes.", "string" => "The :attribute must be at least :min characters.", "array" => "The :attribute must have at least :min items.", - ], - "not_in" => "The selected :attribute is invalid.", - "numeric" => "The :attribute must be a number.", - "regex" => "The :attribute format is invalid.", - "required" => "The :attribute field is required.", - "required_if" => "The :attribute field is required when :other is :value.", - "required_with" => "The :attribute field is required when :values is present.", - "required_with_all" => "The :attribute field is required when :values is present.", - "required_without" => "The :attribute field is required when :values is not present.", - "required_without_all" => "The :attribute field is required when none of :values are present.", - "same" => "The :attribute and :other must match.", - "size" => [ + ), + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "regex" => "The :attribute format is invalid.", + "required" => "The :attribute field is required.", + "required_if" => "The :attribute field is required when :other is :value.", + "required_with" => "The :attribute field is required when :values is present.", + "required_without" => "The :attribute field is required when :values is not present.", + "same" => "The :attribute and :other must match.", + "size" => array( "numeric" => "The :attribute must be :size.", "file" => "The :attribute must be :size kilobytes.", "string" => "The :attribute must be :size characters.", "array" => "The :attribute must contain :size items.", - ], - "unique" => "The :attribute has already been taken.", - "url" => "The :attribute format is invalid.", - "timezone" => "The :attribute must be a valid zone.", + ), + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + + "positive" => "The :attribute must be greater than zero.", + "has_credit" => "The client does not have enough credit.", + "notmasked" => "The values are masked", /* |-------------------------------------------------------------------------- @@ -85,11 +84,7 @@ return [ | */ - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', - ], - ], + 'custom' => array(), /* |-------------------------------------------------------------------------- @@ -102,6 +97,6 @@ return [ | */ - 'attributes' => [], + 'attributes' => array(), -]; +); diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php deleted file mode 100644 index b5c6e2caf492..000000000000 --- a/resources/views/app.blade.php +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - Laravel - - - - - - - - - - - - - - @yield('content') - - - - - - diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php deleted file mode 100644 index 0a2bcbd64442..000000000000 --- a/resources/views/auth/login.blade.php +++ /dev/null @@ -1,61 +0,0 @@ -@extends('app') - -@section('content') -
-
-
-
-
Login
-
- @if (count($errors) > 0) -
- Whoops! There were some problems with your input.

-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
- @endif - -
- - -
- -
- -
-
- -
- -
- -
-
- -
-
-
- -
-
-
- -
-
- - - Forgot Your Password? -
-
-
-
-
-
-
-
-@endsection diff --git a/resources/views/auth/password.blade.php b/resources/views/auth/password.blade.php deleted file mode 100644 index 050224a2c66b..000000000000 --- a/resources/views/auth/password.blade.php +++ /dev/null @@ -1,50 +0,0 @@ -@extends('app') - -@section('content') -
-
-
-
-
Reset Password
-
- @if (session('status')) -
- {{ session('status') }} -
- @endif - - @if (count($errors) > 0) -
- Whoops! There were some problems with your input.

-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
- @endif - -
- - -
- -
- -
-
- -
-
- -
-
-
-
-
-
-
-
-@endsection diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php deleted file mode 100644 index 21771e45ef60..000000000000 --- a/resources/views/auth/register.blade.php +++ /dev/null @@ -1,65 +0,0 @@ -@extends('app') - -@section('content') -
-
-
-
-
Register
-
- @if (count($errors) > 0) -
- Whoops! There were some problems with your input.

-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
- @endif - -
- - -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
- -
-
-
-
-
-
-
-
-@endsection diff --git a/resources/views/auth/reset.blade.php b/resources/views/auth/reset.blade.php deleted file mode 100644 index 3c3536caac05..000000000000 --- a/resources/views/auth/reset.blade.php +++ /dev/null @@ -1,59 +0,0 @@ -@extends('app') - -@section('content') -
-
-
-
-
Reset Password
-
- @if (count($errors) > 0) -
- Whoops! There were some problems with your input.

-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
- @endif - -
- - - -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
- -
-
-
-
-
-
-
-
-@endsection diff --git a/resources/views/emails/password.blade.php b/resources/views/emails/password.blade.php deleted file mode 100644 index 20305393690c..000000000000 --- a/resources/views/emails/password.blade.php +++ /dev/null @@ -1 +0,0 @@ -Click here to reset your password: {{ url('password/reset/'.$token) }} diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php deleted file mode 100644 index 669dcb800aa3..000000000000 --- a/resources/views/errors/503.blade.php +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - -
-
-
Be right back.
-
-
- - diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php deleted file mode 100644 index 8f5e70585864..000000000000 --- a/resources/views/home.blade.php +++ /dev/null @@ -1,17 +0,0 @@ -@extends('app') - -@section('content') -
-
-
-
-
Home
- -
- You are logged in! -
-
-
-
-
-@endsection diff --git a/resources/views/vendor/.gitkeep b/resources/views/vendor/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php deleted file mode 100644 index c3e69386ade4..000000000000 --- a/resources/views/welcome.blade.php +++ /dev/null @@ -1,48 +0,0 @@ - - - Laravel - - - - - - -
-
-
Laravel 5
-
{{ Inspiring::quote() }}
-
-
- - diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php index 1ea4acd2606e..990a8389a7ee 100644 --- a/tests/ExampleTest.php +++ b/tests/ExampleTest.php @@ -9,9 +9,8 @@ class ExampleTest extends TestCase { */ public function testBasicExample() { - $response = $this->call('GET', '/'); - - $this->assertEquals(200, $response->getStatusCode()); + $crawler = $this->client->request('GET', '/'); + $this->assertTrue($this->client->getResponse()->isRedirect()); } -} +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index 69726c3b3d87..49b80fc274bc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,15 +5,15 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase { /** * Creates the application. * - * @return \Illuminate\Foundation\Application + * @return Symfony\Component\HttpKernel\HttpKernelInterface */ public function createApplication() { - $app = require __DIR__.'/../bootstrap/app.php'; + $unitTesting = true; - $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); + $testEnvironment = 'testing'; - return $app; + return require __DIR__.'/../../bootstrap/start.php'; } }