├── database ├── .gitignore ├── seeds │ ├── SalesTableSeeder.php │ ├── UserTableSeeder.php │ ├── FileCategoryTableSeeder.php │ ├── FileUploadsTableSeeder.php │ ├── FilesTableSeeder.php │ └── DatabaseSeeder.php ├── migrations │ ├── 2017_11_17_233343_create_roles_table.php │ ├── 2018_01_09_220606_create_images_table.php │ ├── 2018_01_22_045328_create_categories_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2018_01_01_233358_add_stripe_connect_columns_to_user_table.php │ ├── 2018_01_23_153819_create_notifications_table.php │ ├── 2017_11_17_233726_create_user_role_table.php │ ├── 2018_01_12_204240_create_comments_table.php │ ├── 2018_01_22_045422_create_category_file_table.php │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2017_10_29_002256_create_file_approvals_table.php │ ├── 2017_10_29_171614_create_uploads_table.php │ ├── 2017_12_24_001756_create_sales_table.php │ └── 2017_10_21_232741_create_files_table.php └── factories │ ├── FileCategoryFactory.php │ ├── UploadFactory.php │ ├── UserFactory.php │ ├── SaleFactory.php │ └── FileFactory.php ├── bootstrap ├── cache │ └── .gitignore └── app.php ├── storage ├── debugbar │ └── .gitignore ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── cache │ └── .gitignore │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── public ├── robots.txt ├── favicon.ico ├── images │ ├── home │ │ ├── default.png │ │ ├── dashboard.JPG │ │ ├── watermark.png │ │ ├── multiple-select.png │ │ └── process │ │ │ ├── create-files.svg │ │ │ ├── profits.svg │ │ │ ├── publish.svg │ │ │ ├── one.svg │ │ │ ├── signup.svg │ │ │ ├── connect-stripe.svg │ │ │ ├── four.svg │ │ │ ├── two.svg │ │ │ ├── three.svg │ │ │ └── five.svg │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-128x128.png │ │ ├── marketplace-logo.png │ │ └── avatar.svg ├── fonts │ └── vendor │ │ └── bootstrap-sass │ │ └── bootstrap │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 ├── mix-manifest.json ├── .htaccess ├── web.config └── index.php ├── Procfile ├── .travis.yml ├── config ├── marketplace.php ├── image.php ├── sentry.php ├── view.php ├── services.php ├── broadcasting.php └── filesystems.php ├── .gitattributes ├── app ├── Role.php ├── Category.php ├── Http │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── TrimStrings.php │ │ ├── AbortIfNotAdmin.php │ │ ├── Admin │ │ │ └── Impersonate.php │ │ ├── RedirectIfMarketplaceConnected.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── RedirectIfMarketplaceNotConnected.php │ │ └── TrustProxies.php │ ├── Controllers │ │ ├── Controller.php │ │ ├── Api │ │ │ └── FileController.php │ │ ├── HomeController.php │ │ ├── Admin │ │ │ ├── PreviewFileController.php │ │ │ ├── FileController.php │ │ │ ├── CategoriesController.php │ │ │ └── AdminController.php │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ └── ResetPasswordController.php │ │ ├── Account │ │ │ ├── EmailController.php │ │ │ ├── AvatarController.php │ │ │ ├── DatabaseNotificationsController.php │ │ │ └── MarketPlaceConnectController.php │ │ └── Checkout │ │ │ └── CheckoutController.php │ ├── Resources │ │ └── FilesCollection.php │ ├── ViewComposers │ │ ├── AdminStatsComposer.php │ │ └── AccountStatsComposer.php │ └── Requests │ │ ├── File │ │ └── UpdateFileRequest.php │ │ ├── Account │ │ ├── UpdateSettingsRequest.php │ │ ├── PasswordStoreRequest.php │ │ └── StoreAvatarFormRequest.php │ │ ├── Checkout │ │ └── FreeCheckoutRequest.php │ │ └── Category │ │ └── FileCategoryRequest.php ├── Policies │ ├── UploadPolicy.php │ └── FilePolicy.php ├── Listeners │ └── Checkout │ │ └── SendEmailToBuyer.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── AppServiceProvider.php │ ├── PaymentGatewayServiceProvider.php │ ├── RoleServiceProvider.php │ ├── AuthServiceProvider.php │ ├── EventServiceProvider.php │ ├── ComposerServiceProvider.php │ └── RouteServiceProvider.php ├── Events │ └── Checkout │ │ └── SaleCreated.php ├── Image.php ├── Traits │ ├── HasApprovals.php │ └── HasRoles.php ├── Rules │ ├── CurrentPassword.php │ └── Recaptcha.php ├── Mail │ └── Checkout │ │ └── SaleConfirmationToBuyer.php ├── Console │ └── Kernel.php ├── helpers.php ├── Notifications │ ├── PasswordChanged.php │ ├── FileChanges.php │ ├── UpdatedFileApproved.php │ ├── FileApproved.php │ ├── EmailVerification.php │ ├── FileRejected.php │ └── UpdatedFileRejection.php ├── Comment.php ├── FileApproval.php ├── Exceptions │ └── Handler.php ├── Upload.php ├── Jobs │ └── Checkout │ │ └── CreateSale.php └── Sale.php ├── after.sh ├── .gitignore ├── tests ├── TestCase.php ├── Unit │ └── ExampleTest.php ├── Feature │ └── ExampleTest.php └── CreatesApplication.php ├── nginx.conf ├── resources ├── views │ ├── admin │ │ ├── files │ │ │ ├── new │ │ │ │ ├── index.blade.php │ │ │ │ └── rejection.blade.php │ │ │ └── updated │ │ │ │ ├── index.blade.php │ │ │ │ └── rejection.blade.php │ │ ├── layouts │ │ │ └── default.blade.php │ │ ├── partials │ │ │ ├── _file_to_approve.blade.php │ │ │ ├── _file_updated_to_approve.blade.php │ │ │ ├── _stats.blade.php │ │ │ └── _navigation.blade.php │ │ ├── categories │ │ │ ├── index.blade.php │ │ │ ├── create.blade.php │ │ │ └── edit.blade.php │ │ └── users │ │ │ └── index.blade.php │ ├── account │ │ ├── files │ │ │ ├── index.blade.php │ │ │ └── notify │ │ │ │ └── index.blade.php │ │ ├── partials │ │ │ ├── _email_verification.blade.php │ │ │ └── _file.blade.php │ │ ├── layouts │ │ │ ├── default.blade.php │ │ │ └── _stats.blade.php │ │ ├── marketplace │ │ │ └── index.blade.php │ │ ├── notifications │ │ │ ├── show.blade.php │ │ │ ├── index.blade.php │ │ │ └── all-notifications.blade.php │ │ ├── bought │ │ │ └── index.blade.php │ │ └── password │ │ │ └── index.blade.php │ ├── layouts │ │ ├── plain.blade.php │ │ ├── partials │ │ │ ├── _flash.blade.php │ │ │ ├── _scripts.blade.php │ │ │ └── _footer.blade.php │ │ ├── app.blade.php │ │ └── home.blade.php │ ├── files │ │ ├── partials │ │ │ ├── _checkout_form_free.blade.php │ │ │ └── _checkout_form.blade.php │ │ └── index.blade.php │ ├── emails │ │ ├── checkout │ │ │ └── confirmation.blade.php │ │ └── layouts │ │ │ └── default.blade.php │ ├── vendor │ │ └── notifications │ │ │ └── email.blade.php │ └── auth │ │ └── passwords │ │ └── email.blade.php ├── lang │ └── en │ │ ├── pagination.php │ │ ├── auth.php │ │ └── passwords.php └── assets │ ├── js │ ├── app.js │ ├── components │ │ ├── files │ │ │ ├── partials │ │ │ │ └── File.vue │ │ │ └── Index.vue │ │ └── AvatarUpload.vue │ └── bootstrap.js │ └── sass │ └── _variables.scss ├── routes ├── channels.php ├── console.php └── api.php ├── server.php ├── .env.example ├── webpack.mix.js ├── phpunit.xml ├── package.json ├── Vagrantfile ├── artisan └── composer.json /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: $(composer config bin-dir)/heroku-php-nginx -C nginx.conf public/ 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '7.0' 4 | before_script: 5 | - composer install 6 | 7 | script: phpunit -------------------------------------------------------------------------------- /public/images/home/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/home/default.png -------------------------------------------------------------------------------- /config/marketplace.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'commission' => 20, 6 | 'remaining' => 80 7 | ] 8 | ]; -------------------------------------------------------------------------------- /public/images/home/dashboard.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/home/dashboard.JPG -------------------------------------------------------------------------------- /public/images/home/watermark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/home/watermark.png -------------------------------------------------------------------------------- /public/images/home/multiple-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/home/multiple-select.png -------------------------------------------------------------------------------- /public/images/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/images/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/images/icons/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/icons/favicon-128x128.png -------------------------------------------------------------------------------- /public/images/icons/marketplace-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidtrushkov/marketplace/HEAD/public/images/icons/marketplace-logo.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /config/image.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'relative' => public_path($relative = 'images/avatar'), 6 | 'absolute' => $relative 7 | ] 8 | ]; -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | routes.php 3 | schedule-* 4 | compiled.php 5 | services.json 6 | events.scanned.php 7 | routes.scanned.php 8 | down 9 | -------------------------------------------------------------------------------- /app/Role.php: -------------------------------------------------------------------------------- 1 | create(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css", 4 | "/js/plugins/multiple-select.js": "/js/plugins/multiple-select.js", 5 | "/css/plugins/multiple-select.css": "/css/plugins/multiple-select.css" 6 | } -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | location / { 2 | # try to serve file directly, fallback to rewrite 3 | try_files $uri @rewriteapp; 4 | } 5 | 6 | location @rewriteapp { 7 | # rewrite all to app.php 8 | rewrite ^(.*)$ /index.php$1 last; 9 | } 10 | 11 | client_max_body_size 100m; -------------------------------------------------------------------------------- /app/Category.php: -------------------------------------------------------------------------------- 1 | belongsToMany(File::class); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /resources/views/admin/files/new/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 | @if($files->count()) 5 | @each('admin.partials._file_to_approve', $files, 'file') 6 | @else 7 |

There are no new files waiting for approval.

8 | @endif 9 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/files/updated/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 | @if($files->count()) 5 | @each('admin.partials._file_updated_to_approve', $files, 'file') 6 | @else 7 |

There are no updated files waiting for approval.

8 | @endif 9 | @endsection -------------------------------------------------------------------------------- /database/seeds/UserTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/seeds/FileCategoryTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/seeds/FileUploadsTableSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/views/account/files/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 | @if($files->count()) 6 | @each('account.partials._file', $files, 'file') 7 | @else 8 |

You have no files.

9 | @endif 10 |
11 | @endsection -------------------------------------------------------------------------------- /config/sentry.php: -------------------------------------------------------------------------------- 1 | env('SENTRY_DSN'), 5 | 6 | // capture release as git sha 7 | // 'release' => trim(exec('git log --pretty="%h" -n1 HEAD')), 8 | 9 | // Capture bindings on SQL queries 10 | 'breadcrumbs.sql_bindings' => true, 11 | 12 | // Capture default user context 13 | 'user_context' => true, 14 | ); 15 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | create(); 16 | factory(App\Comment::class, 400)->create(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/views/layouts/plain.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @include('layouts.partials._head') 5 | 6 | 7 | 8 |
9 | @include('layouts.partials._navigation') 10 | @yield('content') 11 |
12 | 13 | @include('layouts.partials._footer') 14 | @include('layouts.partials._scripts') 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/Policies/UploadPolicy.php: -------------------------------------------------------------------------------- 1 | id == $upload->user_id; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | readyToBeShown()->paginate(self::PERPAGE)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Listeners/Checkout/SendEmailToBuyer.php: -------------------------------------------------------------------------------- 1 | sale->buyer_email)->send(new SaleConfirmationToBuyer($event->sale)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/Controllers/HomeController.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | sale = $sale; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Image.php: -------------------------------------------------------------------------------- 1 | path; 17 | } 18 | 19 | 20 | /** 21 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 22 | */ 23 | public function user() { 24 | return $this->belongsTo(User::class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Middleware/AbortIfNotAdmin.php: -------------------------------------------------------------------------------- 1 | user()->hasRole('admin')) { 19 | return abort(404); 20 | } 21 | return $next($request); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Policies/FilePolicy.php: -------------------------------------------------------------------------------- 1 | id == $file->user_id; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Http/ViewComposers/AdminStatsComposer.php: -------------------------------------------------------------------------------- 1 | with([ 18 | 'fileCount' => File::finished()->approved()->count(), 19 | 'saleCount' => Sale::count(), 20 | 'lifetimeCommission' => Sale::lifetimeCommission(), 21 | 'thisMonthCommission' => Sale::commissionThisMonth() 22 | ]); 23 | } 24 | } -------------------------------------------------------------------------------- /app/Http/Middleware/Admin/Impersonate.php: -------------------------------------------------------------------------------- 1 | has('impersonate')) { 19 | \Auth::onceUsingId(session('impersonate')); 20 | } 21 | 22 | return $next($request); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 16 | }); 17 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | '' 23 | ]); 24 | } 25 | 26 | public function messages() { 27 | return parent::messages(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 18 | })->describe('Display an inspiring quote'); 19 | -------------------------------------------------------------------------------- /app/Traits/HasApprovals.php: -------------------------------------------------------------------------------- 1 | where('approved', true); 12 | } 13 | 14 | 15 | public function scopeUnapproved(Builder $builder) { 16 | return $builder->where('approved', false); 17 | } 18 | 19 | 20 | public function scopeWithoutPreviewFiles(Builder $builder) { 21 | return $builder->where('preview', false); 22 | } 23 | 24 | public function scopeWithPreviewFiles(Builder $builder) { 25 | return $builder->where('preview', true); 26 | } 27 | } -------------------------------------------------------------------------------- /resources/views/layouts/partials/_flash.blade.php: -------------------------------------------------------------------------------- 1 | @if(session()->has('success')) 2 | 6 | @endif 7 | 8 | @if(session()->has('error')) 9 | 13 | @endif -------------------------------------------------------------------------------- /app/Providers/PaymentGatewayServiceProvider.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfMarketplaceConnected.php: -------------------------------------------------------------------------------- 1 | user()->stripe_id) { 20 | return redirect()->route('account'); 21 | } 22 | 23 | return $next($request); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 17 | return $request->user(); 18 | }); 19 | 20 | //Route::get('/files', 'Api\FileController@index'); 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 21 | return redirect('/home'); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfMarketplaceNotConnected.php: -------------------------------------------------------------------------------- 1 | user()->stripe_id) { 20 | return redirect()->route('account.connect'); 21 | } 22 | 23 | return $next($request); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Redirect Trailing Slashes If Not A Folder... 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteCond %{REQUEST_URI} (.+)/$ 11 | RewriteRule ^ %1 [L,R=301] 12 | 13 | # Handle Front Controller... 14 | RewriteCond %{REQUEST_FILENAME} !-d 15 | RewriteCond %{REQUEST_FILENAME} !-f 16 | RewriteRule ^ index.php [L] 17 | 18 | # Handle Authorization Header 19 | RewriteCond %{HTTP:Authorization} . 20 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 21 | 22 | -------------------------------------------------------------------------------- /app/Http/Requests/Account/UpdateSettingsRequest.php: -------------------------------------------------------------------------------- 1 | user(); 27 | 28 | return [ 29 | 'name' => 'required|string|max:25|unique:users,name,'.$user->id, 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Requests/Checkout/FreeCheckoutRequest.php: -------------------------------------------------------------------------------- 1 | 'required|email' 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Traits/HasRoles.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Role::class, 'user_role'); 15 | } 16 | 17 | 18 | /** 19 | * Check if the current user is attached to any roles in the 'roles' table. 20 | * If not, return false, else, return true. 21 | * @param $role 22 | * 23 | * @return bool 24 | */ 25 | public function hasRole($role) { 26 | if (!$this->roles->contains('name', $role)) { 27 | return false; 28 | } 29 | 30 | return true; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /app/Providers/RoleServiceProvider.php: -------------------------------------------------------------------------------- 1 | check() && auth()->user()->hasRole($role); 19 | }); 20 | } 21 | 22 | /** 23 | * Register the application services. 24 | * 25 | * @return void 26 | */ 27 | public function register() 28 | { 29 | // 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/views/files/partials/_checkout_form_free.blade.php: -------------------------------------------------------------------------------- 1 |
2 | {{ csrf_field() }} 3 | 4 |
5 | 6 | @if ($errors->has('email')) 7 | 8 | {{ $errors->first('email') }} 9 | 10 | @endif 11 |
12 | 13 |
14 | 15 |
16 | 17 |
-------------------------------------------------------------------------------- /public/images/home/process/create-files.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Http/Requests/Category/FileCategoryRequest.php: -------------------------------------------------------------------------------- 1 | id); 28 | 29 | return [ 30 | 'name' => 'required|string|max:25|unique:categories,name,'.$category->id, 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/views/account/partials/_email_verification.blade.php: -------------------------------------------------------------------------------- 1 | @if(auth()->user()->isVerified()) 2 |
3 |
4 |

Please verify your email address.

5 | 6 |
7 |
8 | {{ csrf_field() }} 9 | 10 | 11 |
12 |
13 |
14 |
15 | @endif -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Marketplace 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_LOG_LEVEL=debug 6 | APP_URL=http://localhost 7 | 8 | DB_CONNECTION=mysql 9 | DB_HOST=127.0.0.1 10 | DB_PORT= 11 | DB_DATABASE= 12 | DB_USERNAME= 13 | DB_PASSWORD= 14 | 15 | BROADCAST_DRIVER=log 16 | CACHE_DRIVER=file 17 | SESSION_DRIVER=file 18 | QUEUE_DRIVER=sync 19 | 20 | REDIS_HOST=127.0.0.1 21 | REDIS_PASSWORD=null 22 | REDIS_PORT=6379 23 | 24 | MAIL_DRIVER=smtp 25 | MAIL_HOST=smtp.mailtrap.io 26 | MAIL_PORT=2525 27 | MAIL_USERNAME=null 28 | MAIL_PASSWORD=null 29 | MAIL_ENCRYPTION=null 30 | 31 | PUSHER_APP_ID= 32 | PUSHER_APP_KEY= 33 | PUSHER_APP_SECRET= 34 | 35 | SENTRY_DSN= 36 | 37 | STRIPE_KEY= 38 | STRIPE_SECRET= 39 | STRIPE_CONNECT_KEY= 40 | 41 | RECAPTCHA_SECRET= 42 | -------------------------------------------------------------------------------- /database/migrations/2017_11_17_233343_create_roles_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('roles'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Rules/CurrentPassword.php: -------------------------------------------------------------------------------- 1 | user()->password); 21 | } 22 | 23 | /** 24 | * Get the validation error message. 25 | * 26 | * @return string 27 | */ 28 | public function message() 29 | { 30 | return 'Your current password you entered is incorrect.'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/views/emails/checkout/confirmation.blade.php: -------------------------------------------------------------------------------- 1 | @extends('emails.layouts.default') 2 | 3 | @section('content') 4 |
5 |
6 | Download Icon 7 |
8 |

9 | 10 |

Thanks for downloading: {{ $sale->file->title }} from Marketplace

11 | 12 |

13 | 14 | Download your file 15 | 16 |

17 | 18 |

Or, copy and paste this into your browser.
19 | {{ route('files.download', [$sale->file, $sale]) }} 20 |

21 | @endsection -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'App\Policies\FilePolicy', 17 | 'App\Upload' => 'App\Policies\UploadPolicy', 18 | ]; 19 | 20 | /** 21 | * Register any authentication / authorization services. 22 | * 23 | * @return void 24 | */ 25 | public function boot() 26 | { 27 | $this->registerPolicies(); 28 | 29 | // 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'App\Listeners\Checkout\SendEmailToBuyer', 18 | ], 19 | ]; 20 | 21 | /** 22 | * Register any events for your application. 23 | * 24 | * @return void 25 | */ 26 | public function boot() 27 | { 28 | parent::boot(); 29 | 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/views/admin/layouts/default.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 | 8 |
9 |
10 | @include('admin.partials._navigation') 11 |
12 |
13 | @include('layouts.partials._flash') 14 | @yield('admin.content') 15 |
16 |
17 |
18 | @include('admin.partials._stats') 19 |
20 |
21 | @endsection -------------------------------------------------------------------------------- /public/images/home/process/profits.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Http/Requests/Account/PasswordStoreRequest.php: -------------------------------------------------------------------------------- 1 | ['required', new CurrentPassword()], 29 | 'password' => 'required|min:6|max:16|confirmed', 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Mail/Checkout/SaleConfirmationToBuyer.php: -------------------------------------------------------------------------------- 1 | sale = $sale; 26 | } 27 | 28 | /** 29 | * Build the message. 30 | * 31 | * @return $this 32 | */ 33 | public function build() 34 | { 35 | return $this->subject('Your purchase confirmation')->view('emails.checkout.confirmation'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel application. By default, we are compiling the Sass 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | mix.js('resources/assets/js/app.js', 'public/js') 15 | .sass('resources/assets/sass/app.scss', 'public/css') 16 | .js('resources/assets/js/plugins/multiple-select.js', 'public/js/plugins/multiple-select.js'); 17 | 18 | mix.styles([ 19 | 'resources/assets/css/multiple-select.css', 20 | ], 'public/css/plugins/multiple-select.css'); -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | 'FORWARDED', 24 | Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR', 25 | Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST', 26 | Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT', 27 | Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO', 28 | ]; 29 | } 30 | -------------------------------------------------------------------------------- /app/Providers/ComposerServiceProvider.php: -------------------------------------------------------------------------------- 1 | 2 |

