Merge branch 'development' into release
This commit is contained in:
		
						commit
						3424351e84
					
				| 
						 | 
					@ -215,10 +215,11 @@ LDAP_SERVER=false
 | 
				
			||||||
LDAP_BASE_DN=false
 | 
					LDAP_BASE_DN=false
 | 
				
			||||||
LDAP_DN=false
 | 
					LDAP_DN=false
 | 
				
			||||||
LDAP_PASS=false
 | 
					LDAP_PASS=false
 | 
				
			||||||
LDAP_USER_FILTER=false
 | 
					LDAP_USER_FILTER="(&(uid={user}))"
 | 
				
			||||||
LDAP_VERSION=false
 | 
					LDAP_VERSION=false
 | 
				
			||||||
LDAP_START_TLS=false
 | 
					LDAP_START_TLS=false
 | 
				
			||||||
LDAP_TLS_INSECURE=false
 | 
					LDAP_TLS_INSECURE=false
 | 
				
			||||||
 | 
					LDAP_TLS_CA_CERT=false
 | 
				
			||||||
LDAP_ID_ATTRIBUTE=uid
 | 
					LDAP_ID_ATTRIBUTE=uid
 | 
				
			||||||
LDAP_EMAIL_ATTRIBUTE=mail
 | 
					LDAP_EMAIL_ATTRIBUTE=mail
 | 
				
			||||||
LDAP_DISPLAY_NAME_ATTRIBUTE=cn
 | 
					LDAP_DISPLAY_NAME_ATTRIBUTE=cn
 | 
				
			||||||
| 
						 | 
					@ -267,6 +268,7 @@ OIDC_ISSUER_DISCOVER=false
 | 
				
			||||||
OIDC_PUBLIC_KEY=null
 | 
					OIDC_PUBLIC_KEY=null
 | 
				
			||||||
OIDC_AUTH_ENDPOINT=null
 | 
					OIDC_AUTH_ENDPOINT=null
 | 
				
			||||||
OIDC_TOKEN_ENDPOINT=null
 | 
					OIDC_TOKEN_ENDPOINT=null
 | 
				
			||||||
 | 
					OIDC_USERINFO_ENDPOINT=null
 | 
				
			||||||
OIDC_ADDITIONAL_SCOPES=null
 | 
					OIDC_ADDITIONAL_SCOPES=null
 | 
				
			||||||
OIDC_DUMP_USER_DETAILS=false
 | 
					OIDC_DUMP_USER_DETAILS=false
 | 
				
			||||||
OIDC_USER_TO_GROUPS=false
 | 
					OIDC_USER_TO_GROUPS=false
 | 
				
			||||||
| 
						 | 
					@ -324,6 +326,14 @@ FILE_UPLOAD_SIZE_LIMIT=50
 | 
				
			||||||
# Can be 'a4' or 'letter'.
 | 
					# Can be 'a4' or 'letter'.
 | 
				
			||||||
EXPORT_PAGE_SIZE=a4
 | 
					EXPORT_PAGE_SIZE=a4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Export PDF Command
 | 
				
			||||||
 | 
					# Set a command which can be used to convert a HTML file into a PDF file.
 | 
				
			||||||
 | 
					# When false this will not be used.
 | 
				
			||||||
 | 
					# String values represent the command to be called for conversion.
 | 
				
			||||||
 | 
					# Supports '{input_html_path}' and '{output_pdf_path}' placeholder values.
 | 
				
			||||||
 | 
					# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
 | 
				
			||||||
 | 
					EXPORT_PDF_COMMAND=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Set path to wkhtmltopdf binary for PDF generation.
 | 
					# Set path to wkhtmltopdf binary for PDF generation.
 | 
				
			||||||
# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
 | 
					# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
 | 
				
			||||||
# When false, BookStack will attempt to find a wkhtmltopdf in the application
 | 
					# When false, BookStack will attempt to find a wkhtmltopdf in the application
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -389,7 +389,7 @@ Marc Hagen (MarcHagen) :: Dutch
 | 
				
			||||||
Kasper Alsøe (zeonos) :: Danish
 | 
					Kasper Alsøe (zeonos) :: Danish
 | 
				
			||||||
sultani :: Persian
 | 
					sultani :: Persian
 | 
				
			||||||
renge :: Korean
 | 
					renge :: Korean
 | 
				
			||||||
TheGatesDev (thegatesdev) :: Dutch
 | 
					Tim (thegatesdev) :: Dutch; German Informal; Romanian; French; Catalan; Czech; Danish; German; Finnish; Hungarian; Italian; Japanese; Korean; Polish; Russian; Ukrainian; Chinese Simplified; Chinese Traditional; Portuguese, Brazilian; Persian; Spanish, Argentina; Croatian; Norwegian Nynorsk; Estonian; Uzbek; Norwegian Bokmal
 | 
				
			||||||
Irdi (irdiOL) :: Albanian
 | 
					Irdi (irdiOL) :: Albanian
 | 
				
			||||||
KateBarber :: Welsh
 | 
					KateBarber :: Welsh
 | 
				
			||||||
Twister (theuncles75) :: Hebrew
 | 
					Twister (theuncles75) :: Hebrew
 | 
				
			||||||
| 
						 | 
					@ -410,3 +410,15 @@ cracrayol :: French
 | 
				
			||||||
CapuaSC :: Dutch
 | 
					CapuaSC :: Dutch
 | 
				
			||||||
Guardian75 :: German Informal
 | 
					Guardian75 :: German Informal
 | 
				
			||||||
mr-kanister :: German
 | 
					mr-kanister :: German
 | 
				
			||||||
 | 
					Michele Bastianelli (makoblaster) :: Italian
 | 
				
			||||||
 | 
					jespernissen :: Danish
 | 
				
			||||||
 | 
					Andrey (avmaksimov) :: Russian
 | 
				
			||||||
 | 
					Gonzalo Loyola (AlFcl) :: Spanish, Argentina; Spanish
 | 
				
			||||||
 | 
					grobert63 :: French
 | 
				
			||||||
 | 
					wusst. (Supporti) :: German
 | 
				
			||||||
 | 
					MaximMaximS :: Czech
 | 
				
			||||||
 | 
					damian-klima :: Slovak
 | 
				
			||||||
 | 
					crow_ :: Latvian
 | 
				
			||||||
 | 
					JocelynDelalande :: French
 | 
				
			||||||
 | 
					Jan (JW-CH) :: German Informal
 | 
				
			||||||
 | 
					Timo B (lommes) :: German Informal
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ jobs:
 | 
				
			||||||
    - name: Setup PHP
 | 
					    - name: Setup PHP
 | 
				
			||||||
      uses: shivammathur/setup-php@v2
 | 
					      uses: shivammathur/setup-php@v2
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        php-version: 8.1
 | 
					        php-version: 8.3
 | 
				
			||||||
        extensions: gd, mbstring, json, curl, xml, mysql, ldap
 | 
					        extensions: gd, mbstring, json, curl, xml, mysql, ldap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Get Composer Cache Directory
 | 
					    - name: Get Composer Cache Directory
 | 
				
			||||||
