Initial commit
This commit is contained in:
42
app/Http/Controllers/Admin/ImpersonationController.php
Normal file
42
app/Http/Controllers/Admin/ImpersonationController.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Forms\Form;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ImpersonationController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('admin');
|
||||
}
|
||||
|
||||
public function impersonate($identifier) {
|
||||
$user = null;
|
||||
if (is_numeric($identifier)) {
|
||||
$user = User::find($identifier);
|
||||
} elseif (filter_var($identifier, FILTER_VALIDATE_EMAIL)) {
|
||||
$user = User::whereEmail($identifier)->first();
|
||||
} else {
|
||||
// Find by form slug
|
||||
$form = Form::whereSlug($identifier)->first();
|
||||
if ($form) {
|
||||
$user = $form->creator;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$user) return $this->error([
|
||||
'message'=> 'User not found.'
|
||||
]);
|
||||
|
||||
// Be this user
|
||||
$token = auth()->login($user);
|
||||
return $this->success([
|
||||
'token' => $token
|
||||
]);
|
||||
}
|
||||
}
|
||||
46
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
46
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
use SendsPasswordResetEmails;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset link.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendResetLinkResponse(Request $request, $response)
|
||||
{
|
||||
return ['status' => trans($response)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a failed password reset link.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendResetLinkFailedResponse(Request $request, $response)
|
||||
{
|
||||
return response()->json(['email' => trans($response)], 400);
|
||||
}
|
||||
}
|
||||
114
app/Http/Controllers/Auth/LoginController.php
Normal file
114
app/Http/Controllers/Auth/LoginController.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Exceptions\VerifyEmailException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest')->except('logout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to log the user into the application.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function attemptLogin(Request $request)
|
||||
{
|
||||
$token = $this->guard()->attempt($this->credentials($request));
|
||||
|
||||
if (! $token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->guard()->user();
|
||||
if ($user instanceof MustVerifyEmail && ! $user->hasVerifiedEmail()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->guard()->setToken($token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the needed authorization credentials from the request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
protected function credentials(Request $request)
|
||||
{
|
||||
return [
|
||||
$this->username() => strtolower($request->get($this->username())),
|
||||
'password' => $request->password,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the response after the user was authenticated.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendLoginResponse(Request $request)
|
||||
{
|
||||
$this->clearLoginAttempts($request);
|
||||
|
||||
$token = (string) $this->guard()->getToken();
|
||||
$expiration = $this->guard()->getPayload()->get('exp');
|
||||
|
||||
return response()->json([
|
||||
'token' => $token,
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => $expiration - time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request)
|
||||
{
|
||||
$user = $this->guard()->user();
|
||||
if ($user instanceof MustVerifyEmail && ! $user->hasVerifiedEmail()) {
|
||||
throw VerifyEmailException::forUser($user);
|
||||
}
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
$this->username() => [trans('auth.failed')],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out of the application.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$this->guard()->logout();
|
||||
}
|
||||
}
|
||||
112
app/Http/Controllers/Auth/OAuthController.php
Normal file
112
app/Http/Controllers/Auth/OAuthController.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Exceptions\EmailTakenException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\OAuthProvider;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
class OAuthController extends Controller
|
||||
{
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
config([
|
||||
'services.github.redirect' => route('oauth.callback', 'github'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the user to the provider authentication page.
|
||||
*
|
||||
* @param string $provider
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function redirect($provider)
|
||||
{
|
||||
return [
|
||||
'url' => Socialite::driver($provider)->stateless()->redirect()->getTargetUrl(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the user information from the provider.
|
||||
*
|
||||
* @param string $driver
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function handleCallback($provider)
|
||||
{
|
||||
$user = Socialite::driver($provider)->stateless()->user();
|
||||
$user = $this->findOrCreateUser($provider, $user);
|
||||
|
||||
$this->guard()->setToken(
|
||||
$token = $this->guard()->login($user)
|
||||
);
|
||||
|
||||
return view('oauth/callback', [
|
||||
'token' => $token,
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $provider
|
||||
* @param \Laravel\Socialite\Contracts\User $sUser
|
||||
* @return \App\Models\User
|
||||
*/
|
||||
protected function findOrCreateUser($provider, $user)
|
||||
{
|
||||
$oauthProvider = OAuthProvider::where('provider', $provider)
|
||||
->where('provider_user_id', $user->getId())
|
||||
->first();
|
||||
|
||||
if ($oauthProvider) {
|
||||
$oauthProvider->update([
|
||||
'access_token' => $user->token,
|
||||
'refresh_token' => $user->refreshToken,
|
||||
]);
|
||||
|
||||
return $oauthProvider->user;
|
||||
}
|
||||
|
||||
if (User::where('email', $user->getEmail())->exists()) {
|
||||
throw new EmailTakenException;
|
||||
}
|
||||
|
||||
return $this->createUser($provider, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $provider
|
||||
* @param \Laravel\Socialite\Contracts\User $sUser
|
||||
* @return \App\Models\User
|
||||
*/
|
||||
protected function createUser($provider, $sUser)
|
||||
{
|
||||
$user = User::create([
|
||||
'name' => $sUser->getName(),
|
||||
'email' => $sUser->getEmail(),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$user->oauthProviders()->create([
|
||||
'provider' => $provider,
|
||||
'provider_user_id' => $sUser->getId(),
|
||||
'access_token' => $sUser->token,
|
||||
'refresh_token' => $sUser->refreshToken,
|
||||
]);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
88
app/Http/Controllers/Auth/RegisterController.php
Normal file
88
app/Http/Controllers/Auth/RegisterController.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Workspace;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
use RegistersUsers;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* The user has been registered.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\User $user
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function registered(Request $request, User $user)
|
||||
{
|
||||
if ($user instanceof MustVerifyEmail) {
|
||||
return response()->json(['status' => trans('verification.sent')]);
|
||||
}
|
||||
|
||||
return response()->json($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email:filter|max:255|unique:users',
|
||||
'password' => 'required|min:6|confirmed',
|
||||
'hear_about_us' => 'required|string'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \App\User
|
||||
*/
|
||||
protected function create(array $data)
|
||||
{
|
||||
$workspace = Workspace::create([
|
||||
'name' => 'My Workspace',
|
||||
'icon' => '🧪',
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => strtolower($data['email']),
|
||||
'password' => bcrypt($data['password']),
|
||||
'hear_about_us' => $data['hear_about_us']
|
||||
]);
|
||||
|
||||
// Add relation with user
|
||||
$user->workspaces()->sync([
|
||||
$workspace->id => [
|
||||
'role' => 'admin'
|
||||
]
|
||||
], false);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
46
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
46
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
use ResetsPasswords;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendResetResponse(Request $request, $response)
|
||||
{
|
||||
return ['status' => trans($response)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a failed password reset.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendResetFailedResponse(Request $request, $response)
|
||||
{
|
||||
return response()->json(['email' => trans($response)], 400);
|
||||
}
|
||||
}
|
||||
34
app/Http/Controllers/Auth/UserController.php
Normal file
34
app/Http/Controllers/Auth/UserController.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get authenticated user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function current(Request $request)
|
||||
{
|
||||
return response()->json($request->user());
|
||||
}
|
||||
|
||||
public function deleteAccount() {
|
||||
$this->middleware('auth');
|
||||
if (Auth::user()->admin) {
|
||||
return $this->error([
|
||||
'message' => 'Cannot delete an admin. Stay with us 🙏'
|
||||
]);
|
||||
}
|
||||
Auth::user()->delete();
|
||||
return $this->success([
|
||||
'message' => 'User deleted.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
82
app/Http/Controllers/Auth/VerificationController.php
Normal file
82
app/Http/Controllers/Auth/VerificationController.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class VerificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('throttle:6,1')->only('verify', 'resend');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the user's email address as verified.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\User $user
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function verify(Request $request, User $user)
|
||||
{
|
||||
if (! URL::hasValidSignature($request)) {
|
||||
return response()->json([
|
||||
'status' => trans('verification.invalid'),
|
||||
], 400);
|
||||
}
|
||||
|
||||
if ($user->hasVerifiedEmail()) {
|
||||
return response()->json([
|
||||
'status' => trans('verification.already_verified'),
|
||||
], 400);
|
||||
}
|
||||
|
||||
$user->markEmailAsVerified();
|
||||
|
||||
event(new Verified($user));
|
||||
|
||||
return response()->json([
|
||||
'status' => trans('verification.verified'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend the email verification notification.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function resend(Request $request)
|
||||
{
|
||||
$this->validate($request, ['email' => 'required|email']);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (is_null($user)) {
|
||||
throw ValidationException::withMessages([
|
||||
'email' => [trans('verification.user')],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($user->hasVerifiedEmail()) {
|
||||
throw ValidationException::withMessages([
|
||||
'email' => [trans('verification.already_verified')],
|
||||
]);
|
||||
}
|
||||
|
||||
$user->sendEmailVerificationNotification();
|
||||
|
||||
return response()->json(['status' => trans('verification.sent')]);
|
||||
}
|
||||
}
|
||||
22
app/Http/Controllers/Content/ChangelogController.php
Normal file
22
app/Http/Controllers/Content/ChangelogController.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Content;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ChangelogController extends Controller
|
||||
{
|
||||
const CANNY_ENDPOINT = 'https://canny.io/api/v1/';
|
||||
|
||||
public function index()
|
||||
{
|
||||
return \Cache::remember('changelog_entries', now()->addHour(), function () {
|
||||
$response = \Http:: post(self::CANNY_ENDPOINT.'entries/list',[
|
||||
'apiKey' => config('services.canny.api_key'),
|
||||
'limit' => 3,
|
||||
]);
|
||||
return $response->json('entries');
|
||||
});
|
||||
}
|
||||
}
|
||||
46
app/Http/Controllers/Content/SignedStorageUrlController.php
Normal file
46
app/Http/Controllers/Content/SignedStorageUrlController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Content;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Vapor\Http\Controllers\SignedStorageUrlController as Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SignedStorageUrlController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new signed URL.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->ensureEnvironmentVariablesAreAvailable($request);
|
||||
$bucket = $request->input('bucket') ?: $_ENV['AWS_BUCKET'];
|
||||
|
||||
$client = $this->storageClient();
|
||||
|
||||
$uuid = (string) Str::uuid();
|
||||
|
||||
$expiresAfter = config('vapor.signed_storage_url_expires_after', 5);
|
||||
|
||||
$signedRequest = $client->createPresignedRequest(
|
||||
$this->createCommand($request, $client, $bucket, $key = ('tmp/'.$uuid)),
|
||||
sprintf('+%s minutes', $expiresAfter)
|
||||
);
|
||||
|
||||
$uri = $signedRequest->getUri();
|
||||
|
||||
return response()->json([
|
||||
'uuid' => $uuid,
|
||||
'bucket' => $bucket,
|
||||
'key' => $key,
|
||||
'url' => $uri->getScheme().'://'.$uri->getAuthority().$uri->getPath().'?'.$uri->getQuery(),
|
||||
'headers' => $this->headers($request, $signedRequest),
|
||||
], 201);
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/Controller.php
Normal file
27
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
|
||||
public function success($data = [])
|
||||
{
|
||||
return response()->json(array_merge([
|
||||
'type' => 'success'
|
||||
], $data));
|
||||
}
|
||||
|
||||
public function error($data = [], $statusCode = 400)
|
||||
{
|
||||
return response()->json(array_merge([
|
||||
'type' => 'error'
|
||||
], $data), $statusCode);
|
||||
}
|
||||
}
|
||||
189
app/Http/Controllers/Forms/FormController.php
Normal file
189
app/Http/Controllers/Forms/FormController.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Forms;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\StoreFormRequest;
|
||||
use App\Http\Requests\UpdateFormRequest;
|
||||
use App\Http\Requests\UploadAssetRequest;
|
||||
use App\Http\Resources\FormResource;
|
||||
use App\Models\Forms\Form;
|
||||
use App\Models\Workspace;
|
||||
use App\Service\Forms\FormCleaner;
|
||||
use App\Service\Storage\StorageFileNameParser;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
const ASSETS_UPLOAD_PATH = 'assets/forms';
|
||||
|
||||
private FormCleaner $formCleaner;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->formCleaner = new FormCleaner();
|
||||
}
|
||||
|
||||
public function index($workspaceId)
|
||||
{
|
||||
$workspace = Workspace::findOrFail($workspaceId);
|
||||
$this->authorize('view', $workspace);
|
||||
$this->authorize('viewAny', Form::class);
|
||||
|
||||
return FormResource::collection($workspace->forms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all user forms, used for zapier
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function indexAll()
|
||||
{
|
||||
$forms = collect();
|
||||
foreach (Auth::user()->workspaces as $workspace) {
|
||||
$this->authorize('view', $workspace);
|
||||
$this->authorize('viewAny', Form::class);
|
||||
|
||||
$forms = $forms->merge($workspace->forms);
|
||||
}
|
||||
return FormResource::collection($forms);
|
||||
}
|
||||
|
||||
public function store(StoreFormRequest $request)
|
||||
{
|
||||
$this->authorize('create', Form::class);
|
||||
|
||||
$workspace = Workspace::findOrFail($request->get('workspace_id'));
|
||||
$this->authorize('view', $workspace);
|
||||
|
||||
$formData = $this->formCleaner
|
||||
->processRequest($request)
|
||||
->simulateCleaning($workspace)
|
||||
->getData();
|
||||
|
||||
$form = Form::create(array_merge($formData, [
|
||||
'creator_id' => $request->user()->id
|
||||
]));
|
||||
|
||||
return $this->success([
|
||||
'message' => $this->formCleaner->hasCleaned() ? 'Form successfully created, but the Pro features you used will be disabled when sharing your form:' : 'Form created.',
|
||||
'form_cleaning' => $this->formCleaner->getPerformedCleanings(),
|
||||
'form' => new FormResource($form),
|
||||
'users_first_form' => $request->user()->forms()->count() == 1
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateFormRequest $request, string $id)
|
||||
{
|
||||
$form = Form::findOrFail($id);
|
||||
$this->authorize('update', $form);
|
||||
|
||||
$formData = $this->formCleaner
|
||||
->processRequest($request)
|
||||
->simulateCleaning($form->workspace)
|
||||
->getData();
|
||||
|
||||
// Set Removed Properties
|
||||
$formData['removed_properties'] = array_merge($form->removed_properties, collect($form->properties)->filter(function ($field) use ($formData) {
|
||||
return (!Str::of($field['type'])->startsWith('nf-') && !in_array($field['id'], collect($formData['properties'])->pluck("id")->toArray()));
|
||||
})->toArray());
|
||||
|
||||
$form->update($formData);
|
||||
|
||||
return $this->success([
|
||||
'message' => $this->formCleaner->hasCleaned() ? 'Form successfully updated, but the Pro features you used will be disabled when sharing your form:' : 'Form updated.',
|
||||
'form_cleaning' => $this->formCleaner->getPerformedCleanings(),
|
||||
'form' => new FormResource($form)
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$form = Form::findOrFail($id);
|
||||
$this->authorize('delete', $form);
|
||||
|
||||
$form->delete();
|
||||
return $this->success([
|
||||
'message' => 'Form was deleted.'
|
||||
]);
|
||||
}
|
||||
|
||||
public function duplicate($id)
|
||||
{
|
||||
$form = Form::findOrFail($id);
|
||||
$this->authorize('update', $form);
|
||||
|
||||
// Create copy
|
||||
$formCopy = $form->replicate();
|
||||
$formCopy->title = 'Copy of '.$formCopy->title;
|
||||
$formCopy->save();
|
||||
|
||||
return $this->success([
|
||||
'message' => 'Form successfully duplicated.',
|
||||
'new_form' => new FormResource($formCopy)
|
||||
]);
|
||||
}
|
||||
|
||||
public function regenerateLink($id, $option)
|
||||
{
|
||||
$form = Form::findOrFail($id);
|
||||
$this->authorize('update', $form);
|
||||
|
||||
if ( $option == 'slug') {
|
||||
$form->generateSlug();
|
||||
} elseif ($option == 'uuid') {
|
||||
$form->slug = Str::uuid();
|
||||
}
|
||||
$form->save();
|
||||
|
||||
return $this->success([
|
||||
'message' => 'Form url successfully updated. Your new form url now is: '.$form->share_url.'.',
|
||||
'form' => new FormResource($form)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a form asset
|
||||
*/
|
||||
public function uploadAsset(UploadAssetRequest $request)
|
||||
{
|
||||
$this->authorize('viewAny', Form::class);
|
||||
|
||||
$fileNameParser = StorageFileNameParser::parse($request->url);
|
||||
|
||||
// Make sure we retrieve the file in tmp storage, move it to persistent
|
||||
$fileName = PublicFormController::TMP_FILE_UPLOAD_PATH.'/'.$fileNameParser->uuid;;
|
||||
if (!Storage::disk('s3')->exists($fileName)) {
|
||||
// File not found, we skip
|
||||
return null;
|
||||
}
|
||||
$newPath = self::ASSETS_UPLOAD_PATH.'/'.$fileNameParser->getMovedFileName();
|
||||
Storage::disk('s3')->move($fileName, $newPath);
|
||||
|
||||
return $this->success([
|
||||
'message' => 'File uploaded.',
|
||||
'url' => route("forms.assets.show", [$fileNameParser->getMovedFileName()])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* File uploads retrieval
|
||||
*/
|
||||
public function viewFile($id, $fileName)
|
||||
{
|
||||
$form = Form::findOrFail($id);
|
||||
$this->authorize('view', $form);
|
||||
|
||||
$path = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $form->id).'/'.$fileName;
|
||||
if (!Storage::disk('s3')->exists($path)) {
|
||||
return $this->error([
|
||||
'message' => 'File not found.'
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->to(Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(5)));
|
||||
}
|
||||
}
|
||||
39
app/Http/Controllers/Forms/FormStatsController.php
Normal file
39
app/Http/Controllers/Forms/FormStatsController.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Forms;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Forms\Form;
|
||||
use Carbon\CarbonPeriod;
|
||||
use App\Models\Forms\FormStatistic;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FormStatsController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function getFormStats(Request $request)
|
||||
{
|
||||
$form = $request->form; // Added by ProForm middleware
|
||||
$this->authorize('view', $form);
|
||||
|
||||
$formStats = $form->statistics()->where('date','>',now()->subDays(29)->startOfDay())->get();
|
||||
$periodStats = ["views" => [], "submissions" => []];
|
||||
foreach (CarbonPeriod::create(now()->subDays(29), now()) as $dateObj) {
|
||||
$date = $dateObj->format('d-m-Y');
|
||||
|
||||
$statisticData = $formStats->where('date', $dateObj->format('Y-m-d'))->first();
|
||||
$periodStats["views"][$date] = $statisticData->data["views"] ?? 0;
|
||||
$periodStats["submissions"][$date] = $statisticData->data["submissions"] ?? 0;
|
||||
|
||||
if($dateObj->toDateString() === now()->toDateString()){
|
||||
$periodStats["views"][$date] += $form->views()->count();
|
||||
$periodStats["submissions"][$date] += $form->submissions()->whereDate('created_at', '>=', now()->startOfDay())->count();
|
||||
}
|
||||
}
|
||||
return $periodStats;
|
||||
}
|
||||
}
|
||||
69
app/Http/Controllers/Forms/FormSubmissionController.php
Normal file
69
app/Http/Controllers/Forms/FormSubmissionController.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Forms;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\FormSubmissionResource;
|
||||
use App\Models\Forms\Form;
|
||||
use App\Exports\FormSubmissionExport;
|
||||
use App\Service\Forms\FormSubmissionFormatter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
class FormSubmissionController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function submissions(string $id)
|
||||
{
|
||||
$form = Form::findOrFail((int) $id);
|
||||
$this->authorize('view', $form);
|
||||
|
||||
return FormSubmissionResource::collection($form->submissions()->paginate(100));
|
||||
}
|
||||
|
||||
public function export(string $id)
|
||||
{
|
||||
$form = Form::findOrFail((int) $id);
|
||||
$this->authorize('view', $form);
|
||||
|
||||
$allRows = [];
|
||||
foreach ($form->submissions->toArray() as $row) {
|
||||
$formatter = (new FormSubmissionFormatter($form, $row['data']))
|
||||
->outputStringsOnly()
|
||||
->setEmptyForNoValue();
|
||||
$tmp = $formatter->getCleanKeyValue();
|
||||
$tmp['Create Date'] = date("Y-m-d H:i", strtotime($row['created_at']));
|
||||
$allRows[] = $tmp;
|
||||
}
|
||||
$csvExport = (new FormSubmissionExport($allRows));
|
||||
return Excel::download(
|
||||
$csvExport,
|
||||
$form->slug.'-submission-data.csv',
|
||||
\Maatwebsite\Excel\Excel::CSV
|
||||
);
|
||||
}
|
||||
|
||||
public function submissionFile($id, $fileName)
|
||||
{
|
||||
$form = Form::findOrFail((int) $id);
|
||||
$this->authorize('view', $form);
|
||||
|
||||
$fileName = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $id).'/'
|
||||
.urldecode($fileName);
|
||||
|
||||
if (!Storage::disk('s3')->exists($fileName)) {
|
||||
return $this->error([
|
||||
'message' => 'File not found.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return redirect(
|
||||
Storage::disk('s3')->temporaryUrl($fileName, now()->addMinute())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Forms\Integration;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Integration\StoreFormZapierWebhookRequest;
|
||||
use App\Models\Integration\FormZapierWebhook;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\WebhookServer\WebhookCall;
|
||||
|
||||
class FormZapierWebhookController extends Controller
|
||||
{
|
||||
/**
|
||||
* Controller for Zappier webhook subscriptions.
|
||||
*/
|
||||
public function __construct() {
|
||||
// $this->middleware('subscribed');
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function store(StoreFormZapierWebhookRequest $request) {
|
||||
$hook = $request->instanciateHook();
|
||||
$this->authorize('store', $hook);
|
||||
|
||||
$hook->save();
|
||||
return $this->success([
|
||||
'message' => 'Webhook created.',
|
||||
'hook' => $hook
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete($id) {
|
||||
$hook = FormZapierWebhook::findOrFail($id);
|
||||
$this->authorize('store', $hook);
|
||||
|
||||
$hook->delete();
|
||||
return $this->success([
|
||||
'message' => 'Webhook deleted.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
91
app/Http/Controllers/Forms/PublicFormController.php
Normal file
91
app/Http/Controllers/Forms/PublicFormController.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Forms;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AnswerFormRequest;
|
||||
use App\Http\Resources\FormResource;
|
||||
use App\Jobs\Form\StoreFormSubmissionJob;
|
||||
use App\Models\Forms\Form;
|
||||
use App\Service\Forms\FormCleaner;
|
||||
use App\Service\WorkspaceHelper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PublicFormController extends Controller
|
||||
{
|
||||
|
||||
const FILE_UPLOAD_PATH = 'forms/?/submissions';
|
||||
const TMP_FILE_UPLOAD_PATH = 'tmp/';
|
||||
|
||||
public function show(Request $request, string $slug)
|
||||
{
|
||||
$form = Form::whereSlug($slug)->firstOrFail();
|
||||
if ($form->workspace == null) {
|
||||
// Workspace deleted
|
||||
return $this->error([
|
||||
'message' => 'Form not found.'
|
||||
], 404);
|
||||
}
|
||||
|
||||
$formCleaner = new FormCleaner();
|
||||
|
||||
// Disable pro features if needed
|
||||
$form->fill($formCleaner
|
||||
->processForm($request, $form)
|
||||
->performCleaning($form->workspace)
|
||||
->getData()
|
||||
);
|
||||
|
||||
// Increase form view counter if not login
|
||||
if(!Auth::check()){
|
||||
$form->views()->create();
|
||||
}
|
||||
|
||||
$formResource = new FormResource($form);
|
||||
$formResource->setCleanings($formCleaner->getPerformedCleanings());
|
||||
return $formResource;
|
||||
}
|
||||
|
||||
public function listUsers(Request $request)
|
||||
{
|
||||
// Check that form has user field
|
||||
$form = $request->form;
|
||||
if (!$form->has_user_field) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Use serializer
|
||||
$workspace = $form->workspace;
|
||||
return (new WorkspaceHelper($workspace))->getAllUsers();
|
||||
}
|
||||
|
||||
public function showAsset($assetFileName)
|
||||
{
|
||||
$path = FormController::ASSETS_UPLOAD_PATH.'/'.$assetFileName;
|
||||
if (!Storage::disk('s3')->exists($path)) {
|
||||
return $this->error([
|
||||
'message' => 'File not found.',
|
||||
'file_name' => $assetFileName
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->to(Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(5)));
|
||||
}
|
||||
|
||||
public function answer(AnswerFormRequest $request)
|
||||
{
|
||||
$form = $request->form;
|
||||
|
||||
StoreFormSubmissionJob::dispatch($form, $request->validated());
|
||||
return $this->success(array_merge([
|
||||
'message' => 'Form submission saved.',
|
||||
], $request->form->is_pro && $request->form->redirect_url ? [
|
||||
'redirect' => true,
|
||||
'redirect_url' => $request->form->redirect_url
|
||||
] : [
|
||||
'redirect' => false
|
||||
]));
|
||||
}
|
||||
}
|
||||
28
app/Http/Controllers/Settings/PasswordController.php
Normal file
28
app/Http/Controllers/Settings/PasswordController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Update the user's password.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]);
|
||||
|
||||
$request->user()->update([
|
||||
'password' => bcrypt($request->password),
|
||||
]);
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/Settings/ProfileController.php
Normal file
30
app/Http/Controllers/Settings/ProfileController.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$this->validate($request, [
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users,email,'.$user->id,
|
||||
]);
|
||||
|
||||
return tap($user)->update([
|
||||
'name' => $request->name,
|
||||
'email' => strtolower($request->email),
|
||||
]);
|
||||
}
|
||||
}
|
||||
39
app/Http/Controllers/SitemapController.php
Normal file
39
app/Http/Controllers/SitemapController.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Sitemap\Sitemap;
|
||||
use Spatie\Sitemap\Tags\Url;
|
||||
|
||||
class SitemapController extends Controller
|
||||
{
|
||||
/**
|
||||
* Contains route name and the associated priority
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $urls = [
|
||||
['/', 1],
|
||||
['/privacy-policy', 0.5],
|
||||
['/terms-conditions', 0.5],
|
||||
['/login', 0.4],
|
||||
['/register', 0.4],
|
||||
['/password/reset', 0.3],
|
||||
];
|
||||
|
||||
public function getSitemap(Request $request)
|
||||
{
|
||||
$sitemap = Sitemap::create();
|
||||
foreach ($this->urls as $url) {
|
||||
$sitemap->add($this->createUrl($url[0], $url[1]));
|
||||
}
|
||||
|
||||
return $sitemap->toResponse($request);
|
||||
}
|
||||
|
||||
private function createUrl($url, $priority, $frequency = 'daily')
|
||||
{
|
||||
return Url::create($url)->setPriority($priority)->setChangeFrequency($frequency);
|
||||
}
|
||||
}
|
||||
16
app/Http/Controllers/SpaController.php
Normal file
16
app/Http/Controllers/SpaController.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
class SpaController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get the SPA view.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return view('spa');
|
||||
}
|
||||
}
|
||||
76
app/Http/Controllers/SubscriptionController.php
Normal file
76
app/Http/Controllers/SubscriptionController.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Laravel\Cashier\Subscription;
|
||||
|
||||
class SubscriptionController extends Controller
|
||||
{
|
||||
const SUBSCRIPTION_PLANS = ['monthly_2022', 'yearly_2022'];
|
||||
|
||||
const PRO_SUBSCRIPTION_NAME = 'default';
|
||||
const ENTERPRISE_SUBSCRIPTION_NAME = 'enterprise';
|
||||
|
||||
const SUBSCRIPTION_NAMES = [
|
||||
self::PRO_SUBSCRIPTION_NAME,
|
||||
self::ENTERPRISE_SUBSCRIPTION_NAME
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns stripe checkout URL
|
||||
*
|
||||
* $plan is constrained with regex in the api.php
|
||||
*/
|
||||
public function checkout($pricing, $plan, $trial = null)
|
||||
{
|
||||
$this->middleware('not-subscribed');
|
||||
|
||||
// Check User does not have a pending subscription
|
||||
$user = Auth::user();
|
||||
if ($user->subscriptions()->where('stripe_status', 'past_due')->first()) {
|
||||
return $this->error([
|
||||
'message' => 'You already have a past due subscription. Please verify your details in the billing page,
|
||||
and contact us if the issue persists.'
|
||||
]);
|
||||
}
|
||||
|
||||
$checkoutBuilder = $user
|
||||
->newSubscription($pricing, $this->getPricing($pricing)[$plan])
|
||||
->allowPromotionCodes();
|
||||
|
||||
if ($trial != null) {
|
||||
$checkoutBuilder->trialDays(3);
|
||||
}
|
||||
|
||||
$checkout = $checkoutBuilder
|
||||
->collectTaxIds()
|
||||
->checkout([
|
||||
'success_url' => url('/subscriptions/success'),
|
||||
'cancel_url' => url('/subscriptions/error'),
|
||||
]);
|
||||
|
||||
return $this->success([
|
||||
'checkout_url' => $checkout->url
|
||||
]);
|
||||
}
|
||||
|
||||
public function billingPortal()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
if (!Auth::user()->has_customer_id) {
|
||||
return $this->error([
|
||||
"message" => "Please subscribe before accessing your billing portal."
|
||||
]);
|
||||
}
|
||||
return $this->success([
|
||||
'portal_url' => Auth::user()->billingPortalUrl(url('/home'))
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPricing($product = 'pro')
|
||||
{
|
||||
return App::environment() == 'production' ? config('pricing.production.'.$product.'.pricing') : config('pricing.test.'.$product.'.pricing');
|
||||
}
|
||||
}
|
||||
131
app/Http/Controllers/Webhook/StripeController.php
Normal file
131
app/Http/Controllers/Webhook/StripeController.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Webhook;
|
||||
|
||||
use App\Notifications\Subscription\FailedPaymentNotification;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Laravel\Cashier\Http\Controllers\WebhookController;
|
||||
use Stripe\Subscription as StripeSubscription;
|
||||
|
||||
class StripeController extends WebhookController
|
||||
{
|
||||
|
||||
public function handleCustomerSubscriptionCreated(array $payload)
|
||||
{
|
||||
return parent::handleCustomerSubscriptionCreated($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to add a sleep, and to detect plan upgrades
|
||||
* @param array $payload
|
||||
* @return \Symfony\Component\HttpFoundation\Response|void
|
||||
*/
|
||||
protected function handleCustomerSubscriptionUpdated(array $payload)
|
||||
{
|
||||
sleep(1);
|
||||
|
||||
if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
|
||||
$data = $payload['data']['object'];
|
||||
|
||||
$subscription = $user->subscriptions()->firstOrNew(['stripe_id' => $data['id']]);
|
||||
|
||||
if (
|
||||
isset($data['status']) &&
|
||||
$data['status'] === StripeSubscription::STATUS_INCOMPLETE_EXPIRED
|
||||
) {
|
||||
$subscription->items()->delete();
|
||||
$subscription->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription->name = $subscription->name ?? $data['metadata']['name'] ?? $this->newSubscriptionName($payload);
|
||||
|
||||
$firstItem = $data['items']['data'][0];
|
||||
$isSinglePrice = count($data['items']['data']) === 1;
|
||||
|
||||
// Price...
|
||||
$subscription->stripe_price = $isSinglePrice ? $firstItem['price']['id'] : null;
|
||||
|
||||
// Name...
|
||||
$subscription->name = $this->getSubscriptionName($data['plan']['product']);
|
||||
|
||||
// Quantity...
|
||||
$subscription->quantity = $isSinglePrice && isset($firstItem['quantity']) ? $firstItem['quantity'] : null;
|
||||
|
||||
// Trial ending date...
|
||||
if (isset($data['trial_end'])) {
|
||||
$trialEnd = Carbon::createFromTimestamp($data['trial_end']);
|
||||
|
||||
if (!$subscription->trial_ends_at || $subscription->trial_ends_at->ne($trialEnd)) {
|
||||
$subscription->trial_ends_at = $trialEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Cancellation date...
|
||||
if (isset($data['cancel_at_period_end'])) {
|
||||
if ($data['cancel_at_period_end']) {
|
||||
$subscription->ends_at = $subscription->onTrial()
|
||||
? $subscription->trial_ends_at
|
||||
: Carbon::createFromTimestamp($data['current_period_end']);
|
||||
} elseif (isset($data['cancel_at'])) {
|
||||
$subscription->ends_at = Carbon::createFromTimestamp($data['cancel_at']);
|
||||
} else {
|
||||
$subscription->ends_at = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Status...
|
||||
if (isset($data['status'])) {
|
||||
$subscription->stripe_status = $data['status'];
|
||||
}
|
||||
|
||||
$subscription->save();
|
||||
|
||||
// Update subscription items...
|
||||
if (isset($data['items'])) {
|
||||
$prices = [];
|
||||
|
||||
foreach ($data['items']['data'] as $item) {
|
||||
$prices[] = $item['price']['id'];
|
||||
|
||||
$subscription->items()->updateOrCreate([
|
||||
'stripe_id' => $item['id'],
|
||||
], [
|
||||
'stripe_product' => $item['price']['product'],
|
||||
'stripe_price' => $item['price']['id'],
|
||||
'quantity' => $item['quantity'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
// Delete items that aren't attached to the subscription anymore...
|
||||
$subscription->items()->whereNotIn('stripe_price', $prices)->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->successMethod();
|
||||
}
|
||||
|
||||
protected function handleChargeFailed(array $payload)
|
||||
{
|
||||
if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
|
||||
$user->notify(new FailedPaymentNotification());
|
||||
}
|
||||
|
||||
return $this->successMethod();
|
||||
}
|
||||
|
||||
private function getSubscriptionName(string $stripeProductId)
|
||||
{
|
||||
$config = App::environment() == 'production' ? config('pricing.production') : config('pricing.test');
|
||||
foreach ($config as $plan => $data) {
|
||||
if ($stripeProductId == $config[$plan]['product_id']) {
|
||||
return $plan;
|
||||
}
|
||||
}
|
||||
return 'default';
|
||||
}
|
||||
|
||||
}
|
||||
71
app/Http/Controllers/WorkspaceController.php
Normal file
71
app/Http/Controllers/WorkspaceController.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Workspace;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Service\WorkspaceHelper;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class WorkspaceController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('viewAny', Workspace::class);
|
||||
return Auth::user()->workspaces;
|
||||
}
|
||||
|
||||
public function listUsers(Request $request, $workspaceId)
|
||||
{
|
||||
$workspace = Workspace::findOrFail($workspaceId);
|
||||
$this->authorize('view', $workspace);
|
||||
|
||||
return (new WorkspaceHelper($workspace))->getAllUsers();
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$workspace = Workspace::findOrFail($id);
|
||||
$this->authorize('delete', $workspace);
|
||||
|
||||
$id = $workspace->id;
|
||||
$workspace->delete();
|
||||
return $this->success([
|
||||
'message' => 'Workspace deleted.',
|
||||
'workspace_id' => $id
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$this->validate($request, [
|
||||
'name' => 'required'
|
||||
]);
|
||||
|
||||
// Create workspace
|
||||
$workspace = Workspace::create([
|
||||
'name' => $request->name,
|
||||
'icon' => ($request->emoji) ? $request->emoji : '',
|
||||
]);
|
||||
|
||||
// Add relation with user
|
||||
$user->workspaces()->sync([
|
||||
$workspace->id => [
|
||||
'role' => 'admin'
|
||||
]
|
||||
], false);
|
||||
|
||||
return $this->success([
|
||||
'message' => 'Workspace created.',
|
||||
'workspace_id' => $workspace->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user