{{ $file->title }}

3 |
{{ str_limit($file->overview_short, 150) }}
4 |
5 | 6 | Preview file 7 | 8 | 9 | Approve 10 | 11 | 12 | 16 | 17 | 18 | Reject 19 | 20 | 21 | -------------------------------------------------------------------------------- /resources/views/account/layouts/default.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 | 8 |
9 | @include('account.partials._email_verification') 10 |
11 | @include('account.partials._navigation') 12 |
13 |
14 | @include('layouts.partials._flash') 15 | @yield('account.content') 16 |
17 |
18 |
19 | @include('account.layouts._stats') 20 |
21 |
22 | @endsection -------------------------------------------------------------------------------- /database/migrations/2018_01_09_220606_create_images_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('path'); 19 | $table->integer('user_id')->nullable(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('images'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2018_01_22_045328_create_categories_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 25)->unique(); 19 | $table->string('slug'); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('categories'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/views/admin/partials/_file_updated_to_approve.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

{{ $file->title }}

3 |
{{ str_limit($file->overview_short, 150) }}
4 |
5 | 6 | Preview changes 7 | 8 | 9 | Approve 10 | 11 | 12 | 16 | 17 | 18 | Reject 19 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/views/layouts/partials/_scripts.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Passwords must be at least six characters and match the confirmation.', 17 | 'reset' => 'Your password has been reset!', 18 | 'sent' => 'We have e-mailed your password reset link!', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that e-mail address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/views/files/partials/_checkout_form.blade.php: -------------------------------------------------------------------------------- 1 |
2 | {{ csrf_field() }} 3 |

${{ $file->price }}

4 | 17 | @if(auth()->user() && $currentUserOwnsThisFile > 0) 18 |
19 |

You already own this file

20 | @endif 21 |
-------------------------------------------------------------------------------- /app/Http/Requests/Account/StoreAvatarFormRequest.php: -------------------------------------------------------------------------------- 1 | 'image|max:2048' 28 | ]; 29 | } 30 | 31 | public function messages() { 32 | return [ 33 | 'avatar.image' => 'Not a valid image.', 34 | 'avatar.max' => 'Maximum size of image can only be 2MB.' 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2018_01_01_233358_add_stripe_connect_columns_to_user_table.php: -------------------------------------------------------------------------------- 1 | string('stripe_id')->nullable(); 18 | $table->string('stripe_key')->nullable(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table('users', function (Blueprint $table) { 30 | $table->dropColumn('stripe_id', 'stripe_key'); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Http/ViewComposers/AccountStatsComposer.php: -------------------------------------------------------------------------------- 1 | user(); 17 | 18 | // Grab user sales. 19 | $sales = $user->sales; 20 | 21 | // Grab user files that are 'finished'. 22 | // -- "finished" scope on 'File' model 23 | $files = $user->files()->finished(); 24 | 25 | // Grab the sales earned overall for this authenticated user. 26 | // -- "saleValueOverLifetime" coming from 'User' model. 27 | $lifetimeEarned = $user->saleValueOverLifetime(); 28 | 29 | $view->with([ 30 | 'fileCount' => $files->count(), 31 | 'saleCount' => $sales->count(), 32 | 'thisMonthEarned' => $user->saleValueThisMonth(), 33 | 'lifetimeEarned' => $lifetimeEarned 34 | ]); 35 | } 36 | } -------------------------------------------------------------------------------- /app/Rules/Recaptcha.php: -------------------------------------------------------------------------------- 1 | post('https://www.google.com/recaptcha/api/siteverify', [ 21 | 'secret' => config('services.recaptcha.secret'), 22 | 'response' => $value, 23 | 'remoteip' => request()->ip() 24 | ]); 25 | 26 | return $response->json()['success']; 27 | } 28 | 29 | 30 | /** 31 | * Get the validation error message. 32 | * 33 | * @return string 34 | */ 35 | public function message() 36 | { 37 | return 'The recaptcha verification failed. Try again..'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/migrations/2018_01_23_153819_create_notifications_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 18 | $table->string('type'); 19 | $table->morphs('notifiable'); 20 | $table->text('data'); 21 | $table->timestamp('read_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('notifications'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2017_11_17_233726_create_user_role_table.php: -------------------------------------------------------------------------------- 1 | integer('user_id')->unsigned()->index(); 18 | $table->integer('role_id')->unsigned(); 19 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 20 | $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('user_role'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Http/Controllers/Admin/PreviewFileController.php: -------------------------------------------------------------------------------- 1 | visible() ) { 22 | return abort( 404 ); 23 | } 24 | 25 | $fileUploads = $file->uploads()->withoutPreviewFiles()->latest()->get(); 26 | 27 | $uploadPreviews = $file->uploads()->withPreviewFiles()->latest()->get(); 28 | 29 | $updatedChanges = $approvals = $file->approvals->first(); 30 | 31 | return view( 'admin.previews.index', compact('file', 'fileUploads', 'uploadPreviews', 'updatedChanges')); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/factories/FileCategoryFactory.php: -------------------------------------------------------------------------------- 1 | define(App\Category::class, function (Faker $faker) { 17 | return [ 18 | 'name' => $slug = $faker->unique()->randomElement($array = array ('Code', 'Templates', 'Photoshop', 'Images', 'Graphics', 'Icons', 'Fonts')), 19 | 'slug' => str_slug($slug), 20 | 'created_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null), 21 | 'updated_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null) 22 | ]; 23 | }); 24 | -------------------------------------------------------------------------------- /public/images/home/process/publish.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Http/Controllers/Admin/FileController.php: -------------------------------------------------------------------------------- 1 | replaceFilePropertiesWithUnapprovedChanges($file); 20 | 21 | return view('files.show',[ 22 | 'file' => $file, 23 | 'uploads' => $file->uploads 24 | ]); 25 | } 26 | 27 | 28 | /** 29 | * Get the unapproved changes made to the file 30 | * @param File $file 31 | * 32 | * @return File 33 | */ 34 | protected function replaceFilePropertiesWithUnapprovedChanges(File $file) { 35 | if ($file->approvals->count()) { 36 | $file->fill($file->approvals->first()->toArray()); 37 | } 38 | 39 | return $file; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire') 28 | // ->hourly(); 29 | } 30 | 31 | /** 32 | * Register the commands for the application. 33 | * 34 | * @return void 35 | */ 36 | protected function commands() 37 | { 38 | $this->load(__DIR__.'/Commands'); 39 | 40 | require base_path('routes/console.php'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/helpers.php: -------------------------------------------------------------------------------- 1 | user = $user; 21 | } 22 | 23 | /** 24 | * Get the notification's delivery channels. 25 | * 26 | * @param mixed $notifiable 27 | * @return array 28 | */ 29 | public function via($notifiable) { 30 | return ['mail']; 31 | } 32 | 33 | 34 | public function toMail($notifiable) { 35 | 36 | $image = '/images/icons/password.svg'; 37 | $imageAlt = 'Your password has been changed'; 38 | 39 | return (new MailMessage) 40 | ->markdown('vendor.notifications.email', ['image' => $image, 'imageAlt' => $imageAlt]) 41 | ->line('Your password has been changed successfully'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Comment.php: -------------------------------------------------------------------------------- 1 | morphTo(); 17 | } 18 | 19 | 20 | /** 21 | * A comment belongs to a user. 22 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 23 | */ 24 | public function user() { 25 | return $this->belongsTo(User::class); 26 | } 27 | 28 | 29 | /** 30 | * A comment has many replies, and references "parent_id" 31 | * @return \Illuminate\Database\Eloquent\Relations\HasMany 32 | */ 33 | public function replies() { 34 | return $this->hasMany(Comment::class, 'parent_id'); 35 | } 36 | 37 | 38 | /** 39 | * A 'parent' comment belongs to the "parent_id". 40 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 41 | */ 42 | public function parent() { 43 | return $this->belongsTo(Comment::class, 'parent_id'); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /database/migrations/2018_01_12_204240_create_comments_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned()->index(); 19 | $table->integer('parent_id')->unsigned()->index()->nullable(); 20 | $table->text('body', 2500); 21 | $table->integer('commentable_id')->nullable(); 22 | $table->string('commentable_type')->nullable(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('comments'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/FileApproval.php: -------------------------------------------------------------------------------- 1 | file->approvals->each->delete(); 35 | }); 36 | } 37 | 38 | 39 | /** 40 | * An approval belongs to a file. 41 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 42 | */ 43 | public function file() { 44 | return $this->belongsTo(File::class); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /resources/views/admin/categories/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 | 5 |
6 | Create Category 7 |


8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | @foreach($categories as $category) 19 | 20 | 23 | 28 | 29 | @endforeach 30 | 31 |
Name
21 | {{ $category->name }} 22 | 24 | 25 | 26 | 27 |
32 | 33 | {{ $categories->render() }} 34 | 35 | @endsection -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | middleware('guest')->except('logout'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => realpath(storage_path('framework/views')), 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /database/migrations/2018_01_22_045422_create_category_file_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('category_id')->unsigned(); 19 | $table->integer('file_id')->unsigned(); 20 | $table->foreign('category_id')->references('id')->on('categories')->onUpdate('cascade')->onDelete('cascade'); 21 | $table->foreign('file_id')->references('id')->on('files')->onUpdate('cascade')->onDelete('cascade'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('category_file'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/Feature 14 | 15 | 16 | 17 | ./tests/Unit 18 | 19 | 20 | 21 | 22 | ./app 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 25)->unique(); 19 | $table->string('email')->unique(); 20 | $table->string('password'); 21 | $table->boolean('verified')->default(false); 22 | $table->string('token')->nullable(); 23 | $table->string('avatar')->nullable(); 24 | $table->boolean('user_notifications')->default(false); 25 | $table->rememberToken(); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('users'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/seeds/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | insert([ 15 | 'name' => 'admin' 16 | ]); 17 | 18 | DB::table('users')->insert([ 19 | 'name' => 'David Trushkov', 20 | 'email' => 'davidisback4good@hotmail.com', 21 | 'password' => bcrypt('d16331633'), 22 | 'remember_token' => str_random(10), 23 | 'verified' => 1, 24 | 'token' => '', 25 | 'avatar' => '', 26 | 'created_at' => \Carbon\Carbon::now()->format('Y-m-d H:i:s'), 27 | 'updated_at' => \Carbon\Carbon::now()->format('Y-m-d H:i:s'), 28 | ]); 29 | 30 | DB::table('user_role')->insert([ 31 | 'user_id' => 1, 32 | 'role_id' => 1 33 | ]); 34 | 35 | $this->call(UserTableSeeder::class); 36 | $this->call(FilesTableSeeder::class); 37 | $this->call(FileUploadsTableSeeder::class); 38 | $this->call(SalesTableSeeder::class); 39 | $this->call(FileCategoryTableSeeder::class); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @include('layouts.partials._head') 5 | 6 | 7 | 27 | 28 |
29 | @if (!in_array(Route::currentRouteName(), ['login', 'register'])) 30 | @include('layouts.partials._navigation') 31 | @endif 32 | 33 | @yield('content') 34 |
35 | 36 | @include('layouts.partials._footer') 37 | @include('layouts.partials._scripts') 38 | @yield('scripts') 39 | 40 | 41 | -------------------------------------------------------------------------------- /database/factories/UploadFactory.php: -------------------------------------------------------------------------------- 1 | define(App\Upload::class, function (Faker $faker) { 17 | 18 | $user_id = App\File::all()->random()->user_id; 19 | 20 | $file_id = \App\File::where('user_id', '=', $user_id)->first(); 21 | 22 | return [ 23 | 'user_id' => $user_id, 24 | 'file_id' => $file_id, 25 | 'filename' => $faker->image('public/images/fake',1000,700, null, false), 26 | 'size' => $faker->numberBetween($min = 1000, $max = 1000000), 27 | 'approved' => rand(0, 1), 28 | 'preview' => rand(0, 1), 29 | 'created_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null), 30 | 'updated_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null) 31 | ]; 32 | }); -------------------------------------------------------------------------------- /database/migrations/2017_10_29_002256_create_file_approvals_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('file_id')->unsigned()->index(); 19 | $table->foreign('file_id')->references('id')->on('files')->onDelete('cascade'); 20 | $table->string('title'); 21 | $table->string('overview_short', 300); 22 | $table->text('overview'); 23 | $table->string('youtube_url')->nullable(); 24 | $table->string('vimeo_url')->nullable(); 25 | $table->softDeletes(); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('file_approvals'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /resources/assets/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * First we will load all of this project's JavaScript dependencies which 4 | * includes Vue and other libraries. It is a great starting point when 5 | * building robust, powerful web applications using Vue and Laravel. 6 | */ 7 | 8 | require('./bootstrap'); 9 | 10 | window.Vue = require('vue'); 11 | 12 | import VueRouter from 'vue-router' 13 | Vue.use(VueRouter) 14 | 15 | /** 16 | * Next, we will create a fresh Vue application instance and attach it to 17 | * the page. Then, you may begin adding components to this application 18 | * or customize the JavaScript scaffolding to fit your unique needs. 19 | */ 20 | 21 | Vue.component('avatar-upload', require('./components/AvatarUpload.vue')); 22 | 23 | // const FileIndex = require('./components/files/Index.vue'); 24 | 25 | // const routes = [ 26 | // { 27 | // path: '/files', 28 | // name: 'files.index', 29 | // component: FileIndex 30 | // } 31 | // ]; 32 | // 33 | // 34 | // const router = new VueRouter({ 35 | // mode: 'history', 36 | // routes 37 | // }); 38 | 39 | const app = new Vue({ 40 | el: '#app', 41 | //router 42 | }); 43 | 44 | $(function () { 45 | $('[data-toggle="tooltip"]').tooltip() 46 | }) -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | define(App\User::class, function (Faker $faker) { 17 | 18 | static $password; 19 | 20 | return [ 21 | 'name' => $faker->unique()->firstName, 22 | 'email' => $faker->unique()->safeEmail, 23 | 'password' => $password ?: $password = bcrypt('secret'), 24 | 'remember_token' => str_random(10), 25 | 'verified' => 1, 26 | 'token' => '', 27 | 'avatar' => $faker->image('public/images/avatars',1000,700, null, false), 28 | 'stripe_id' => '', 29 | 'stripe_key' => '', 30 | 'created_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null), 31 | 'updated_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null) 32 | ]; 33 | }); -------------------------------------------------------------------------------- /public/images/home/process/one.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | ], 21 | 22 | 'ses' => [ 23 | 'key' => env('SES_KEY'), 24 | 'secret' => env('SES_SECRET'), 25 | 'region' => 'us-east-1', 26 | ], 27 | 28 | 'sparkpost' => [ 29 | 'secret' => env('SPARKPOST_SECRET'), 30 | ], 31 | 32 | 'stripe' => [ 33 | 'model' => App\User::class, 34 | 'key' => env('STRIPE_KEY'), 35 | 'secret' => env('STRIPE_SECRET'), 36 | ], 37 | 38 | 'stripe_connect' => [ 39 | 'key' => env('STRIPE_CONNECT_KEY'), 40 | ], 41 | 42 | 'recaptcha' => [ 43 | 'secret' => env('RECAPTCHA_SECRET') 44 | ] 45 | ]; 46 | -------------------------------------------------------------------------------- /resources/views/account/partials/_file.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{ $file->title }} 4 |

5 | 6 |
7 | 8 | {{ $file->isFree() ? 'Free' : '$' . $file->price }} 9 | 10 | @if(!$file->approved) 11 | 12 | Pending approval 13 | 14 | @endif 15 | 16 | 17 | {{ $file->live ? 'Live' : 'Not Live' }} 18 | 19 | 20 | Make changes 21 | 22 | {{ $file->sales->count() }} {{ str_plural('sale', $file->sales->count()) }} 23 | @if($file->sales->count() > 0) 24 | 27 | 28 |
29 | Send Notification Users 30 |
31 | @endif 32 |
33 | -------------------------------------------------------------------------------- /public/images/home/process/signup.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/layouts/home.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @include('layouts.partials._head') 5 | 6 | @if(Route::current()->getName() == 'home') 7 | 15 | @endif 16 | 17 | 18 | 19 | 39 | 40 |
41 | @include('layouts.partials._navigation') 42 | 43 | @yield('content') 44 |
45 | 46 | @include('layouts.partials._footer') 47 | @include('layouts.partials._scripts') 48 | 49 | 50 | -------------------------------------------------------------------------------- /database/migrations/2017_10_29_171614_create_uploads_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned()->index(); 19 | $table->integer('file_id')->unsigned()->index(); 20 | $table->string('filename'); 21 | $table->bigInteger('size'); 22 | $table->boolean('approved')->default(false); 23 | $table->boolean('preview')->default(false); 24 | $table->softDeletes(); 25 | $table->timestamps(); 26 | 27 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 28 | $table->foreign('file_id')->references('id')->on('files')->onDelete('cascade'); 29 | }); 30 | } 31 | 32 | /** 33 | * Reverse the migrations. 34 | * 35 | * @return void 36 | */ 37 | public function down() 38 | { 39 | Schema::dropIfExists('uploads'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /resources/views/admin/categories/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 | 5 |
6 |
7 | create a file category 8 |
9 |
10 | {{ csrf_field() }} 11 | 12 |
13 | 14 |
15 | 16 | 17 | @if($errors->has('name')) 18 | {{ $errors->first('name') }} 19 | @endif 20 |
21 | 22 |
23 | 24 |
25 | Back   26 | 27 |
28 |

29 |
30 |
31 |
32 | 33 | @endsection -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.16.2", 14 | "bootstrap-sass": "^3.3.7", 15 | "cross-env": "^5.0.1", 16 | "jquery": "^3.1.1", 17 | "laravel-mix": "^1.0", 18 | "lodash": "^4.17.4", 19 | "vue": "^2.1.10" 20 | }, 21 | "dependencies": { 22 | "dropzone": "^5.2.0", 23 | "hover.css": "^2.2.1", 24 | "trix": "^0.11.1", 25 | "vue-router": "^3.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/EmailController.php: -------------------------------------------------------------------------------- 1 | firstOrFail()->confirmEmail(); 25 | 26 | \Auth::logout(); 27 | 28 | // Flash a success message saying user has been confirmed. 29 | return redirect('/login')->withSuccess('You are now confirmed. You can now login.'); 30 | } 31 | 32 | 33 | /** 34 | * Send a email verification code to user if they have not gotten the email when they registered. 35 | * 36 | * @return \Illuminate\Http\RedirectResponse 37 | */ 38 | public function confirmEmailAgain() { 39 | 40 | $user = auth()->user(); 41 | 42 | $user->notify(new EmailVerification($user)); 43 | 44 | return back()->withSuccess('Email verification code sent to your email. Please check spam folder to.'); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /public/images/home/process/connect-stripe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/emails/layouts/default.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | 24 | 25 |
26 |
27 |

Marketplace

28 |
29 | 30 |
31 | @yield('content') 32 | 33 |

Thanks,
{{ config('app.name') }}

34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/views/admin/categories/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 | 5 |
6 |
7 | edit file category 8 |
9 |
10 | {{ csrf_field() }} 11 | 12 |
13 | 14 |
15 | 16 | 17 | @if($errors->has('name')) 18 | {{ $errors->first('name') }} 19 | @endif 20 |
21 | 22 |
23 | 24 |
25 | Back   26 | 27 |
28 |

29 |
30 |
31 |
32 | 33 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/files/new/rejection.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 |
5 |
6 | Give a reason for rejecting this file so the user knows what to fix? 7 |
8 |
9 |
10 | {{ csrf_field() }} 11 | 12 |
13 | 14 | 15 | @if ($errors->has('data')) 16 | 17 | {{ $errors->first('data') }} 18 | 19 | @endif 20 |
21 | 22 |
23 | 24 |
25 | Go Back 26 | 27 |
28 | 29 |
30 |
31 |
32 | @endsection -------------------------------------------------------------------------------- /database/factories/SaleFactory.php: -------------------------------------------------------------------------------- 1 | define(App\Sale::class, function (Faker $faker) { 17 | 18 | $userId = \App\File::all()->random()->user_id; 19 | 20 | $fileId = \App\File::where('user_id', $userId)->first(); 21 | 22 | $boughtUserId = App\User::all()->random()->id; 23 | 24 | $buyerEmail = App\User::where('id', $boughtUserId)->first(); 25 | 26 | return [ 27 | 'identifier' => uniqid(), 28 | 'user_id' => $userId, 29 | 'bought_user_id' => App\User::all()->random()->id, 30 | 'file_id' => $fileId->id, 31 | 'buyer_email' => $buyerEmail->email, 32 | 'sale_price' => $faker->randomFloat($nbMaxDecimals = 2, $min = 0, $max = 125), 33 | 'sale_commission' => $faker->randomFloat($nbMaxDecimals = 2, $min = 0, $max = 50), 34 | 'created_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null), 35 | 'updated_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null) 36 | ]; 37 | }); -------------------------------------------------------------------------------- /resources/views/admin/files/updated/rejection.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 |
5 |
6 | Give a reason for rejecting this file so the user knows what to fix? 7 |
8 |
9 |
10 | {{ csrf_field() }} 11 | 12 |
13 | 14 | 15 | @if ($errors->has('data')) 16 | 17 | {{ $errors->first('data') }} 18 | 19 | @endif 20 |
21 | 22 |
23 | 24 |
25 | Go Back 26 | 27 |
28 | 29 |
30 |
31 |
32 | @endsection -------------------------------------------------------------------------------- /database/migrations/2017_12_24_001756_create_sales_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('identifier')->unique(); 19 | $table->integer('user_id')->unsigned()->index()->nullable(); 20 | $table->integer('bought_user_id')->unsigned()->index()->nullable(); 21 | $table->integer('file_id')->unsigned()->index()->nullable(); 22 | $table->string('buyer_email'); 23 | $table->decimal('sale_price', 6, 2); 24 | $table->decimal('sale_commission', 6, 2); 25 | $table->timestamps(); 26 | 27 | $table->foreign('user_id')->references('id')->on('users')->onDelete('set null'); 28 | $table->foreign('file_id')->references('id')->on('files')->onDelete('set null'); 29 | $table->foreign('bought_user_id')->references('id')->on('users')->onDelete('set null'); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | * 36 | * @return void 37 | */ 38 | public function down() 39 | { 40 | Schema::dropIfExists('sales'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Notifications/FileChanges.php: -------------------------------------------------------------------------------- 1 | data = $data; 26 | $this->header = $header; 27 | $this->owner = $owner; 28 | $this->file = $file; 29 | } 30 | 31 | 32 | /** 33 | * Get the notification's delivery channels. 34 | * 35 | * @param mixed $notifiable 36 | * @return array 37 | */ 38 | public function via($notifiable) { 39 | return ['database']; 40 | } 41 | 42 | 43 | /** 44 | * Get the array representation of the notification. 45 | * 46 | * @param mixed $notifiable 47 | * @return array 48 | */ 49 | public function toArray($notifiable) { 50 | return [ 51 | 'header' => isset($this->header) ? $this->header : 'File updated for: '.$this->file->title, 52 | 'data' => $this->data, 53 | 'file' => $this->file->title, 54 | 'slug' => $this->file->identifier, 55 | 'owner_avatar' => $this->owner->avatar, 56 | 'owner_name' => $this->owner->name 57 | ]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /public/images/home/process/four.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /resources/views/account/layouts/_stats.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 21 | 22 |
23 |

Files: {{ $fileCount }}

24 |
25 |
26 |

Sales: {{ $saleCount }}

27 |
28 |
29 |

Sales This Month: ${{ $thisMonthEarned }}

30 |
31 |
32 |

Lifetime Sales: ${{ $lifetimeEarned }}

33 |
34 |
35 |
36 |
-------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | bound('sentry') && $this->shouldReport($exception)) { 40 | app('sentry')->captureException($exception); 41 | } 42 | 43 | parent::report($exception); 44 | } 45 | 46 | /** 47 | * Render an exception into an HTTP response. 48 | * 49 | * @param \Illuminate\Http\Request $request 50 | * @param \Exception $exception 51 | * @return \Illuminate\Http\Response 52 | */ 53 | public function render($request, Exception $exception) 54 | { 55 | return parent::render($request, $exception); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/images/home/process/two.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /resources/views/admin/partials/_stats.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 21 | 22 |
23 |

Total Files: {{ $fileCount }}

24 |
25 |
26 |

Total Sales: {{ $saleCount }}

27 |
28 |
29 |

Commission This Month: ${{ $thisMonthCommission }}

30 |
31 |
32 |

Lifetime Commission: ${{ $lifetimeCommission }}

33 |
34 |
35 |
36 |
-------------------------------------------------------------------------------- /database/migrations/2017_10_21_232741_create_files_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('identifier')->unique(); 19 | $table->integer('user_id')->unsigned()->index(); 20 | $table->string('title', 100); 21 | $table->string('overview_short', 300); 22 | $table->text('overview'); 23 | $table->decimal('price', 6, 2); 24 | $table->boolean('live')->default(false); 25 | $table->boolean('approved')->default(false); 26 | $table->boolean('finished')->default(false); 27 | $table->string('avatar')->nullable(); 28 | $table->string('youtube_url')->nullable(); 29 | $table->string('vimeo_url')->nullable(); 30 | $table->softDeletes(); 31 | $table->timestamps(); 32 | 33 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 34 | }); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function down() 43 | { 44 | Schema::dropIfExists('files'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Upload.php: -------------------------------------------------------------------------------- 1 | belongsTo(File::class); 28 | } 29 | 30 | 31 | /** 32 | * An upload belongs to a user. 33 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 34 | */ 35 | public function user() { 36 | return $this->belongsTo(User::class); 37 | } 38 | 39 | 40 | /** 41 | * Access the full path of a filename 42 | * @return string 43 | */ 44 | public function getPathAttribute() { 45 | return storage_path('app/files/' . $this->file->identifier . '/' . $this->filename); 46 | } 47 | 48 | 49 | /** 50 | * Convert file size into readable names. 51 | * @param $size 52 | * @param int $precision 53 | * 54 | * @return int|string 55 | */ 56 | public static function formatBytes($size, $precision = 2) { 57 | if ($size > 0) { 58 | $size = (int) $size; 59 | $base = log($size) / log(1024); 60 | $suffixes = array(' bytes', ' KB', ' MB', ' GB', ' TB'); 61 | 62 | return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)]; 63 | } else { 64 | return $size; 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/Jobs/Checkout/CreateSale.php: -------------------------------------------------------------------------------- 1 | file = $file; 33 | $this->email = $email; 34 | } 35 | 36 | 37 | /** 38 | * Execute the job. 39 | * 40 | * @return void 41 | */ 42 | public function handle() 43 | { 44 | $sale = new Sale; 45 | 46 | $isUser = auth()->user() ? auth()->user()->id : NULL; 47 | 48 | $sale->fill([ 49 | 'identifier' => uniqid(true), 50 | 'bought_user_id' => $isUser, 51 | 'buyer_email' => $this->email, 52 | 'sale_price' => $this->file->price, 53 | 'sale_commission' => $this->file->calculateCommission() 54 | ]); 55 | 56 | $sale->file()->associate($this->file); 57 | $sale->user()->associate($this->file->user); 58 | 59 | $sale->save(); 60 | 61 | // Fire a new event 62 | event(new SaleCreated($sale)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /resources/views/account/marketplace/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 |
6 |

Please connect your Stripe account to start creating and selling files.

7 |
8 |

9 | In order for us to pay you out, you need to make an account with Stripe 10 | so you can receive the portion of your payment when someone buys your files. 11 | You can also sign into your existing account if you have one. 12 |

13 | Connect your Stripe account 14 |
15 |
16 | 17 | 24 |
25 | @endsection -------------------------------------------------------------------------------- /public/images/home/process/three.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /resources/views/vendor/notifications/email.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message', ['image' => $image, 'imageAlt' => $imageAlt]) 2 | 3 | @isset($image) 4 |
5 | {{ $imageAlt }} 6 |
7 | @endisset 8 | 9 | {{-- Greeting --}} 10 | @if (! empty($greeting)) 11 | # {{ $greeting }} 12 | @else 13 | @if ($level == 'error') 14 | # Whoops! 15 | @else 16 | {{--# Hello!--}} 17 | @endif 18 | @endif 19 | 20 | {{-- Intro Lines --}} 21 | @foreach ($introLines as $line) 22 | {{ $line }} 23 | 24 | @endforeach 25 | 26 | {{-- Action Button --}} 27 | @isset($actionText) 28 | 40 | @component('mail::button', ['url' => $actionUrl, 'color' => $color]) 41 | {{ $actionText }} 42 | @endcomponent 43 | @endisset 44 | 45 | {{-- Outro Lines --}} 46 | @foreach ($outroLines as $line) 47 | {{ $line }} 48 | 49 | @endforeach 50 | 51 | {{-- Salutation --}} 52 | @if (! empty($salutation)) 53 | {{ $salutation }} 54 | @else 55 | Regards,
{{ config('app.name') }} 56 | @endif 57 | 58 | {{-- Subcopy --}} 59 | @isset($actionText) 60 | @component('mail::subcopy') 61 | If you’re having trouble clicking the "{{ $actionText }}" button, copy and paste the URL below 62 | into your web browser: [{{ $actionUrl }}]({{ $actionUrl }}) 63 | @endcomponent 64 | @endisset 65 | @endcomponent 66 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | require 'json' 5 | require 'yaml' 6 | 7 | VAGRANTFILE_API_VERSION ||= "2" 8 | confDir = $confDir ||= File.expand_path("vendor/laravel/homestead", File.dirname(__FILE__)) 9 | 10 | homesteadYamlPath = File.expand_path("Homestead.yaml", File.dirname(__FILE__)) 11 | homesteadJsonPath = File.expand_path("Homestead.json", File.dirname(__FILE__)) 12 | afterScriptPath = "after.sh" 13 | aliasesPath = "aliases" 14 | 15 | require File.expand_path(confDir + '/scripts/homestead.rb') 16 | 17 | Vagrant.require_version '>= 1.9.0' 18 | 19 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 20 | if File.exist? aliasesPath then 21 | config.vm.provision "file", source: aliasesPath, destination: "/tmp/bash_aliases" 22 | config.vm.provision "shell" do |s| 23 | s.inline = "awk '{ sub(\"\r$\", \"\"); print }' /tmp/bash_aliases > /home/vagrant/.bash_aliases" 24 | end 25 | end 26 | 27 | if File.exist? homesteadYamlPath then 28 | settings = YAML::load(File.read(homesteadYamlPath)) 29 | elsif File.exist? homesteadJsonPath then 30 | settings = JSON.parse(File.read(homesteadJsonPath)) 31 | else 32 | abort "Homestead settings file not found in #{confDir}" 33 | end 34 | 35 | Homestead.configure(config, settings) 36 | 37 | if File.exist? afterScriptPath then 38 | config.vm.provision "shell", path: afterScriptPath, privileged: false 39 | end 40 | 41 | if defined? VagrantPlugins::HostsUpdater 42 | config.hostsupdater.aliases = settings['sites'].map { |site| site['map'] } 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/AvatarController.php: -------------------------------------------------------------------------------- 1 | user(); 23 | 24 | // Delete the old avatar in the public images directory 25 | \File::delete(public_path() . '/images/avatars/'.$user->avatar); 26 | 27 | // Get the current file uploaded 28 | $avatar = request()->file( 'avatar' ); 29 | 30 | // Get the file name 31 | $avatarName = sha1( $avatar->getClientOriginalName() ); 32 | 33 | // Get the file extension 34 | $avatarExtension = $avatar->getClientOriginalExtension(); 35 | 36 | // Combine the image name and extension 37 | $image = "{$avatarName}.{$avatarExtension}"; 38 | 39 | // Move the file to a path with the image name 40 | $request->file( 'avatar' )->move( 41 | base_path() . '/public/images/avatars/', $image 42 | ); 43 | 44 | // Set the users avatar to the image 45 | $user->avatar = $image; 46 | 47 | // Save the image to database 48 | $user->save(); 49 | 50 | return back(); 51 | } else { 52 | return back(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /public/images/home/process/five.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/Notifications/UpdatedFileApproved.php: -------------------------------------------------------------------------------- 1 | user = $user; 27 | $this->file = $file; 28 | } 29 | 30 | /** 31 | * Get the notification's delivery channels. 32 | * 33 | * @param mixed $notifiable 34 | * @return array 35 | */ 36 | public function via($notifiable) 37 | { 38 | return ['mail']; 39 | } 40 | 41 | /** 42 | * Get the mail representation of the notification. 43 | * 44 | * @param mixed $notifiable 45 | * @return \Illuminate\Notifications\Messages\MailMessage 46 | */ 47 | public function toMail($notifiable) 48 | { 49 | 50 | $image = '/images/icons/checkbox.svg'; 51 | $imageAlt = 'Your file updates has been approved'; 52 | 53 | return (new MailMessage) 54 | ->markdown('vendor.notifications.email', ['image' => $image, 'imageAlt' => $imageAlt]) 55 | ->line('File name: ' . $this->file->title) 56 | ->line('Your file updates has been approved!'); 57 | } 58 | 59 | /** 60 | * Get the array representation of the notification. 61 | * 62 | * @param mixed $notifiable 63 | * @return array 64 | */ 65 | public function toArray($notifiable) 66 | { 67 | return [ 68 | // 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/DatabaseNotificationsController.php: -------------------------------------------------------------------------------- 1 | first(); 18 | 19 | if (!$file->sales->count() > 0) { 20 | return redirect(route('account.files.index')); 21 | } 22 | 23 | return view( 'account.files.notify.index', compact('file')); 24 | } 25 | 26 | 27 | public function notifyOfChanges(Request $request, $identifier) { 28 | 29 | $this->validate($request, [ 30 | 'data' => 'required|min:15|max:2500', 31 | 'header' => 'nullable|max:50|min:5' 32 | ]); 33 | 34 | $data = $request->data; 35 | $header = $request->header; 36 | 37 | $file = File::where('identifier', $identifier)->first(); 38 | 39 | $owner = User::where('id', $file->user_id)->first(); 40 | 41 | $sales = Sale::where('file_id', $file->id)->get(); 42 | 43 | $userIds = $sales->pluck('bought_user_id'); 44 | 45 | $users = User::whereIn('id', $userIds)->where('user_notifications', 0)->get(); 46 | 47 | \Notification::send($users, new FileChanges($data, $header, $owner, $file)); 48 | 49 | return redirect(route('account.files.index'))->withSuccess('Notification sent successfully to users.'); 50 | } 51 | 52 | 53 | public function markAllAsRead() { 54 | 55 | auth()->user()->unreadNotifications->markAsRead(); 56 | 57 | return back()->withSuccess('Marked all as read'); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /resources/assets/js/components/files/partials/File.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | // 40 | ], 41 | ], 42 | 43 | 'redis' => [ 44 | 'driver' => 'redis', 45 | 'connection' => 'default', 46 | ], 47 | 48 | 'log' => [ 49 | 'driver' => 'log', 50 | ], 51 | 52 | 'null' => [ 53 | 'driver' => 'null', 54 | ], 55 | 56 | ], 57 | 58 | ]; 59 | -------------------------------------------------------------------------------- /app/Notifications/FileApproved.php: -------------------------------------------------------------------------------- 1 | user = $user; 27 | $this->file = $file; 28 | } 29 | 30 | /** 31 | * Get the notification's delivery channels. 32 | * 33 | * @param mixed $notifiable 34 | * @return array 35 | */ 36 | public function via($notifiable) 37 | { 38 | return ['mail']; 39 | } 40 | 41 | /** 42 | * Get the mail representation of the notification. 43 | * 44 | * @param mixed $notifiable 45 | * @return \Illuminate\Notifications\Messages\MailMessage 46 | */ 47 | public function toMail($notifiable) 48 | { 49 | 50 | $image = '/images/icons/checkbox.svg'; 51 | $imageAlt = 'Your file has been approved'; 52 | 53 | return (new MailMessage) 54 | ->markdown('vendor.notifications.email', ['image' => $image, 'imageAlt' => $imageAlt]) 55 | ->line('File name: ' . $this->file->title) 56 | ->line('Your file has been approved!'); 57 | } 58 | 59 | /** 60 | * Get the array representation of the notification. 61 | * 62 | * @param mixed $notifiable 63 | * @return array 64 | */ 65 | public function toArray($notifiable) 66 | { 67 | return [ 68 | // 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/Notifications/EmailVerification.php: -------------------------------------------------------------------------------- 1 | user = $user; 25 | } 26 | 27 | /** 28 | * Get the notification's delivery channels. 29 | * 30 | * @param mixed $notifiable 31 | * @return array 32 | */ 33 | public function via($notifiable) 34 | { 35 | return ['mail']; 36 | } 37 | 38 | /** 39 | * Get the mail representation of the notification. 40 | * 41 | * @param mixed $notifiable 42 | * @return \Illuminate\Notifications\Messages\MailMessage 43 | */ 44 | public function toMail($notifiable) 45 | { 46 | $image = '/images/icons/email.svg'; 47 | $imageAlt = 'Please verify your email address icon image'; 48 | 49 | return (new MailMessage) 50 | ->markdown('vendor.notifications.email', ['image' => $image, 'imageAlt' => $imageAlt]) 51 | ->action('Verify email address', route('verify.email', $this->user->token)) 52 | ->line('Thanks for registering! In order to protect your account and verify who you are, please confirm your email.'); 53 | } 54 | 55 | /** 56 | * Get the array representation of the notification. 57 | * 58 | * @param mixed $notifiable 59 | * @return array 60 | */ 61 | public function toArray($notifiable) 62 | { 63 | return [ 64 | // 65 | ]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /resources/views/account/notifications/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 | 6 |
7 |
8 | 9 | {{ isset($notification->data['owner_name']) ? $notification->data['owner_name'] : 'Avatar Image' }} 12 | 13 |
14 |
15 |

{{ $notification->data['header'] }}


16 |

{{ $notification->data['data'] }}

17 |
18 |
19 | @if(isset($notification->data['owner_name'])) 20 | From: {{ $notification->data['owner_name'] }} 21 | @endif 22 | {{ $notification->created_at->diffForHumans() }} 23 |
24 |
25 |
26 | 27 |
28 |
29 | @if($notification->read_at === null) 30 | Mark as Read 31 | @endif 32 | read_at === null)style="margin-right: 15px;"@endif>Go Back 33 |
34 | 35 |
36 | @endsection -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /resources/views/layouts/partials/_footer.blade.php: -------------------------------------------------------------------------------- 1 | 55 | -------------------------------------------------------------------------------- /resources/assets/js/components/files/Index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 39 | 40 | $this->mapWebRoutes(); 41 | 42 | // 43 | } 44 | 45 | /** 46 | * Define the "web" routes for the application. 47 | * 48 | * These routes all receive session state, CSRF protection, etc. 49 | * 50 | * @return void 51 | */ 52 | protected function mapWebRoutes() 53 | { 54 | Route::middleware('web') 55 | ->namespace($this->namespace) 56 | ->group(base_path('routes/web.php')); 57 | } 58 | 59 | /** 60 | * Define the "api" routes for the application. 61 | * 62 | * These routes are typically stateless. 63 | * 64 | * @return void 65 | */ 66 | protected function mapApiRoutes() 67 | { 68 | Route::prefix('api') 69 | ->middleware('api') 70 | ->namespace($this->namespace) 71 | ->group(base_path('routes/api.php')); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /resources/assets/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | 2 | window._ = require('lodash'); 3 | 4 | /** 5 | * We'll load jQuery and the Bootstrap jQuery plugin which provides support 6 | * for JavaScript based Bootstrap features such as modals and tabs. This 7 | * code may be modified to fit the specific needs of your application. 8 | */ 9 | 10 | try { 11 | window.$ = window.jQuery = require('jquery'); 12 | 13 | require('bootstrap-sass'); 14 | } catch (e) {} 15 | 16 | /** 17 | * We'll load the axios HTTP library which allows us to easily issue requests 18 | * to our Laravel back-end. This library automatically handles sending the 19 | * CSRF token as a header based on the value of the "XSRF" token cookie. 20 | */ 21 | 22 | window.axios = require('axios'); 23 | 24 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 25 | 26 | /** 27 | * Next we will register the CSRF Token as a common header with Axios so that 28 | * all outgoing HTTP requests automatically have it attached. This is just 29 | * a simple convenience so we don't have to attach every token manually. 30 | */ 31 | 32 | let token = document.head.querySelector('meta[name="csrf-token"]'); 33 | 34 | if (token) { 35 | window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; 36 | } else { 37 | console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); 38 | } 39 | 40 | /** 41 | * Echo exposes an expressive API for subscribing to channels and listening 42 | * for events that are broadcast by Laravel. Echo and event broadcasting 43 | * allows your team to easily build robust real-time web applications. 44 | */ 45 | 46 | // import Echo from 'laravel-echo' 47 | 48 | // window.Pusher = require('pusher-js'); 49 | 50 | // window.Echo = new Echo({ 51 | // broadcaster: 'pusher', 52 | // key: 'your-pusher-key' 53 | // }); 54 | 55 | 56 | window.Dropzone = require('dropzone'); 57 | Dropzone.autoDiscover = false; -------------------------------------------------------------------------------- /resources/views/admin/users/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.default') 2 | 3 | @section('admin.content') 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @foreach($users as $user) 16 | 17 | 22 | 25 | 30 | 33 | 43 | 44 | @endforeach 45 | 46 |
NameEmailJoinedImpersonate
23 | {{ $user->name }} 24 | 26 | 27 | {{ $user->email }} 28 | 29 | 31 | {{ $user->created_at->format('m/d/Y H:i:s') }} 32 | 34 | @if(auth()->user()->id !== $user->id) 35 |
36 | {{ csrf_field() }} 37 | 40 |
41 | @endif 42 |
47 | 48 | {{ $users->render() }} 49 | @endsection -------------------------------------------------------------------------------- /app/Notifications/FileRejected.php: -------------------------------------------------------------------------------- 1 | user = $user; 23 | $this->file = $file; 24 | $this->data = $data; 25 | } 26 | 27 | 28 | /** 29 | * Get the notification's delivery channels. 30 | * 31 | * @param mixed $notifiable 32 | * @return array 33 | */ 34 | public function via($notifiable) { 35 | return ['mail', 'database']; 36 | } 37 | 38 | 39 | /** 40 | * Get the mail representation of the notification. 41 | * 42 | * @param mixed $notifiable 43 | * @return \Illuminate\Notifications\Messages\MailMessage 44 | */ 45 | public function toMail($notifiable) { 46 | 47 | $image = '/images/icons/rejected.svg'; 48 | $imageAlt = 'Your file has been rejected'; 49 | 50 | return (new MailMessage) 51 | ->markdown('vendor.notifications.email', ['image' => $image, 'imageAlt' => $imageAlt]) 52 | ->line('File name: ' . $this->file->title) 53 | ->line('Your file has been rejected') 54 | ->line('You can check your notifications on Marketplace to see if the admin gave a reason on why your file was rejected'); 55 | } 56 | 57 | /** 58 | * Get the array representation of the notification. 59 | * 60 | * @param mixed $notifiable 61 | * @return array 62 | */ 63 | public function toArray($notifiable) { 64 | return [ 65 | 'header' => 'File: "'.$this->file->title.'" has been rejected by admin', 66 | 'data' => isset($this->data) ? $this->data : 'Admin gave no reason for file rejection.', 67 | 'file' => $this->file->title, 68 | ]; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/MarketPlaceConnectController.php: -------------------------------------------------------------------------------- 1 | middleware(['auth', 'has.marketplace']); 18 | } 19 | 20 | 21 | /** 22 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 23 | */ 24 | public function index() { 25 | 26 | session(['stripe_token' => str_random(60)]); 27 | 28 | return view('account.marketplace.index'); 29 | } 30 | 31 | 32 | /** 33 | * @param Request $request 34 | * @param Guzzle $guzzle 35 | * 36 | * @return \Illuminate\Http\RedirectResponse 37 | */ 38 | public function store(Request $request, Guzzle $guzzle) { 39 | 40 | // If no request code found, return redirect to account connect route. 41 | if (!$request->code) { 42 | return redirect()->route('account.connect'); 43 | } 44 | 45 | // If no session found, return redirect to account connect route. 46 | if ($request->state !== session('stripe_token')) { 47 | return redirect()->route('account.connect'); 48 | } 49 | 50 | $stripeRequest = $guzzle->request('POST', 'https://connect.stripe.com/oauth/token', [ 51 | 'form_params' => [ 52 | 'client_secret' => config('services.stripe.secret'), 53 | 'code' => $request->code, 54 | 'grant_type' => 'authorization_code' 55 | ] 56 | ]); 57 | 58 | $stripeRequest = json_decode($stripeRequest->getBody()); 59 | 60 | $request->user()->update([ 61 | 'stripe_id' => $stripeRequest->stripe_user_id, 62 | 'stripe_key' => $stripeRequest->stripe_publishable_key, 63 | ]); 64 | 65 | return redirect()->route('account')->withSuccess('You have connected your Stripe account.'); 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/Notifications/UpdatedFileRejection.php: -------------------------------------------------------------------------------- 1 | user = $user; 23 | $this->file = $file; 24 | $this->data = $data; 25 | } 26 | 27 | /** 28 | * Get the notification's delivery channels. 29 | * 30 | * @param mixed $notifiable 31 | * @return array 32 | */ 33 | public function via($notifiable) { 34 | return ['mail', 'database']; 35 | } 36 | 37 | /** 38 | * Get the mail representation of the notification. 39 | * 40 | * @param mixed $notifiable 41 | * @return \Illuminate\Notifications\Messages\MailMessage 42 | */ 43 | public function toMail($notifiable) { 44 | 45 | $image = '/images/icons/rejected.svg'; 46 | $imageAlt = 'Your file updates has been rejected'; 47 | 48 | return (new MailMessage) 49 | ->markdown('vendor.notifications.email', ['image' => $image, 'imageAlt' => $imageAlt]) 50 | ->line('File name: ' . $this->file->title) 51 | ->line('Your file updates has been rejected') 52 | ->line('You can check your notifications on Marketplace to see if the admin gave a reason on why your file was rejected'); 53 | } 54 | 55 | 56 | /** 57 | * Get the array representation of the notification. 58 | * 59 | * @param mixed $notifiable 60 | * @return array 61 | */ 62 | public function toArray($notifiable) { 63 | return [ 64 | 'header' => 'File: "'.$this->file->title.'" has been rejected by admin', 65 | 'data' => isset($this->data) ? $this->data : 'Admin gave no reason for file rejection.', 66 | 'file' => $this->file->title, 67 | ]; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /database/factories/FileFactory.php: -------------------------------------------------------------------------------- 1 | define(App\File::class, function (Faker $faker) { 17 | 18 | $live = rand(0, 1); 19 | 20 | return [ 21 | 'identifier' => uniqid(), 22 | 'user_id' => App\User::all()->random()->id, 23 | 'title' => $faker->sentence($nbWords = 4), 24 | 'overview_short' => $faker->sentence, 25 | 'overview' => $faker->text($maxNbChars = 500), 26 | 'price' => $faker->randomFloat($nbMaxDecimals = 2, $min = 0, $max = 125), 27 | 'live' => $live, 28 | 'approved' => $live, 29 | 'finished' => $live, 30 | 'avatar' => $faker->image('public/images/files/cover',1000,700, null, false), 31 | 'youtube_url' => 'https://www.youtube.com/watch?v=Ff6bi8rkLpg', 32 | 'vimeo_url' => 'https://vimeo.com/99232333', 33 | 'created_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null), 34 | 'updated_at' =>$faker->dateTimeBetween($startDate = '-2 years', $endDate = 'now', $timezone = null) 35 | ]; 36 | }); 37 | 38 | 39 | 40 | $factory->define(App\Comment::class, function (Faker $faker) { 41 | 42 | $file = rand(1, 50); 43 | 44 | return [ 45 | 'user_id' => App\User::all()->random()->id, 46 | 'parent_id' => null, 47 | 'body' => $faker->sentence, 48 | 'commentable_id' => $file, 49 | 'commentable_type' => 'App\File', 50 | 'created_at' =>$faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now', $timezone = null), 51 | 'updated_at' =>$faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now', $timezone = null) 52 | ]; 53 | }); -------------------------------------------------------------------------------- /resources/views/account/bought/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 | @if(\App\Sale::boughtUserId()->count() > 0) 5 | @foreach($boughtFiles as $bought) 6 |
7 |
8 | 19 | 29 |
30 |
31 | @endforeach 32 | 33 | {!! $boughtFiles->render() !!} 34 | @else 35 |

You have not purchased any files on this account yet.

36 | @endif 37 | 38 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/partials/_navigation.blade.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 24 |
25 | 28 | 29 |
30 |
31 | 39 |
40 | 41 |
42 | 50 |
51 |
52 |
-------------------------------------------------------------------------------- /app/Http/Controllers/Admin/CategoriesController.php: -------------------------------------------------------------------------------- 1 | paginate(50); 19 | 20 | return view('admin.categories.index', compact ('categories')); 21 | } 22 | 23 | 24 | /** 25 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 26 | */ 27 | public function create() { 28 | return view('admin.categories.create'); 29 | } 30 | 31 | 32 | /** 33 | * @param $slug 34 | * 35 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 36 | */ 37 | public function edit($slug) { 38 | 39 | $category = Category::where('slug', $slug)->first(); 40 | 41 | return view('admin.categories.edit', compact('category')); 42 | } 43 | 44 | 45 | /** 46 | * @param Request $request 47 | * 48 | * @return mixed 49 | */ 50 | public function store(Request $request) { 51 | 52 | $this->validate($request,[ 53 | 'name' => 'required|string|max:25|unique:categories,name' 54 | ]); 55 | 56 | Category::create([ 57 | 'name' => $request->name, 58 | 'slug' => str_slug($request->name) 59 | ]); 60 | 61 | return redirect(route('admin.categories'))->withSuccess("Category created."); 62 | } 63 | 64 | 65 | /** 66 | * @param FileCategoryRequest $request 67 | * @param $id 68 | * 69 | * @return mixed 70 | */ 71 | public function update(FileCategoryRequest $request, $id) { 72 | 73 | $category = Category::find($id); 74 | 75 | $category->update([ 76 | 'name' => $request->name, 77 | 'slug' => str_slug($request->name) 78 | ]); 79 | 80 | return redirect(route('admin.categories'))->withSuccess("Category updated."); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/laravel", 3 | "description": "The Laravel Framework.", 4 | "keywords": ["framework", "laravel"], 5 | "license": "MIT", 6 | "type": "project", 7 | "require": { 8 | "php": ">=7.0.0", 9 | "barryvdh/laravel-ide-helper": "^2.4", 10 | "chumper/zipper": "^1.0", 11 | "fideloper/proxy": "~3.3", 12 | "guzzlehttp/guzzle": "^6.3", 13 | "intervention/image": "^2.4", 14 | "kitetail/zttp": "^0.3.0", 15 | "laravel/framework": "5.5.*", 16 | "laravel/tinker": "~1.0", 17 | "sentry/sentry-laravel": "^0.8.0", 18 | "stripe/stripe-php": "^5.8", 19 | "ext-gd":" *" 20 | }, 21 | "require-dev": { 22 | "filp/whoops": "~2.0", 23 | "fzaninotto/faker": "~1.4", 24 | "laravel/homestead": "^6.2", 25 | "mockery/mockery": "0.9.*", 26 | "phpunit/phpunit": "~6.0" 27 | }, 28 | "autoload": { 29 | "classmap": [ 30 | "database/seeds", 31 | "database/factories" 32 | ], 33 | "files": [ 34 | "app/helpers.php" 35 | ], 36 | "psr-4": { 37 | "App\\": "app/" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "Tests\\": "tests/" 43 | } 44 | }, 45 | "extra": { 46 | "laravel": { 47 | "dont-discover": [ 48 | ] 49 | } 50 | }, 51 | "scripts": { 52 | "post-root-package-install": [ 53 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 54 | ], 55 | "post-create-project-cmd": [ 56 | "@php artisan key:generate" 57 | ], 58 | "post-autoload-dump": [ 59 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 60 | "@php artisan package:discover" 61 | ] 62 | }, 63 | "config": { 64 | "preferred-install": "dist", 65 | "sort-packages": true, 66 | "optimize-autoloader": true 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | define('LARAVEL_START', microtime(true)); 11 | 12 | /* 13 | |-------------------------------------------------------------------------- 14 | | Register The Auto Loader 15 | |-------------------------------------------------------------------------- 16 | | 17 | | Composer provides a convenient, automatically generated class loader for 18 | | our application. We just need to utilize it! We'll simply require it 19 | | into the script here so that we don't have to worry about manual 20 | | loading any of our classes later on. It feels great to relax. 21 | | 22 | */ 23 | 24 | require __DIR__.'/../vendor/autoload.php'; 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Turn On The Lights 29 | |-------------------------------------------------------------------------- 30 | | 31 | | We need to illuminate PHP development, so let us turn on the lights. 32 | | This bootstraps the framework and gets it ready for use, then it 33 | | will load up this application so that we can run it and send 34 | | the responses back to the browser and delight our users. 35 | | 36 | */ 37 | 38 | $app = require_once __DIR__.'/../bootstrap/app.php'; 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Run The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once we have the application, we can handle the incoming request 46 | | through the kernel, and send the associated response back to 47 | | the client's browser allowing them to enjoy the creative 48 | | and wonderful application we have prepared for them. 49 | | 50 | */ 51 | 52 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 53 | 54 | $response = $kernel->handle( 55 | $request = Illuminate\Http\Request::capture() 56 | ); 57 | 58 | $response->send(); 59 | 60 | $kernel->terminate($request, $response); 61 | -------------------------------------------------------------------------------- /resources/views/files/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.plain') 2 | 3 | @section('content') 4 |
5 | @include('files.partials._top_filter_nav') 6 |
7 | @if($files->isEmpty()) 8 |

No files found

9 | @else 10 | @foreach($files as $file) 11 | 30 | @endforeach 31 | 32 | {{ $files->appends(request()->input())->links() }} 33 | @endif 34 |
35 |
36 | @endsection -------------------------------------------------------------------------------- /resources/views/account/password/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 |
Change Password
6 |
7 |
8 | {{ csrf_field() }} 9 | 10 |
11 | 12 | 13 | @if ($errors->has('password_current')) 14 | 15 | {{ $errors->first('password_current') }} 16 | 17 | @endif 18 |
19 | 20 |
21 | 22 |
23 | 24 | 25 | @if ($errors->has('password')) 26 | 27 | {{ $errors->first('password') }} 28 | 29 | @endif 30 |
31 | 32 |
33 | 34 | 35 | 36 | 37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 | @endsection -------------------------------------------------------------------------------- /resources/assets/js/components/AvatarUpload.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 28 | 29 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 |
7 |
8 |
9 |

10 | 11 | Marketplace logo 12 | 13 |

14 |

Rest Password

15 | @if (session('status')) 16 |
17 | {{ session('status') }} 18 |
19 | @endif 20 | 21 |
22 | {{ csrf_field() }} 23 | 24 |
25 | 26 | @if ($errors->has('email')) 27 | 28 | {{ $errors->first('email') }} 29 | 30 | @endif 31 |
32 | 33 |
34 |
35 | 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | @endsection 47 | -------------------------------------------------------------------------------- /app/Sale.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 34 | } 35 | 36 | 37 | /** 38 | * Get the relationship of user who bought file for a particular Sale. 39 | * -- References "id" on user table, and "bought_user_id" on sales table. 40 | * 41 | * @return \Illuminate\Database\Eloquent\Relations\HasMany 42 | */ 43 | public function boughtUser() { 44 | return $this->hasMany(User::class, 'id','bought_user_id'); 45 | } 46 | 47 | 48 | /** 49 | * A Sale belongs to a file. 50 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 51 | */ 52 | public function file() { 53 | return $this->belongsTo(File::class); 54 | } 55 | 56 | 57 | /** 58 | * Get the user where the 'bought_user_id' is the currently signed in user. 59 | * @param Builder $builder 60 | * 61 | * @return $this 62 | */ 63 | public function scopeBoughtUserId(Builder $builder) { 64 | return $builder->where('bought_user_id', '=', auth()->user()->id); 65 | } 66 | 67 | 68 | /** 69 | * Sum up all the sales commissions. 70 | * @return mixed 71 | */ 72 | public static function lifetimeCommission() { 73 | return static::get()->sum('sale_commission'); 74 | } 75 | 76 | 77 | /** Grab the sales for month from now, and get all sales. 78 | * @return mixed 79 | */ 80 | public static function commissionThisMonth() { 81 | $now = Carbon::now(); 82 | 83 | return static::whereBetween('created_at', [ 84 | $now->startOfMonth(), 85 | $now->copy()->endOfMonth() 86 | ])->get()->sum('sale_commission'); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/Http/Controllers/Checkout/CheckoutController.php: -------------------------------------------------------------------------------- 1 | isFree()) { 25 | return back(); 26 | } 27 | 28 | // Create a 'Job' to create a sale 29 | $this->dispatch(new CreateSale($file, $request->email)); 30 | 31 | return back()->withSuccess('We\'ve emailed your download link to you. '); 32 | } 33 | 34 | 35 | /** 36 | * @param Request $request 37 | * @param File $file 38 | * 39 | * @return mixed 40 | */ 41 | public function payment(Request $request, File $file) { 42 | 43 | try { 44 | $charge = Charge::create([ 45 | 'amount' => $file->price * 100, 46 | 'currency' => 'usd', 47 | 'description' => 'Charge for: '. str_limit($file->title, 100), 48 | 'source' => $request->stripeToken, 49 | 'application_fee' => floor($file->calculateCommission() * 100), 50 | 'metadata' => [ 51 | 'file_identifier' => $file->identifier, 52 | 'file_title' => $file->title, 53 | 'file_short_description' => $file->overview_short, 54 | 'file_user' => $file->user->name 55 | ] 56 | ], [ 57 | 'stripe_account' => $file->user->stripe_id 58 | ]); 59 | } catch (\Exception $e) { 60 | return back()->withError('Something went wrong while processing your payment.'); 61 | } 62 | 63 | // Create a 'Job' to create a sale 64 | $this->dispatch(new CreateSale($file, $request->stripeEmail)); 65 | 66 | return back()->withSuccess('Payment complete. We\'ve emailed your download link to you.'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /resources/assets/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // Body 3 | $body-bg: #f5f8fa; 4 | 5 | // Borders 6 | $laravel-border-color: darken($body-bg, 10%); 7 | $list-group-border: $laravel-border-color; 8 | $navbar-default-border: $laravel-border-color; 9 | $panel-default-border: $laravel-border-color; 10 | $panel-inner-border: $laravel-border-color; 11 | 12 | // Brands 13 | $brand-primary: #3097D1; 14 | $brand-info: #8eb4cb; 15 | $brand-success: #2ab27b; 16 | $brand-warning: #cbb956; 17 | $brand-danger: #bf5329; 18 | 19 | // Typography 20 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 21 | $font-family-sans-serif: "Raleway", sans-serif; 22 | $font-size-base: 14px; 23 | $line-height-base: 1.6; 24 | $text-color: #636b6f; 25 | 26 | // Navbar 27 | $navbar-default-bg: #fff; 28 | 29 | // Buttons 30 | $btn-default-color: $text-color; 31 | 32 | // Inputs 33 | $input-border: lighten($text-color, 40%); 34 | $input-border-focus: lighten($brand-primary, 25%); 35 | $input-color-placeholder: lighten($text-color, 30%); 36 | 37 | // Panels 38 | $panel-default-heading-bg: #fff; 39 | 40 | 41 | .MAIN-BACKGROUND-OVERLAY { 42 | background: linear-gradient(45deg, #0B4182 1%, #1e88e5 64%, #40BAF5 97%); 43 | background-image: -ms-linear-gradient(45deg, #0B4182 1%, #1e88e5 64%, #40BAF5 97%); 44 | background-image: -moz-linear-gradient(45deg, #0B4182 1%, #1e88e5 64%, #40BAF5 97%); 45 | background-image: -o-linear-gradient(45deg, #0B4182 1%, #1e88e5 64%, #40BAF5 97%); 46 | background-image: -webkit-linear-gradient(45deg, #0B4182 1%, #1e88e5 64%, #40BAF5 97%); 47 | background-image: linear-gradient(45deg, #0B4182 1%, #1e88e5 64%, #40BAF5 97%); 48 | } 49 | 50 | .FORM-INPUT { 51 | -webkit-appearance: none; 52 | -moz-appearance: none; 53 | appearance: none; 54 | 55 | height: 48px; 56 | border: 1px solid #cacbcc; 57 | font-size: 16px; 58 | box-shadow: none; 59 | color: #555; 60 | margin-bottom: 10px; 61 | -webkit-border-radius: 2px; 62 | -moz-border-radius: 2px; 63 | border-radius: 2px; 64 | } 65 | 66 | .SPECIAL-BOX-SHADOW { 67 | -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); 68 | -moz-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); 69 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); 70 | padding: 15px 20px 20px 20px; 71 | border-radius: 4px; 72 | } -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DRIVER', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Cloud Filesystem Disk 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Many applications store files both locally and in the cloud. For this 24 | | reason, you may specify a default "cloud" driver here. This driver 25 | | will be bound as the Cloud disk implementation in the container. 26 | | 27 | */ 28 | 29 | 'cloud' => env('FILESYSTEM_CLOUD', 's3'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Filesystem Disks 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here you may configure as many filesystem "disks" as you wish, and you 37 | | may even configure multiple disks of the same driver. Defaults have 38 | | been setup for each driver as an example of the required options. 39 | | 40 | | Supported Drivers: "local", "ftp", "s3", "rackspace" 41 | | 42 | */ 43 | 44 | 'disks' => [ 45 | 46 | 'local' => [ 47 | 'driver' => 'local', 48 | 'root' => storage_path('app'), 49 | ], 50 | 51 | 'public' => [ 52 | 'driver' => 'local', 53 | 'root' => storage_path('app/public'), 54 | 'url' => env('APP_URL').'/storage', 55 | 'visibility' => 'public', 56 | ], 57 | 58 | 's3' => [ 59 | 'driver' => 's3', 60 | 'key' => env('AWS_KEY'), 61 | 'secret' => env('AWS_SECRET'), 62 | 'region' => env('AWS_REGION'), 63 | 'bucket' => env('AWS_BUCKET'), 64 | ], 65 | 66 | ], 67 | 68 | ]; 69 | -------------------------------------------------------------------------------- /public/images/icons/avatar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Http/Controllers/Admin/AdminController.php: -------------------------------------------------------------------------------- 1 | ', Carbon::now()->firstOfMonth()) 23 | ->where('sale_price', '>', 0) 24 | ->selectRaw('DATE_FORMAT(created_at, "%m/%d") as day, sum(sale_price) as sale_price') 25 | ->groupBy('day') 26 | ->pluck('sale_price', 'day'); 27 | 28 | // Get all the sales there ever was. 29 | $overallSales = Sale::where('sale_price', '>', 0) 30 | ->selectRaw('DATE_FORMAT(created_at, "%Y/%m") as year, sum(sale_price) as sale_price') 31 | ->groupBy('year') 32 | ->pluck('sale_price', 'year'); 33 | 34 | return view('admin.index', compact('salesThisMonth', 'overallSales')); 35 | } 36 | 37 | 38 | /** 39 | * Get a list of all current signed up users. 40 | * 41 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 42 | */ 43 | public function users() { 44 | 45 | $users = User::latest()->paginate(50); 46 | 47 | return view('admin.users.index', compact('users')); 48 | } 49 | 50 | 51 | /** 52 | * Start impersonating a user. Login as them. 53 | * 54 | * @param Request $request 55 | * @param $id 56 | * 57 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 58 | */ 59 | public function impersonate(Request $request, $id) { 60 | 61 | if (!auth()->user()->isAdmin()) { 62 | $user = User::where( 'id', '=', $id )->first(); 63 | 64 | session()->put( 'impersonate', $user->id ); 65 | 66 | return redirect( '/' ); 67 | } 68 | 69 | return back(); 70 | } 71 | 72 | 73 | /** 74 | * Stop impersonating a user. Remove session. 75 | * 76 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 77 | */ 78 | public function destroyImpersonate() { 79 | 80 | session()->forget('impersonate'); 81 | 82 | return redirect('/'); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /resources/views/account/files/notify/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 | 6 |
7 |
8 | Send users who own this file a notification 9 |
10 |
11 |
12 | {{ csrf_field() }} 13 | 14 | 17 | 18 |
19 | 20 | 21 | @if ($errors->has('header')) 22 | 23 | {{ $errors->first('header') }} 24 | 25 | @endif 26 |
27 | 28 | 31 | 32 |
33 | 34 |
35 | 36 | 37 | @if ($errors->has('data')) 38 | 39 | {{ $errors->first('data') }} 40 | 41 | @endif 42 |
43 | 44 |
45 | 46 |
47 | 48 |
49 | 50 |
51 |
52 |
53 |
54 | @endsection -------------------------------------------------------------------------------- /resources/views/account/notifications/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 |
6 | 7 | 11 | 12 |
13 | @if($unreadNotifications->count() > 0) 14 | Mark All as Read 15 | 16 | @foreach($unreadNotifications as $unread) 17 |
18 |
19 | 20 | {{ isset($unread->data['owner_name']) ? $unread->data['owner_name'] : 'Avatar Image' }} 23 | 24 |
25 |
26 |

{{ $unread->data['header'] }}

27 | Read more... 28 |
29 |
30 | @if(isset($unread->data['owner_name'])) 31 | From: {{ $unread->data['owner_name'] }} 32 | @endif 33 | {{ $unread->created_at->diffForHumans() }} 34 |
35 |
36 |
37 | @endforeach 38 | 39 | {{ $unreadNotifications->render() }} 40 | @else 41 |

You have no unread notifications

42 | @endif 43 |
44 |
45 | @endsection -------------------------------------------------------------------------------- /resources/views/account/notifications/all-notifications.blade.php: -------------------------------------------------------------------------------- 1 | @extends('account.layouts.default') 2 | 3 | @section('account.content') 4 |
5 |
6 | 7 | 11 | 12 |
13 | @if($notifications->count() > 0) 14 | @foreach($notifications as $allNotifications) 15 |
16 |
17 | 18 | {{ isset($allNotifications->data['owner_name']) ? $allNotifications->data['owner_name'] : 'Avatar Image' }} 21 | 22 |
23 |
24 |

{{ $allNotifications->data['header'] }}

25 | Read more... 26 |
27 |
28 | @if(isset($allNotifications->data['owner_name'])) 29 | From: {{ $allNotifications->data['owner_name'] }} 30 | @endif 31 | {{ $allNotifications->created_at->diffForHumans() }} 32 |
33 |
34 |
35 | @endforeach 36 | 37 | {{ $notifications->render() }} 38 | @else 39 |

You have no notifications

40 | @endif 41 |
42 | 43 |
44 | @endsection --------------------------------------------------------------------------------