| 
						 | 
					@ -27,10 +27,10 @@ jobs:
 | 
				
			||||||
        echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
					        echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Cache composer packages
 | 
					    - name: Cache composer packages
 | 
				
			||||||
      uses: actions/cache@v3
 | 
					      uses: actions/cache@v4
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        path: ${{ steps.composer-cache.outputs.dir }}
 | 
					        path: ${{ steps.composer-cache.outputs.dir }}
 | 
				
			||||||
        key: ${{ runner.os }}-composer-8.1
 | 
					        key: ${{ runner.os }}-composer-8.3
 | 
				
			||||||
        restore-keys: ${{ runner.os }}-composer-
 | 
					        restore-keys: ${{ runner.os }}-composer-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Install composer dependencies
 | 
					    - name: Install composer dependencies
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ jobs:
 | 
				
			||||||
    runs-on: ubuntu-22.04
 | 
					    runs-on: ubuntu-22.04
 | 
				
			||||||
    strategy:
 | 
					    strategy:
 | 
				
			||||||
      matrix:
 | 
					      matrix:
 | 
				
			||||||
        php: ['8.0', '8.1', '8.2', '8.3']
 | 
					        php: ['8.1', '8.2', '8.3']
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v1
 | 
					      - uses: actions/checkout@v1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ jobs:
 | 
				
			||||||
          echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
					          echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Cache composer packages
 | 
					      - name: Cache composer packages
 | 
				
			||||||
        uses: actions/cache@v3
 | 
					        uses: actions/cache@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          path: ${{ steps.composer-cache.outputs.dir }}
 | 
					          path: ${{ steps.composer-cache.outputs.dir }}
 | 
				
			||||||
          key: ${{ runner.os }}-composer-${{ matrix.php }}
 | 
					          key: ${{ runner.os }}-composer-${{ matrix.php }}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ jobs:
 | 
				
			||||||
    runs-on: ubuntu-22.04
 | 
					    runs-on: ubuntu-22.04
 | 
				
			||||||
    strategy:
 | 
					    strategy:
 | 
				
			||||||
      matrix:
 | 
					      matrix:
 | 
				
			||||||
        php: ['8.0', '8.1', '8.2', '8.3']
 | 
					        php: ['8.1', '8.2', '8.3']
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
    - uses: actions/checkout@v1
 | 
					    - uses: actions/checkout@v1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ jobs:
 | 
				
			||||||
        echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
					        echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Cache composer packages
 | 
					    - name: Cache composer packages
 | 
				
			||||||
      uses: actions/cache@v3
 | 
					      uses: actions/cache@v4
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        path: ${{ steps.composer-cache.outputs.dir }}
 | 
					        path: ${{ steps.composer-cache.outputs.dir }}
 | 
				
			||||||
        key: ${{ runner.os }}-composer-${{ matrix.php }}
 | 
					        key: ${{ runner.os }}-composer-${{ matrix.php }}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								LICENSE
								
								
								
								
							
							
						
						
									
										2
									
								
								LICENSE
								
								
								
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
The MIT License (MIT)
 | 
					The MIT License (MIT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Copyright (c) 2015-2023, Dan Brown and the BookStack Project contributors.
 | 
					Copyright (c) 2015-2024, Dan Brown and the BookStack Project contributors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
of this software and associated documentation files (the "Software"), to deal
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,20 +19,25 @@ class MfaTotpController extends Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected const SETUP_SECRET_SESSION_KEY = 'mfa-setup-totp-secret';
 | 
					    protected const SETUP_SECRET_SESSION_KEY = 'mfa-setup-totp-secret';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(
 | 
				
			||||||
 | 
					        protected TotpService $totp
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Show a view that generates and displays a TOTP QR code.
 | 
					     * Show a view that generates and displays a TOTP QR code.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function generate(TotpService $totp)
 | 
					    public function generate()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (session()->has(static::SETUP_SECRET_SESSION_KEY)) {
 | 
					        if (session()->has(static::SETUP_SECRET_SESSION_KEY)) {
 | 
				
			||||||
            $totpSecret = decrypt(session()->get(static::SETUP_SECRET_SESSION_KEY));
 | 
					            $totpSecret = decrypt(session()->get(static::SETUP_SECRET_SESSION_KEY));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $totpSecret = $totp->generateSecret();
 | 
					            $totpSecret = $this->totp->generateSecret();
 | 
				
			||||||
            session()->put(static::SETUP_SECRET_SESSION_KEY, encrypt($totpSecret));
 | 
					            session()->put(static::SETUP_SECRET_SESSION_KEY, encrypt($totpSecret));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $qrCodeUrl = $totp->generateUrl($totpSecret, $this->currentOrLastAttemptedUser());
 | 
					        $qrCodeUrl = $this->totp->generateUrl($totpSecret, $this->currentOrLastAttemptedUser());
 | 
				
			||||||
        $svg = $totp->generateQrCodeSvg($qrCodeUrl);
 | 
					        $svg = $this->totp->generateQrCodeSvg($qrCodeUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->setPageTitle(trans('auth.mfa_gen_totp_title'));
 | 
					        $this->setPageTitle(trans('auth.mfa_gen_totp_title'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,7 +61,7 @@ class MfaTotpController extends Controller
 | 
				
			||||||
            'code' => [
 | 
					            'code' => [
 | 
				
			||||||
                'required',
 | 
					                'required',
 | 
				
			||||||
                'max:12', 'min:4',
 | 
					                'max:12', 'min:4',
 | 
				
			||||||
                new TotpValidationRule($totpSecret),
 | 
					                new TotpValidationRule($totpSecret, $this->totp),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +92,7 @@ class MfaTotpController extends Controller
 | 
				
			||||||
            'code' => [
 | 
					            'code' => [
 | 
				
			||||||
                'required',
 | 
					                'required',
 | 
				
			||||||
                'max:12', 'min:4',
 | 
					                'max:12', 'min:4',
 | 
				
			||||||
                new TotpValidationRule($totpSecret),
 | 
					                new TotpValidationRule($totpSecret, $this->totp),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,24 +15,13 @@ use Illuminate\Validation\Rules\Password;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RegisterController extends Controller
 | 
					class RegisterController extends Controller
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected SocialDriverManager $socialDriverManager;
 | 
					 | 
				
			||||||
    protected RegistrationService $registrationService;
 | 
					 | 
				
			||||||
    protected LoginService $loginService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create a new controller instance.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function __construct(
 | 
					    public function __construct(
 | 
				
			||||||
        SocialDriverManager $socialDriverManager,
 | 
					        protected SocialDriverManager $socialDriverManager,
 | 
				
			||||||
        RegistrationService $registrationService,
 | 
					        protected RegistrationService $registrationService,
 | 
				
			||||||
        LoginService $loginService
 | 
					        protected LoginService $loginService
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        $this->middleware('guest');
 | 
					        $this->middleware('guest');
 | 
				
			||||||
        $this->middleware('guard:standard');
 | 
					        $this->middleware('guard:standard');
 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->socialDriverManager = $socialDriverManager;
 | 
					 | 
				
			||||||
        $this->registrationService = $registrationService;
 | 
					 | 
				
			||||||
        $this->loginService = $loginService;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -87,6 +76,8 @@ class RegisterController extends Controller
 | 
				
			||||||
            'name'     => ['required', 'min:2', 'max:100'],
 | 
					            'name'     => ['required', 'min:2', 'max:100'],
 | 
				
			||||||
            'email'    => ['required', 'email', 'max:255', 'unique:users'],
 | 
					            'email'    => ['required', 'email', 'max:255', 'unique:users'],
 | 
				
			||||||
            'password' => ['required', Password::default()],
 | 
					            'password' => ['required', Password::default()],
 | 
				
			||||||
 | 
					            // Basic honey for bots that must not be filled in
 | 
				
			||||||
 | 
					            'username' => ['prohibited'],
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,6 +209,12 @@ class LdapService
 | 
				
			||||||
            $this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
 | 
					            $this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Configure any user-provided CA cert files for LDAP.
 | 
				
			||||||
 | 
					        // This option works globally and must be set before a connection is created.
 | 
				
			||||||
 | 
					        if ($this->config['tls_ca_cert']) {
 | 
				
			||||||
 | 
					            $this->configureTlsCaCerts($this->config['tls_ca_cert']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $ldapHost = $this->parseServerString($this->config['server']);
 | 
					        $ldapHost = $this->parseServerString($this->config['server']);
 | 
				
			||||||
        $ldapConnection = $this->ldap->connect($ldapHost);
 | 
					        $ldapConnection = $this->ldap->connect($ldapHost);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -223,7 +229,14 @@ class LdapService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Start and verify TLS if it's enabled
 | 
					        // Start and verify TLS if it's enabled
 | 
				
			||||||
        if ($this->config['start_tls']) {
 | 
					        if ($this->config['start_tls']) {
 | 
				
			||||||
            $started = $this->ldap->startTls($ldapConnection);
 | 
					            try {
 | 
				
			||||||
 | 
					                $started = $this->ldap->startTls($ldapConnection);
 | 
				
			||||||
 | 
					            } catch (\Exception $exception) {
 | 
				
			||||||
 | 
					                $error = $exception->getMessage() . ' :: ' . ldap_error($ldapConnection);
 | 
				
			||||||
 | 
					                ldap_get_option($ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $detail);
 | 
				
			||||||
 | 
					                Log::info("LDAP STARTTLS failure: {$error} {$detail}");
 | 
				
			||||||
 | 
					                throw new LdapException('Could not start TLS connection. Further details in the application log.');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (!$started) {
 | 
					            if (!$started) {
 | 
				
			||||||
                throw new LdapException('Could not start TLS connection');
 | 
					                throw new LdapException('Could not start TLS connection');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -234,6 +247,33 @@ class LdapService
 | 
				
			||||||
        return $this->ldapConnection;
 | 
					        return $this->ldapConnection;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Configure TLS CA certs globally for ldap use.
 | 
				
			||||||
 | 
					     * This will detect if the given path is a directory or file, and set the relevant
 | 
				
			||||||
 | 
					     * LDAP TLS options appropriately otherwise throw an exception if no file/folder found.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note: When using a folder, certificates are expected to be correctly named by hash
 | 
				
			||||||
 | 
					     * which can be done via the c_rehash utility.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws LdapException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function configureTlsCaCerts(string $caCertPath): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $errMessage = "Provided path [{$caCertPath}] for LDAP TLS CA certs could not be resolved to an existing location";
 | 
				
			||||||
 | 
					        $path = realpath($caCertPath);
 | 
				
			||||||
 | 
					        if ($path === false) {
 | 
				
			||||||
 | 
					            throw new LdapException($errMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (is_dir($path)) {
 | 
				
			||||||
 | 
					            $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTDIR, $path);
 | 
				
			||||||
 | 
					        } else if (is_file($path)) {
 | 
				
			||||||
 | 
					            $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTFILE, $path);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            throw new LdapException($errMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Parse an LDAP server string and return the host suitable for a connection.
 | 
					     * Parse an LDAP server string and return the host suitable for a connection.
 | 
				
			||||||
     * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'.
 | 
					     * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'.
 | 
				
			||||||
| 
						 | 
					@ -249,13 +289,18 @@ class LdapService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Build a filter string by injecting common variables.
 | 
					     * Build a filter string by injecting common variables.
 | 
				
			||||||
 | 
					     * Both "${var}" and "{var}" style placeholders are supported.
 | 
				
			||||||
 | 
					     * Dollar based are old format but supported for compatibility.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function buildFilter(string $filterString, array $attrs): string
 | 
					    protected function buildFilter(string $filterString, array $attrs): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $newAttrs = [];
 | 
					        $newAttrs = [];
 | 
				
			||||||
        foreach ($attrs as $key => $attrText) {
 | 
					        foreach ($attrs as $key => $attrText) {
 | 
				
			||||||
            $newKey = '${' . $key . '}';
 | 
					            $escapedText = $this->ldap->escape($attrText);
 | 
				
			||||||
            $newAttrs[$newKey] = $this->ldap->escape($attrText);
 | 
					            $oldVarKey = '${' . $key . '}';
 | 
				
			||||||
 | 
					            $newVarKey = '{' . $key . '}';
 | 
				
			||||||
 | 
					            $newAttrs[$oldVarKey] = $escapedText;
 | 
				
			||||||
 | 
					            $newAttrs[$newVarKey] = $escapedText;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return strtr($filterString, $newAttrs);
 | 
					        return strtr($filterString, $newAttrs);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,36 +2,26 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace BookStack\Access\Mfa;
 | 
					namespace BookStack\Access\Mfa;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Contracts\Validation\Rule;
 | 
					use Closure;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Validation\ValidationRule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TotpValidationRule implements Rule
 | 
					class TotpValidationRule implements ValidationRule
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected $secret;
 | 
					 | 
				
			||||||
    protected $totpService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Create a new rule instance.
 | 
					     * Create a new rule instance.
 | 
				
			||||||
     * Takes the TOTP secret that must be system provided, not user provided.
 | 
					     * Takes the TOTP secret that must be system provided, not user provided.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function __construct(string $secret)
 | 
					    public function __construct(
 | 
				
			||||||
    {
 | 
					        protected string $secret,
 | 
				
			||||||
        $this->secret = $secret;
 | 
					        protected TotpService $totpService,
 | 
				
			||||||
        $this->totpService = app()->make(TotpService::class);
 | 
					    ) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    public function validate(string $attribute, mixed $value, Closure $fail): void
 | 
				
			||||||
     * Determine if the validation rule passes.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function passes($attribute, $value)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->totpService->verifyCode($value, $this->secret);
 | 
					        $passes = $this->totpService->verifyCode($value, $this->secret);
 | 
				
			||||||
    }
 | 
					        if (!$passes) {
 | 
				
			||||||
 | 
					            $fail(trans('validation.totp'));
 | 
				
			||||||
    /**
 | 
					        }
 | 
				
			||||||
     * Get the validation error message.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function message()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return trans('validation.totp');
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,58 +2,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace BookStack\Access\Oidc;
 | 
					namespace BookStack\Access\Oidc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OidcIdToken
 | 
					class OidcIdToken extends OidcJwtWithClaims implements ProvidesClaims
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected array $header;
 | 
					 | 
				
			||||||
    protected array $payload;
 | 
					 | 
				
			||||||
    protected string $signature;
 | 
					 | 
				
			||||||
    protected string $issuer;
 | 
					 | 
				
			||||||
    protected array $tokenParts = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @var array[]|string[]
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected array $keys;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct(string $token, string $issuer, array $keys)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->keys = $keys;
 | 
					 | 
				
			||||||
        $this->issuer = $issuer;
 | 
					 | 
				
			||||||
        $this->parse($token);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Parse the token content into its components.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function parse(string $token): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->tokenParts = explode('.', $token);
 | 
					 | 
				
			||||||
        $this->header = $this->parseEncodedTokenPart($this->tokenParts[0]);
 | 
					 | 
				
			||||||
        $this->payload = $this->parseEncodedTokenPart($this->tokenParts[1] ?? '');
 | 
					 | 
				
			||||||
        $this->signature = $this->base64UrlDecode($this->tokenParts[2] ?? '') ?: '';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Parse a Base64-JSON encoded token part.
 | 
					 | 
				
			||||||
     * Returns the data as a key-value array or empty array upon error.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function parseEncodedTokenPart(string $part): array
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $json = $this->base64UrlDecode($part) ?: '{}';
 | 
					 | 
				
			||||||
        $decoded = json_decode($json, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return is_array($decoded) ? $decoded : [];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Base64URL decode. Needs some character conversions to be compatible
 | 
					 | 
				
			||||||
     * with PHP's default base64 handling.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function base64UrlDecode(string $encoded): string
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return base64_decode(strtr($encoded, '-_', '+/'));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Validate all possible parts of the id token.
 | 
					     * Validate all possible parts of the id token.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					@ -61,91 +11,12 @@ class OidcIdToken
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function validate(string $clientId): bool
 | 
					    public function validate(string $clientId): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->validateTokenStructure();
 | 
					        parent::validateCommonTokenDetails($clientId);
 | 
				
			||||||
        $this->validateTokenSignature();
 | 
					 | 
				
			||||||
        $this->validateTokenClaims($clientId);
 | 
					        $this->validateTokenClaims($clientId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Fetch a specific claim from this token.
 | 
					 | 
				
			||||||
     * Returns null if it is null or does not exist.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return mixed|null
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function getClaim(string $claim)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return $this->payload[$claim] ?? null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get all returned claims within the token.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function getAllClaims(): array
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return $this->payload;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Replace the existing claim data of this token with that provided.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function replaceClaims(array $claims): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->payload = $claims;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Validate the structure of the given token and ensure we have the required pieces.
 | 
					 | 
				
			||||||
     * As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws OidcInvalidTokenException
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function validateTokenStructure(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        foreach (['header', 'payload'] as $prop) {
 | 
					 | 
				
			||||||
            if (empty($this->$prop) || !is_array($this->$prop)) {
 | 
					 | 
				
			||||||
                throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (empty($this->signature) || !is_string($this->signature)) {
 | 
					 | 
				
			||||||
            throw new OidcInvalidTokenException('Could not parse out a valid signature within the provided token');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Validate the signature of the given token and ensure it validates against the provided key.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws OidcInvalidTokenException
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function validateTokenSignature(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if ($this->header['alg'] !== 'RS256') {
 | 
					 | 
				
			||||||
            throw new OidcInvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $parsedKeys = array_map(function ($key) {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                return new OidcJwtSigningKey($key);
 | 
					 | 
				
			||||||
            } catch (OidcInvalidKeyException $e) {
 | 
					 | 
				
			||||||
                throw new OidcInvalidTokenException('Failed to read signing key with error: ' . $e->getMessage());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }, $this->keys);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $parsedKeys = array_filter($parsedKeys);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
 | 
					 | 
				
			||||||
        /** @var OidcJwtSigningKey $parsedKey */
 | 
					 | 
				
			||||||
        foreach ($parsedKeys as $parsedKey) {
 | 
					 | 
				
			||||||
            if ($parsedKey->verify($contentToSign, $this->signature)) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Validate the claims of the token.
 | 
					     * Validate the claims of the token.
 | 
				
			||||||
     * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation.
 | 
					     * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation.
 | 
				
			||||||
| 
						 | 
					@ -156,27 +27,18 @@ class OidcIdToken
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)
 | 
					        // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)
 | 
				
			||||||
        // MUST exactly match the value of the iss (issuer) Claim.
 | 
					        // MUST exactly match the value of the iss (issuer) Claim.
 | 
				
			||||||
        if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) {
 | 
					        // Already done in parent.
 | 
				
			||||||
            throw new OidcInvalidTokenException('Missing or non-matching token issuer value');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered
 | 
					        // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered
 | 
				
			||||||
        // at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected
 | 
					        // at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected
 | 
				
			||||||
        // if the ID Token does not list the Client as a valid audience, or if it contains additional
 | 
					        // if the ID Token does not list the Client as a valid audience, or if it contains additional
 | 
				
			||||||
        // audiences not trusted by the Client.
 | 
					        // audiences not trusted by the Client.
 | 
				
			||||||
        if (empty($this->payload['aud'])) {
 | 
					        // Partially done in parent.
 | 
				
			||||||
            throw new OidcInvalidTokenException('Missing token audience value');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud'];
 | 
					        $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud'];
 | 
				
			||||||
        if (count($aud) !== 1) {
 | 
					        if (count($aud) !== 1) {
 | 
				
			||||||
            throw new OidcInvalidTokenException('Token audience value has ' . count($aud) . ' values, Expected 1');
 | 
					            throw new OidcInvalidTokenException('Token audience value has ' . count($aud) . ' values, Expected 1');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($aud[0] !== $clientId) {
 | 
					 | 
				
			||||||
            throw new OidcInvalidTokenException('Token audience value did not match the expected client_id');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 3. If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present.
 | 
					        // 3. If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present.
 | 
				
			||||||
        // NOTE: Addressed by enforcing a count of 1 above.
 | 
					        // NOTE: Addressed by enforcing a count of 1 above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,174 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\Access\Oidc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OidcJwtWithClaims implements ProvidesClaims
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected array $header;
 | 
				
			||||||
 | 
					    protected array $payload;
 | 
				
			||||||
 | 
					    protected string $signature;
 | 
				
			||||||
 | 
					    protected string $issuer;
 | 
				
			||||||
 | 
					    protected array $tokenParts = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @var array[]|string[]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected array $keys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(string $token, string $issuer, array $keys)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->keys = $keys;
 | 
				
			||||||
 | 
					        $this->issuer = $issuer;
 | 
				
			||||||
 | 
					        $this->parse($token);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parse the token content into its components.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function parse(string $token): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->tokenParts = explode('.', $token);
 | 
				
			||||||
 | 
					        $this->header = $this->parseEncodedTokenPart($this->tokenParts[0]);
 | 
				
			||||||
 | 
					        $this->payload = $this->parseEncodedTokenPart($this->tokenParts[1] ?? '');
 | 
				
			||||||
 | 
					        $this->signature = $this->base64UrlDecode($this->tokenParts[2] ?? '') ?: '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parse a Base64-JSON encoded token part.
 | 
				
			||||||
 | 
					     * Returns the data as a key-value array or empty array upon error.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function parseEncodedTokenPart(string $part): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $json = $this->base64UrlDecode($part) ?: '{}';
 | 
				
			||||||
 | 
					        $decoded = json_decode($json, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return is_array($decoded) ? $decoded : [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Base64URL decode. Needs some character conversions to be compatible
 | 
				
			||||||
 | 
					     * with PHP's default base64 handling.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function base64UrlDecode(string $encoded): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return base64_decode(strtr($encoded, '-_', '+/'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Validate common parts of OIDC JWT tokens.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws OidcInvalidTokenException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function validateCommonTokenDetails(string $clientId): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->validateTokenStructure();
 | 
				
			||||||
 | 
					        $this->validateTokenSignature();
 | 
				
			||||||
 | 
					        $this->validateCommonClaims($clientId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch a specific claim from this token.
 | 
				
			||||||
 | 
					     * Returns null if it is null or does not exist.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function getClaim(string $claim): mixed
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->payload[$claim] ?? null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all returned claims within the token.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function getAllClaims(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->payload;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Replace the existing claim data of this token with that provided.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function replaceClaims(array $claims): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->payload = $claims;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Validate the structure of the given token and ensure we have the required pieces.
 | 
				
			||||||
 | 
					     * As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws OidcInvalidTokenException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function validateTokenStructure(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        foreach (['header', 'payload'] as $prop) {
 | 
				
			||||||
 | 
					            if (empty($this->$prop) || !is_array($this->$prop)) {
 | 
				
			||||||
 | 
					                throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (empty($this->signature) || !is_string($this->signature)) {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException('Could not parse out a valid signature within the provided token');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Validate the signature of the given token and ensure it validates against the provided key.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws OidcInvalidTokenException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function validateTokenSignature(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->header['alg'] !== 'RS256') {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $parsedKeys = array_map(function ($key) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                return new OidcJwtSigningKey($key);
 | 
				
			||||||
 | 
					            } catch (OidcInvalidKeyException $e) {
 | 
				
			||||||
 | 
					                throw new OidcInvalidTokenException('Failed to read signing key with error: ' . $e->getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, $this->keys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $parsedKeys = array_filter($parsedKeys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
 | 
				
			||||||
 | 
					        /** @var OidcJwtSigningKey $parsedKey */
 | 
				
			||||||
 | 
					        foreach ($parsedKeys as $parsedKey) {
 | 
				
			||||||
 | 
					            if ($parsedKey->verify($contentToSign, $this->signature)) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Validate common claims for OIDC JWT tokens.
 | 
				
			||||||
 | 
					     * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation
 | 
				
			||||||
 | 
					     * and https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws OidcInvalidTokenException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function validateCommonClaims(string $clientId): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)
 | 
				
			||||||
 | 
					        // MUST exactly match the value of the iss (issuer) Claim.
 | 
				
			||||||
 | 
					        if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException('Missing or non-matching token issuer value');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered
 | 
				
			||||||
 | 
					        // at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected
 | 
				
			||||||
 | 
					        // if the ID Token does not list the Client as a valid audience.
 | 
				
			||||||
 | 
					        if (empty($this->payload['aud'])) {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException('Missing token audience value');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud'];
 | 
				
			||||||
 | 
					        if (!in_array($clientId, $aud, true)) {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException('Token audience value did not match the expected client_id');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -18,10 +18,10 @@ class OidcProviderSettings
 | 
				
			||||||
    public string $issuer;
 | 
					    public string $issuer;
 | 
				
			||||||
    public string $clientId;
 | 
					    public string $clientId;
 | 
				
			||||||
    public string $clientSecret;
 | 
					    public string $clientSecret;
 | 
				
			||||||
    public ?string $redirectUri;
 | 
					 | 
				
			||||||
    public ?string $authorizationEndpoint;
 | 
					    public ?string $authorizationEndpoint;
 | 
				
			||||||
    public ?string $tokenEndpoint;
 | 
					    public ?string $tokenEndpoint;
 | 
				
			||||||
    public ?string $endSessionEndpoint;
 | 
					    public ?string $endSessionEndpoint;
 | 
				
			||||||
 | 
					    public ?string $userinfoEndpoint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @var string[]|array[]
 | 
					     * @var string[]|array[]
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ class OidcProviderSettings
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Apply an array of settings to populate setting properties within this class.
 | 
					     * Apply an array of settings to populate setting properties within this class.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function applySettingsFromArray(array $settingsArray)
 | 
					    protected function applySettingsFromArray(array $settingsArray): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach ($settingsArray as $key => $value) {
 | 
					        foreach ($settingsArray as $key => $value) {
 | 
				
			||||||
            if (property_exists($this, $key)) {
 | 
					            if (property_exists($this, $key)) {
 | 
				
			||||||
| 
						 | 
					@ -51,9 +51,9 @@ class OidcProviderSettings
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @throws InvalidArgumentException
 | 
					     * @throws InvalidArgumentException
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function validateInitial()
 | 
					    protected function validateInitial(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $required = ['clientId', 'clientSecret', 'redirectUri', 'issuer'];
 | 
					        $required = ['clientId', 'clientSecret', 'issuer'];
 | 
				
			||||||
        foreach ($required as $prop) {
 | 
					        foreach ($required as $prop) {
 | 
				
			||||||
            if (empty($this->$prop)) {
 | 
					            if (empty($this->$prop)) {
 | 
				
			||||||
                throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value");
 | 
					                throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value");
 | 
				
			||||||
| 
						 | 
					@ -73,12 +73,20 @@ class OidcProviderSettings
 | 
				
			||||||
    public function validate(): void
 | 
					    public function validate(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->validateInitial();
 | 
					        $this->validateInitial();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $required = ['keys', 'tokenEndpoint', 'authorizationEndpoint'];
 | 
					        $required = ['keys', 'tokenEndpoint', 'authorizationEndpoint'];
 | 
				
			||||||
        foreach ($required as $prop) {
 | 
					        foreach ($required as $prop) {
 | 
				
			||||||
            if (empty($this->$prop)) {
 | 
					            if (empty($this->$prop)) {
 | 
				
			||||||
                throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value");
 | 
					                throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $endpointProperties = ['tokenEndpoint', 'authorizationEndpoint', 'userinfoEndpoint'];
 | 
				
			||||||
 | 
					        foreach ($endpointProperties as $prop) {
 | 
				
			||||||
 | 
					            if (is_string($this->$prop) && !str_starts_with($this->$prop, 'https://')) {
 | 
				
			||||||
 | 
					                throw new InvalidArgumentException("Endpoint value for \"{$prop}\" must start with https://");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -86,7 +94,7 @@ class OidcProviderSettings
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @throws OidcIssuerDiscoveryException
 | 
					     * @throws OidcIssuerDiscoveryException
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function discoverFromIssuer(ClientInterface $httpClient, Repository $cache, int $cacheMinutes)
 | 
					    public function discoverFromIssuer(ClientInterface $httpClient, Repository $cache, int $cacheMinutes): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $cacheKey = 'oidc-discovery::' . $this->issuer;
 | 
					            $cacheKey = 'oidc-discovery::' . $this->issuer;
 | 
				
			||||||
| 
						 | 
					@ -128,6 +136,10 @@ class OidcProviderSettings
 | 
				
			||||||
            $discoveredSettings['tokenEndpoint'] = $result['token_endpoint'];
 | 
					            $discoveredSettings['tokenEndpoint'] = $result['token_endpoint'];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!empty($result['userinfo_endpoint'])) {
 | 
				
			||||||
 | 
					            $discoveredSettings['userinfoEndpoint'] = $result['userinfo_endpoint'];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!empty($result['jwks_uri'])) {
 | 
					        if (!empty($result['jwks_uri'])) {
 | 
				
			||||||
            $keys = $this->loadKeysFromUri($result['jwks_uri'], $httpClient);
 | 
					            $keys = $this->loadKeysFromUri($result['jwks_uri'], $httpClient);
 | 
				
			||||||
            $discoveredSettings['keys'] = $this->filterKeys($keys);
 | 
					            $discoveredSettings['keys'] = $this->filterKeys($keys);
 | 
				
			||||||
| 
						 | 
					@ -175,9 +187,9 @@ class OidcProviderSettings
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the settings needed by an OAuth provider, as a key=>value array.
 | 
					     * Get the settings needed by an OAuth provider, as a key=>value array.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function arrayForProvider(): array
 | 
					    public function arrayForOAuthProvider(): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $settingKeys = ['clientId', 'clientSecret', 'redirectUri', 'authorizationEndpoint', 'tokenEndpoint'];
 | 
					        $settingKeys = ['clientId', 'clientSecret', 'authorizationEndpoint', 'tokenEndpoint', 'userinfoEndpoint'];
 | 
				
			||||||
        $settings = [];
 | 
					        $settings = [];
 | 
				
			||||||
        foreach ($settingKeys as $setting) {
 | 
					        foreach ($settingKeys as $setting) {
 | 
				
			||||||
            $settings[$setting] = $this->$setting;
 | 
					            $settings[$setting] = $this->$setting;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,6 @@ use BookStack\Facades\Theme;
 | 
				
			||||||
use BookStack\Http\HttpRequestService;
 | 
					use BookStack\Http\HttpRequestService;
 | 
				
			||||||
use BookStack\Theming\ThemeEvents;
 | 
					use BookStack\Theming\ThemeEvents;
 | 
				
			||||||
use BookStack\Users\Models\User;
 | 
					use BookStack\Users\Models\User;
 | 
				
			||||||
use Illuminate\Support\Arr;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Cache;
 | 
					use Illuminate\Support\Facades\Cache;
 | 
				
			||||||
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
 | 
					use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
 | 
				
			||||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
 | 
					use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
 | 
				
			||||||
| 
						 | 
					@ -91,10 +90,10 @@ class OidcService
 | 
				
			||||||
            'issuer'                => $config['issuer'],
 | 
					            'issuer'                => $config['issuer'],
 | 
				
			||||||
            'clientId'              => $config['client_id'],
 | 
					            'clientId'              => $config['client_id'],
 | 
				
			||||||
            'clientSecret'          => $config['client_secret'],
 | 
					            'clientSecret'          => $config['client_secret'],
 | 
				
			||||||
            'redirectUri'           => url('/oidc/callback'),
 | 
					 | 
				
			||||||
            'authorizationEndpoint' => $config['authorization_endpoint'],
 | 
					            'authorizationEndpoint' => $config['authorization_endpoint'],
 | 
				
			||||||
            'tokenEndpoint'         => $config['token_endpoint'],
 | 
					            'tokenEndpoint'         => $config['token_endpoint'],
 | 
				
			||||||
            'endSessionEndpoint'    => is_string($config['end_session_endpoint']) ? $config['end_session_endpoint'] : null,
 | 
					            'endSessionEndpoint'    => is_string($config['end_session_endpoint']) ? $config['end_session_endpoint'] : null,
 | 
				
			||||||
 | 
					            'userinfoEndpoint'      => $config['userinfo_endpoint'],
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Use keys if configured
 | 
					        // Use keys if configured
 | 
				
			||||||
| 
						 | 
					@ -129,7 +128,10 @@ class OidcService
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider
 | 
					    protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $provider = new OidcOAuthProvider($settings->arrayForProvider(), [
 | 
					        $provider = new OidcOAuthProvider([
 | 
				
			||||||
 | 
					            ...$settings->arrayForOAuthProvider(),
 | 
				
			||||||
 | 
					            'redirectUri' => url('/oidc/callback'),
 | 
				
			||||||
 | 
					        ], [
 | 
				
			||||||
            'httpClient'     => $this->http->buildClient(5),
 | 
					            'httpClient'     => $this->http->buildClient(5),
 | 
				
			||||||
            'optionProvider' => new HttpBasicAuthOptionProvider(),
 | 
					            'optionProvider' => new HttpBasicAuthOptionProvider(),
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
| 
						 | 
					@ -156,69 +158,6 @@ class OidcService
 | 
				
			||||||
        return array_filter($scopeArr);
 | 
					        return array_filter($scopeArr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Calculate the display name.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function getUserDisplayName(OidcIdToken $token, string $defaultValue): string
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $displayNameAttrString = $this->config()['display_name_claims'] ?? '';
 | 
					 | 
				
			||||||
        $displayNameAttrs = explode('|', $displayNameAttrString);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $displayName = [];
 | 
					 | 
				
			||||||
        foreach ($displayNameAttrs as $dnAttr) {
 | 
					 | 
				
			||||||
            $dnComponent = $token->getClaim($dnAttr) ?? '';
 | 
					 | 
				
			||||||
            if ($dnComponent !== '') {
 | 
					 | 
				
			||||||
                $displayName[] = $dnComponent;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (count($displayName) == 0) {
 | 
					 | 
				
			||||||
            $displayName[] = $defaultValue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return implode(' ', $displayName);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Extract the assigned groups from the id token.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return string[]
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function getUserGroups(OidcIdToken $token): array
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $groupsAttr = $this->config()['groups_claim'];
 | 
					 | 
				
			||||||
        if (empty($groupsAttr)) {
 | 
					 | 
				
			||||||
            return [];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $groupsList = Arr::get($token->getAllClaims(), $groupsAttr);
 | 
					 | 
				
			||||||
        if (!is_array($groupsList)) {
 | 
					 | 
				
			||||||
            return [];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return array_values(array_filter($groupsList, function ($val) {
 | 
					 | 
				
			||||||
            return is_string($val);
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Extract the details of a user from an ID token.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return array{name: string, email: string, external_id: string, groups: string[]}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected function getUserDetails(OidcIdToken $token): array
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $idClaim = $this->config()['external_id_claim'];
 | 
					 | 
				
			||||||
        $id = $token->getClaim($idClaim);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return [
 | 
					 | 
				
			||||||
            'external_id' => $id,
 | 
					 | 
				
			||||||
            'email'       => $token->getClaim('email'),
 | 
					 | 
				
			||||||
            'name'        => $this->getUserDisplayName($token, $id),
 | 
					 | 
				
			||||||
            'groups'      => $this->getUserGroups($token),
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Processes a received access token for a user. Login the user when
 | 
					     * Processes a received access token for a user. Login the user when
 | 
				
			||||||
     * they exist, optionally registering them automatically.
 | 
					     * they exist, optionally registering them automatically.
 | 
				
			||||||
| 
						 | 
					@ -255,34 +194,35 @@ class OidcService
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $idToken->validate($settings->clientId);
 | 
					            $idToken->validate($settings->clientId);
 | 
				
			||||||
        } catch (OidcInvalidTokenException $exception) {
 | 
					        } catch (OidcInvalidTokenException $exception) {
 | 
				
			||||||
            throw new OidcException("ID token validate failed with error: {$exception->getMessage()}");
 | 
					            throw new OidcException("ID token validation failed with error: {$exception->getMessage()}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $userDetails = $this->getUserDetails($idToken);
 | 
					        $userDetails = $this->getUserDetailsFromToken($idToken, $accessToken, $settings);
 | 
				
			||||||
        $isLoggedIn = auth()->check();
 | 
					        if (empty($userDetails->email)) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (empty($userDetails['email'])) {
 | 
					 | 
				
			||||||
            throw new OidcException(trans('errors.oidc_no_email_address'));
 | 
					            throw new OidcException(trans('errors.oidc_no_email_address'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (empty($userDetails->name)) {
 | 
				
			||||||
 | 
					            $userDetails->name = $userDetails->externalId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $isLoggedIn = auth()->check();
 | 
				
			||||||
        if ($isLoggedIn) {
 | 
					        if ($isLoggedIn) {
 | 
				
			||||||
            throw new OidcException(trans('errors.oidc_already_logged_in'));
 | 
					            throw new OidcException(trans('errors.oidc_already_logged_in'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $user = $this->registrationService->findOrRegister(
 | 
					            $user = $this->registrationService->findOrRegister(
 | 
				
			||||||
                $userDetails['name'],
 | 
					                $userDetails->name,
 | 
				
			||||||
                $userDetails['email'],
 | 
					                $userDetails->email,
 | 
				
			||||||
                $userDetails['external_id']
 | 
					                $userDetails->externalId
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        } catch (UserRegistrationException $exception) {
 | 
					        } catch (UserRegistrationException $exception) {
 | 
				
			||||||
            throw new OidcException($exception->getMessage());
 | 
					            throw new OidcException($exception->getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->shouldSyncGroups()) {
 | 
					        if ($this->shouldSyncGroups()) {
 | 
				
			||||||
            $groups = $userDetails['groups'];
 | 
					 | 
				
			||||||
            $detachExisting = $this->config()['remove_from_groups'];
 | 
					            $detachExisting = $this->config()['remove_from_groups'];
 | 
				
			||||||
            $this->groupService->syncUserWithFoundGroups($user, $groups, $detachExisting);
 | 
					            $this->groupService->syncUserWithFoundGroups($user, $userDetails->groups ?? [], $detachExisting);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->loginService->login($user, 'oidc');
 | 
					        $this->loginService->login($user, 'oidc');
 | 
				
			||||||
| 
						 | 
					@ -290,6 +230,45 @@ class OidcService
 | 
				
			||||||
        return $user;
 | 
					        return $user;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @throws OidcException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function getUserDetailsFromToken(OidcIdToken $idToken, OidcAccessToken $accessToken, OidcProviderSettings $settings): OidcUserDetails
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $userDetails = new OidcUserDetails();
 | 
				
			||||||
 | 
					        $userDetails->populate(
 | 
				
			||||||
 | 
					            $idToken,
 | 
				
			||||||
 | 
					            $this->config()['external_id_claim'],
 | 
				
			||||||
 | 
					            $this->config()['display_name_claims'] ?? '',
 | 
				
			||||||
 | 
					            $this->config()['groups_claim'] ?? ''
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!$userDetails->isFullyPopulated($this->shouldSyncGroups()) && !empty($settings->userinfoEndpoint)) {
 | 
				
			||||||
 | 
					            $provider = $this->getProvider($settings);
 | 
				
			||||||
 | 
					            $request = $provider->getAuthenticatedRequest('GET', $settings->userinfoEndpoint, $accessToken->getToken());
 | 
				
			||||||
 | 
					            $response = new OidcUserinfoResponse(
 | 
				
			||||||
 | 
					                $provider->getResponse($request),
 | 
				
			||||||
 | 
					                $settings->issuer,
 | 
				
			||||||
 | 
					                $settings->keys,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                $response->validate($idToken->getClaim('sub'), $settings->clientId);
 | 
				
			||||||
 | 
					            } catch (OidcInvalidTokenException $exception) {
 | 
				
			||||||
 | 
					                throw new OidcException("Userinfo endpoint response validation failed with error: {$exception->getMessage()}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $userDetails->populate(
 | 
				
			||||||
 | 
					                $response,
 | 
				
			||||||
 | 
					                $this->config()['external_id_claim'],
 | 
				
			||||||
 | 
					                $this->config()['display_name_claims'] ?? '',
 | 
				
			||||||
 | 
					                $this->config()['groups_claim'] ?? ''
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $userDetails;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the OIDC config from the application.
 | 
					     * Get the OIDC config from the application.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,75 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\Access\Oidc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Support\Arr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OidcUserDetails
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function __construct(
 | 
				
			||||||
 | 
					        public ?string $externalId = null,
 | 
				
			||||||
 | 
					        public ?string $email = null,
 | 
				
			||||||
 | 
					        public ?string $name = null,
 | 
				
			||||||
 | 
					        public ?array $groups = null,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the user details are fully populated for our usage.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function isFullyPopulated(bool $groupSyncActive): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $hasEmpty = empty($this->externalId)
 | 
				
			||||||
 | 
					            || empty($this->email)
 | 
				
			||||||
 | 
					            || empty($this->name)
 | 
				
			||||||
 | 
					            || ($groupSyncActive && empty($this->groups));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return !$hasEmpty;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Populate user details from the given claim data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function populate(
 | 
				
			||||||
 | 
					        ProvidesClaims $claims,
 | 
				
			||||||
 | 
					        string $idClaim,
 | 
				
			||||||
 | 
					        string $displayNameClaims,
 | 
				
			||||||
 | 
					        string $groupsClaim,
 | 
				
			||||||
 | 
					    ): void {
 | 
				
			||||||
 | 
					        $this->externalId = $claims->getClaim($idClaim) ?? $this->externalId;
 | 
				
			||||||
 | 
					        $this->email = $claims->getClaim('email') ?? $this->email;
 | 
				
			||||||
 | 
					        $this->name = static::getUserDisplayName($displayNameClaims, $claims) ?? $this->name;
 | 
				
			||||||
 | 
					        $this->groups = static::getUserGroups($groupsClaim, $claims) ?? $this->groups;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function getUserDisplayName(string $displayNameClaims, ProvidesClaims $token): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $displayNameClaimParts = explode('|', $displayNameClaims);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $displayName = [];
 | 
				
			||||||
 | 
					        foreach ($displayNameClaimParts as $claim) {
 | 
				
			||||||
 | 
					            $component = $token->getClaim(trim($claim)) ?? '';
 | 
				
			||||||
 | 
					            if ($component !== '') {
 | 
				
			||||||
 | 
					                $displayName[] = $component;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return implode(' ', $displayName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function getUserGroups(string $groupsClaim, ProvidesClaims $token): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (empty($groupsClaim)) {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $groupsList = Arr::get($token->getAllClaims(), $groupsClaim);
 | 
				
			||||||
 | 
					        if (!is_array($groupsList)) {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return array_values(array_filter($groupsList, function ($val) {
 | 
				
			||||||
 | 
					            return is_string($val);
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\Access\Oidc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Psr\Http\Message\ResponseInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OidcUserinfoResponse implements ProvidesClaims
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected array $claims = [];
 | 
				
			||||||
 | 
					    protected ?OidcJwtWithClaims $jwt = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(ResponseInterface $response, string $issuer, array $keys)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $contentType = $response->getHeader('Content-Type')[0];
 | 
				
			||||||
 | 
					        if ($contentType === 'application/json') {
 | 
				
			||||||
 | 
					            $this->claims = json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($contentType === 'application/jwt') {
 | 
				
			||||||
 | 
					            $this->jwt = new OidcJwtWithClaims($response->getBody()->getContents(), $issuer, $keys);
 | 
				
			||||||
 | 
					            $this->claims = $this->jwt->getAllClaims();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @throws OidcInvalidTokenException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function validate(string $idTokenSub, string $clientId): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!is_null($this->jwt)) {
 | 
				
			||||||
 | 
					            $this->jwt->validateCommonTokenDetails($clientId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $sub = $this->getClaim('sub');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Spec: v1.0 5.3.2: The sub (subject) Claim MUST always be returned in the UserInfo Response.
 | 
				
			||||||
 | 
					        if (!is_string($sub) || empty($sub)) {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException("No valid subject value found in userinfo data");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Spec: v1.0 5.3.2: The sub Claim in the UserInfo Response MUST be verified to exactly match the sub Claim in the ID Token;
 | 
				
			||||||
 | 
					        // if they do not match, the UserInfo Response values MUST NOT be used.
 | 
				
			||||||
 | 
					        if ($idTokenSub !== $sub) {
 | 
				
			||||||
 | 
					            throw new OidcInvalidTokenException("Subject value provided in the userinfo endpoint does not match the provided ID token value");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Spec v1.0 5.3.4 Defines the following:
 | 
				
			||||||
 | 
					        // Verify that the OP that responded was the intended OP through a TLS server certificate check, per RFC 6125 [RFC6125].
 | 
				
			||||||
 | 
					          // This is effectively done as part of the HTTP request we're making through CURLOPT_SSL_VERIFYHOST on the request.
 | 
				
			||||||
 | 
					        // If the Client has provided a userinfo_encrypted_response_alg parameter during Registration, decrypt the UserInfo Response using the keys specified during Registration.
 | 
				
			||||||
 | 
					          // We don't currently support JWT encryption for OIDC
 | 
				
			||||||
 | 
					        // If the response was signed, the Client SHOULD validate the signature according to JWS [JWS].
 | 
				
			||||||
 | 
					          // This is done as part of the validateCommonClaims above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getClaim(string $claim): mixed
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->claims[$claim] ?? null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllClaims(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->claims;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\Access\Oidc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ProvidesClaims
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch a specific claim.
 | 
				
			||||||
 | 
					     * Returns null if it is null or does not exist.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function getClaim(string $claim): mixed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all contained claims.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function getAllClaims(): array;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -27,14 +27,14 @@ class ActivityQueries
 | 
				
			||||||
    public function latest(int $count = 20, int $page = 0): array
 | 
					    public function latest(int $count = 20, int $page = 0): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $activityList = $this->permissions
 | 
					        $activityList = $this->permissions
 | 
				
			||||||
            ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type')
 | 
					            ->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type')
 | 
				
			||||||
            ->orderBy('created_at', 'desc')
 | 
					            ->orderBy('created_at', 'desc')
 | 
				
			||||||
            ->with(['user'])
 | 
					            ->with(['user'])
 | 
				
			||||||
            ->skip($count * $page)
 | 
					            ->skip($count * $page)
 | 
				
			||||||
            ->take($count)
 | 
					            ->take($count)
 | 
				
			||||||
            ->get();
 | 
					            ->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->listLoader->loadIntoRelations($activityList->all(), 'entity', false);
 | 
					        $this->listLoader->loadIntoRelations($activityList->all(), 'loggable', false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this->filterSimilar($activityList);
 | 
					        return $this->filterSimilar($activityList);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -59,14 +59,14 @@ class ActivityQueries
 | 
				
			||||||
        $query->where(function (Builder $query) use ($queryIds) {
 | 
					        $query->where(function (Builder $query) use ($queryIds) {
 | 
				
			||||||
            foreach ($queryIds as $morphClass => $idArr) {
 | 
					            foreach ($queryIds as $morphClass => $idArr) {
 | 
				
			||||||
                $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
 | 
					                $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
 | 
				
			||||||
                    $innerQuery->where('entity_type', '=', $morphClass)
 | 
					                    $innerQuery->where('loggable_type', '=', $morphClass)
 | 
				
			||||||
                        ->whereIn('entity_id', $idArr);
 | 
					                        ->whereIn('loggable_id', $idArr);
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $activity = $query->orderBy('created_at', 'desc')
 | 
					        $activity = $query->orderBy('created_at', 'desc')
 | 
				
			||||||
            ->with(['entity' => function (Relation $query) {
 | 
					            ->with(['loggable' => function (Relation $query) {
 | 
				
			||||||
                $query->withTrashed();
 | 
					                $query->withTrashed();
 | 
				
			||||||
            }, 'user.avatar'])
 | 
					            }, 'user.avatar'])
 | 
				
			||||||
            ->skip($count * ($page - 1))
 | 
					            ->skip($count * ($page - 1))
 | 
				
			||||||
| 
						 | 
					@ -82,7 +82,7 @@ class ActivityQueries
 | 
				
			||||||
    public function userActivity(User $user, int $count = 20, int $page = 0): array
 | 
					    public function userActivity(User $user, int $count = 20, int $page = 0): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $activityList = $this->permissions
 | 
					        $activityList = $this->permissions
 | 
				
			||||||
            ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type')
 | 
					            ->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type')
 | 
				
			||||||
            ->orderBy('created_at', 'desc')
 | 
					            ->orderBy('created_at', 'desc')
 | 
				
			||||||
            ->where('user_id', '=', $user->id)
 | 
					            ->where('user_id', '=', $user->id)
 | 
				
			||||||
            ->skip($count * $page)
 | 
					            ->skip($count * $page)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\Activity\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use BookStack\Activity\Models\Activity;
 | 
				
			||||||
 | 
					use BookStack\Http\ApiController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AuditLogApiController extends ApiController
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a listing of audit log events in the system.
 | 
				
			||||||
 | 
					     * The loggable relation fields currently only relates to core
 | 
				
			||||||
 | 
					     * content types (page, book, bookshelf, chapter) but this may be
 | 
				
			||||||
 | 
					     * used more in the future across other types.
 | 
				
			||||||
 | 
					     * Requires permission to manage both users and system settings.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function list()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->checkPermission('settings-manage');
 | 
				
			||||||
 | 
					        $this->checkPermission('users-manage');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $query = Activity::query()->with(['user']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->apiListingResponse($query, [
 | 
				
			||||||
 | 
					            'id', 'type', 'detail', 'user_id', 'loggable_id', 'loggable_type', 'ip', 'created_at',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ class AuditLogController extends Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $query = Activity::query()
 | 
					        $query = Activity::query()
 | 
				
			||||||
            ->with([
 | 
					            ->with([
 | 
				
			||||||
                'entity' => fn ($query) => $query->withTrashed(),
 | 
					                'loggable' => fn ($query) => $query->withTrashed(),
 | 
				
			||||||
                'user',
 | 
					                'user',
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
            ->orderBy($listOptions->getSort(), $listOptions->getOrder());
 | 
					            ->orderBy($listOptions->getSort(), $listOptions->getOrder());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,26 +15,24 @@ use Illuminate\Support\Str;
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @property string $type
 | 
					 * @property string $type
 | 
				
			||||||
 * @property User   $user
 | 
					 * @property User   $user
 | 
				
			||||||
 * @property Entity $entity
 | 
					 * @property Entity $loggable
 | 
				
			||||||
 * @property string $detail
 | 
					 * @property string $detail
 | 
				
			||||||
 * @property string $entity_type
 | 
					 * @property string $loggable_type
 | 
				
			||||||
 * @property int    $entity_id
 | 
					 * @property int    $loggable_id
 | 
				
			||||||
 * @property int    $user_id
 | 
					 * @property int    $user_id
 | 
				
			||||||
 * @property Carbon $created_at
 | 
					 * @property Carbon $created_at
 | 
				
			||||||
 * @property Carbon $updated_at
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class Activity extends Model
 | 
					class Activity extends Model
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the entity for this activity.
 | 
					     * Get the loggable model related to this activity.
 | 
				
			||||||
 | 
					     * Currently only used for entities (previously entity_[id/type] columns).
 | 
				
			||||||
 | 
					     * Could be used for others but will need an audit of uses where assumed
 | 
				
			||||||
 | 
					     * to be entities.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function entity(): MorphTo
 | 
					    public function loggable(): MorphTo
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->entity_type === '') {
 | 
					        return $this->morphTo('loggable');
 | 
				
			||||||
            $this->entity_type = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return $this->morphTo('entity');
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -47,8 +45,8 @@ class Activity extends Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function jointPermissions(): HasMany
 | 
					    public function jointPermissions(): HasMany
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasMany(JointPermission::class, 'entity_id', 'entity_id')
 | 
					        return $this->hasMany(JointPermission::class, 'entity_id', 'loggable_id')
 | 
				
			||||||
            ->whereColumn('activities.entity_type', '=', 'joint_permissions.entity_type');
 | 
					            ->whereColumn('activities.loggable_type', '=', 'joint_permissions.entity_type');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -74,6 +72,6 @@ class Activity extends Model
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function isSimilarTo(self $activityB): bool
 | 
					    public function isSimilarTo(self $activityB): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return [$this->type, $this->entity_type, $this->entity_id] === [$activityB->type, $activityB->entity_type, $activityB->entity_id];
 | 
					        return [$this->type, $this->loggable_type, $this->loggable_id] === [$activityB->type, $activityB->loggable_type, $activityB->loggable_id];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,8 @@ class TagRepo
 | 
				
			||||||
                DB::raw('SUM(IF(entity_type = \'book\', 1, 0)) as book_count'),
 | 
					                DB::raw('SUM(IF(entity_type = \'book\', 1, 0)) as book_count'),
 | 
				
			||||||
                DB::raw('SUM(IF(entity_type = \'bookshelf\', 1, 0)) as shelf_count'),
 | 
					                DB::raw('SUM(IF(entity_type = \'bookshelf\', 1, 0)) as shelf_count'),
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
            ->orderBy($sort, $listOptions->getOrder());
 | 
					            ->orderBy($sort, $listOptions->getOrder())
 | 
				
			||||||
 | 
					            ->whereHas('entity');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($nameFilter) {
 | 
					        if ($nameFilter) {
 | 
				
			||||||
            $query->where('name', '=', $nameFilter);
 | 
					            $query->where('name', '=', $nameFilter);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,8 +32,8 @@ class ActivityLogger
 | 
				
			||||||
        $activity->detail = $detailToStore;
 | 
					        $activity->detail = $detailToStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($detail instanceof Entity) {
 | 
					        if ($detail instanceof Entity) {
 | 
				
			||||||
            $activity->entity_id = $detail->id;
 | 
					            $activity->loggable_id = $detail->id;
 | 
				
			||||||
            $activity->entity_type = $detail->getMorphClass();
 | 
					            $activity->loggable_type = $detail->getMorphClass();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $activity->save();
 | 
					        $activity->save();
 | 
				
			||||||
| 
						 | 
					@ -64,9 +64,9 @@ class ActivityLogger
 | 
				
			||||||
    public function removeEntity(Entity $entity): void
 | 
					    public function removeEntity(Entity $entity): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $entity->activity()->update([
 | 
					        $entity->activity()->update([
 | 
				
			||||||
            'detail'       => $entity->name,
 | 
					            'detail'         => $entity->name,
 | 
				
			||||||
            'entity_id'    => null,
 | 
					            'loggable_id'    => null,
 | 
				
			||||||
            'entity_type'  => null,
 | 
					            'loggable_type'  => null,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,6 @@ use BookStack\Entities\Queries\QueryRecentlyViewed;
 | 
				
			||||||
use BookStack\Entities\Queries\QueryTopFavourites;
 | 
					use BookStack\Entities\Queries\QueryTopFavourites;
 | 
				
			||||||
use BookStack\Entities\Tools\PageContent;
 | 
					use BookStack\Entities\Tools\PageContent;
 | 
				
			||||||
use BookStack\Http\Controller;
 | 
					use BookStack\Http\Controller;
 | 
				
			||||||
use BookStack\Uploads\FaviconHandler;
 | 
					 | 
				
			||||||
use BookStack\Util\SimpleListOptions;
 | 
					use BookStack\Util\SimpleListOptions;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,48 +111,4 @@ class HomeController extends Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return view('home.default', $commonData);
 | 
					        return view('home.default', $commonData);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Show the view for /robots.txt.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function robots()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $sitePublic = setting('app-public', false);
 | 
					 | 
				
			||||||
        $allowRobots = config('app.allow_robots');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if ($allowRobots === null) {
 | 
					 | 
				
			||||||
            $allowRobots = $sitePublic;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return response()
 | 
					 | 
				
			||||||
            ->view('misc.robots', ['allowRobots' => $allowRobots])
 | 
					 | 
				
			||||||
            ->header('Content-Type', 'text/plain');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Show the route for 404 responses.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function notFound()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return response()->view('errors.404', [], 404);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Serve the application favicon.
 | 
					 | 
				
			||||||
     * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served
 | 
					 | 
				
			||||||
     * directly by the webserver in the future.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function favicon(FaviconHandler $favicons)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $exists = $favicons->restoreOriginalIfNotExists();
 | 
					 | 
				
			||||||
        return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Serve a PWA application manifest.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function pwaManifest(PwaManifestBuilder $manifestBuilder)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return response()->json($manifestBuilder->build());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\App;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use BookStack\Http\Controller;
 | 
				
			||||||
 | 
					use BookStack\Uploads\FaviconHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MetaController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Show the view for /robots.txt.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function robots()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $sitePublic = setting('app-public', false);
 | 
				
			||||||
 | 
					        $allowRobots = config('app.allow_robots');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($allowRobots === null) {
 | 
				
			||||||
 | 
					            $allowRobots = $sitePublic;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()
 | 
				
			||||||
 | 
					            ->view('misc.robots', ['allowRobots' => $allowRobots])
 | 
				
			||||||
 | 
					            ->header('Content-Type', 'text/plain');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Show the route for 404 responses.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function notFound()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return response()->view('errors.404', [], 404);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serve the application favicon.
 | 
				
			||||||
 | 
					     * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served
 | 
				
			||||||
 | 
					     * directly by the webserver in the future.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function favicon(FaviconHandler $favicons)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $exists = $favicons->restoreOriginalIfNotExists();
 | 
				
			||||||
 | 
					        return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Serve a PWA application manifest.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function pwaManifest(PwaManifestBuilder $manifestBuilder)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return response()->json($manifestBuilder->build());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Show license information for the application.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function licenses()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->setPageTitle(trans('settings.licenses'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return view('help.licenses', [
 | 
				
			||||||
 | 
					            'license' => file_get_contents(base_path('LICENSE')),
 | 
				
			||||||
 | 
					            'phpLibData' => file_get_contents(base_path('dev/licensing/php-library-licenses.txt')),
 | 
				
			||||||
 | 
					            'jsLibData' => file_get_contents(base_path('dev/licensing/js-library-licenses.txt')),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ class AppServiceProvider extends ServiceProvider
 | 
				
			||||||
     * Custom container bindings to register.
 | 
					     * Custom container bindings to register.
 | 
				
			||||||
     * @var string[]
 | 
					     * @var string[]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public $bindings = [
 | 
					    public array $bindings = [
 | 
				
			||||||
        ExceptionRenderer::class => BookStackExceptionHandlerPage::class,
 | 
					        ExceptionRenderer::class => BookStackExceptionHandlerPage::class,
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@ class AppServiceProvider extends ServiceProvider
 | 
				
			||||||
     * Custom singleton bindings to register.
 | 
					     * Custom singleton bindings to register.
 | 
				
			||||||
     * @var string[]
 | 
					     * @var string[]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public $singletons = [
 | 
					    public array $singletons = [
 | 
				
			||||||
        'activity' => ActivityLogger::class,
 | 
					        'activity' => ActivityLogger::class,
 | 
				
			||||||
        SettingService::class => SettingService::class,
 | 
					        SettingService::class => SettingService::class,
 | 
				
			||||||
        SocialDriverManager::class => SocialDriverManager::class,
 | 
					        SocialDriverManager::class => SocialDriverManager::class,
 | 
				
			||||||
| 
						 | 
					@ -42,11 +42,19 @@ class AppServiceProvider extends ServiceProvider
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Bootstrap any application services.
 | 
					     * Register any application services.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function boot()
 | 
					    public function register(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->app->singleton(PermissionApplicator::class, function ($app) {
 | 
				
			||||||
 | 
					            return new PermissionApplicator(null);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Bootstrap any application services.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Set root URL
 | 
					        // Set root URL
 | 
				
			||||||
        $appUrl = config('app.url');
 | 
					        $appUrl = config('app.url');
 | 
				
			||||||
| 
						 | 
					@ -67,16 +75,4 @@ class AppServiceProvider extends ServiceProvider
 | 
				
			||||||
            'page'      => Page::class,
 | 
					            'page'      => Page::class,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Register any application services.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function register()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->app->singleton(PermissionApplicator::class, function ($app) {
 | 
					 | 
				
			||||||
            return new PermissionApplicator(null);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,10 +18,8 @@ class AuthServiceProvider extends ServiceProvider
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Bootstrap the application services.
 | 
					     * Bootstrap the application services.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function boot()
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Password Configuration
 | 
					        // Password Configuration
 | 
				
			||||||
        // Changes here must be reflected in ApiDocsGenerate@getValidationAsString.
 | 
					        // Changes here must be reflected in ApiDocsGenerate@getValidationAsString.
 | 
				
			||||||
| 
						 | 
					@ -58,10 +56,8 @@ class AuthServiceProvider extends ServiceProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Register the application services.
 | 
					     * Register the application services.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function register()
 | 
					    public function register(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Auth::provider('external-users', function ($app, array $config) {
 | 
					        Auth::provider('external-users', function ($app, array $config) {
 | 
				
			||||||
            return new ExternalBaseUserProvider($config['model']);
 | 
					            return new ExternalBaseUserProvider($config['model']);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,20 +29,16 @@ class EventServiceProvider extends ServiceProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Register any events for your application.
 | 
					     * Register any events for your application.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function boot()
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Determine if events and listeners should be automatically discovered.
 | 
					     * Determine if events and listeners should be automatically discovered.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return bool
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function shouldDiscoverEvents()
 | 
					    public function shouldDiscoverEvents(): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,10 +24,8 @@ class RouteServiceProvider extends ServiceProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Define your route model bindings, pattern filters, etc.
 | 
					     * Define your route model bindings, pattern filters, etc.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function boot()
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->configureRateLimiting();
 | 
					        $this->configureRateLimiting();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,10 +39,8 @@ class RouteServiceProvider extends ServiceProvider
 | 
				
			||||||
     * Define the "web" routes for the application.
 | 
					     * Define the "web" routes for the application.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * These routes all receive session state, CSRF protection, etc.
 | 
					     * These routes all receive session state, CSRF protection, etc.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function mapWebRoutes()
 | 
					    protected function mapWebRoutes(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Route::group([
 | 
					        Route::group([
 | 
				
			||||||
            'middleware' => 'web',
 | 
					            'middleware' => 'web',
 | 
				
			||||||
| 
						 | 
					@ -65,10 +61,8 @@ class RouteServiceProvider extends ServiceProvider
 | 
				
			||||||
     * Define the "api" routes for the application.
 | 
					     * Define the "api" routes for the application.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * These routes are typically stateless.
 | 
					     * These routes are typically stateless.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function mapApiRoutes()
 | 
					    protected function mapApiRoutes(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Route::group([
 | 
					        Route::group([
 | 
				
			||||||
            'middleware' => 'api',
 | 
					            'middleware' => 'api',
 | 
				
			||||||
| 
						 | 
					@ -81,10 +75,8 @@ class RouteServiceProvider extends ServiceProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Configure the rate limiters for the application.
 | 
					     * Configure the rate limiters for the application.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function configureRateLimiting()
 | 
					    protected function configureRateLimiting(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        RateLimiter::for('api', function (Request $request) {
 | 
					        RateLimiter::for('api', function (Request $request) {
 | 
				
			||||||
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
 | 
					            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,10 +10,8 @@ class ThemeServiceProvider extends ServiceProvider
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Register services.
 | 
					     * Register services.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function register()
 | 
					    public function register(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Register the ThemeService as a singleton
 | 
					        // Register the ThemeService as a singleton
 | 
				
			||||||
        $this->app->singleton(ThemeService::class, fn ($app) => new ThemeService());
 | 
					        $this->app->singleton(ThemeService::class, fn ($app) => new ThemeService());
 | 
				
			||||||
| 
						 | 
					@ -21,10 +19,8 @@ class ThemeServiceProvider extends ServiceProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Bootstrap services.
 | 
					     * Bootstrap services.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function boot()
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Boot up the theme system
 | 
					        // Boot up the theme system
 | 
				
			||||||
        $themeService = $this->app->make(ThemeService::class);
 | 
					        $themeService = $this->app->make(ThemeService::class);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,10 +11,8 @@ class TranslationServiceProvider extends BaseProvider
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Register the service provider.
 | 
					     * Register the service provider.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function register()
 | 
					    public function register(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->registerLoader();
 | 
					        $this->registerLoader();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,10 +39,8 @@ class TranslationServiceProvider extends BaseProvider
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Register the translation line loader.
 | 
					     * Register the translation line loader.
 | 
				
			||||||
     * Overrides the default register action from Laravel so a custom loader can be used.
 | 
					     * Overrides the default register action from Laravel so a custom loader can be used.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function registerLoader()
 | 
					    protected function registerLoader(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->app->singleton('translation.loader', function ($app) {
 | 
					        $this->app->singleton('translation.loader', function ($app) {
 | 
				
			||||||
            return new FileLoader($app['files'], $app['path.lang']);
 | 
					            return new FileLoader($app['files'], $app['path.lang']);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,10 +12,8 @@ class ViewTweaksServiceProvider extends ServiceProvider
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Bootstrap services.
 | 
					     * Bootstrap services.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function boot()
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Set paginator to use bootstrap-style pagination
 | 
					        // Set paginator to use bootstrap-style pagination
 | 
				
			||||||
        Paginator::useBootstrap();
 | 
					        Paginator::useBootstrap();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Support\Facades\Facade;
 | 
					use Illuminate\Support\Facades\Facade;
 | 
				
			||||||
 | 
					use Illuminate\Support\ServiceProvider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return [
 | 
					return [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,46 +114,20 @@ return [
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Application Service Providers
 | 
					    // Application Service Providers
 | 
				
			||||||
    'providers' => [
 | 
					    'providers' => ServiceProvider::defaultProviders()->merge([
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Laravel Framework Service Providers...
 | 
					 | 
				
			||||||
        Illuminate\Auth\AuthServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Bus\BusServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Cache\CacheServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Cookie\CookieServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Database\DatabaseServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Encryption\EncryptionServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Filesystem\FilesystemServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Hashing\HashServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Mail\MailServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Notifications\NotificationServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Pagination\PaginationServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Pipeline\PipelineServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Queue\QueueServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Redis\RedisServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Session\SessionServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\Validation\ValidationServiceProvider::class,
 | 
					 | 
				
			||||||
        Illuminate\View\ViewServiceProvider::class,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Third party service providers
 | 
					        // Third party service providers
 | 
				
			||||||
        Barryvdh\DomPDF\ServiceProvider::class,
 | 
					 | 
				
			||||||
        Barryvdh\Snappy\ServiceProvider::class,
 | 
					 | 
				
			||||||
        SocialiteProviders\Manager\ServiceProvider::class,
 | 
					        SocialiteProviders\Manager\ServiceProvider::class,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // BookStack custom service providers
 | 
					        // BookStack custom service providers
 | 
				
			||||||
        \BookStack\App\Providers\ThemeServiceProvider::class,
 | 
					        BookStack\App\Providers\ThemeServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\AppServiceProvider::class,
 | 
					        BookStack\App\Providers\AppServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\AuthServiceProvider::class,
 | 
					        BookStack\App\Providers\AuthServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\EventServiceProvider::class,
 | 
					        BookStack\App\Providers\EventServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\RouteServiceProvider::class,
 | 
					        BookStack\App\Providers\RouteServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\TranslationServiceProvider::class,
 | 
					        BookStack\App\Providers\TranslationServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\ValidationRuleServiceProvider::class,
 | 
					        BookStack\App\Providers\ValidationRuleServiceProvider::class,
 | 
				
			||||||
        \BookStack\App\Providers\ViewTweaksServiceProvider::class,
 | 
					        BookStack\App\Providers\ViewTweaksServiceProvider::class,
 | 
				
			||||||
    ],
 | 
					    ])->toArray(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Class Aliases
 | 
					    // Class Aliases
 | 
				
			||||||
    // This array of class aliases to be registered on application start.
 | 
					    // This array of class aliases to be registered on application start.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,8 @@ return [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'file' => [
 | 
					        'file' => [
 | 
				
			||||||
            'driver' => 'file',
 | 
					            'driver' => 'file',
 | 
				
			||||||
            'path'   => storage_path('framework/cache'),
 | 
					            'path'   => storage_path('framework/cache/data'),
 | 
				
			||||||
 | 
					            'lock_path' => storage_path('framework/cache/data'),
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'memcached' => [
 | 
					        'memcached' => [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,45 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * DOMPDF configuration options.
 | 
					 * Export configuration options.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Changes to these config files are not supported by BookStack and may break upon updates.
 | 
					 * Changes to these config files are not supported by BookStack and may break upon updates.
 | 
				
			||||||
 * Configuration should be altered via the `.env` file or environment variables.
 | 
					 * Configuration should be altered via the `.env` file or environment variables.
 | 
				
			||||||
 * Do not edit this file unless you're happy to maintain any changes yourself.
 | 
					 * Do not edit this file unless you're happy to maintain any changes yourself.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$snappyPaperSizeMap = [
 | 
				
			||||||
 | 
					    'a4'     => 'A4',
 | 
				
			||||||
 | 
					    'letter' => 'Letter',
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$dompdfPaperSizeMap = [
 | 
					$dompdfPaperSizeMap = [
 | 
				
			||||||
    'a4'     => 'a4',
 | 
					    'a4'     => 'a4',
 | 
				
			||||||
    'letter' => 'letter',
 | 
					    'letter' => 'letter',
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$exportPageSize = env('EXPORT_PAGE_SIZE', 'a4');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return [
 | 
					return [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'show_warnings' => false,   // Throw an Exception on warnings from dompdf
 | 
					    // Set a command which can be used to convert a HTML file into a PDF file.
 | 
				
			||||||
 | 
					    // When false this will not be used.
 | 
				
			||||||
 | 
					    // String values represent the command to be called for conversion.
 | 
				
			||||||
 | 
					    // Supports '{input_html_path}' and '{output_pdf_path}' placeholder values.
 | 
				
			||||||
 | 
					    // Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
 | 
				
			||||||
 | 
					    'pdf_command' => env('EXPORT_PDF_COMMAND', false),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'options'       => [
 | 
					    // 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support.
 | 
				
			||||||
 | 
					    'snappy' => [
 | 
				
			||||||
 | 
					        'pdf_binary' => env('WKHTMLTOPDF', false),
 | 
				
			||||||
 | 
					        'options' => [
 | 
				
			||||||
 | 
					            'print-media-type' => true,
 | 
				
			||||||
 | 
					            'outline'   => true,
 | 
				
			||||||
 | 
					            'page-size' => $snappyPaperSizeMap[$exportPageSize] ?? 'A4',
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'dompdf' => [
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * The location of the DOMPDF font directory.
 | 
					         * The location of the DOMPDF font directory.
 | 
				
			||||||
         *
 | 
					         *
 | 
				
			||||||
| 
						 | 
					@ -101,7 +123,7 @@ return [
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Whether to enable font subsetting or not.
 | 
					         * Whether to enable font subsetting or not.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        'enable_fontsubsetting' => false,
 | 
					        'enable_font_subsetting' => false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * The PDF rendering backend to use.
 | 
					         * The PDF rendering backend to use.
 | 
				
			||||||
| 
						 | 
					@ -165,7 +187,7 @@ return [
 | 
				
			||||||
         *
 | 
					         *
 | 
				
			||||||
         * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
 | 
					         * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        'default_paper_size' => $dompdfPaperSizeMap[env('EXPORT_PAGE_SIZE', 'a4')] ?? 'a4',
 | 
					        'default_paper_size' => $dompdfPaperSizeMap[$exportPageSize] ?? 'a4',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * The default paper orientation.
 | 
					         * The default paper orientation.
 | 
				
			||||||
| 
						 | 
					@ -268,15 +290,6 @@ return [
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        'font_height_ratio' => 1.1,
 | 
					        'font_height_ratio' => 1.1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Enable CSS float.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * Allows people to disabled CSS float support
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @var bool
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        'enable_css_float' => true,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Use the HTML5 Lib parser.
 | 
					         * Use the HTML5 Lib parser.
 | 
				
			||||||
         *
 | 
					         *
 | 
				
			||||||
| 
						 | 
					@ -286,5 +299,4 @@ return [
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        'enable_html5_parser' => true,
 | 
					        'enable_html5_parser' => true,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,8 @@ return [
 | 
				
			||||||
    // passwords are hashed using the Bcrypt algorithm. This will allow you
 | 
					    // passwords are hashed using the Bcrypt algorithm. This will allow you
 | 
				
			||||||
    // to control the amount of time it takes to hash the given password.
 | 
					    // to control the amount of time it takes to hash the given password.
 | 
				
			||||||
    'bcrypt' => [
 | 
					    'bcrypt' => [
 | 
				
			||||||
        'rounds' => env('BCRYPT_ROUNDS', 10),
 | 
					        'rounds' => env('BCRYPT_ROUNDS', 12),
 | 
				
			||||||
 | 
					        'verify' => true,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Argon Options
 | 
					    // Argon Options
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ use Monolog\Formatter\LineFormatter;
 | 
				
			||||||
use Monolog\Handler\ErrorLogHandler;
 | 
					use Monolog\Handler\ErrorLogHandler;
 | 
				
			||||||
use Monolog\Handler\NullHandler;
 | 
					use Monolog\Handler\NullHandler;
 | 
				
			||||||
use Monolog\Handler\StreamHandler;
 | 
					use Monolog\Handler\StreamHandler;
 | 
				
			||||||
 | 
					use Monolog\Processor\PsrLogMessageProcessor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Logging configuration options.
 | 
					 * Logging configuration options.
 | 
				
			||||||
| 
						 | 
					@ -49,6 +50,7 @@ return [
 | 
				
			||||||
            'path'   => storage_path('logs/laravel.log'),
 | 
					            'path'   => storage_path('logs/laravel.log'),
 | 
				
			||||||
            'level'  => 'debug',
 | 
					            'level'  => 'debug',
 | 
				
			||||||
            'days'   => 14,
 | 
					            'days'   => 14,
 | 
				
			||||||
 | 
					            'replace_placeholders' => true,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'daily' => [
 | 
					        'daily' => [
 | 
				
			||||||
| 
						 | 
					@ -56,6 +58,7 @@ return [
 | 
				
			||||||
            'path'   => storage_path('logs/laravel.log'),
 | 
					            'path'   => storage_path('logs/laravel.log'),
 | 
				
			||||||
            'level'  => 'debug',
 | 
					            'level'  => 'debug',
 | 
				
			||||||
            'days'   => 7,
 | 
					            'days'   => 7,
 | 
				
			||||||
 | 
					            'replace_placeholders' => true,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'stderr' => [
 | 
					        'stderr' => [
 | 
				
			||||||
| 
						 | 
					@ -65,16 +68,20 @@ return [
 | 
				
			||||||
            'with'    => [
 | 
					            'with'    => [
 | 
				
			||||||
                'stream' => 'php://stderr',
 | 
					                'stream' => 'php://stderr',
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
 | 
					            'processors' => [PsrLogMessageProcessor::class],
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'syslog' => [
 | 
					        'syslog' => [
 | 
				
			||||||
            'driver' => 'syslog',
 | 
					            'driver' => 'syslog',
 | 
				
			||||||
            'level'  => 'debug',
 | 
					            'level'  => 'debug',
 | 
				
			||||||
 | 
					            'facility' => LOG_USER,
 | 
				
			||||||
 | 
					            'replace_placeholders' => true,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'errorlog' => [
 | 
					        'errorlog' => [
 | 
				
			||||||
            'driver' => 'errorlog',
 | 
					            'driver' => 'errorlog',
 | 
				
			||||||
            'level'  => 'debug',
 | 
					            'level'  => 'debug',
 | 
				
			||||||
 | 
					            'replace_placeholders' => true,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Custom errorlog implementation that logs out a plain,
 | 
					        // Custom errorlog implementation that logs out a plain,
 | 
				
			||||||
| 
						 | 
					@ -88,6 +95,7 @@ return [
 | 
				
			||||||
            'formatter_with' => [
 | 
					            'formatter_with' => [
 | 
				
			||||||
                'format' => '%message%',
 | 
					                'format' => '%message%',
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
 | 
					            'replace_placeholders' => true,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'null' => [
 | 
					        'null' => [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,7 @@ return [
 | 
				
			||||||
    // OAuth2 endpoints.
 | 
					    // OAuth2 endpoints.
 | 
				
			||||||
    'authorization_endpoint' => env('OIDC_AUTH_ENDPOINT', null),
 | 
					    'authorization_endpoint' => env('OIDC_AUTH_ENDPOINT', null),
 | 
				
			||||||
    'token_endpoint'         => env('OIDC_TOKEN_ENDPOINT', null),
 | 
					    'token_endpoint'         => env('OIDC_TOKEN_ENDPOINT', null),
 | 
				
			||||||
 | 
					    'userinfo_endpoint'      => env('OIDC_USERINFO_ENDPOINT', null),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // OIDC RP-Initiated Logout endpoint URL.
 | 
					    // OIDC RP-Initiated Logout endpoint URL.
 | 
				
			||||||
    // A false value force-disables RP-Initiated Logout.
 | 
					    // A false value force-disables RP-Initiated Logout.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,12 @@ return [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Job batching
 | 
				
			||||||
 | 
					    'batching' => [
 | 
				
			||||||
 | 
					        'database' => 'mysql',
 | 
				
			||||||
 | 
					        'table' => 'job_batches',
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Failed queue job logging
 | 
					    // Failed queue job logging
 | 
				
			||||||
    'failed' => [
 | 
					    'failed' => [
 | 
				
			||||||
        'driver'   => 'database-uuids',
 | 
					        'driver'   => 'database-uuids',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,7 +123,7 @@ return [
 | 
				
			||||||
        'dn'                     => env('LDAP_DN', false),
 | 
					        'dn'                     => env('LDAP_DN', false),
 | 
				
			||||||
        'pass'                   => env('LDAP_PASS', false),
 | 
					        'pass'                   => env('LDAP_PASS', false),
 | 
				
			||||||
        'base_dn'                => env('LDAP_BASE_DN', false),
 | 
					        'base_dn'                => env('LDAP_BASE_DN', false),
 | 
				
			||||||
        'user_filter'            => env('LDAP_USER_FILTER', '(&(uid=${user}))'),
 | 
					        'user_filter'            => env('LDAP_USER_FILTER', '(&(uid={user}))'),
 | 
				
			||||||
        'version'                => env('LDAP_VERSION', false),
 | 
					        'version'                => env('LDAP_VERSION', false),
 | 
				
			||||||
        'id_attribute'           => env('LDAP_ID_ATTRIBUTE', 'uid'),
 | 
					        'id_attribute'           => env('LDAP_ID_ATTRIBUTE', 'uid'),
 | 
				
			||||||
        'email_attribute'        => env('LDAP_EMAIL_ATTRIBUTE', 'mail'),
 | 
					        'email_attribute'        => env('LDAP_EMAIL_ATTRIBUTE', 'mail'),
 | 
				
			||||||
| 
						 | 
					@ -133,6 +133,7 @@ return [
 | 
				
			||||||
        'group_attribute'        => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'),
 | 
					        'group_attribute'        => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'),
 | 
				
			||||||
        'remove_from_groups'     => env('LDAP_REMOVE_FROM_GROUPS', false),
 | 
					        'remove_from_groups'     => env('LDAP_REMOVE_FROM_GROUPS', false),
 | 
				
			||||||
        'tls_insecure'           => env('LDAP_TLS_INSECURE', false),
 | 
					        'tls_insecure'           => env('LDAP_TLS_INSECURE', false),
 | 
				
			||||||
 | 
					        'tls_ca_cert'            => env('LDAP_TLS_CA_CERT', false),
 | 
				
			||||||
        'start_tls'              => env('LDAP_START_TLS', false),
 | 
					        'start_tls'              => env('LDAP_START_TLS', false),
 | 
				
			||||||
        'thumbnail_attribute'    => env('LDAP_THUMBNAIL_ATTRIBUTE', null),
 | 
					        'thumbnail_attribute'    => env('LDAP_THUMBNAIL_ATTRIBUTE', null),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,4 +85,11 @@ return [
 | 
				
			||||||
    // do not enable this as other CSRF protection services are in place.
 | 
					    // do not enable this as other CSRF protection services are in place.
 | 
				
			||||||
    // Options: lax, strict, none
 | 
					    // Options: lax, strict, none
 | 
				
			||||||
    'same_site' => 'lax',
 | 
					    'same_site' => 'lax',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Partitioned Cookies
 | 
				
			||||||
 | 
					    // Setting this value to true will tie the cookie to the top-level site for
 | 
				
			||||||
 | 
					    // a cross-site context. Partitioned cookies are accepted by the browser
 | 
				
			||||||
 | 
					    // when flagged "secure" and the Same-Site attribute is set to "none".
 | 
				
			||||||
 | 
					    'partitioned' => false,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,34 +0,0 @@
 | 
				
			||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * SnappyPDF configuration options.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Changes to these config files are not supported by BookStack and may break upon updates.
 | 
					 | 
				
			||||||
 * Configuration should be altered via the `.env` file or environment variables.
 | 
					 | 
				
			||||||
 * Do not edit this file unless you're happy to maintain any changes yourself.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$snappyPaperSizeMap = [
 | 
					 | 
				
			||||||
    'a4'     => 'A4',
 | 
					 | 
				
			||||||
    'letter' => 'Letter',
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return [
 | 
					 | 
				
			||||||
    'pdf' => [
 | 
					 | 
				
			||||||
        'enabled' => true,
 | 
					 | 
				
			||||||
        'binary'  => file_exists(base_path('wkhtmltopdf')) ? base_path('wkhtmltopdf') : env('WKHTMLTOPDF', false),
 | 
					 | 
				
			||||||
        'timeout' => false,
 | 
					 | 
				
			||||||
        'options' => [
 | 
					 | 
				
			||||||
            'outline'   => true,
 | 
					 | 
				
			||||||
            'page-size' => $snappyPaperSizeMap[env('EXPORT_PAGE_SIZE', 'a4')] ?? 'A4',
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        'env'     => [],
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    'image' => [
 | 
					 | 
				
			||||||
        'enabled' => false,
 | 
					 | 
				
			||||||
        'binary'  => '/usr/local/bin/wkhtmltoimage',
 | 
					 | 
				
			||||||
        'timeout' => false,
 | 
					 | 
				
			||||||
        'options' => [],
 | 
					 | 
				
			||||||
        'env'     => [],
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ class ClearActivityCommand extends Command
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @var string
 | 
					     * @var string
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected $description = 'Clear user activity from the system';
 | 
					    protected $description = 'Clear user (audit-log) activity from the system';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Execute the console command.
 | 
					     * Execute the console command.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,7 +137,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function activity(): MorphMany
 | 
					    public function activity(): MorphMany
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(Activity::class, 'entity')
 | 
					        return $this->morphMany(Activity::class, 'loggable')
 | 
				
			||||||
            ->orderBy('created_at', 'desc');
 | 
					            ->orderBy('created_at', 'desc');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,27 +2,28 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace BookStack\Entities\Tools;
 | 
					namespace BookStack\Entities\Tools;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Barryvdh\DomPDF\Facade\Pdf as DomPDF;
 | 
					use BookStack\Exceptions\PdfExportException;
 | 
				
			||||||
use Barryvdh\Snappy\Facades\SnappyPdf;
 | 
					use Knp\Snappy\Pdf as SnappyPdf;
 | 
				
			||||||
 | 
					use Dompdf\Dompdf;
 | 
				
			||||||
 | 
					use Symfony\Component\Process\Process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PdfGenerator
 | 
					class PdfGenerator
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const ENGINE_DOMPDF = 'dompdf';
 | 
					    const ENGINE_DOMPDF = 'dompdf';
 | 
				
			||||||
    const ENGINE_WKHTML = 'wkhtml';
 | 
					    const ENGINE_WKHTML = 'wkhtml';
 | 
				
			||||||
 | 
					    const ENGINE_COMMAND = 'command';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Generate PDF content from the given HTML content.
 | 
					     * Generate PDF content from the given HTML content.
 | 
				
			||||||
 | 
					     * @throws PdfExportException
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function fromHtml(string $html): string
 | 
					    public function fromHtml(string $html): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->getActiveEngine() === self::ENGINE_WKHTML) {
 | 
					        return match ($this->getActiveEngine()) {
 | 
				
			||||||
            $pdf = SnappyPDF::loadHTML($html);
 | 
					            self::ENGINE_COMMAND => $this->renderUsingCommand($html),
 | 
				
			||||||
            $pdf->setOption('print-media-type', true);
 | 
					            self::ENGINE_WKHTML => $this->renderUsingWkhtml($html),
 | 
				
			||||||
        } else {
 | 
					            default => $this->renderUsingDomPdf($html)
 | 
				
			||||||
            $pdf = DomPDF::loadHTML($html);
 | 
					        };
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return $pdf->output();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -31,8 +32,101 @@ class PdfGenerator
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function getActiveEngine(): string
 | 
					    public function getActiveEngine(): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $useWKHTML = config('snappy.pdf.binary') !== false && config('app.allow_untrusted_server_fetching') === true;
 | 
					        if (config('exports.pdf_command')) {
 | 
				
			||||||
 | 
					            return self::ENGINE_COMMAND;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $useWKHTML ? self::ENGINE_WKHTML : self::ENGINE_DOMPDF;
 | 
					        if ($this->getWkhtmlBinaryPath() && config('app.allow_untrusted_server_fetching') === true) {
 | 
				
			||||||
 | 
					            return self::ENGINE_WKHTML;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self::ENGINE_DOMPDF;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function getWkhtmlBinaryPath(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $wkhtmlBinaryPath = config('exports.snappy.pdf_binary');
 | 
				
			||||||
 | 
					        if (file_exists(base_path('wkhtmltopdf'))) {
 | 
				
			||||||
 | 
					            $wkhtmlBinaryPath = base_path('wkhtmltopdf');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $wkhtmlBinaryPath ?: '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function renderUsingDomPdf(string $html): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $options = config('exports.dompdf');
 | 
				
			||||||
 | 
					        $domPdf = new Dompdf($options);
 | 
				
			||||||
 | 
					        $domPdf->setBasePath(base_path('public'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $domPdf->loadHTML($this->convertEntities($html));
 | 
				
			||||||
 | 
					        $domPdf->render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (string) $domPdf->output();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @throws PdfExportException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function renderUsingCommand(string $html): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $command = config('exports.pdf_command');
 | 
				
			||||||
 | 
					        $inputHtml = tempnam(sys_get_temp_dir(), 'bs-pdfgen-html-');
 | 
				
			||||||
 | 
					        $outputPdf = tempnam(sys_get_temp_dir(), 'bs-pdfgen-output-');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $replacementsByPlaceholder = [
 | 
				
			||||||
 | 
					            '{input_html_path}' => $inputHtml,
 | 
				
			||||||
 | 
					            '{output_pdf_path}' => $outputPdf,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($replacementsByPlaceholder as $placeholder => $replacement) {
 | 
				
			||||||
 | 
					            $command = str_replace($placeholder, escapeshellarg($replacement), $command);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        file_put_contents($inputHtml, $html);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $process = Process::fromShellCommandline($command);
 | 
				
			||||||
 | 
					        $process->setTimeout(15);
 | 
				
			||||||
 | 
					        $process->run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!$process->isSuccessful()) {
 | 
				
			||||||
 | 
					            throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $pdfContents = file_get_contents($outputPdf);
 | 
				
			||||||
 | 
					        unlink($outputPdf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($pdfContents === false) {
 | 
				
			||||||
 | 
					            throw new PdfExportException("PDF Export via command failed, unable to read PDF output file");
 | 
				
			||||||
 | 
					        } else if (empty($pdfContents)) {
 | 
				
			||||||
 | 
					            throw new PdfExportException("PDF Export via command failed, PDF output file is empty");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $pdfContents;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function renderUsingWkhtml(string $html): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $snappy = new SnappyPdf($this->getWkhtmlBinaryPath());
 | 
				
			||||||
 | 
					        $options = config('exports.snappy.options');
 | 
				
			||||||
 | 
					        return $snappy->getOutputFromHtml($html, $options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Taken from https://github.com/barryvdh/laravel-dompdf/blob/v2.1.1/src/PDF.php
 | 
				
			||||||
 | 
					     * Copyright (c) 2021 barryvdh, MIT License
 | 
				
			||||||
 | 
					     * https://github.com/barryvdh/laravel-dompdf/blob/v2.1.1/LICENSE
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected function convertEntities(string $subject): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $entities = [
 | 
				
			||||||
 | 
					            '€' => '€',
 | 
				
			||||||
 | 
					            '£' => '£',
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($entities as $search => $replace) {
 | 
				
			||||||
 | 
					            $subject = str_replace($search, $replace, $subject);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $subject;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BookStack\Exceptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PdfExportException extends \Exception
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -45,11 +45,11 @@ class Kernel extends HttpKernel
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The application's route middleware.
 | 
					     * The application's middleware aliases.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @var array
 | 
					     * @var array
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected $routeMiddleware = [
 | 
					    protected $middlewareAliases = [
 | 
				
			||||||
        'auth'       => \BookStack\Http\Middleware\Authenticate::class,
 | 
					        'auth'       => \BookStack\Http\Middleware\Authenticate::class,
 | 
				
			||||||
        'can'        => \BookStack\Http\Middleware\CheckUserHasPermission::class,
 | 
					        'can'        => \BookStack\Http\Middleware\CheckUserHasPermission::class,
 | 
				
			||||||
        'guest'      => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
 | 
					        'guest'      => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,19 +6,16 @@ use BookStack\App\Providers\RouteServiceProvider;
 | 
				
			||||||
use Closure;
 | 
					use Closure;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Illuminate\Support\Facades\Auth;
 | 
					use Illuminate\Support\Facades\Auth;
 | 
				
			||||||
 | 
					use Symfony\Component\HttpFoundation\Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RedirectIfAuthenticated
 | 
					class RedirectIfAuthenticated
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handle an incoming request.
 | 
					     * Handle an incoming request.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param \Illuminate\Http\Request $request
 | 
					     * @param Closure(Request): (Response) $next
 | 
				
			||||||
     * @param \Closure                 $next
 | 
					 | 
				
			||||||
     * @param string|null              ...$guards
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return mixed
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle(Request $request, Closure $next, ...$guards)
 | 
					    public function handle(Request $request, Closure $next, string ...$guards): Response
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $guards = empty($guards) ? [null] : $guards;
 | 
					        $guards = empty($guards) ? [null] : $guards;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ class ThrottleApiRequests extends Middleware
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Resolve the number of attempts if the user is authenticated or not.
 | 
					     * Resolve the number of attempts if the user is authenticated or not.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function resolveMaxAttempts($request, $maxAttempts)
 | 
					    protected function resolveMaxAttempts($request, $maxAttempts): int
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return (int) config('api.requests_per_minute');
 | 
					        return (int) config('api.requests_per_minute');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +9,9 @@ class TrustHosts extends Middleware
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the host patterns that should be trusted.
 | 
					     * Get the host patterns that should be trusted.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return array
 | 
					     * @return array<int, string|null>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function hosts()
 | 
					    public function hosts(): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            $this->allSubdomainsOfApplicationUrl(),
 | 
					            $this->allSubdomainsOfApplicationUrl(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,16 +8,22 @@ class FileLoader extends BaseLoader
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Load the messages for the given locale.
 | 
					     * Load the messages for the given locale.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
     * Extends Laravel's translation FileLoader to look in multiple directories
 | 
					     * Extends Laravel's translation FileLoader to look in multiple directories
 | 
				
			||||||
     * so that we can load in translation overrides from the theme file if wanted.
 | 
					     * so that we can load in translation overrides from the theme file if wanted.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 | 
					     * Note: As of using Laravel 10, this may now be redundant since Laravel's
 | 
				
			||||||
 | 
					     * file loader supports multiple paths. This needs further testing though
 | 
				
			||||||
 | 
					     * to confirm if Laravel works how we expect, since we specifically need
 | 
				
			||||||
 | 
					     * the theme folder to be able to partially override core lang files.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
     * @param string      $locale
 | 
					     * @param string      $locale
 | 
				
			||||||
     * @param string      $group
 | 
					     * @param string      $group
 | 
				
			||||||
     * @param string|null $namespace
 | 
					     * @param string|null $namespace
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return array
 | 
					     * @return array
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function load($locale, $group, $namespace = null)
 | 
					    public function load($locale, $group, $namespace = null): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($group === '*' && $namespace === '*') {
 | 
					        if ($group === '*' && $namespace === '*') {
 | 
				
			||||||
            return $this->loadJsonPaths($locale);
 | 
					            return $this->loadJsonPaths($locale);
 | 
				
			||||||
| 
						 | 
					@ -25,8 +31,8 @@ class FileLoader extends BaseLoader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (is_null($namespace) || $namespace === '*') {
 | 
					        if (is_null($namespace) || $namespace === '*') {
 | 
				
			||||||
            $themePath = theme_path('lang');
 | 
					            $themePath = theme_path('lang');
 | 
				
			||||||
            $themeTranslations = $themePath ? $this->loadPath($themePath, $locale, $group) : [];
 | 
					            $themeTranslations = $themePath ? $this->loadPaths([$themePath], $locale, $group) : [];
 | 
				
			||||||
            $originalTranslations = $this->loadPath($this->path, $locale, $group);
 | 
					            $originalTranslations = $this->loadPaths($this->paths, $locale, $group);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return array_merge($originalTranslations, $themeTranslations);
 | 
					            return array_merge($originalTranslations, $themeTranslations);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,12 @@ use BookStack\Exceptions\ImageUploadException;
 | 
				
			||||||
use Exception;
 | 
					use Exception;
 | 
				
			||||||
use GuzzleHttp\Psr7\Utils;
 | 
					use GuzzleHttp\Psr7\Utils;
 | 
				
			||||||
use Illuminate\Support\Facades\Cache;
 | 
					use Illuminate\Support\Facades\Cache;
 | 
				
			||||||
use Intervention\Image\Gd\Driver;
 | 
					use Intervention\Image\Decoders\BinaryImageDecoder;
 | 
				
			||||||
use Intervention\Image\Image as InterventionImage;
 | 
					use Intervention\Image\Drivers\Gd\Driver;
 | 
				
			||||||
 | 
					use Intervention\Image\Encoders\AutoEncoder;
 | 
				
			||||||
 | 
					use Intervention\Image\Encoders\PngEncoder;
 | 
				
			||||||
 | 
					use Intervention\Image\Interfaces\ImageInterface as InterventionImage;
 | 
				
			||||||
 | 
					use Intervention\Image\ImageManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ImageResizer
 | 
					class ImageResizer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -124,15 +128,17 @@ class ImageResizer
 | 
				
			||||||
        $this->orientImageToOriginalExif($thumb, $imageData);
 | 
					        $this->orientImageToOriginalExif($thumb, $imageData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($keepRatio) {
 | 
					        if ($keepRatio) {
 | 
				
			||||||
            $thumb->resize($width, $height, function ($constraint) {
 | 
					            $thumb->scaleDown($width, $height);
 | 
				
			||||||
                $constraint->aspectRatio();
 | 
					 | 
				
			||||||
                $constraint->upsize();
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $thumb->fit($width, $height);
 | 
					            $thumb->cover($width, $height);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $thumbData = (string) $thumb->encode($format);
 | 
					        $encoder = match ($format) {
 | 
				
			||||||
 | 
					            'png' => new PngEncoder(),
 | 
				
			||||||
 | 
					            default => new AutoEncoder(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $thumbData = (string) $thumb->encode($encoder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Use original image data if we're keeping the ratio
 | 
					        // Use original image data if we're keeping the ratio
 | 
				
			||||||
        // and the resizing does not save any space.
 | 
					        // and the resizing does not save any space.
 | 
				
			||||||
| 
						 | 
					@ -150,8 +156,9 @@ class ImageResizer
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function interventionFromImageData(string $imageData): InterventionImage
 | 
					    protected function interventionFromImageData(string $imageData): InterventionImage
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $driver = new Driver();
 | 
					        $manager = new ImageManager(new Driver());
 | 
				
			||||||
        return $driver->decoder->initFromBinary($imageData);
 | 
					
 | 
				
			||||||
 | 
					        return $manager->read($imageData, BinaryImageDecoder::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@
 | 
				
			||||||
    "license": "MIT",
 | 
					    "license": "MIT",
 | 
				
			||||||
    "type": "project",
 | 
					    "type": "project",
 | 
				
			||||||
    "require": {
 | 
					    "require": {
 | 
				
			||||||
        "php": "^8.0.2",
 | 
					        "php": "^8.1.0",
 | 
				
			||||||
        "ext-curl": "*",
 | 
					        "ext-curl": "*",
 | 
				
			||||||
        "ext-dom": "*",
 | 
					        "ext-dom": "*",
 | 
				
			||||||
        "ext-fileinfo": "*",
 | 
					        "ext-fileinfo": "*",
 | 
				
			||||||
| 
						 | 
					@ -17,14 +17,14 @@
 | 
				
			||||||
        "ext-mbstring": "*",
 | 
					        "ext-mbstring": "*",
 | 
				
			||||||
        "ext-xml": "*",
 | 
					        "ext-xml": "*",
 | 
				
			||||||
        "bacon/bacon-qr-code": "^2.0",
 | 
					        "bacon/bacon-qr-code": "^2.0",
 | 
				
			||||||
        "barryvdh/laravel-dompdf": "^2.0",
 | 
					 | 
				
			||||||
        "barryvdh/laravel-snappy": "^1.0",
 | 
					 | 
				
			||||||
        "doctrine/dbal": "^3.5",
 | 
					        "doctrine/dbal": "^3.5",
 | 
				
			||||||
 | 
					        "dompdf/dompdf": "^2.0",
 | 
				
			||||||
        "guzzlehttp/guzzle": "^7.4",
 | 
					        "guzzlehttp/guzzle": "^7.4",
 | 
				
			||||||
        "intervention/image": "^2.7",
 | 
					        "intervention/image": "^3.5",
 | 
				
			||||||
        "laravel/framework": "^9.0",
 | 
					        "knplabs/knp-snappy": "^1.5",
 | 
				
			||||||
 | 
					        "laravel/framework": "^10.10",
 | 
				
			||||||
        "laravel/socialite": "^5.10",
 | 
					        "laravel/socialite": "^5.10",
 | 
				
			||||||
        "laravel/tinker": "^2.6",
 | 
					        "laravel/tinker": "^2.8",
 | 
				
			||||||
        "league/commonmark": "^2.3",
 | 
					        "league/commonmark": "^2.3",
 | 
				
			||||||
        "league/flysystem-aws-s3-v3": "^3.0",
 | 
					        "league/flysystem-aws-s3-v3": "^3.0",
 | 
				
			||||||
        "league/html-to-markdown": "^5.0.0",
 | 
					        "league/html-to-markdown": "^5.0.0",
 | 
				
			||||||
| 
						 | 
					@ -39,17 +39,17 @@
 | 
				
			||||||
        "socialiteproviders/okta": "^4.2",
 | 
					        "socialiteproviders/okta": "^4.2",
 | 
				
			||||||
        "socialiteproviders/twitch": "^5.3",
 | 
					        "socialiteproviders/twitch": "^5.3",
 | 
				
			||||||
        "ssddanbrown/htmldiff": "^1.0.2",
 | 
					        "ssddanbrown/htmldiff": "^1.0.2",
 | 
				
			||||||
        "ssddanbrown/symfony-mailer": "6.0.x-dev"
 | 
					        "ssddanbrown/symfony-mailer": "6.4.x-dev"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "require-dev": {
 | 
					    "require-dev": {
 | 
				
			||||||
        "fakerphp/faker": "^1.21",
 | 
					        "fakerphp/faker": "^1.21",
 | 
				
			||||||
        "itsgoingd/clockwork": "^5.1",
 | 
					        "itsgoingd/clockwork": "^5.1",
 | 
				
			||||||
        "mockery/mockery": "^1.5",
 | 
					        "mockery/mockery": "^1.5",
 | 
				
			||||||
        "nunomaduro/collision": "^6.4",
 | 
					        "nunomaduro/collision": "^7.0",
 | 
				
			||||||
        "larastan/larastan": "^2.7",
 | 
					        "larastan/larastan": "^2.7",
 | 
				
			||||||
        "phpunit/phpunit": "^9.5",
 | 
					        "phpunit/phpunit": "^10.0",
 | 
				
			||||||
        "squizlabs/php_codesniffer": "^3.7",
 | 
					        "squizlabs/php_codesniffer": "^3.7",
 | 
				
			||||||
        "ssddanbrown/asserthtml": "^2.0"
 | 
					        "ssddanbrown/asserthtml": "^3.0"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "autoload": {
 | 
					    "autoload": {
 | 
				
			||||||
        "psr-4": {
 | 
					        "psr-4": {
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,10 @@
 | 
				
			||||||
        "lint": "phpcs",
 | 
					        "lint": "phpcs",
 | 
				
			||||||
        "test": "phpunit",
 | 
					        "test": "phpunit",
 | 
				
			||||||
        "t-reset": "@php artisan test --recreate-databases",
 | 
					        "t-reset": "@php artisan test --recreate-databases",
 | 
				
			||||||
 | 
					        "build-licenses": [
 | 
				
			||||||
 | 
					            "@php ./dev/licensing/gen-js-licenses",
 | 
				
			||||||
 | 
					            "@php ./dev/licensing/gen-php-licenses"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
        "post-autoload-dump": [
 | 
					        "post-autoload-dump": [
 | 
				
			||||||
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
 | 
					            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
 | 
				
			||||||
            "@php artisan package:discover --ansi"
 | 
					            "@php artisan package:discover --ansi"
 | 
				
			||||||
| 
						 | 
					@ -99,7 +103,7 @@
 | 
				
			||||||
        "preferred-install": "dist",
 | 
					        "preferred-install": "dist",
 | 
				
			||||||
        "sort-packages": true,
 | 
					        "sort-packages": true,
 | 
				
			||||||
        "platform": {
 | 
					        "platform": {
 | 
				
			||||||
            "php": "8.0.2"
 | 
					            "php": "8.1.0"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "extra": {
 | 
					    "extra": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -1,16 +1,17 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('users', function (Blueprint $table) {
 | 
					        Schema::create('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -26,17 +27,15 @@ return new class extends Migration
 | 
				
			||||||
            'name'       => 'Admin',
 | 
					            'name'       => 'Admin',
 | 
				
			||||||
            'email'      => 'admin@admin.com',
 | 
					            'email'      => 'admin@admin.com',
 | 
				
			||||||
            'password'   => bcrypt('password'),
 | 
					            'password'   => bcrypt('password'),
 | 
				
			||||||
            'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'created_at' => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            'updated_at' => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'updated_at' => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('users');
 | 
					        Schema::drop('users');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('password_resets', function (Blueprint $table) {
 | 
					        Schema::create('password_resets', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('email')->index();
 | 
					            $table->string('email')->index();
 | 
				
			||||||
| 
						 | 
					@ -21,10 +20,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('password_resets');
 | 
					        Schema::drop('password_resets');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('books', function (Blueprint $table) {
 | 
					        Schema::create('books', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -23,10 +22,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('books');
 | 
					        Schema::drop('books');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('pages', function (Blueprint $table) {
 | 
					        Schema::create('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -27,10 +26,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('pages');
 | 
					        Schema::drop('pages');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('images', function (Blueprint $table) {
 | 
					        Schema::create('images', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -22,10 +21,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('images');
 | 
					        Schema::drop('images');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('chapters', function (Blueprint $table) {
 | 
					        Schema::create('chapters', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -25,10 +24,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('chapters');
 | 
					        Schema::drop('chapters');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->integer('created_by');
 | 
					            $table->integer('created_by');
 | 
				
			||||||
| 
						 | 
					@ -32,10 +31,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('created_by');
 | 
					            $table->dropColumn('created_by');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('page_revisions', function (Blueprint $table) {
 | 
					        Schema::create('page_revisions', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -25,10 +24,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('page_revisions');
 | 
					        Schema::drop('page_revisions');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('activities', function (Blueprint $table) {
 | 
					        Schema::create('activities', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -26,10 +25,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('activities');
 | 
					        Schema::drop('activities');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,17 +10,18 @@
 | 
				
			||||||
 * @url https://github.com/Zizaco/entrust
 | 
					 * @url https://github.com/Zizaco/entrust
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Create table for storing roles
 | 
					        // Create table for storing roles
 | 
				
			||||||
        Schema::create('roles', function (Blueprint $table) {
 | 
					        Schema::create('roles', function (Blueprint $table) {
 | 
				
			||||||
| 
						 | 
					@ -71,22 +72,22 @@ return new class extends Migration
 | 
				
			||||||
            'name'         => 'admin',
 | 
					            'name'         => 'admin',
 | 
				
			||||||
            'display_name' => 'Admin',
 | 
					            'display_name' => 'Admin',
 | 
				
			||||||
            'description'  => 'Administrator of the whole application',
 | 
					            'description'  => 'Administrator of the whole application',
 | 
				
			||||||
            'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        $editorId = DB::table('roles')->insertGetId([
 | 
					        $editorId = DB::table('roles')->insertGetId([
 | 
				
			||||||
            'name'         => 'editor',
 | 
					            'name'         => 'editor',
 | 
				
			||||||
            'display_name' => 'Editor',
 | 
					            'display_name' => 'Editor',
 | 
				
			||||||
            'description'  => 'User can edit Books, Chapters & Pages',
 | 
					            'description'  => 'User can edit Books, Chapters & Pages',
 | 
				
			||||||
            'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        $viewerId = DB::table('roles')->insertGetId([
 | 
					        $viewerId = DB::table('roles')->insertGetId([
 | 
				
			||||||
            'name'         => 'viewer',
 | 
					            'name'         => 'viewer',
 | 
				
			||||||
            'display_name' => 'Viewer',
 | 
					            'display_name' => 'Viewer',
 | 
				
			||||||
            'description'  => 'User can view books & their content behind authentication',
 | 
					            'description'  => 'User can view books & their content behind authentication',
 | 
				
			||||||
            'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create default CRUD permissions and allocate to admins and editors
 | 
					        // Create default CRUD permissions and allocate to admins and editors
 | 
				
			||||||
| 
						 | 
					@ -97,8 +98,8 @@ return new class extends Migration
 | 
				
			||||||
                $newPermId = DB::table('permissions')->insertGetId([
 | 
					                $newPermId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity . 's',
 | 
					                    'display_name' => $op . ' ' . $entity . 's',
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                DB::table('permission_role')->insert([
 | 
					                DB::table('permission_role')->insert([
 | 
				
			||||||
                    ['permission_id' => $newPermId, 'role_id' => $adminId],
 | 
					                    ['permission_id' => $newPermId, 'role_id' => $adminId],
 | 
				
			||||||
| 
						 | 
					@ -115,8 +116,8 @@ return new class extends Migration
 | 
				
			||||||
                $newPermId = DB::table('permissions')->insertGetId([
 | 
					                $newPermId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity,
 | 
					                    'display_name' => $op . ' ' . $entity,
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                DB::table('permission_role')->insert([
 | 
					                DB::table('permission_role')->insert([
 | 
				
			||||||
                    'permission_id' => $newPermId,
 | 
					                    'permission_id' => $newPermId,
 | 
				
			||||||
| 
						 | 
					@ -138,10 +139,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('permission_role');
 | 
					        Schema::drop('permission_role');
 | 
				
			||||||
        Schema::drop('permissions');
 | 
					        Schema::drop('permissions');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('settings', function (Blueprint $table) {
 | 
					        Schema::create('settings', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('setting_key')->primary()->indexed();
 | 
					            $table->string('setting_key')->primary()->indexed();
 | 
				
			||||||
| 
						 | 
					@ -21,10 +20,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('settings');
 | 
					        Schema::drop('settings');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,13 +2,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -23,15 +23,14 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $sm = Schema::getConnection()->getDoctrineSchemaManager();
 | 
					        $sm = Schema::getConnection()->getDoctrineSchemaManager();
 | 
				
			||||||
        $pages = $sm->listTableDetails('pages');
 | 
					        $prefix = DB::getTablePrefix();
 | 
				
			||||||
        $books = $sm->listTableDetails('books');
 | 
					        $pages = $sm->introspectTable($prefix . 'pages');
 | 
				
			||||||
        $chapters = $sm->listTableDetails('chapters');
 | 
					        $books = $sm->introspectTable($prefix . 'books');
 | 
				
			||||||
 | 
					        $chapters = $sm->introspectTable($prefix . 'chapters');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($pages->hasIndex('search')) {
 | 
					        if ($pages->hasIndex('search')) {
 | 
				
			||||||
            Schema::table('pages', function (Blueprint $table) {
 | 
					            Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('social_accounts', function (Blueprint $table) {
 | 
					        Schema::create('social_accounts', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -24,10 +23,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('social_accounts');
 | 
					        Schema::drop('social_accounts');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('users', function (Blueprint $table) {
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->boolean('email_confirmed')->default(true);
 | 
					            $table->boolean('email_confirmed')->default(true);
 | 
				
			||||||
| 
						 | 
					@ -26,10 +25,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('users', function (Blueprint $table) {
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('email_confirmed');
 | 
					            $table->dropColumn('email_confirmed');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('views', function (Blueprint $table) {
 | 
					        Schema::create('views', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -24,10 +23,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('views');
 | 
					        Schema::drop('views');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('books', function (Blueprint $table) {
 | 
					        Schema::table('books', function (Blueprint $table) {
 | 
				
			||||||
            $table->index('slug');
 | 
					            $table->index('slug');
 | 
				
			||||||
| 
						 | 
					@ -48,10 +47,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('books', function (Blueprint $table) {
 | 
					        Schema::table('books', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropIndex('books_slug_index');
 | 
					            $table->dropIndex('books_slug_index');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,13 +2,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -23,15 +23,14 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $sm = Schema::getConnection()->getDoctrineSchemaManager();
 | 
					        $sm = Schema::getConnection()->getDoctrineSchemaManager();
 | 
				
			||||||
        $pages = $sm->listTableDetails('pages');
 | 
					        $prefix = DB::getTablePrefix();
 | 
				
			||||||
        $books = $sm->listTableDetails('books');
 | 
					        $pages = $sm->introspectTable($prefix . 'pages');
 | 
				
			||||||
        $chapters = $sm->listTableDetails('chapters');
 | 
					        $books = $sm->introspectTable($prefix . 'books');
 | 
				
			||||||
 | 
					        $chapters = $sm->introspectTable($prefix . 'chapters');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($pages->hasIndex('name_search')) {
 | 
					        if ($pages->hasIndex('name_search')) {
 | 
				
			||||||
            Schema::table('pages', function (Blueprint $table) {
 | 
					            Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,15 +3,14 @@
 | 
				
			||||||
use BookStack\Uploads\Image;
 | 
					use BookStack\Uploads\Image;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('images', function (Blueprint $table) {
 | 
					        Schema::table('images', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('path', 400);
 | 
					            $table->string('path', 400);
 | 
				
			||||||
| 
						 | 
					@ -27,10 +26,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('images', function (Blueprint $table) {
 | 
					        Schema::table('images', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('type');
 | 
					            $table->dropColumn('type');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('users', function (Blueprint $table) {
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->integer('image_id')->default(0);
 | 
					            $table->integer('image_id')->default(0);
 | 
				
			||||||
| 
						 | 
					@ -19,10 +18,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('users', function (Blueprint $table) {
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('image_id');
 | 
					            $table->dropColumn('image_id');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('users', function (Blueprint $table) {
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('external_auth_id')->index();
 | 
					            $table->string('external_auth_id')->index();
 | 
				
			||||||
| 
						 | 
					@ -19,10 +18,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('users', function (Blueprint $table) {
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('external_auth_id');
 | 
					            $table->dropColumn('external_auth_id');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('page_revisions', function (Blueprint $table) {
 | 
					        Schema::table('page_revisions', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('slug');
 | 
					            $table->string('slug');
 | 
				
			||||||
| 
						 | 
					@ -22,10 +21,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('page_revisions', function (Blueprint $table) {
 | 
					        Schema::table('page_revisions', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('slug');
 | 
					            $table->dropColumn('slug');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,15 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Get roles with permissions we need to change
 | 
					        // Get roles with permissions we need to change
 | 
				
			||||||
        $adminRoleId = DB::table('roles')->where('name', '=', 'admin')->first()->id;
 | 
					        $adminRoleId = DB::table('roles')->where('name', '=', 'admin')->first()->id;
 | 
				
			||||||
| 
						 | 
					@ -30,8 +30,8 @@ return new class extends Migration
 | 
				
			||||||
            $permissionId = DB::table('permissions')->insertGetId([
 | 
					            $permissionId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                'name'         => $name,
 | 
					                'name'         => $name,
 | 
				
			||||||
                'display_name' => $displayName,
 | 
					                'display_name' => $displayName,
 | 
				
			||||||
                'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            DB::table('permission_role')->insert([
 | 
					            DB::table('permission_role')->insert([
 | 
				
			||||||
                'role_id'       => $adminRoleId,
 | 
					                'role_id'       => $adminRoleId,
 | 
				
			||||||
| 
						 | 
					@ -47,8 +47,8 @@ return new class extends Migration
 | 
				
			||||||
                $permissionId = DB::table('permissions')->insertGetId([
 | 
					                $permissionId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity . 's',
 | 
					                    'display_name' => $op . ' ' . $entity . 's',
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                DB::table('permission_role')->insert([
 | 
					                DB::table('permission_role')->insert([
 | 
				
			||||||
                    'role_id'       => $adminRoleId,
 | 
					                    'role_id'       => $adminRoleId,
 | 
				
			||||||
| 
						 | 
					@ -66,10 +66,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Get roles with permissions we need to change
 | 
					        // Get roles with permissions we need to change
 | 
				
			||||||
        $adminRoleId = DB::table('roles')->where('name', '=', 'admin')->first()->id;
 | 
					        $adminRoleId = DB::table('roles')->where('name', '=', 'admin')->first()->id;
 | 
				
			||||||
| 
						 | 
					@ -85,8 +83,8 @@ return new class extends Migration
 | 
				
			||||||
                $permissionId = DB::table('permissions')->insertGetId([
 | 
					                $permissionId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity . 's',
 | 
					                    'display_name' => $op . ' ' . $entity . 's',
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                DB::table('permission_role')->insert([
 | 
					                DB::table('permission_role')->insert([
 | 
				
			||||||
                    'role_id'       => $adminRoleId,
 | 
					                    'role_id'       => $adminRoleId,
 | 
				
			||||||
| 
						 | 
					@ -103,8 +101,8 @@ return new class extends Migration
 | 
				
			||||||
                $permissionId = DB::table('permissions')->insertGetId([
 | 
					                $permissionId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower($op),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity,
 | 
					                    'display_name' => $op . ' ' . $entity,
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                DB::table('permission_role')->insert([
 | 
					                DB::table('permission_role')->insert([
 | 
				
			||||||
                    'role_id'       => $adminRoleId,
 | 
					                    'role_id'       => $adminRoleId,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('images', function (Blueprint $table) {
 | 
					        Schema::table('images', function (Blueprint $table) {
 | 
				
			||||||
            $table->integer('uploaded_to')->default(0);
 | 
					            $table->integer('uploaded_to')->default(0);
 | 
				
			||||||
| 
						 | 
					@ -46,10 +45,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('images', function (Blueprint $table) {
 | 
					        Schema::table('images', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('uploaded_to');
 | 
					            $table->dropColumn('uploaded_to');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('page_revisions', function (Blueprint $table) {
 | 
					        Schema::table('page_revisions', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('type')->default('version');
 | 
					            $table->string('type')->default('version');
 | 
				
			||||||
| 
						 | 
					@ -20,10 +19,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('page_revisions', function (Blueprint $table) {
 | 
					        Schema::table('page_revisions', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('type');
 | 
					            $table->dropColumn('type');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->boolean('draft')->default(false);
 | 
					            $table->boolean('draft')->default(false);
 | 
				
			||||||
| 
						 | 
					@ -20,10 +19,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('draft');
 | 
					            $table->dropColumn('draft');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->longText('markdown')->default('');
 | 
					            $table->longText('markdown')->default('');
 | 
				
			||||||
| 
						 | 
					@ -23,10 +22,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('markdown');
 | 
					            $table->dropColumn('markdown');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,15 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $currentRoles = DB::table('roles')->get();
 | 
					        $currentRoles = DB::table('roles')->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,8 +21,8 @@ return new class extends Migration
 | 
				
			||||||
                $permId = DB::table('permissions')->insertGetId([
 | 
					                $permId = DB::table('permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity . 's',
 | 
					                    'display_name' => $op . ' ' . $entity . 's',
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                // Assign view permission to all current roles
 | 
					                // Assign view permission to all current roles
 | 
				
			||||||
                foreach ($currentRoles as $role) {
 | 
					                foreach ($currentRoles as $role) {
 | 
				
			||||||
| 
						 | 
					@ -37,10 +37,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Delete the new view permission
 | 
					        // Delete the new view permission
 | 
				
			||||||
        $entities = ['Book', 'Page', 'Chapter'];
 | 
					        $entities = ['Book', 'Page', 'Chapter'];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,18 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('joint_permissions', function (Blueprint $table) {
 | 
					        Schema::create('joint_permissions', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -48,8 +49,8 @@ return new class extends Migration
 | 
				
			||||||
            'description'  => 'The role given to public visitors if allowed',
 | 
					            'description'  => 'The role given to public visitors if allowed',
 | 
				
			||||||
            'system_name'  => 'public',
 | 
					            'system_name'  => 'public',
 | 
				
			||||||
            'hidden'       => true,
 | 
					            'hidden'       => true,
 | 
				
			||||||
            'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					            'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Ensure unique name
 | 
					        // Ensure unique name
 | 
				
			||||||
| 
						 | 
					@ -79,10 +80,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('joint_permissions');
 | 
					        Schema::drop('joint_permissions');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('tags', function (Blueprint $table) {
 | 
					        Schema::create('tags', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -30,10 +29,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::drop('tags');
 | 
					        Schema::drop('tags');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,14 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('page_revisions', function ($table) {
 | 
					        Schema::table('page_revisions', function ($table) {
 | 
				
			||||||
            $table->string('summary')->nullable();
 | 
					            $table->string('summary')->nullable();
 | 
				
			||||||
| 
						 | 
					@ -18,10 +17,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('page_revisions', function ($table) {
 | 
					        Schema::table('page_revisions', function ($table) {
 | 
				
			||||||
            $table->dropColumn('summary');
 | 
					            $table->dropColumn('summary');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,17 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Remove the hidden property from roles
 | 
					        // Remove the hidden property from roles
 | 
				
			||||||
        Schema::table('roles', function (Blueprint $table) {
 | 
					        Schema::table('roles', function (Blueprint $table) {
 | 
				
			||||||
| 
						 | 
					@ -29,8 +29,8 @@ return new class extends Migration
 | 
				
			||||||
            'name'            => 'Guest',
 | 
					            'name'            => 'Guest',
 | 
				
			||||||
            'system_name'     => 'public',
 | 
					            'system_name'     => 'public',
 | 
				
			||||||
            'email_confirmed' => true,
 | 
					            'email_confirmed' => true,
 | 
				
			||||||
            'created_at'      => \Carbon\Carbon::now(),
 | 
					            'created_at'      => Carbon::now(),
 | 
				
			||||||
            'updated_at'      => \Carbon\Carbon::now(),
 | 
					            'updated_at'      => Carbon::now(),
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get the public role
 | 
					        // Get the public role
 | 
				
			||||||
| 
						 | 
					@ -45,10 +45,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('roles', function (Blueprint $table) {
 | 
					        Schema::table('roles', function (Blueprint $table) {
 | 
				
			||||||
            $table->boolean('hidden')->default(false);
 | 
					            $table->boolean('hidden')->default(false);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,17 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('attachments', function (Blueprint $table) {
 | 
					        Schema::create('attachments', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -40,8 +40,8 @@ return new class extends Migration
 | 
				
			||||||
            $permissionId = DB::table('role_permissions')->insertGetId([
 | 
					            $permissionId = DB::table('role_permissions')->insertGetId([
 | 
				
			||||||
                'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
					                'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
				
			||||||
                'display_name' => $op . ' ' . $entity . 's',
 | 
					                'display_name' => $op . ' ' . $entity . 's',
 | 
				
			||||||
                'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            DB::table('permission_role')->insert([
 | 
					            DB::table('permission_role')->insert([
 | 
				
			||||||
                'role_id'       => $adminRoleId,
 | 
					                'role_id'       => $adminRoleId,
 | 
				
			||||||
| 
						 | 
					@ -52,10 +52,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::dropIfExists('attachments');
 | 
					        Schema::dropIfExists('attachments');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('cache', function (Blueprint $table) {
 | 
					        Schema::create('cache', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('key')->unique();
 | 
					            $table->string('key')->unique();
 | 
				
			||||||
| 
						 | 
					@ -22,10 +20,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::dropIfExists('cache');
 | 
					        Schema::dropIfExists('cache');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('sessions', function (Blueprint $table) {
 | 
					        Schema::create('sessions', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('id')->unique();
 | 
					            $table->string('id')->unique();
 | 
				
			||||||
| 
						 | 
					@ -25,10 +23,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::dropIfExists('sessions');
 | 
					        Schema::dropIfExists('sessions');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,16 +2,15 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('search_terms', function (Blueprint $table) {
 | 
					        Schema::create('search_terms', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -27,9 +26,10 @@ return new class extends Migration
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $sm = Schema::getConnection()->getDoctrineSchemaManager();
 | 
					        $sm = Schema::getConnection()->getDoctrineSchemaManager();
 | 
				
			||||||
        $pages = $sm->listTableDetails('pages');
 | 
					        $prefix = DB::getTablePrefix();
 | 
				
			||||||
        $books = $sm->listTableDetails('books');
 | 
					        $pages = $sm->introspectTable($prefix . 'pages');
 | 
				
			||||||
        $chapters = $sm->listTableDetails('chapters');
 | 
					        $books = $sm->introspectTable($prefix . 'books');
 | 
				
			||||||
 | 
					        $chapters = $sm->introspectTable($prefix . 'chapters');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($pages->hasIndex('search')) {
 | 
					        if ($pages->hasIndex('search')) {
 | 
				
			||||||
            Schema::table('pages', function (Blueprint $table) {
 | 
					            Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
| 
						 | 
					@ -55,10 +55,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // This was removed for v0.24 since these indexes are removed anyway
 | 
					        // This was removed for v0.24 since these indexes are removed anyway
 | 
				
			||||||
        // and will cause issues for db engines that don't support such indexes.
 | 
					        // and will cause issues for db engines that don't support such indexes.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,16 +2,15 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->integer('revision_count');
 | 
					            $table->integer('revision_count');
 | 
				
			||||||
| 
						 | 
					@ -29,10 +28,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('revision_count');
 | 
					            $table->dropColumn('revision_count');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,6 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -18,8 +16,6 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,17 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('comments', function (Blueprint $table) {
 | 
					        Schema::create('comments', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id')->unsigned();
 | 
					            $table->increments('id')->unsigned();
 | 
				
			||||||
| 
						 | 
					@ -37,8 +37,8 @@ return new class extends Migration
 | 
				
			||||||
                $permissionId = DB::table('role_permissions')->insertGetId([
 | 
					                $permissionId = DB::table('role_permissions')->insertGetId([
 | 
				
			||||||
                    'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
					                    'name'         => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
 | 
				
			||||||
                    'display_name' => $op . ' ' . $entity . 's',
 | 
					                    'display_name' => $op . ' ' . $entity . 's',
 | 
				
			||||||
                    'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                    'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                    'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
                DB::table('permission_role')->insert([
 | 
					                DB::table('permission_role')->insert([
 | 
				
			||||||
                    'role_id'       => $adminRoleId,
 | 
					                    'role_id'       => $adminRoleId,
 | 
				
			||||||
| 
						 | 
					@ -50,10 +50,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::dropIfExists('comments');
 | 
					        Schema::dropIfExists('comments');
 | 
				
			||||||
        // Delete comment role permissions
 | 
					        // Delete comment role permissions
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('books', function (Blueprint $table) {
 | 
					        Schema::table('books', function (Blueprint $table) {
 | 
				
			||||||
            $table->integer('image_id')->nullable()->default(null);
 | 
					            $table->integer('image_id')->nullable()->default(null);
 | 
				
			||||||
| 
						 | 
					@ -20,10 +18,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('books', function (Blueprint $table) {
 | 
					        Schema::table('books', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('image_id');
 | 
					            $table->dropColumn('image_id');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('roles', function (Blueprint $table) {
 | 
					        Schema::table('roles', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('external_auth_id', 180)->default('');
 | 
					            $table->string('external_auth_id', 180)->default('');
 | 
				
			||||||
| 
						 | 
					@ -21,10 +19,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('roles', function (Blueprint $table) {
 | 
					        Schema::table('roles', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('external_auth_id');
 | 
					            $table->dropColumn('external_auth_id');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
use Illuminate\Support\Facades\DB;
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
| 
						 | 
					@ -9,10 +10,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Convert the existing entity tables to InnoDB.
 | 
					        // Convert the existing entity tables to InnoDB.
 | 
				
			||||||
| 
						 | 
					@ -83,8 +82,8 @@ return new class extends Migration
 | 
				
			||||||
            $permId = DB::table('role_permissions')->insertGetId([
 | 
					            $permId = DB::table('role_permissions')->insertGetId([
 | 
				
			||||||
                'name'         => 'bookshelf-' . $dbOpName,
 | 
					                'name'         => 'bookshelf-' . $dbOpName,
 | 
				
			||||||
                'display_name' => $op . ' ' . 'BookShelves',
 | 
					                'display_name' => $op . ' ' . 'BookShelves',
 | 
				
			||||||
                'created_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                'created_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
                'updated_at'   => \Carbon\Carbon::now()->toDateTimeString(),
 | 
					                'updated_at'   => Carbon::now()->toDateTimeString(),
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $rowsToInsert = $roleIdsWithBookPermission->filter(function ($roleId) {
 | 
					            $rowsToInsert = $roleIdsWithBookPermission->filter(function ($roleId) {
 | 
				
			||||||
| 
						 | 
					@ -103,10 +102,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Drop created permissions
 | 
					        // Drop created permissions
 | 
				
			||||||
        $ops = ['bookshelf-create-all', 'bookshelf-create-own', 'bookshelf-delete-all', 'bookshelf-delete-own', 'bookshelf-update-all', 'bookshelf-update-own', 'bookshelf-view-all', 'bookshelf-view-own'];
 | 
					        $ops = ['bookshelf-create-all', 'bookshelf-create-own', 'bookshelf-delete-all', 'bookshelf-delete-own', 'bookshelf-update-all', 'bookshelf-update-own', 'bookshelf-view-all', 'bookshelf-view-own'];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,16 +3,15 @@
 | 
				
			||||||
use Carbon\Carbon;
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->boolean('template')->default(false);
 | 
					            $table->boolean('template')->default(false);
 | 
				
			||||||
| 
						 | 
					@ -35,10 +34,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('pages', function (Blueprint $table) {
 | 
					        Schema::table('pages', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('template');
 | 
					            $table->dropColumn('template');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::create('user_invites', function (Blueprint $table) {
 | 
					        Schema::create('user_invites', function (Blueprint $table) {
 | 
				
			||||||
            $table->increments('id');
 | 
					            $table->increments('id');
 | 
				
			||||||
| 
						 | 
					@ -23,10 +21,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::dropIfExists('user_invites');
 | 
					        Schema::dropIfExists('user_invites');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,16 +3,15 @@
 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
use Illuminate\Support\Carbon;
 | 
					use Illuminate\Support\Carbon;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add API tokens table
 | 
					        // Add API tokens table
 | 
				
			||||||
| 
						 | 
					@ -42,10 +41,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Remove API tokens table
 | 
					        // Remove API tokens table
 | 
				
			||||||
        Schema::dropIfExists('api_tokens');
 | 
					        Schema::dropIfExists('api_tokens');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('joint_permissions', function (Blueprint $table) {
 | 
					        Schema::table('joint_permissions', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('id');
 | 
					            $table->dropColumn('id');
 | 
				
			||||||
| 
						 | 
					@ -21,10 +19,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('joint_permissions', function (Blueprint $table) {
 | 
					        Schema::table('joint_permissions', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropPrimary(['role_id', 'entity_type', 'entity_id', 'action']);
 | 
					            $table->dropPrimary(['role_id', 'entity_type', 'entity_id', 'action']);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,8 @@ return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function up()
 | 
					    public function up(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('roles', function (Blueprint $table) {
 | 
					        Schema::table('roles', function (Blueprint $table) {
 | 
				
			||||||
            $table->dropColumn('name');
 | 
					            $table->dropColumn('name');
 | 
				
			||||||
| 
						 | 
					@ -21,10 +19,8 @@ return new class extends Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Reverse the migrations.
 | 
					     * Reverse the migrations.
 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function down()
 | 
					    public function down(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Schema::table('roles', function (Blueprint $table) {
 | 
					        Schema::table('roles', function (Blueprint $table) {
 | 
				
			||||||
            $table->string('name')->index();
 | 
					            $table->string('name')->index();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue