diff --git a/app/Actions/Activity.php b/app/Actions/Activity.php index c8590f0b2..6a8a9bcd0 100644 --- a/app/Actions/Activity.php +++ b/app/Actions/Activity.php @@ -11,16 +11,15 @@ use Illuminate\Support\Str; /** * @property string $type - * @property User $user + * @property User $user * @property Entity $entity * @property string $detail * @property string $entity_type - * @property int $entity_id - * @property int $user_id + * @property int $entity_id + * @property int $user_id */ class Activity extends Model { - /** * Get the entity for this activity. */ @@ -29,6 +28,7 @@ class Activity extends Model if ($this->entity_type === '') { $this->entity_type = null; } + return $this->morphTo('entity'); } @@ -54,7 +54,7 @@ class Activity extends Model public function isForEntity(): bool { return Str::startsWith($this->type, [ - 'page_', 'chapter_', 'book_', 'bookshelf_' + 'page_', 'chapter_', 'book_', 'bookshelf_', ]); } diff --git a/app/Actions/ActivityService.php b/app/Actions/ActivityService.php index 73f827e70..dce7dc7b2 100644 --- a/app/Actions/ActivityService.php +++ b/app/Actions/ActivityService.php @@ -1,4 +1,6 @@ -activity->newInstance()->forceFill([ 'type' => strtolower($type), - 'user_id' => user()->id, + 'user_id' => user()->id, ]); } @@ -67,8 +70,8 @@ class ActivityService { $entity->activity()->update([ 'detail' => $entity->name, - 'entity_id' => null, - 'entity_type' => null, + 'entity_id' => null, + 'entity_type' => null, ]); } @@ -98,10 +101,10 @@ class ActivityService $queryIds = [$entity->getMorphClass() => [$entity->id]]; if ($entity->isA('book')) { - $queryIds[(new Chapter)->getMorphClass()] = $entity->chapters()->visible()->pluck('id'); + $queryIds[(new Chapter())->getMorphClass()] = $entity->chapters()->visible()->pluck('id'); } if ($entity->isA('book') || $entity->isA('chapter')) { - $queryIds[(new Page)->getMorphClass()] = $entity->pages()->visible()->pluck('id'); + $queryIds[(new Page())->getMorphClass()] = $entity->pages()->visible()->pluck('id'); } $query = $this->activity->newQuery(); @@ -143,7 +146,9 @@ class ActivityService /** * Filters out similar activity. + * * @param Activity[] $activities + * * @return array */ protected function filterSimilar(iterable $activities): array @@ -185,7 +190,7 @@ class ActivityService return; } - $message = str_replace("%u", $username, $message); + $message = str_replace('%u', $username, $message); $channel = config('logging.failed_login.channel'); Log::channel($channel)->warning($message); } diff --git a/app/Actions/ActivityType.php b/app/Actions/ActivityType.php index ec02bed25..a19f143b7 100644 --- a/app/Actions/ActivityType.php +++ b/app/Actions/ActivityType.php @@ -1,4 +1,6 @@ -comment = $comment; @@ -46,6 +46,7 @@ class CommentRepo $entity->comments()->save($comment); ActivityService::addForEntity($entity, ActivityType::COMMENTED_ON); + return $comment; } @@ -58,6 +59,7 @@ class CommentRepo $comment->text = $text; $comment->html = $this->commentToHtml($text); $comment->save(); + return $comment; } @@ -75,8 +77,8 @@ class CommentRepo public function commentToHtml(string $commentText): string { $converter = new CommonMarkConverter([ - 'html_input' => 'strip', - 'max_nesting_level' => 10, + 'html_input' => 'strip', + 'max_nesting_level' => 10, 'allow_unsafe_links' => false, ]); @@ -89,6 +91,7 @@ class CommentRepo protected function getNextLocalId(Entity $entity): int { $comments = $entity->comments(false)->orderBy('local_id', 'desc')->first(); + return ($comments->local_id ?? 0) + 1; } } diff --git a/app/Actions/Favourite.php b/app/Actions/Favourite.php index 107a76578..f45894182 100644 --- a/app/Actions/Favourite.php +++ b/app/Actions/Favourite.php @@ -1,4 +1,6 @@ -name) .'%5D'); + return url('/search?term=%5B' . urlencode($this->name) . '%5D'); } /** @@ -29,6 +31,6 @@ class Tag extends Model */ public function valueUrl(): string { - return url('/search?term=%5B' . urlencode($this->name) .'%3D' . urlencode($this->value) . '%5D'); + return url('/search?term=%5B' . urlencode($this->name) . '%3D' . urlencode($this->value) . '%5D'); } } diff --git a/app/Actions/TagRepo.php b/app/Actions/TagRepo.php index c80e8abe3..ca65b78e8 100644 --- a/app/Actions/TagRepo.php +++ b/app/Actions/TagRepo.php @@ -1,4 +1,6 @@ -permissionService->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type'); + return $query->get(['name'])->pluck('name'); } @@ -62,11 +64,12 @@ class TagRepo } $query = $this->permissionService->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type'); + return $query->get(['value'])->pluck('value'); } /** - * Save an array of tags to an entity + * Save an array of tags to an entity. */ public function saveTagsToEntity(Entity $entity, array $tags = []): iterable { @@ -89,6 +92,7 @@ class TagRepo { $name = trim($input['name']); $value = isset($input['value']) ? trim($input['value']) : ''; + return $this->tag->newInstance(['name' => $name, 'value' => $value]); } } diff --git a/app/Actions/View.php b/app/Actions/View.php index de30900c7..16961bd91 100644 --- a/app/Actions/View.php +++ b/app/Actions/View.php @@ -1,4 +1,6 @@ -generate(); Cache::put($cacheKey, $docs, 60 * 24); } + return $docs; } @@ -42,6 +44,7 @@ class ApiDocsGenerator $apiRoutes = $this->loadDetailsFromControllers($apiRoutes); $apiRoutes = $this->loadDetailsFromFiles($apiRoutes); $apiRoutes = $apiRoutes->groupBy('base_model'); + return $apiRoutes; } @@ -57,6 +60,7 @@ class ApiDocsGenerator $exampleContent = file_exists($exampleFile) ? file_get_contents($exampleFile) : null; $route["example_{$exampleType}"] = $exampleContent; } + return $route; }); } @@ -71,12 +75,14 @@ class ApiDocsGenerator $comment = $method->getDocComment(); $route['description'] = $comment ? $this->parseDescriptionFromMethodComment($comment) : null; $route['body_params'] = $this->getBodyParamsFromClass($route['controller'], $route['controller_method']); + return $route; }); } /** * Load body params and their rules by inspecting the given class and method name. + * * @throws BindingResolutionException */ protected function getBodyParamsFromClass(string $className, string $methodName): ?array @@ -92,6 +98,7 @@ class ApiDocsGenerator foreach ($rules as $param => $ruleString) { $rules[$param] = explode('|', $ruleString); } + return count($rules) > 0 ? $rules : null; } @@ -102,11 +109,13 @@ class ApiDocsGenerator { $matches = []; preg_match_all('/^\s*?\*\s((?![@\s]).*?)$/m', $comment, $matches); + return implode(' ', $matches[1] ?? []); } /** * Get a reflection method from the given class name and method name. + * * @throws ReflectionException */ protected function getReflectionMethod(string $className, string $methodName): ReflectionMethod @@ -131,14 +140,15 @@ class ApiDocsGenerator [$controller, $controllerMethod] = explode('@', $route->action['uses']); $baseModelName = explode('.', explode('/', $route->uri)[1])[0]; $shortName = $baseModelName . '-' . $controllerMethod; + return [ - 'name' => $shortName, - 'uri' => $route->uri, - 'method' => $route->methods[0], - 'controller' => $controller, - 'controller_method' => $controllerMethod, + 'name' => $shortName, + 'uri' => $route->uri, + 'method' => $route->methods[0], + 'controller' => $controller, + 'controller_method' => $controllerMethod, 'controller_method_kebab' => Str::kebab($controllerMethod), - 'base_model' => $baseModelName, + 'base_model' => $baseModelName, ]; }); } diff --git a/app/Api/ApiToken.php b/app/Api/ApiToken.php index defaa7e95..f44fde19a 100644 --- a/app/Api/ApiToken.php +++ b/app/Api/ApiToken.php @@ -1,4 +1,6 @@ - 'date:Y-m-d' + 'expires_at' => 'date:Y-m-d', ]; /** diff --git a/app/Api/ApiTokenGuard.php b/app/Api/ApiTokenGuard.php index 59ab72f4e..75ed5cb35 100644 --- a/app/Api/ApiTokenGuard.php +++ b/app/Api/ApiTokenGuard.php @@ -12,7 +12,6 @@ use Symfony\Component\HttpFoundation\Request; class ApiTokenGuard implements Guard { - use GuardHelpers; /** @@ -20,9 +19,9 @@ class ApiTokenGuard implements Guard */ protected $request; - /** * The last auth exception thrown in this request. + * * @var ApiAuthException */ protected $lastAuthException; @@ -34,7 +33,7 @@ class ApiTokenGuard implements Guard { $this->request = $request; } - + /** * @inheritDoc */ @@ -47,6 +46,7 @@ class ApiTokenGuard implements Guard } $user = null; + try { $user = $this->getAuthorisedUserFromRequest(); } catch (ApiAuthException $exception) { @@ -54,19 +54,20 @@ class ApiTokenGuard implements Guard } $this->user = $user; + return $user; } /** * Determine if current user is authenticated. If not, throw an exception. * - * @return \Illuminate\Contracts\Auth\Authenticatable - * * @throws ApiAuthException + * + * @return \Illuminate\Contracts\Auth\Authenticatable */ public function authenticate() { - if (! is_null($user = $this->user())) { + if (!is_null($user = $this->user())) { return $user; } @@ -79,6 +80,7 @@ class ApiTokenGuard implements Guard /** * Check the API token in the request and fetch a valid authorised user. + * * @throws ApiAuthException */ protected function getAuthorisedUserFromRequest(): Authenticatable @@ -98,6 +100,7 @@ class ApiTokenGuard implements Guard /** * Validate the format of the token header value string. + * * @throws ApiAuthException */ protected function validateTokenHeaderValue(string $authToken): void @@ -114,6 +117,7 @@ class ApiTokenGuard implements Guard /** * Validate the given secret against the given token and ensure the token * currently has access to the instance API. + * * @throws ApiAuthException */ protected function validateToken(?ApiToken $token, string $secret): void diff --git a/app/Api/ListingResponseBuilder.php b/app/Api/ListingResponseBuilder.php index df4cb8bf1..02b3f680c 100644 --- a/app/Api/ListingResponseBuilder.php +++ b/app/Api/ListingResponseBuilder.php @@ -1,4 +1,6 @@ - '<', 'gte' => '>=', 'lte' => '<=', - 'like' => 'like' + 'like' => 'like', ]; /** @@ -42,7 +43,7 @@ class ListingResponseBuilder $data = $this->fetchData($filteredQuery); return response()->json([ - 'data' => $data, + 'data' => $data, 'total' => $total, ]); } @@ -54,6 +55,7 @@ class ListingResponseBuilder { $query = $this->countAndOffsetQuery($query); $query = $this->sortQuery($query); + return $query->get($this->fields); } @@ -95,6 +97,7 @@ class ListingResponseBuilder } $queryOperator = $this->filterOperators[$filterOperator]; + return [$field, $queryOperator, $value]; } diff --git a/app/Application.php b/app/Application.php index 499fdeaa6..d409d14bc 100644 --- a/app/Application.php +++ b/app/Application.php @@ -4,11 +4,11 @@ namespace BookStack; class Application extends \Illuminate\Foundation\Application { - /** * Get the path to the application configuration files. * - * @param string $path Optionally, a path to append to the config path + * @param string $path Optionally, a path to append to the config path + * * @return string */ public function configPath($path = '') @@ -18,6 +18,6 @@ class Application extends \Illuminate\Foundation\Application . 'app' . DIRECTORY_SEPARATOR . 'Config' - . ($path ? DIRECTORY_SEPARATOR.$path : $path); + . ($path ? DIRECTORY_SEPARATOR . $path : $path); } } diff --git a/app/Auth/Access/EmailConfirmationService.php b/app/Auth/Access/EmailConfirmationService.php index 9aa3b9b98..3425c1e72 100644 --- a/app/Auth/Access/EmailConfirmationService.php +++ b/app/Auth/Access/EmailConfirmationService.php @@ -1,4 +1,6 @@ -display_name))); + return in_array($roleName, $groupNames); } @@ -57,7 +58,7 @@ class ExternalAuthService } /** - * Sync the groups to the user roles for the current user + * Sync the groups to the user roles for the current user. */ public function syncWithGroups(User $user, array $userGroups): void { diff --git a/app/Auth/Access/ExternalBaseUserProvider.php b/app/Auth/Access/ExternalBaseUserProvider.php index 69295ee4e..fde610c3e 100644 --- a/app/Auth/Access/ExternalBaseUserProvider.php +++ b/app/Auth/Access/ExternalBaseUserProvider.php @@ -7,7 +7,6 @@ use Illuminate\Contracts\Auth\UserProvider; class ExternalBaseUserProvider implements UserProvider { - /** * The user model. * @@ -17,7 +16,8 @@ class ExternalBaseUserProvider implements UserProvider /** * LdapUserProvider constructor. - * @param $model + * + * @param $model */ public function __construct(string $model) { @@ -32,13 +32,15 @@ class ExternalBaseUserProvider implements UserProvider public function createModel() { $class = '\\' . ltrim($this->model, '\\'); - return new $class; + + return new $class(); } /** * Retrieve a user by their unique identifier. * - * @param mixed $identifier + * @param mixed $identifier + * * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveById($identifier) @@ -49,8 +51,9 @@ class ExternalBaseUserProvider implements UserProvider /** * Retrieve a user by their unique identifier and "remember me" token. * - * @param mixed $identifier - * @param string $token + * @param mixed $identifier + * @param string $token + * * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByToken($identifier, $token) @@ -58,12 +61,12 @@ class ExternalBaseUserProvider implements UserProvider return null; } - /** * Update the "remember me" token for the given user in storage. * - * @param \Illuminate\Contracts\Auth\Authenticatable $user - * @param string $token + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string $token + * * @return void */ public function updateRememberToken(Authenticatable $user, $token) @@ -74,13 +77,15 @@ class ExternalBaseUserProvider implements UserProvider /** * Retrieve a user by the given credentials. * - * @param array $credentials + * @param array $credentials + * * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByCredentials(array $credentials) { // Search current user base by looking up a uid $model = $this->createModel(); + return $model->newQuery() ->where('external_auth_id', $credentials['external_auth_id']) ->first(); @@ -89,8 +94,9 @@ class ExternalBaseUserProvider implements UserProvider /** * Validate a user against the given credentials. * - * @param \Illuminate\Contracts\Auth\Authenticatable $user - * @param array $credentials + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param array $credentials + * * @return bool */ public function validateCredentials(Authenticatable $user, array $credentials) diff --git a/app/Auth/Access/Guards/ExternalBaseSessionGuard.php b/app/Auth/Access/Guards/ExternalBaseSessionGuard.php index b133754d8..9c3b47e97 100644 --- a/app/Auth/Access/Guards/ExternalBaseSessionGuard.php +++ b/app/Auth/Access/Guards/ExternalBaseSessionGuard.php @@ -84,7 +84,7 @@ class ExternalBaseSessionGuard implements StatefulGuard // If we've already retrieved the user for the current request we can just // return it back immediately. We do not want to fetch the user data on // every call to this method because that would be tremendously slow. - if (! is_null($this->user)) { + if (!is_null($this->user)) { return $this->user; } @@ -92,7 +92,7 @@ class ExternalBaseSessionGuard implements StatefulGuard // First we will try to load the user using the // identifier in the session if one exists. - if (! is_null($id)) { + if (!is_null($id)) { $this->user = $this->provider->retrieveById($id); } @@ -118,7 +118,8 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Log a user into the application without sessions or cookies. * - * @param array $credentials + * @param array $credentials + * * @return bool */ public function once(array $credentials = []) @@ -135,12 +136,13 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Log the given user ID into the application without sessions or cookies. * - * @param mixed $id + * @param mixed $id + * * @return \Illuminate\Contracts\Auth\Authenticatable|false */ public function onceUsingId($id) { - if (! is_null($user = $this->provider->retrieveById($id))) { + if (!is_null($user = $this->provider->retrieveById($id))) { $this->setUser($user); return $user; @@ -152,7 +154,8 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Validate a user's credentials. * - * @param array $credentials + * @param array $credentials + * * @return bool */ public function validate(array $credentials = []) @@ -160,12 +163,12 @@ class ExternalBaseSessionGuard implements StatefulGuard return false; } - /** * Attempt to authenticate a user using the given credentials. * - * @param array $credentials - * @param bool $remember + * @param array $credentials + * @param bool $remember + * * @return bool */ public function attempt(array $credentials = [], $remember = false) @@ -176,13 +179,14 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Log the given user ID into the application. * - * @param mixed $id - * @param bool $remember + * @param mixed $id + * @param bool $remember + * * @return \Illuminate\Contracts\Auth\Authenticatable|false */ public function loginUsingId($id, $remember = false) { - if (! is_null($user = $this->provider->retrieveById($id))) { + if (!is_null($user = $this->provider->retrieveById($id))) { $this->login($user, $remember); return $user; @@ -194,8 +198,9 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Log a user into the application. * - * @param \Illuminate\Contracts\Auth\Authenticatable $user - * @param bool $remember + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param bool $remember + * * @return void */ public function login(AuthenticatableContract $user, $remember = false) @@ -208,7 +213,8 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Update the session with the given ID. * - * @param string $id + * @param string $id + * * @return void */ protected function updateSession($id) @@ -262,7 +268,7 @@ class ExternalBaseSessionGuard implements StatefulGuard */ public function getName() { - return 'login_'.$this->name.'_'.sha1(static::class); + return 'login_' . $this->name . '_' . sha1(static::class); } /** @@ -288,7 +294,8 @@ class ExternalBaseSessionGuard implements StatefulGuard /** * Set the current user. * - * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * * @return $this */ public function setUser(AuthenticatableContract $user) diff --git a/app/Auth/Access/Guards/LdapSessionGuard.php b/app/Auth/Access/Guards/LdapSessionGuard.php index 71417aed2..7f6965937 100644 --- a/app/Auth/Access/Guards/LdapSessionGuard.php +++ b/app/Auth/Access/Guards/LdapSessionGuard.php @@ -6,8 +6,8 @@ use BookStack\Auth\Access\LdapService; use BookStack\Auth\Access\RegistrationService; use BookStack\Auth\User; use BookStack\Exceptions\LdapException; -use BookStack\Exceptions\LoginAttemptException; use BookStack\Exceptions\LoginAttemptEmailNeededException; +use BookStack\Exceptions\LoginAttemptException; use BookStack\Exceptions\UserRegistrationException; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Session\Session; @@ -15,7 +15,6 @@ use Illuminate\Support\Str; class LdapSessionGuard extends ExternalBaseSessionGuard { - protected $ldapService; /** @@ -36,8 +35,10 @@ class LdapSessionGuard extends ExternalBaseSessionGuard * Validate a user's credentials. * * @param array $credentials - * @return bool + * * @throws LdapException + * + * @return bool */ public function validate(array $credentials = []) { @@ -45,7 +46,7 @@ class LdapSessionGuard extends ExternalBaseSessionGuard if (isset($userDetails['uid'])) { $this->lastAttempted = $this->provider->retrieveByCredentials([ - 'external_auth_id' => $userDetails['uid'] + 'external_auth_id' => $userDetails['uid'], ]); } @@ -56,10 +57,12 @@ class LdapSessionGuard extends ExternalBaseSessionGuard * Attempt to authenticate a user using the given credentials. * * @param array $credentials - * @param bool $remember - * @return bool + * @param bool $remember + * * @throws LoginAttemptException * @throws LdapException + * + * @return bool */ public function attempt(array $credentials = [], $remember = false) { @@ -69,7 +72,7 @@ class LdapSessionGuard extends ExternalBaseSessionGuard $user = null; if (isset($userDetails['uid'])) { $this->lastAttempted = $user = $this->provider->retrieveByCredentials([ - 'external_auth_id' => $userDetails['uid'] + 'external_auth_id' => $userDetails['uid'], ]); } @@ -96,11 +99,13 @@ class LdapSessionGuard extends ExternalBaseSessionGuard } $this->login($user, $remember); + return true; } /** - * Create a new user from the given ldap credentials and login credentials + * Create a new user from the given ldap credentials and login credentials. + * * @throws LoginAttemptEmailNeededException * @throws LoginAttemptException * @throws UserRegistrationException @@ -114,14 +119,15 @@ class LdapSessionGuard extends ExternalBaseSessionGuard } $details = [ - 'name' => $ldapUserDetails['name'], - 'email' => $ldapUserDetails['email'] ?: $credentials['email'], + 'name' => $ldapUserDetails['name'], + 'email' => $ldapUserDetails['email'] ?: $credentials['email'], 'external_auth_id' => $ldapUserDetails['uid'], - 'password' => Str::random(32), + 'password' => Str::random(32), ]; $user = $this->registrationService->registerUser($details, null, false); $this->ldapService->saveAndAttachAvatar($user, $ldapUserDetails); + return $user; } } diff --git a/app/Auth/Access/Guards/Saml2SessionGuard.php b/app/Auth/Access/Guards/Saml2SessionGuard.php index 044c2f383..eacd5d21e 100644 --- a/app/Auth/Access/Guards/Saml2SessionGuard.php +++ b/app/Auth/Access/Guards/Saml2SessionGuard.php @@ -3,7 +3,7 @@ namespace BookStack\Auth\Access\Guards; /** - * Saml2 Session Guard + * Saml2 Session Guard. * * The saml2 login process is async in nature meaning it does not fit very well * into the default laravel 'Guard' auth flow. Instead most of the logic is done @@ -16,6 +16,7 @@ class Saml2SessionGuard extends ExternalBaseSessionGuard * Validate a user's credentials. * * @param array $credentials + * * @return bool */ public function validate(array $credentials = []) @@ -27,7 +28,8 @@ class Saml2SessionGuard extends ExternalBaseSessionGuard * Attempt to authenticate a user using the given credentials. * * @param array $credentials - * @param bool $remember + * @param bool $remember + * * @return bool */ public function attempt(array $credentials = [], $remember = false) diff --git a/app/Auth/Access/Ldap.php b/app/Auth/Access/Ldap.php index 352231df5..b5c70e498 100644 --- a/app/Auth/Access/Ldap.php +++ b/app/Auth/Access/Ldap.php @@ -1,4 +1,6 @@ -search($ldapConnection, $baseDn, $filter, $attributes); + return $this->getEntries($ldapConnection, $search); } /** * Bind to LDAP directory. + * * @param resource $ldapConnection * @param string $bindRdn * @param string $bindPassword + * * @return bool */ public function bind($ldapConnection, $bindRdn = null, $bindPassword = null) @@ -102,8 +118,10 @@ class Ldap /** * Explode a LDAP dn string into an array of components. + * * @param string $dn - * @param int $withAttrib + * @param int $withAttrib + * * @return array */ public function explodeDn(string $dn, int $withAttrib) @@ -113,12 +131,14 @@ class Ldap /** * Escape a string for use in an LDAP filter. + * * @param string $value * @param string $ignore - * @param int $flags + * @param int $flags + * * @return string */ - public function escape(string $value, string $ignore = "", int $flags = 0) + public function escape(string $value, string $ignore = '', int $flags = 0) { return ldap_escape($value, $ignore, $flags); } diff --git a/app/Auth/Access/LdapService.php b/app/Auth/Access/LdapService.php index 2f632b0b5..7bfdb5328 100644 --- a/app/Auth/Access/LdapService.php +++ b/app/Auth/Access/LdapService.php @@ -1,4 +1,6 @@ -getUserResponseProperty($user, 'cn', null); $formatted = [ - 'uid' => $this->getUserResponseProperty($user, $idAttr, $user['dn']), - 'name' => $this->getUserResponseProperty($user, $displayNameAttr, $userCn), - 'dn' => $user['dn'], + 'uid' => $this->getUserResponseProperty($user, $idAttr, $user['dn']), + 'name' => $this->getUserResponseProperty($user, $displayNameAttr, $userCn), + 'dn' => $user['dn'], 'email' => $this->getUserResponseProperty($user, $emailAttr, null), 'avatar'=> $thumbnailAttr ? $this->getUserResponseProperty($user, $thumbnailAttr, null) : null, ]; if ($this->config['dump_user_details']) { throw new JsonDebugException([ - 'details_from_ldap' => $user, + 'details_from_ldap' => $user, 'details_bookstack_parsed' => $formatted, ]); } @@ -137,6 +141,7 @@ class LdapService extends ExternalAuthService /** * Check if the given credentials are valid for the given user. + * * @throws LdapException */ public function validateUserCredentials(?array $ldapUserDetails, string $password): bool @@ -146,6 +151,7 @@ class LdapService extends ExternalAuthService } $ldapConnection = $this->getConnection(); + try { $ldapBind = $this->ldap->bind($ldapConnection, $ldapUserDetails['dn'], $password); } catch (ErrorException $e) { @@ -158,7 +164,9 @@ class LdapService extends ExternalAuthService /** * Bind the system user to the LDAP connection using the given credentials * otherwise anonymous access is attempted. + * * @param $connection + * * @throws LdapException */ protected function bindSystemUser($connection) @@ -181,8 +189,10 @@ class LdapService extends ExternalAuthService /** * Get the connection to the LDAP server. * Creates a new connection if one does not exist. - * @return resource + * * @throws LdapException + * + * @return resource */ protected function getConnection() { @@ -222,6 +232,7 @@ class LdapService extends ExternalAuthService } $this->ldapConnection = $ldapConnection; + return $this->ldapConnection; } @@ -241,6 +252,7 @@ class LdapService extends ExternalAuthService // Otherwise, extract the port out $hostName = $serverNameParts[0]; $ldapPort = (count($serverNameParts) > 1) ? intval($serverNameParts[1]) : 389; + return ['host' => $hostName, 'port' => $ldapPort]; } @@ -254,11 +266,13 @@ class LdapService extends ExternalAuthService $newKey = '${' . $key . '}'; $newAttrs[$newKey] = $this->ldap->escape($attrText); } + return strtr($filterString, $newAttrs); } /** * Get the groups a user is a part of on ldap. + * * @throws LdapException */ public function getUserGroups(string $userName): array @@ -272,11 +286,13 @@ class LdapService extends ExternalAuthService $userGroups = $this->groupFilter($user); $userGroups = $this->getGroupsRecursive($userGroups, []); + return $userGroups; } /** * Get the parent groups of an array of groups. + * * @throws LdapException */ private function getGroupsRecursive(array $groupsArray, array $checked): array @@ -303,6 +319,7 @@ class LdapService extends ExternalAuthService /** * Get the parent groups of a single group. + * * @throws LdapException */ private function getGroupGroups(string $groupName): array @@ -336,7 +353,7 @@ class LdapService extends ExternalAuthService $count = 0; if (isset($userGroupSearchResponse[$groupsAttr]['count'])) { - $count = (int)$userGroupSearchResponse[$groupsAttr]['count']; + $count = (int) $userGroupSearchResponse[$groupsAttr]['count']; } for ($i = 0; $i < $count; $i++) { @@ -351,6 +368,7 @@ class LdapService extends ExternalAuthService /** * Sync the LDAP groups to the user roles for the current user. + * * @throws LdapException */ public function syncGroups(User $user, string $username) diff --git a/app/Auth/Access/RegistrationService.php b/app/Auth/Access/RegistrationService.php index 68b17771d..16e3edbb4 100644 --- a/app/Auth/Access/RegistrationService.php +++ b/app/Auth/Access/RegistrationService.php @@ -1,4 +1,6 @@ -flash('sent-email-confirmation', true); } catch (Exception $e) { $message = trans('auth.email_confirm_send_error'); + throw new UserRegistrationException($message, '/register/confirm'); } } @@ -94,6 +99,7 @@ class RegistrationService /** * Ensure that the given email meets any active email domain registration restrictions. * Throws if restrictions are active and the email does not match an allowed domain. + * * @throws UserRegistrationException */ protected function ensureEmailDomainAllowed(string $userEmail): void @@ -105,9 +111,10 @@ class RegistrationService } $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict)); - $userEmailDomain = $domain = mb_substr(mb_strrchr($userEmail, "@"), 1); + $userEmailDomain = $domain = mb_substr(mb_strrchr($userEmail, '@'), 1); if (!in_array($userEmailDomain, $restrictedEmailDomains)) { $redirect = $this->registrationAllowed() ? '/register' : '/login'; + throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), $redirect); } } diff --git a/app/Auth/Access/Saml2Service.php b/app/Auth/Access/Saml2Service.php index 105853997..28d4d4030 100644 --- a/app/Auth/Access/Saml2Service.php +++ b/app/Auth/Access/Saml2Service.php @@ -1,4 +1,6 @@ -getToolkit(); $returnRoute = url('/saml2/acs'); + return [ 'url' => $toolKit->login($returnRoute, [], false, false, true), - 'id' => $toolKit->getLastRequestID(), + 'id' => $toolKit->getLastRequestID(), ]; } /** * Initiate a logout flow. + * * @throws Error */ public function logout(): array @@ -78,6 +83,7 @@ class Saml2Service extends ExternalAuthService * Process the ACS response from the idp and return the * matching, or new if registration active, user matched to the idp. * Returns null if not authenticated. + * * @throws Error * @throws SamlException * @throws ValidationError @@ -92,7 +98,7 @@ class Saml2Service extends ExternalAuthService if (!empty($errors)) { throw new Error( - 'Invalid ACS Response: '.implode(', ', $errors) + 'Invalid ACS Response: ' . implode(', ', $errors) ); } @@ -108,6 +114,7 @@ class Saml2Service extends ExternalAuthService /** * Process a response for the single logout service. + * * @throws Error */ public function processSlsResponse(?string $requestId): ?string @@ -119,11 +126,12 @@ class Saml2Service extends ExternalAuthService if (!empty($errors)) { throw new Error( - 'Invalid SLS Response: '.implode(', ', $errors) + 'Invalid SLS Response: ' . implode(', ', $errors) ); } $this->actionLogout(); + return $redirect; } @@ -138,6 +146,7 @@ class Saml2Service extends ExternalAuthService /** * Get the metadata for this service provider. + * * @throws Error */ public function metadata(): string @@ -149,7 +158,7 @@ class Saml2Service extends ExternalAuthService if (!empty($errors)) { throw new Error( - 'Invalid SP metadata: '.implode(', ', $errors), + 'Invalid SP metadata: ' . implode(', ', $errors), Error::METADATA_SP_INVALID ); } @@ -159,6 +168,7 @@ class Saml2Service extends ExternalAuthService /** * Load the underlying Onelogin SAML2 toolkit. + * * @throws Error * @throws Exception */ @@ -178,6 +188,7 @@ class Saml2Service extends ExternalAuthService $spSettings = $this->loadOneloginServiceProviderDetails(); $settings = array_replace_recursive($settings, $spSettings, $metaDataSettings, $overrides); + return new Auth($settings); } @@ -187,18 +198,18 @@ class Saml2Service extends ExternalAuthService protected function loadOneloginServiceProviderDetails(): array { $spDetails = [ - 'entityId' => url('/saml2/metadata'), + 'entityId' => url('/saml2/metadata'), 'assertionConsumerService' => [ 'url' => url('/saml2/acs'), ], 'singleLogoutService' => [ - 'url' => url('/saml2/sls') + 'url' => url('/saml2/sls'), ], ]; return [ 'baseurl' => url('/saml2'), - 'sp' => $spDetails + 'sp' => $spDetails, ]; } @@ -211,7 +222,7 @@ class Saml2Service extends ExternalAuthService } /** - * Calculate the display name + * Calculate the display name. */ protected function getUserDisplayName(array $samlAttributes, string $defaultValue): string { @@ -261,9 +272,9 @@ class Saml2Service extends ExternalAuthService return [ 'external_id' => $externalId, - 'name' => $this->getUserDisplayName($samlAttributes, $externalId), - 'email' => $email, - 'saml_id' => $samlID, + 'name' => $this->getUserDisplayName($samlAttributes, $externalId), + 'email' => $email, + 'saml_id' => $samlID, ]; } @@ -297,6 +308,7 @@ class Saml2Service extends ExternalAuthService $data = $data[0]; break; } + return $data; } @@ -315,6 +327,7 @@ class Saml2Service extends ExternalAuthService /** * Get the user from the database for the specified details. + * * @throws UserRegistrationException */ protected function getOrRegisterUser(array $userDetails): ?User @@ -325,9 +338,9 @@ class Saml2Service extends ExternalAuthService if (is_null($user)) { $userData = [ - 'name' => $userDetails['name'], - 'email' => $userDetails['email'], - 'password' => Str::random(32), + 'name' => $userDetails['name'], + 'email' => $userDetails['email'], + 'password' => Str::random(32), 'external_auth_id' => $userDetails['external_id'], ]; @@ -340,6 +353,7 @@ class Saml2Service extends ExternalAuthService /** * Process the SAML response for a user. Login the user when * they exist, optionally registering them automatically. + * * @throws SamlException * @throws JsonDebugException * @throws UserRegistrationException @@ -351,8 +365,8 @@ class Saml2Service extends ExternalAuthService if ($this->config['dump_user_details']) { throw new JsonDebugException([ - 'id_from_idp' => $samlID, - 'attrs_from_idp' => $samlAttributes, + 'id_from_idp' => $samlID, + 'attrs_from_idp' => $samlAttributes, 'attrs_after_parsing' => $userDetails, ]); } @@ -378,6 +392,7 @@ class Saml2Service extends ExternalAuthService auth()->login($user); Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}"); Theme::dispatch(ThemeEvents::AUTH_LOGIN, 'saml2', $user); + return $user; } } diff --git a/app/Auth/Access/SocialAuthService.php b/app/Auth/Access/SocialAuthService.php index a03eb2b1d..2f1a6876a 100644 --- a/app/Auth/Access/SocialAuthService.php +++ b/app/Auth/Access/SocialAuthService.php @@ -1,4 +1,6 @@ - */ protected $configureForRedirectCallbacks = []; @@ -61,26 +66,31 @@ class SocialAuthService /** * Start the social login path. + * * @throws SocialDriverNotConfigured */ public function startLogIn(string $socialDriver): RedirectResponse { $driver = $this->validateDriver($socialDriver); + return $this->getDriverForRedirect($driver)->redirect(); } /** - * Start the social registration process + * Start the social registration process. + * * @throws SocialDriverNotConfigured */ public function startRegister(string $socialDriver): RedirectResponse { $driver = $this->validateDriver($socialDriver); + return $this->getDriverForRedirect($driver)->redirect(); } /** * Handle the social registration process on callback. + * * @throws UserRegistrationException */ public function handleRegistrationCallback(string $socialDriver, SocialUser $socialUser): SocialUser @@ -92,6 +102,7 @@ class SocialAuthService if (User::query()->where('email', '=', $socialUser->getEmail())->exists()) { $email = $socialUser->getEmail(); + throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $email]), '/login'); } @@ -100,16 +111,19 @@ class SocialAuthService /** * Get the social user details via the social driver. + * * @throws SocialDriverNotConfigured */ public function getSocialUser(string $socialDriver): SocialUser { $driver = $this->validateDriver($socialDriver); + return $this->socialite->driver($driver)->user(); } /** * Handle the login process on a oAuth callback. + * * @throws SocialSignInAccountNotUsed */ public function handleLoginCallback(string $socialDriver, SocialUser $socialUser) @@ -128,6 +142,7 @@ class SocialAuthService auth()->login($socialAccount->user); Activity::add(ActivityType::AUTH_LOGIN, $socialAccount); Theme::dispatch(ThemeEvents::AUTH_LOGIN, $socialDriver, $socialAccount->user); + return redirect()->intended('/'); } @@ -137,18 +152,21 @@ class SocialAuthService $account = $this->newSocialAccount($socialDriver, $socialUser); $currentUser->socialAccounts()->save($account); session()->flash('success', trans('settings.users_social_connected', ['socialAccount' => $titleCaseDriver])); + return redirect($currentUser->getEditUrl()); } // When a user is logged in and the social account exists and is already linked to the current user. if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) { session()->flash('error', trans('errors.social_account_existing', ['socialAccount' => $titleCaseDriver])); + return redirect($currentUser->getEditUrl()); } // When a user is logged in, A social account exists but the users do not match. if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) { session()->flash('error', trans('errors.social_account_already_used_existing', ['socialAccount' => $titleCaseDriver])); + return redirect($currentUser->getEditUrl()); } @@ -163,6 +181,7 @@ class SocialAuthService /** * Ensure the social driver is correct and supported. + * * @throws SocialDriverNotConfigured */ protected function validateDriver(string $socialDriver): string @@ -188,6 +207,7 @@ class SocialAuthService $lowerName = strtolower($driver); $configPrefix = 'services.' . $lowerName . '.'; $config = [config($configPrefix . 'client_id'), config($configPrefix . 'client_secret'), config('services.callback_url')]; + return !in_array(false, $config) && !in_array(null, $config); } @@ -237,9 +257,9 @@ class SocialAuthService public function newSocialAccount(string $socialDriver, SocialUser $socialUser): SocialAccount { return new SocialAccount([ - 'driver' => $socialDriver, + 'driver' => $socialDriver, 'driver_id' => $socialUser->getId(), - 'avatar' => $socialUser->getAvatar() + 'avatar' => $socialUser->getAvatar(), ]); } @@ -252,7 +272,7 @@ class SocialAuthService } /** - * Provide redirect options per service for the Laravel Socialite driver + * Provide redirect options per service for the Laravel Socialite driver. */ protected function getDriverForRedirect(string $driverName): Provider { diff --git a/app/Auth/Access/UserInviteService.php b/app/Auth/Access/UserInviteService.php index 20519fc7d..d884cd636 100644 --- a/app/Auth/Access/UserInviteService.php +++ b/app/Auth/Access/UserInviteService.php @@ -1,4 +1,6 @@ -getEntryByToken($token); @@ -70,40 +79,47 @@ class UserTokenService /** * Creates a unique token within the email confirmation database. + * * @return string */ - protected function generateToken() : string + protected function generateToken(): string { $token = Str::random(24); while ($this->tokenExists($token)) { $token = Str::random(25); } + return $token; } /** * Generate and store a token for the given user. + * * @param User $user + * * @return string */ - protected function createTokenForUser(User $user) : string + protected function createTokenForUser(User $user): string { $token = $this->generateToken(); $this->db->table($this->tokenTable)->insert([ - 'user_id' => $user->id, - 'token' => $token, + 'user_id' => $user->id, + 'token' => $token, 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now() + 'updated_at' => Carbon::now(), ]); + return $token; } /** * Check if the given token exists. + * * @param string $token + * * @return bool */ - protected function tokenExists(string $token) : bool + protected function tokenExists(string $token): bool { return $this->db->table($this->tokenTable) ->where('token', '=', $token)->exists(); @@ -111,7 +127,9 @@ class UserTokenService /** * Get a token entry for the given token. + * * @param string $token + * * @return object|null */ protected function getEntryByToken(string $token) @@ -123,10 +141,12 @@ class UserTokenService /** * Check if the given token entry has expired. + * * @param stdClass $tokenEntry + * * @return bool */ - protected function entryExpired(stdClass $tokenEntry) : bool + protected function entryExpired(stdClass $tokenEntry): bool { return Carbon::now()->subHours($this->expiryTime) ->gt(new Carbon($tokenEntry->created_at)); diff --git a/app/Auth/Permissions/EntityPermission.php b/app/Auth/Permissions/EntityPermission.php index ef61e03ce..131771a38 100644 --- a/app/Auth/Permissions/EntityPermission.php +++ b/app/Auth/Permissions/EntityPermission.php @@ -1,15 +1,17 @@ - function ($query) { $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id', 'chapter_id']); - } + }, ]); } /** * Build joint permissions for the given shelf and role combinations. + * * @throws Throwable */ protected function buildJointPermissionsForShelves(EloquentCollection $shelves, array $roles, bool $deleteOld = false) @@ -169,6 +173,7 @@ class PermissionService /** * Build joint permissions for the given book and role combinations. + * * @throws Throwable */ protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false) @@ -193,6 +198,7 @@ class PermissionService /** * Rebuild the entity jointPermissions for a particular entity. + * * @throws Throwable */ public function buildJointPermissionsForEntity(Entity $entity) @@ -201,6 +207,7 @@ class PermissionService if ($entity instanceof Book) { $books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get(); $this->buildJointPermissionsForBooks($books, Role::query()->get()->all(), true); + return; } @@ -224,6 +231,7 @@ class PermissionService /** * Rebuild the entity jointPermissions for a collection of entities. + * * @throws Throwable */ public function buildJointPermissionsForEntities(array $entities) @@ -263,6 +271,7 @@ class PermissionService /** * Delete all of the entity jointPermissions for a list of entities. + * * @param Role[] $roles */ protected function deleteManyJointPermissionsForRoles($roles) @@ -275,7 +284,9 @@ class PermissionService /** * Delete the entity jointPermissions for a particular entity. + * * @param Entity $entity + * * @throws Throwable */ public function deleteJointPermissionsForEntity(Entity $entity) @@ -285,7 +296,9 @@ class PermissionService /** * Delete all of the entity jointPermissions for a list of entities. + * * @param Entity[] $entities + * * @throws Throwable */ protected function deleteManyJointPermissionsForEntities(array $entities) @@ -295,7 +308,6 @@ class PermissionService } $this->db->transaction(function () use ($entities) { - foreach (array_chunk($entities, 1000) as $entityChunk) { $query = $this->db->table('joint_permissions'); foreach ($entityChunk as $entity) { @@ -311,8 +323,10 @@ class PermissionService /** * Create & Save entity jointPermissions for many entities and roles. + * * @param Entity[] $entities - * @param Role[] $roles + * @param Role[] $roles + * * @throws Throwable */ protected function createManyJointPermissions(array $entities, array $roles) @@ -363,7 +377,6 @@ class PermissionService }); } - /** * Get the actions related to an entity. */ @@ -376,6 +389,7 @@ class PermissionService if ($entity instanceof Book) { $baseActions[] = 'chapter-create'; } + return $baseActions; } @@ -397,6 +411,7 @@ class PermissionService if ($entity->restricted) { $hasAccess = $this->mapHasActiveRestriction($permissionMap, $entity, $role, $restrictionAction); + return $this->createJointPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess); } @@ -433,6 +448,7 @@ class PermissionService protected function mapHasActiveRestriction(array $entityMap, Entity $entity, Role $role, string $action): bool { $key = $entity->getMorphClass() . ':' . $entity->getRawAttribute('id') . ':' . $role->getRawAttribute('id') . ':' . $action; + return $entityMap[$key] ?? false; } @@ -443,18 +459,19 @@ class PermissionService protected function createJointPermissionDataArray(Entity $entity, Role $role, string $action, bool $permissionAll, bool $permissionOwn): array { return [ - 'role_id' => $role->getRawAttribute('id'), - 'entity_id' => $entity->getRawAttribute('id'), - 'entity_type' => $entity->getMorphClass(), - 'action' => $action, - 'has_permission' => $permissionAll, + 'role_id' => $role->getRawAttribute('id'), + 'entity_id' => $entity->getRawAttribute('id'), + 'entity_type' => $entity->getMorphClass(), + 'action' => $action, + 'has_permission' => $permissionAll, 'has_permission_own' => $permissionOwn, - 'owned_by' => $entity->getRawAttribute('owned_by'), + 'owned_by' => $entity->getRawAttribute('owned_by'), ]; } /** * Checks if an entity has a restriction set upon it. + * * @param HasCreatorAndUpdater|HasOwner $ownable */ public function checkOwnableUserAccess(Model $ownable, string $permission): bool @@ -473,7 +490,8 @@ class PermissionService $ownPermission = $user && $user->can($permission . '-own'); $ownerField = ($ownable instanceof Entity) ? 'owned_by' : 'created_by'; $isOwner = $user && $user->id === $ownable->$ownerField; - return ($allPermission || ($isOwner && $ownPermission)); + + return $allPermission || ($isOwner && $ownPermission); } // Handle abnormal create jointPermissions @@ -483,6 +501,7 @@ class PermissionService $hasAccess = $this->entityRestrictionQuery($baseQuery, $action)->count() > 0; $this->clean(); + return $hasAccess; } @@ -509,6 +528,7 @@ class PermissionService $hasPermission = $permissionQuery->count() > 0; $this->clean(); + return $hasPermission; } @@ -529,6 +549,7 @@ class PermissionService }); $this->clean(); + return $q; } @@ -539,6 +560,7 @@ class PermissionService public function restrictEntityQuery(Builder $query, string $ability = 'view'): Builder { $this->clean(); + return $query->where(function (Builder $parentQuery) use ($ability) { $parentQuery->whereHas('jointPermissions', function (Builder $permissionQuery) use ($ability) { $permissionQuery->whereIn('role_id', $this->getCurrentUserRoles()) @@ -580,6 +602,7 @@ class PermissionService /** * Filter items that have entities set as a polymorphic relation. + * * @param Builder|\Illuminate\Database\Query\Builder $query */ public function filterRestrictedEntityRelations($query, string $tableName, string $entityIdColumn, string $entityTypeColumn, string $action = 'view') @@ -600,6 +623,7 @@ class PermissionService }); $this->clean(); + return $q; } @@ -628,12 +652,14 @@ class PermissionService }); $this->clean(); + return $q; } /** * Add the query for checking the given user id has permission * within the join_permissions table. + * * @param QueryBuilder|Builder $query */ protected function addJointHasPermissionCheck($query, int $userIdToCheck) @@ -645,7 +671,7 @@ class PermissionService } /** - * Get the current user + * Get the current user. */ private function currentUser(): User { diff --git a/app/Auth/Permissions/PermissionsRepo.php b/app/Auth/Permissions/PermissionsRepo.php index f54612a43..4d191679d 100644 --- a/app/Auth/Permissions/PermissionsRepo.php +++ b/app/Auth/Permissions/PermissionsRepo.php @@ -1,4 +1,6 @@ -assignRolePermissions($role, $permissions); $this->permissionService->buildJointPermissionForRole($role); Activity::add(ActivityType::ROLE_CREATE, $role); + return $role; } @@ -116,6 +118,7 @@ class PermissionsRepo * Check it's not an admin role or set as default before deleting. * If an migration Role ID is specified the users assign to the current role * will be added to the role of the specified id. + * * @throws PermissionsException * @throws Exception */ @@ -127,7 +130,7 @@ class PermissionsRepo // Prevent deleting admin role or default registration role. if ($role->system_name && in_array($role->system_name, $this->systemRoles)) { throw new PermissionsException(trans('errors.role_system_cannot_be_deleted')); - } else if ($role->id === intval(setting('registration-role'))) { + } elseif ($role->id === intval(setting('registration-role'))) { throw new PermissionsException(trans('errors.role_registration_default_cannot_delete')); } diff --git a/app/Auth/Permissions/RolePermission.php b/app/Auth/Permissions/RolePermission.php index 7f44ff815..0a0e6ff17 100644 --- a/app/Auth/Permissions/RolePermission.php +++ b/app/Auth/Permissions/RolePermission.php @@ -1,4 +1,6 @@ -where('system_name', '=', 'public')->first(); + return static::$defaultUser; } @@ -98,13 +109,15 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * The roles that belong to the user. + * * @return BelongsToMany */ public function roles() { if ($this->id === 0) { - return ; + return; } + return $this->belongsToMany(Role::class); } @@ -194,7 +207,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * Check if the user has a social account, * If a driver is passed it checks for that single account type. + * * @param bool|string $socialDriver + * * @return bool */ public function hasSocialAccount($socialDriver = false) @@ -207,7 +222,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon } /** - * Returns a URL to the user's avatar + * Returns a URL to the user's avatar. */ public function getAvatar(int $size = 50): string { @@ -222,6 +237,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon } catch (Exception $err) { $avatar = $default; } + return $avatar; } @@ -268,6 +284,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon public function getEditUrl(string $path = ''): string { $uri = '/settings/users/' . $this->id . '/' . trim($path, '/'); + return url(rtrim($uri, '/')); } @@ -298,7 +315,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * Send the password reset notification. - * @param string $token + * + * @param string $token + * * @return void */ public function sendPasswordResetNotification($token) @@ -320,6 +339,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon public function refreshSlug(): string { $this->slug = app(SlugGenerator::class)->generate($this); + return $this->slug; } } diff --git a/app/Auth/UserRepo.php b/app/Auth/UserRepo.php index 15ce2cbc9..61ca12dcc 100644 --- a/app/Auth/UserRepo.php +++ b/app/Auth/UserRepo.php @@ -1,4 +1,6 @@ -paginate($count); } - /** + /** * Creates a new user and attaches a role to them. */ public function registerNew(array $data, bool $emailConfirmed = false): User @@ -96,6 +98,7 @@ class UserRepo /** * Assign a user to a system-level role. + * * @throws NotFoundException */ public function attachSystemRole(User $user, string $systemRoleName) @@ -126,6 +129,7 @@ class UserRepo /** * Set the assigned user roles via an array of role IDs. + * * @throws UserUpdateException */ public function setUserRoles(User $user, array $roles) @@ -141,7 +145,7 @@ class UserRepo * Check if the given user is the last admin and their new roles no longer * contains the admin role. */ - protected function demotingLastAdmin(User $user, array $newRoles) : bool + protected function demotingLastAdmin(User $user, array $newRoles): bool { if ($this->isOnlyAdmin($user)) { $adminRole = Role::getSystemRole('admin'); @@ -159,10 +163,10 @@ class UserRepo public function create(array $data, bool $emailConfirmed = false): User { $details = [ - 'name' => $data['name'], - 'email' => $data['email'], - 'password' => bcrypt($data['password']), - 'email_confirmed' => $emailConfirmed, + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => bcrypt($data['password']), + 'email_confirmed' => $emailConfirmed, 'external_auth_id' => $data['external_auth_id'] ?? '', ]; @@ -176,6 +180,7 @@ class UserRepo /** * Remove the given user from storage, Delete all related content. + * * @throws Exception */ public function destroy(User $user, ?int $newOwnerId = null) @@ -184,7 +189,7 @@ class UserRepo $user->apiTokens()->delete(); $user->favourites()->delete(); $user->delete(); - + // Delete user profile images $this->userAvatar->destroyAllForUser($user); @@ -201,7 +206,7 @@ class UserRepo */ protected function migrateOwnership(User $fromUser, User $toUser) { - $entities = (new EntityProvider)->all(); + $entities = (new EntityProvider())->all(); foreach ($entities as $instance) { $instance->newQuery()->where('owned_by', '=', $fromUser->id) ->update(['owned_by' => $toUser->id]); @@ -242,11 +247,12 @@ class UserRepo public function getAssetCounts(User $user): array { $createdBy = ['created_by' => $user->id]; + return [ - 'pages' => Page::visible()->where($createdBy)->count(), - 'chapters' => Chapter::visible()->where($createdBy)->count(), - 'books' => Book::visible()->where($createdBy)->count(), - 'shelves' => Bookshelf::visible()->where($createdBy)->count(), + 'pages' => Page::visible()->where($createdBy)->count(), + 'chapters' => Chapter::visible()->where($createdBy)->count(), + 'books' => Book::visible()->where($createdBy)->count(), + 'shelves' => Bookshelf::visible()->where($createdBy)->count(), ]; } diff --git a/app/Config/api.php b/app/Config/api.php index 6afea2dc8..03f191fee 100644 --- a/app/Config/api.php +++ b/app/Config/api.php @@ -18,6 +18,6 @@ return [ 'max_item_count' => env('API_MAX_ITEM_COUNT', 500), // The number of API requests that can be made per minute by a single user. - 'requests_per_minute' => env('API_REQUESTS_PER_MIN', 180) + 'requests_per_minute' => env('API_REQUESTS_PER_MIN', 180), ]; diff --git a/app/Config/app.php b/app/Config/app.php index 550c98b0a..9d8ba7eb2 100755 --- a/app/Config/app.php +++ b/app/Config/app.php @@ -56,7 +56,7 @@ return [ 'locale' => env('APP_LANG', 'en'), // Locales available - 'locales' => ['en', 'ar', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'lv', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl', 'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',], + 'locales' => ['en', 'ar', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'lv', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl', 'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW'], // Application Fallback Locale 'fallback_locale' => 'en', @@ -140,52 +140,52 @@ return [ 'aliases' => [ // Laravel - 'App' => Illuminate\Support\Facades\App::class, - 'Arr' => Illuminate\Support\Arr::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Bus' => Illuminate\Support\Facades\Bus::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Input' => Illuminate\Support\Facades\Input::class, - 'Inspiring' => Illuminate\Foundation\Inspiring::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, + 'App' => Illuminate\Support\Facades\App::class, + 'Arr' => Illuminate\Support\Arr::class, + 'Artisan' => Illuminate\Support\Facades\Artisan::class, + 'Auth' => Illuminate\Support\Facades\Auth::class, + 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Bus' => Illuminate\Support\Facades\Bus::class, + 'Cache' => Illuminate\Support\Facades\Cache::class, + 'Config' => Illuminate\Support\Facades\Config::class, + 'Cookie' => Illuminate\Support\Facades\Cookie::class, + 'Crypt' => Illuminate\Support\Facades\Crypt::class, + 'DB' => Illuminate\Support\Facades\DB::class, + 'Eloquent' => Illuminate\Database\Eloquent\Model::class, + 'Event' => Illuminate\Support\Facades\Event::class, + 'File' => Illuminate\Support\Facades\File::class, + 'Hash' => Illuminate\Support\Facades\Hash::class, + 'Input' => Illuminate\Support\Facades\Input::class, + 'Inspiring' => Illuminate\Foundation\Inspiring::class, + 'Lang' => Illuminate\Support\Facades\Lang::class, + 'Log' => Illuminate\Support\Facades\Log::class, + 'Mail' => Illuminate\Support\Facades\Mail::class, 'Notification' => Illuminate\Support\Facades\Notification::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'Str' => Illuminate\Support\Str::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - 'Socialite' => Laravel\Socialite\Facades\Socialite::class, + 'Password' => Illuminate\Support\Facades\Password::class, + 'Queue' => Illuminate\Support\Facades\Queue::class, + 'Redirect' => Illuminate\Support\Facades\Redirect::class, + 'Redis' => Illuminate\Support\Facades\Redis::class, + 'Request' => Illuminate\Support\Facades\Request::class, + 'Response' => Illuminate\Support\Facades\Response::class, + 'Route' => Illuminate\Support\Facades\Route::class, + 'Schema' => Illuminate\Support\Facades\Schema::class, + 'Session' => Illuminate\Support\Facades\Session::class, + 'Storage' => Illuminate\Support\Facades\Storage::class, + 'Str' => Illuminate\Support\Str::class, + 'URL' => Illuminate\Support\Facades\URL::class, + 'Validator' => Illuminate\Support\Facades\Validator::class, + 'View' => Illuminate\Support\Facades\View::class, + 'Socialite' => Laravel\Socialite\Facades\Socialite::class, // Third Party 'ImageTool' => Intervention\Image\Facades\Image::class, - 'DomPDF' => Barryvdh\DomPDF\Facade::class, + 'DomPDF' => Barryvdh\DomPDF\Facade::class, 'SnappyPDF' => Barryvdh\Snappy\Facades\SnappyPdf::class, // Custom BookStack - 'Activity' => BookStack\Facades\Activity::class, + 'Activity' => BookStack\Facades\Activity::class, 'Permissions' => BookStack\Facades\Permissions::class, - 'Theme' => BookStack\Facades\Theme::class, + 'Theme' => BookStack\Facades\Theme::class, ], // Proxy configuration diff --git a/app/Config/auth.php b/app/Config/auth.php index 51b152ff1..404b5352d 100644 --- a/app/Config/auth.php +++ b/app/Config/auth.php @@ -18,7 +18,7 @@ return [ // This option controls the default authentication "guard" and password // reset options for your application. 'defaults' => [ - 'guard' => env('AUTH_METHOD', 'standard'), + 'guard' => env('AUTH_METHOD', 'standard'), 'passwords' => 'users', ], @@ -29,15 +29,15 @@ return [ // Supported drivers: "session", "api-token", "ldap-session" 'guards' => [ 'standard' => [ - 'driver' => 'session', + 'driver' => 'session', 'provider' => 'users', ], 'ldap' => [ - 'driver' => 'ldap-session', + 'driver' => 'ldap-session', 'provider' => 'external', ], 'saml2' => [ - 'driver' => 'saml2-session', + 'driver' => 'saml2-session', 'provider' => 'external', ], 'api' => [ @@ -52,11 +52,11 @@ return [ 'providers' => [ 'users' => [ 'driver' => 'eloquent', - 'model' => \BookStack\Auth\User::class, + 'model' => \BookStack\Auth\User::class, ], 'external' => [ 'driver' => 'external-users', - 'model' => \BookStack\Auth\User::class, + 'model' => \BookStack\Auth\User::class, ], ], @@ -67,9 +67,9 @@ return [ 'passwords' => [ 'users' => [ 'provider' => 'users', - 'email' => 'emails.password', - 'table' => 'password_resets', - 'expire' => 60, + 'email' => 'emails.password', + 'table' => 'password_resets', + 'expire' => 60, ], ], diff --git a/app/Config/broadcasting.php b/app/Config/broadcasting.php index 7aaaa5693..5e929d373 100644 --- a/app/Config/broadcasting.php +++ b/app/Config/broadcasting.php @@ -23,18 +23,18 @@ return [ 'connections' => [ 'pusher' => [ - 'driver' => 'pusher', - 'key' => env('PUSHER_APP_KEY'), - 'secret' => env('PUSHER_APP_SECRET'), - 'app_id' => env('PUSHER_APP_ID'), + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), - 'useTLS' => true, + 'useTLS' => true, ], ], 'redis' => [ - 'driver' => 'redis', + 'driver' => 'redis', 'connection' => 'default', ], @@ -46,7 +46,6 @@ return [ 'driver' => 'null', ], - ], ]; diff --git a/app/Config/cache.php b/app/Config/cache.php index 33d3a1a0b..f9b7ed1d2 100644 --- a/app/Config/cache.php +++ b/app/Config/cache.php @@ -42,8 +42,8 @@ return [ ], 'database' => [ - 'driver' => 'database', - 'table' => 'cache', + 'driver' => 'database', + 'table' => 'cache', 'connection' => null, ], @@ -58,7 +58,7 @@ return [ ], 'redis' => [ - 'driver' => 'redis', + 'driver' => 'redis', 'connection' => 'default', ], diff --git a/app/Config/database.php b/app/Config/database.php index 170666ddb..7fb51a13b 100644 --- a/app/Config/database.php +++ b/app/Config/database.php @@ -59,38 +59,38 @@ return [ 'connections' => [ 'mysql' => [ - 'driver' => 'mysql', - 'url' => env('DATABASE_URL'), - 'host' => $mysql_host, - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), - 'unix_socket' => env('DB_SOCKET', ''), - 'port' => $mysql_port, - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => $mysql_host, + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'port' => $mysql_port, + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', 'prefix_indexes' => true, - 'strict' => false, - 'engine' => null, - 'options' => extension_loaded('pdo_mysql') ? array_filter([ + 'strict' => false, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), ]) : [], ], 'mysql_testing' => [ - 'driver' => 'mysql', - 'url' => env('TEST_DATABASE_URL'), - 'host' => '127.0.0.1', - 'database' => 'bookstack-test', - 'username' => env('MYSQL_USER', 'bookstack-test'), - 'password' => env('MYSQL_PASSWORD', 'bookstack-test'), - 'port' => $mysql_port, - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', + 'driver' => 'mysql', + 'url' => env('TEST_DATABASE_URL'), + 'host' => '127.0.0.1', + 'database' => 'bookstack-test', + 'username' => env('MYSQL_USER', 'bookstack-test'), + 'password' => env('MYSQL_PASSWORD', 'bookstack-test'), + 'port' => $mysql_port, + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', 'prefix_indexes' => true, - 'strict' => false, + 'strict' => false, ], ], diff --git a/app/Config/debugbar.php b/app/Config/debugbar.php index fe624eb7d..53b5a0872 100644 --- a/app/Config/debugbar.php +++ b/app/Config/debugbar.php @@ -1,7 +1,7 @@ env('DEBUGBAR_ENABLED', false), - 'except' => [ - 'telescope*' + 'except' => [ + 'telescope*', ], - - // DebugBar stores data for session/ajax requests. - // You can disable this, so the debugbar stores data in headers/session, - // but this can cause problems with large data collectors. - // By default, file storage (in the storage folder) is used. Redis and PDO - // can also be used. For PDO, run the package migrations first. + // DebugBar stores data for session/ajax requests. + // You can disable this, so the debugbar stores data in headers/session, + // but this can cause problems with large data collectors. + // By default, file storage (in the storage folder) is used. Redis and PDO + // can also be used. For PDO, run the package migrations first. 'storage' => [ 'enabled' => true, 'driver' => 'file', // redis, file, pdo, custom 'path' => storage_path('debugbar'), // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) - 'provider' => '' // Instance of StorageInterface for custom driver + 'provider' => '', // Instance of StorageInterface for custom driver ], - // Vendor files are included by default, but can be set to false. - // This can also be set to 'js' or 'css', to only include javascript or css vendor files. - // Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) - // and for js: jquery and and highlight.js - // So if you want syntax highlighting, set it to true. - // jQuery is set to not conflict with existing jQuery scripts. + // Vendor files are included by default, but can be set to false. + // This can also be set to 'js' or 'css', to only include javascript or css vendor files. + // Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) + // and for js: jquery and and highlight.js + // So if you want syntax highlighting, set it to true. + // jQuery is set to not conflict with existing jQuery scripts. 'include_vendors' => true, - // The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), - // you can use this option to disable sending the data through the headers. - // Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. + // The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), + // you can use this option to disable sending the data through the headers. + // Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. - 'capture_ajax' => true, + 'capture_ajax' => true, 'add_ajax_timing' => false, - // When enabled, the Debugbar shows deprecated warnings for Symfony components - // in the Messages tab. + // When enabled, the Debugbar shows deprecated warnings for Symfony components + // in the Messages tab. 'error_handler' => false, - // The Debugbar can emulate the Clockwork headers, so you can use the Chrome - // Extension, without the server-side code. It uses Debugbar collectors instead. + // The Debugbar can emulate the Clockwork headers, so you can use the Chrome + // Extension, without the server-side code. It uses Debugbar collectors instead. 'clockwork' => false, - // Enable/disable DataCollectors + // Enable/disable DataCollectors 'collectors' => [ 'phpinfo' => true, // Php version 'messages' => true, // Messages @@ -82,7 +81,7 @@ return [ 'models' => true, // Display models ], - // Configure some DataCollectors + // Configure some DataCollectors 'options' => [ 'auth' => [ 'show_name' => true, // Also show the users name/email in the debugbar @@ -91,43 +90,43 @@ return [ 'with_params' => true, // Render SQL with the parameters substituted 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. 'timeline' => false, // Add the queries to the timeline - 'explain' => [ // Show EXPLAIN output on queries + 'explain' => [ // Show EXPLAIN output on queries 'enabled' => false, - 'types' => ['SELECT'], // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ + 'types' => ['SELECT'], // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ ], 'hints' => true, // Show hints for common mistakes ], 'mail' => [ - 'full_log' => false + 'full_log' => false, ], 'views' => [ 'data' => false, //Note: Can slow down the application, because the data can be quite large.. ], 'route' => [ - 'label' => true // show complete route on bar + 'label' => true, // show complete route on bar ], 'logs' => [ - 'file' => null + 'file' => null, ], 'cache' => [ - 'values' => true // collect cache values + 'values' => true, // collect cache values ], ], - // Inject Debugbar into the response - // Usually, the debugbar is added just before , by listening to the - // Response after the App is done. If you disable this, you have to add them - // in your template yourself. See http://phpdebugbar.com/docs/rendering.html + // Inject Debugbar into the response + // Usually, the debugbar is added just before , by listening to the + // Response after the App is done. If you disable this, you have to add them + // in your template yourself. See http://phpdebugbar.com/docs/rendering.html 'inject' => true, - // DebugBar route prefix - // Sometimes you want to set route prefix to be used by DebugBar to load - // its resources from. Usually the need comes from misconfigured web server or - // from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 + // DebugBar route prefix + // Sometimes you want to set route prefix to be used by DebugBar to load + // its resources from. Usually the need comes from misconfigured web server or + // from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 'route_prefix' => '_debugbar', - // DebugBar route domain - // By default DebugBar route served from the same domain that request served. - // To override default domain, specify it as a non-empty value. + // DebugBar route domain + // By default DebugBar route served from the same domain that request served. + // To override default domain, specify it as a non-empty value. 'route_domain' => env('APP_URL', '') === 'http://bookstack.dev' ? '' : env('APP_URL', ''), ]; diff --git a/app/Config/dompdf.php b/app/Config/dompdf.php index 094739cd9..71ea716f3 100644 --- a/app/Config/dompdf.php +++ b/app/Config/dompdf.php @@ -10,12 +10,11 @@ return [ - 'show_warnings' => false, // Throw an Exception on warnings from dompdf - 'orientation' => 'portrait', - 'defines' => [ + 'orientation' => 'portrait', + 'defines' => [ /** - * The location of the DOMPDF font directory + * The location of the DOMPDF font directory. * * The location of the directory where DOMPDF will store fonts and font metrics * Note: This directory must exist and be writable by the webserver process. @@ -38,17 +37,17 @@ return [ * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic, * Symbol, ZapfDingbats. */ - "DOMPDF_FONT_DIR" => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782) + 'DOMPDF_FONT_DIR' => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782) /** - * The location of the DOMPDF font cache directory + * The location of the DOMPDF font cache directory. * * This directory contains the cached font metrics for the fonts used by DOMPDF. * This directory can be the same as DOMPDF_FONT_DIR * * Note: This directory must exist and be writable by the webserver process. */ - "DOMPDF_FONT_CACHE" => storage_path('fonts/'), + 'DOMPDF_FONT_CACHE' => storage_path('fonts/'), /** * The location of a temporary directory. @@ -57,10 +56,10 @@ return [ * The temporary directory is required to download remote images and when * using the PFDLib back end. */ - "DOMPDF_TEMP_DIR" => sys_get_temp_dir(), + 'DOMPDF_TEMP_DIR' => sys_get_temp_dir(), /** - * ==== IMPORTANT ==== + * ==== IMPORTANT ====. * * dompdf's "chroot": Prevents dompdf from accessing system files or other * files on the webserver. All local files opened by dompdf must be in a @@ -71,7 +70,7 @@ return [ * direct class use like: * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output(); */ - "DOMPDF_CHROOT" => realpath(base_path()), + 'DOMPDF_CHROOT' => realpath(base_path()), /** * Whether to use Unicode fonts or not. @@ -82,20 +81,19 @@ return [ * When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a * document must be present in your fonts, however. */ - "DOMPDF_UNICODE_ENABLED" => true, + 'DOMPDF_UNICODE_ENABLED' => true, /** * Whether to enable font subsetting or not. */ - "DOMPDF_ENABLE_FONTSUBSETTING" => false, + 'DOMPDF_ENABLE_FONTSUBSETTING' => false, /** - * The PDF rendering backend to use + * The PDF rendering backend to use. * * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will - * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link - * Canvas_Factory} ultimately determines which rendering class to instantiate + * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link * Canvas_Factory} ultimately determines which rendering class to instantiate * based on this setting. * * Both PDFLib & CPDF rendering backends provide sufficient rendering @@ -117,10 +115,10 @@ return [ * @link http://www.ros.co.nz/pdf * @link http://www.php.net/image */ - "DOMPDF_PDF_BACKEND" => "CPDF", + 'DOMPDF_PDF_BACKEND' => 'CPDF', /** - * PDFlib license key + * PDFlib license key. * * If you are using a licensed, commercial version of PDFlib, specify * your license key here. If you are using PDFlib-Lite or are evaluating @@ -143,7 +141,7 @@ return [ * the desired content might be different (e.g. screen or projection view of html file). * Therefore allow specification of content here. */ - "DOMPDF_DEFAULT_MEDIA_TYPE" => "print", + 'DOMPDF_DEFAULT_MEDIA_TYPE' => 'print', /** * The default paper size. @@ -152,18 +150,19 @@ return [ * * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.) */ - "DOMPDF_DEFAULT_PAPER_SIZE" => "a4", + 'DOMPDF_DEFAULT_PAPER_SIZE' => 'a4', /** - * The default font family + * The default font family. * * Used if no suitable fonts can be found. This must exist in the font folder. + * * @var string */ - "DOMPDF_DEFAULT_FONT" => "dejavu sans", + 'DOMPDF_DEFAULT_FONT' => 'dejavu sans', /** - * Image DPI setting + * Image DPI setting. * * This setting determines the default DPI setting for images and fonts. The * DPI may be overridden for inline images by explictly setting the @@ -195,10 +194,10 @@ return [ * * @var int */ - "DOMPDF_DPI" => 96, + 'DOMPDF_DPI' => 96, /** - * Enable inline PHP + * Enable inline PHP. * * If this setting is set to true then DOMPDF will automatically evaluate * inline PHP contained within tags. @@ -209,20 +208,20 @@ return [ * * @var bool */ - "DOMPDF_ENABLE_PHP" => false, + 'DOMPDF_ENABLE_PHP' => false, /** - * Enable inline Javascript + * Enable inline Javascript. * * If this setting is set to true then DOMPDF will automatically insert * JavaScript code contained within tags. * * @var bool */ - "DOMPDF_ENABLE_JAVASCRIPT" => false, + 'DOMPDF_ENABLE_JAVASCRIPT' => false, /** - * Enable remote file access + * Enable remote file access. * * If this setting is set to true, DOMPDF will access remote sites for * images and CSS files as required. @@ -238,29 +237,27 @@ return [ * * @var bool */ - "DOMPDF_ENABLE_REMOTE" => true, + 'DOMPDF_ENABLE_REMOTE' => true, /** - * A ratio applied to the fonts height to be more like browsers' line height + * A ratio applied to the fonts height to be more like browsers' line height. */ - "DOMPDF_FONT_HEIGHT_RATIO" => 1.1, + 'DOMPDF_FONT_HEIGHT_RATIO' => 1.1, /** - * Enable CSS float + * Enable CSS float. * * Allows people to disabled CSS float support + * * @var bool */ - "DOMPDF_ENABLE_CSS_FLOAT" => true, - + 'DOMPDF_ENABLE_CSS_FLOAT' => true, /** - * Use the more-than-experimental HTML5 Lib parser + * Use the more-than-experimental HTML5 Lib parser. */ - "DOMPDF_ENABLE_HTML5PARSER" => true, - + 'DOMPDF_ENABLE_HTML5PARSER' => true, ], - ]; diff --git a/app/Config/filesystems.php b/app/Config/filesystems.php index 30a5c5369..95fc35c2a 100644 --- a/app/Config/filesystems.php +++ b/app/Config/filesystems.php @@ -34,7 +34,7 @@ return [ 'local' => [ 'driver' => 'local', - 'root' => public_path(), + 'root' => public_path(), ], 'local_secure' => [ @@ -43,12 +43,12 @@ return [ ], 's3' => [ - 'driver' => 's3', - 'key' => env('STORAGE_S3_KEY', 'your-key'), - 'secret' => env('STORAGE_S3_SECRET', 'your-secret'), - 'region' => env('STORAGE_S3_REGION', 'your-region'), - 'bucket' => env('STORAGE_S3_BUCKET', 'your-bucket'), - 'endpoint' => env('STORAGE_S3_ENDPOINT', null), + 'driver' => 's3', + 'key' => env('STORAGE_S3_KEY', 'your-key'), + 'secret' => env('STORAGE_S3_SECRET', 'your-secret'), + 'region' => env('STORAGE_S3_REGION', 'your-region'), + 'bucket' => env('STORAGE_S3_BUCKET', 'your-bucket'), + 'endpoint' => env('STORAGE_S3_ENDPOINT', null), 'use_path_style_endpoint' => env('STORAGE_S3_ENDPOINT', null) !== null, ], diff --git a/app/Config/hashing.php b/app/Config/hashing.php index 756718ce2..585ee094c 100644 --- a/app/Config/hashing.php +++ b/app/Config/hashing.php @@ -29,9 +29,9 @@ return [ // passwords are hashed using the Argon algorithm. These will allow you // to control the amount of time it takes to hash the given password. 'argon' => [ - 'memory' => 1024, + 'memory' => 1024, 'threads' => 2, - 'time' => 2, + 'time' => 2, ], ]; diff --git a/app/Config/logging.php b/app/Config/logging.php index afd56e482..220aa0607 100644 --- a/app/Config/logging.php +++ b/app/Config/logging.php @@ -30,66 +30,66 @@ return [ // "custom", "stack" 'channels' => [ 'stack' => [ - 'driver' => 'stack', - 'channels' => ['daily'], + 'driver' => 'stack', + 'channels' => ['daily'], 'ignore_exceptions' => false, ], 'single' => [ 'driver' => 'single', - 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', - 'days' => 14, + 'path' => storage_path('logs/laravel.log'), + 'level' => 'debug', + 'days' => 14, ], 'daily' => [ 'driver' => 'daily', - 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', - 'days' => 7, + 'path' => storage_path('logs/laravel.log'), + 'level' => 'debug', + 'days' => 7, ], 'slack' => [ - 'driver' => 'slack', - 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', - 'emoji' => ':boom:', - 'level' => 'critical', + 'emoji' => ':boom:', + 'level' => 'critical', ], 'stderr' => [ - 'driver' => 'monolog', + 'driver' => 'monolog', 'handler' => StreamHandler::class, - 'with' => [ + 'with' => [ 'stream' => 'php://stderr', ], ], 'syslog' => [ 'driver' => 'syslog', - 'level' => 'debug', + 'level' => 'debug', ], 'errorlog' => [ 'driver' => 'errorlog', - 'level' => 'debug', + 'level' => 'debug', ], // Custom errorlog implementation that logs out a plain, // non-formatted message intended for the webserver log. 'errorlog_plain_webserver' => [ - 'driver' => 'monolog', - 'level' => 'debug', - 'handler' => ErrorLogHandler::class, - 'handler_with' => [4], - 'formatter' => LineFormatter::class, + 'driver' => 'monolog', + 'level' => 'debug', + 'handler' => ErrorLogHandler::class, + 'handler_with' => [4], + 'formatter' => LineFormatter::class, 'formatter_with' => [ - 'format' => "%message%", + 'format' => '%message%', ], ], 'null' => [ - 'driver' => 'monolog', + 'driver' => 'monolog', 'handler' => NullHandler::class, ], @@ -101,7 +101,6 @@ return [ ], ], - // Failed Login Message // Allows a configurable message to be logged when a login request fails. 'failed_login' => [ diff --git a/app/Config/mail.php b/app/Config/mail.php index abdbd382c..34b28fe2a 100644 --- a/app/Config/mail.php +++ b/app/Config/mail.php @@ -23,7 +23,7 @@ return [ // Global "From" address & name 'from' => [ 'address' => env('MAIL_FROM', 'mail@bookstackapp.com'), - 'name' => env('MAIL_FROM_NAME', 'BookStack') + 'name' => env('MAIL_FROM_NAME', 'BookStack'), ], // Email encryption protocol diff --git a/app/Config/queue.php b/app/Config/queue.php index 46f6962c5..0c79fcdd2 100644 --- a/app/Config/queue.php +++ b/app/Config/queue.php @@ -17,24 +17,23 @@ return [ // Queue connection configuration 'connections' => [ - 'sync' => [ 'driver' => 'sync', ], 'database' => [ - 'driver' => 'database', - 'table' => 'jobs', - 'queue' => 'default', + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', 'retry_after' => 90, ], 'redis' => [ - 'driver' => 'redis', - 'connection' => 'default', - 'queue' => env('REDIS_QUEUE', 'default'), + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => 90, - 'block_for' => null, + 'block_for' => null, ], ], diff --git a/app/Config/saml2.php b/app/Config/saml2.php index 8ba969549..fe311057c 100644 --- a/app/Config/saml2.php +++ b/app/Config/saml2.php @@ -31,7 +31,6 @@ return [ // Overrides, in JSON format, to the configuration passed to underlying onelogin library. 'onelogin_overrides' => env('SAML2_ONELOGIN_OVERRIDES', null), - 'onelogin' => [ // If 'strict' is True, then the PHP Toolkit will reject unsigned // or unencrypted messages if it expects them signed or encrypted @@ -81,7 +80,7 @@ return [ 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', // Usually x509cert and privateKey of the SP are provided by files placed at // the certs folder. But we can also provide them with the following parameters - 'x509cert' => '', + 'x509cert' => '', 'privateKey' => '', ], // Identity Provider Data that we want connect with our SP diff --git a/app/Config/services.php b/app/Config/services.php index 0f9f78d1e..2d7253fb8 100644 --- a/app/Config/services.php +++ b/app/Config/services.php @@ -28,16 +28,16 @@ return [ 'redirect' => env('APP_URL') . '/login/service/github/callback', 'name' => 'GitHub', 'auto_register' => env('GITHUB_AUTO_REGISTER', false), - 'auto_confirm' => env('GITHUB_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('GITHUB_AUTO_CONFIRM_EMAIL', false), ], 'google' => [ - 'client_id' => env('GOOGLE_APP_ID', false), - 'client_secret' => env('GOOGLE_APP_SECRET', false), - 'redirect' => env('APP_URL') . '/login/service/google/callback', - 'name' => 'Google', - 'auto_register' => env('GOOGLE_AUTO_REGISTER', false), - 'auto_confirm' => env('GOOGLE_AUTO_CONFIRM_EMAIL', false), + 'client_id' => env('GOOGLE_APP_ID', false), + 'client_secret' => env('GOOGLE_APP_SECRET', false), + 'redirect' => env('APP_URL') . '/login/service/google/callback', + 'name' => 'Google', + 'auto_register' => env('GOOGLE_AUTO_REGISTER', false), + 'auto_confirm' => env('GOOGLE_AUTO_CONFIRM_EMAIL', false), 'select_account' => env('GOOGLE_SELECT_ACCOUNT', false), ], @@ -47,7 +47,7 @@ return [ 'redirect' => env('APP_URL') . '/login/service/slack/callback', 'name' => 'Slack', 'auto_register' => env('SLACK_AUTO_REGISTER', false), - 'auto_confirm' => env('SLACK_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('SLACK_AUTO_CONFIRM_EMAIL', false), ], 'facebook' => [ @@ -56,7 +56,7 @@ return [ 'redirect' => env('APP_URL') . '/login/service/facebook/callback', 'name' => 'Facebook', 'auto_register' => env('FACEBOOK_AUTO_REGISTER', false), - 'auto_confirm' => env('FACEBOOK_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('FACEBOOK_AUTO_CONFIRM_EMAIL', false), ], 'twitter' => [ @@ -65,27 +65,27 @@ return [ 'redirect' => env('APP_URL') . '/login/service/twitter/callback', 'name' => 'Twitter', 'auto_register' => env('TWITTER_AUTO_REGISTER', false), - 'auto_confirm' => env('TWITTER_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('TWITTER_AUTO_CONFIRM_EMAIL', false), ], 'azure' => [ 'client_id' => env('AZURE_APP_ID', false), 'client_secret' => env('AZURE_APP_SECRET', false), - 'tenant' => env('AZURE_TENANT', false), + 'tenant' => env('AZURE_TENANT', false), 'redirect' => env('APP_URL') . '/login/service/azure/callback', 'name' => 'Microsoft Azure', 'auto_register' => env('AZURE_AUTO_REGISTER', false), - 'auto_confirm' => env('AZURE_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('AZURE_AUTO_CONFIRM_EMAIL', false), ], 'okta' => [ - 'client_id' => env('OKTA_APP_ID'), + 'client_id' => env('OKTA_APP_ID'), 'client_secret' => env('OKTA_APP_SECRET'), - 'redirect' => env('APP_URL') . '/login/service/okta/callback', - 'base_url' => env('OKTA_BASE_URL'), + 'redirect' => env('APP_URL') . '/login/service/okta/callback', + 'base_url' => env('OKTA_BASE_URL'), 'name' => 'Okta', 'auto_register' => env('OKTA_AUTO_REGISTER', false), - 'auto_confirm' => env('OKTA_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('OKTA_AUTO_CONFIRM_EMAIL', false), ], 'gitlab' => [ @@ -95,45 +95,45 @@ return [ 'instance_uri' => env('GITLAB_BASE_URI'), // Needed only for self hosted instances 'name' => 'GitLab', 'auto_register' => env('GITLAB_AUTO_REGISTER', false), - 'auto_confirm' => env('GITLAB_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('GITLAB_AUTO_CONFIRM_EMAIL', false), ], 'twitch' => [ - 'client_id' => env('TWITCH_APP_ID'), + 'client_id' => env('TWITCH_APP_ID'), 'client_secret' => env('TWITCH_APP_SECRET'), - 'redirect' => env('APP_URL') . '/login/service/twitch/callback', + 'redirect' => env('APP_URL') . '/login/service/twitch/callback', 'name' => 'Twitch', 'auto_register' => env('TWITCH_AUTO_REGISTER', false), - 'auto_confirm' => env('TWITCH_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('TWITCH_AUTO_CONFIRM_EMAIL', false), ], 'discord' => [ - 'client_id' => env('DISCORD_APP_ID'), + 'client_id' => env('DISCORD_APP_ID'), 'client_secret' => env('DISCORD_APP_SECRET'), - 'redirect' => env('APP_URL') . '/login/service/discord/callback', - 'name' => 'Discord', + 'redirect' => env('APP_URL') . '/login/service/discord/callback', + 'name' => 'Discord', 'auto_register' => env('DISCORD_AUTO_REGISTER', false), - 'auto_confirm' => env('DISCORD_AUTO_CONFIRM_EMAIL', false), + 'auto_confirm' => env('DISCORD_AUTO_CONFIRM_EMAIL', false), ], 'ldap' => [ - 'server' => env('LDAP_SERVER', false), - 'dump_user_details' => env('LDAP_DUMP_USER_DETAILS', false), - 'dn' => env('LDAP_DN', false), - 'pass' => env('LDAP_PASS', false), - 'base_dn' => env('LDAP_BASE_DN', false), - 'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'), - 'version' => env('LDAP_VERSION', false), - 'id_attribute' => env('LDAP_ID_ATTRIBUTE', 'uid'), - 'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'), + 'server' => env('LDAP_SERVER', false), + 'dump_user_details' => env('LDAP_DUMP_USER_DETAILS', false), + 'dn' => env('LDAP_DN', false), + 'pass' => env('LDAP_PASS', false), + 'base_dn' => env('LDAP_BASE_DN', false), + 'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'), + 'version' => env('LDAP_VERSION', false), + 'id_attribute' => env('LDAP_ID_ATTRIBUTE', 'uid'), + 'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'), 'display_name_attribute' => env('LDAP_DISPLAY_NAME_ATTRIBUTE', 'cn'), - 'follow_referrals' => env('LDAP_FOLLOW_REFERRALS', false), - 'user_to_groups' => env('LDAP_USER_TO_GROUPS', false), - 'group_attribute' => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'), - 'remove_from_groups' => env('LDAP_REMOVE_FROM_GROUPS', false), - 'tls_insecure' => env('LDAP_TLS_INSECURE', false), - 'start_tls' => env('LDAP_START_TLS', false), - 'thumbnail_attribute' => env('LDAP_THUMBNAIL_ATTRIBUTE', null), + 'follow_referrals' => env('LDAP_FOLLOW_REFERRALS', false), + 'user_to_groups' => env('LDAP_USER_TO_GROUPS', false), + 'group_attribute' => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'), + 'remove_from_groups' => env('LDAP_REMOVE_FROM_GROUPS', false), + 'tls_insecure' => env('LDAP_TLS_INSECURE', false), + 'start_tls' => env('LDAP_START_TLS', false), + 'thumbnail_attribute' => env('LDAP_THUMBNAIL_ATTRIBUTE', null), ], ]; diff --git a/app/Config/session.php b/app/Config/session.php index c750e1ef9..4bbb78901 100644 --- a/app/Config/session.php +++ b/app/Config/session.php @@ -1,6 +1,6 @@ [ - 'dark-mode-enabled' => env('APP_DEFAULT_DARK_MODE', false), + 'dark-mode-enabled' => env('APP_DEFAULT_DARK_MODE', false), 'bookshelves_view_type' => env('APP_VIEWS_BOOKSHELVES', 'grid'), - 'bookshelf_view_type' =>env('APP_VIEWS_BOOKSHELF', 'grid'), - 'books_view_type' => env('APP_VIEWS_BOOKS', 'grid'), + 'bookshelf_view_type' => env('APP_VIEWS_BOOKSHELF', 'grid'), + 'books_view_type' => env('APP_VIEWS_BOOKS', 'grid'), ], ]; diff --git a/app/Config/snappy.php b/app/Config/snappy.php index f347eda23..0f012bef6 100644 --- a/app/Config/snappy.php +++ b/app/Config/snappy.php @@ -14,7 +14,7 @@ return [ 'binary' => file_exists(base_path('wkhtmltopdf')) ? base_path('wkhtmltopdf') : env('WKHTMLTOPDF', false), 'timeout' => false, 'options' => [ - 'outline' => true + 'outline' => true, ], 'env' => [], ], diff --git a/app/Console/Commands/CleanupImages.php b/app/Console/Commands/CleanupImages.php index 93ca367a2..722150197 100644 --- a/app/Console/Commands/CleanupImages.php +++ b/app/Console/Commands/CleanupImages.php @@ -25,11 +25,11 @@ class CleanupImages extends Command */ protected $description = 'Cleanup images and drawings'; - protected $imageService; /** * Create a new command instance. + * * @param \BookStack\Uploads\ImageService $imageService */ public function __construct(ImageService $imageService) @@ -63,6 +63,7 @@ class CleanupImages extends Command $this->comment($deleteCount . ' images found that would have been deleted'); $this->showDeletedImages($deleted); $this->comment('Run with -f or --force to perform deletions'); + return; } diff --git a/app/Console/Commands/ClearViews.php b/app/Console/Commands/ClearViews.php index 693d93639..0fc6c0195 100644 --- a/app/Console/Commands/ClearViews.php +++ b/app/Console/Commands/ClearViews.php @@ -23,7 +23,6 @@ class ClearViews extends Command /** * Create a new command instance. - * */ public function __construct() { diff --git a/app/Console/Commands/CopyShelfPermissions.php b/app/Console/Commands/CopyShelfPermissions.php index d220c59f9..32adf0683 100644 --- a/app/Console/Commands/CopyShelfPermissions.php +++ b/app/Console/Commands/CopyShelfPermissions.php @@ -54,13 +54,14 @@ class CopyShelfPermissions extends Command if (!$cascadeAll && !$shelfSlug) { $this->error('Either a --slug or --all option must be provided.'); + return; } if ($cascadeAll) { $continue = $this->confirm( - 'Permission settings for all shelves will be cascaded. '. - 'Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. '. + 'Permission settings for all shelves will be cascaded. ' . + 'Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. ' . 'Are you sure you want to proceed?' ); diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php index 3d1a3dca0..a0fb8f315 100644 --- a/app/Console/Commands/CreateAdmin.php +++ b/app/Console/Commands/CreateAdmin.php @@ -38,8 +38,9 @@ class CreateAdmin extends Command /** * Execute the console command. * - * @return mixed * @throws \BookStack\Exceptions\NotFoundException + * + * @return mixed */ public function handle() { @@ -71,7 +72,6 @@ class CreateAdmin extends Command return $this->error('Invalid password provided, Must be at least 5 characters'); } - $user = $this->userRepo->create(['email' => $email, 'name' => $name, 'password' => $password]); $this->userRepo->attachSystemRole($user, 'admin'); $this->userRepo->downloadAndAssignUserAvatar($user); diff --git a/app/Console/Commands/DeleteUsers.php b/app/Console/Commands/DeleteUsers.php index c73c883de..5627dd1f8 100644 --- a/app/Console/Commands/DeleteUsers.php +++ b/app/Console/Commands/DeleteUsers.php @@ -8,7 +8,6 @@ use Illuminate\Console\Command; class DeleteUsers extends Command { - /** * The name and signature of the console command. * @@ -47,7 +46,7 @@ class DeleteUsers extends Command continue; } $this->userRepo->destroy($user); - ++$numDeleted; + $numDeleted++; } $this->info("Deleted $numDeleted of $totalUsers total users."); } else { diff --git a/app/Console/Commands/UpdateUrl.php b/app/Console/Commands/UpdateUrl.php index 2a1688468..a4bb6cf22 100644 --- a/app/Console/Commands/UpdateUrl.php +++ b/app/Console/Commands/UpdateUrl.php @@ -4,7 +4,6 @@ namespace BookStack\Console\Commands; use Illuminate\Console\Command; use Illuminate\Database\Connection; -use Illuminate\Support\Facades\DB; class UpdateUrl extends Command { @@ -49,7 +48,8 @@ class UpdateUrl extends Command $urlPattern = '/https?:\/\/(.+)/'; if (!preg_match($urlPattern, $oldUrl) || !preg_match($urlPattern, $newUrl)) { - $this->error("The given urls are expected to be full urls starting with http:// or https://"); + $this->error('The given urls are expected to be full urls starting with http:// or https://'); + return 1; } @@ -58,11 +58,11 @@ class UpdateUrl extends Command } $columnsToUpdateByTable = [ - "attachments" => ["path"], - "pages" => ["html", "text", "markdown"], - "images" => ["url"], - "settings" => ["value"], - "comments" => ["html", "text"], + 'attachments' => ['path'], + 'pages' => ['html', 'text', 'markdown'], + 'images' => ['url'], + 'settings' => ['value'], + 'comments' => ['html', 'text'], ]; foreach ($columnsToUpdateByTable as $table => $columns) { @@ -73,7 +73,7 @@ class UpdateUrl extends Command } $jsonColumnsToUpdateByTable = [ - "settings" => ["value"], + 'settings' => ['value'], ]; foreach ($jsonColumnsToUpdateByTable as $table => $columns) { @@ -85,10 +85,11 @@ class UpdateUrl extends Command } } - $this->info("URL update procedure complete."); + $this->info('URL update procedure complete.'); $this->info('============================================================================'); $this->info('Be sure to run "php artisan cache:clear" to clear any old URLs in the cache.'); $this->info('============================================================================'); + return 0; } @@ -100,8 +101,9 @@ class UpdateUrl extends Command { $oldQuoted = $this->db->getPdo()->quote($oldUrl); $newQuoted = $this->db->getPdo()->quote($newUrl); + return $this->db->table($table)->update([ - $column => $this->db->raw("REPLACE({$column}, {$oldQuoted}, {$newQuoted})") + $column => $this->db->raw("REPLACE({$column}, {$oldQuoted}, {$newQuoted})"), ]); } @@ -112,8 +114,8 @@ class UpdateUrl extends Command protected function checkUserOkayToProceed(string $oldUrl, string $newUrl): bool { $dangerWarning = "This will search for \"{$oldUrl}\" in your database and replace it with \"{$newUrl}\".\n"; - $dangerWarning .= "Are you sure you want to proceed?"; - $backupConfirmation = "This operation could cause issues if used incorrectly. Have you made a backup of your existing database?"; + $dangerWarning .= 'Are you sure you want to proceed?'; + $backupConfirmation = 'This operation could cause issues if used incorrectly. Have you made a backup of your existing database?'; return $this->confirm($dangerWarning) && $this->confirm($backupConfirmation); } diff --git a/app/Console/Commands/UpgradeDatabaseEncoding.php b/app/Console/Commands/UpgradeDatabaseEncoding.php index a17fc9523..32808729a 100644 --- a/app/Console/Commands/UpgradeDatabaseEncoding.php +++ b/app/Console/Commands/UpgradeDatabaseEncoding.php @@ -23,7 +23,6 @@ class UpgradeDatabaseEncoding extends Command /** * Create a new command instance. - * */ public function __construct() { @@ -44,12 +43,12 @@ class UpgradeDatabaseEncoding extends Command $database = DB::getDatabaseName(); $tables = DB::select('SHOW TABLES'); - $this->line('ALTER DATABASE `'.$database.'` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); - $this->line('USE `'.$database.'`;'); + $this->line('ALTER DATABASE `' . $database . '` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); + $this->line('USE `' . $database . '`;'); $key = 'Tables_in_' . $database; foreach ($tables as $table) { $tableName = $table->$key; - $this->line('ALTER TABLE `'.$tableName.'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); + $this->line('ALTER TABLE `' . $tableName . '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); } DB::setDefaultConnection($connection); diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index e75d93801..11c8018c8 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -1,4 +1,6 @@ -load(__DIR__.'/Commands'); + $this->load(__DIR__ . '/Commands'); } } diff --git a/app/Entities/BreadcrumbsViewComposer.php b/app/Entities/BreadcrumbsViewComposer.php index cf7cf296c..797162dfb 100644 --- a/app/Entities/BreadcrumbsViewComposer.php +++ b/app/Entities/BreadcrumbsViewComposer.php @@ -1,4 +1,6 @@ -bookshelf = new Bookshelf(); @@ -55,15 +55,16 @@ class EntityProvider /** * Fetch all core entity types as an associated array * with their basic names as the keys. + * * @return array */ public function all(): array { return [ 'bookshelf' => $this->bookshelf, - 'book' => $this->book, - 'chapter' => $this->chapter, - 'page' => $this->page, + 'book' => $this->book, + 'chapter' => $this->chapter, + 'page' => $this->page, ]; } @@ -73,6 +74,7 @@ class EntityProvider public function get(string $type): Entity { $type = strtolower($type); + return $this->all()[$type]; } @@ -86,6 +88,7 @@ class EntityProvider $model = $this->get($type); $morphClasses[] = $model->getMorphClass(); } + return $morphClasses; } } diff --git a/app/Entities/Models/Book.php b/app/Entities/Models/Book.php index 6c5676765..df30c1c71 100644 --- a/app/Entities/Models/Book.php +++ b/app/Entities/Models/Book.php @@ -1,4 +1,6 @@ -directPages()->visible()->get(); $chapters = $this->chapters()->visible()->get(); + return $pages->concat($chapters)->sortBy('priority')->sortByDesc('draft'); } } diff --git a/app/Entities/Models/BookChild.php b/app/Entities/Models/BookChild.php index c73fa3959..f61878208 100644 --- a/app/Entities/Models/BookChild.php +++ b/app/Entities/Models/BookChild.php @@ -1,18 +1,21 @@ - $pages * @property mixed description */ @@ -16,7 +19,9 @@ class Chapter extends BookChild /** * Get the pages that this chapter contains. + * * @param string $dir + * * @return mixed */ public function pages($dir = 'ASC') diff --git a/app/Entities/Models/Deletion.php b/app/Entities/Models/Deletion.php index 2295a5e21..764c4a1e3 100644 --- a/app/Entities/Models/Deletion.php +++ b/app/Entities/Models/Deletion.php @@ -1,7 +1,8 @@ -forceFill([ - 'deleted_by' => user()->id, + 'deleted_by' => user()->id, 'deletable_type' => $entity->getMorphClass(), - 'deletable_id' => $entity->id, + 'deletable_id' => $entity->id, ]); $record->save(); + return $record; } public function logDescriptor(): string { $deletable = $this->deletable()->first(); + return "Deletion ({$this->id}) for {$deletable->getType()} ({$deletable->id}) {$deletable->name}"; } diff --git a/app/Entities/Models/Entity.php b/app/Entities/Models/Entity.php index caa25d678..a02926c4d 100644 --- a/app/Entities/Models/Entity.php +++ b/app/Entities/Models/Entity.php @@ -1,4 +1,6 @@ -morphMany(Comment::class, 'entity'); + return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query; } @@ -205,7 +209,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable /** * Check if this instance or class is a certain type of entity. - * Examples of $type are 'page', 'book', 'chapter' + * Examples of $type are 'page', 'book', 'chapter'. */ public static function isA(string $type): bool { @@ -218,6 +222,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable public static function getType(): string { $className = array_slice(explode('\\', static::class), -1, 1)[0]; + return strtolower($className); } @@ -229,6 +234,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable if (mb_strlen($this->name) <= $length) { return $this->name; } + return mb_substr($this->name, 0, $length - 3) . '...'; } @@ -248,14 +254,14 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable $text = $this->getText(); if (mb_strlen($text) > $length) { - $text = mb_substr($text, 0, $length-3) . '...'; + $text = mb_substr($text, 0, $length - 3) . '...'; } return trim($text); } /** - * Get the url of this entity + * Get the url of this entity. */ abstract public function getUrl(string $path = '/'): string; @@ -272,6 +278,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable if ($this instanceof Chapter) { return $this->book()->withTrashed()->first(); } + return null; } @@ -285,7 +292,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable } /** - * Index the current entity for search + * Index the current entity for search. */ public function indexForSearch() { @@ -298,6 +305,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable public function refreshSlug(): string { $this->slug = app(SlugGenerator::class)->generate($this); + return $this->slug; } diff --git a/app/Entities/Models/HasCoverImage.php b/app/Entities/Models/HasCoverImage.php index f3a486d18..f665efce6 100644 --- a/app/Entities/Models/HasCoverImage.php +++ b/app/Entities/Models/HasCoverImage.php @@ -1,13 +1,11 @@ 'boolean', + 'draft' => 'boolean', 'template' => 'boolean', ]; @@ -41,22 +44,26 @@ class Page extends BookChild public function scopeVisible(Builder $query): Builder { $query = Permissions::enforceDraftVisibilityOnQuery($query); + return parent::scopeVisible($query); } /** * Converts this page into a simplified array. + * * @return mixed */ public function toSimpleArray() { $array = array_intersect_key($this->toArray(), array_flip($this->simpleAttributes)); $array['url'] = $this->getUrl(); + return $array; } /** * Get the chapter that this page is in, If applicable. + * * @return BelongsTo */ public function chapter() @@ -66,6 +73,7 @@ class Page extends BookChild /** * Check if this page has a chapter. + * * @return bool */ public function hasChapter() @@ -96,6 +104,7 @@ class Page extends BookChild /** * Get the attachments assigned to this page. + * * @return HasMany */ public function attachments() @@ -120,7 +129,8 @@ class Page extends BookChild } /** - * Get the current revision for the page if existing + * Get the current revision for the page if existing. + * * @return PageRevision|null */ public function getCurrentRevision() @@ -136,6 +146,7 @@ class Page extends BookChild $refreshed = $this->refresh()->unsetRelations()->load(['tags', 'createdBy', 'updatedBy', 'ownedBy']); $refreshed->setHidden(array_diff($refreshed->getHidden(), ['html', 'markdown'])); $refreshed->html = (new PageContent($refreshed))->render(); + return $refreshed; } } diff --git a/app/Entities/Models/PageRevision.php b/app/Entities/Models/PageRevision.php index 76a3b15ff..c1a74f66b 100644 --- a/app/Entities/Models/PageRevision.php +++ b/app/Entities/Models/PageRevision.php @@ -1,29 +1,32 @@ -make(EntityProvider::class); } -} \ No newline at end of file +} diff --git a/app/Entities/Queries/Popular.php b/app/Entities/Queries/Popular.php index 98db2fe62..e6b22a1c9 100644 --- a/app/Entities/Queries/Popular.php +++ b/app/Entities/Queries/Popular.php @@ -1,5 +1,6 @@ -pluck('viewable') ->filter(); } - -} \ No newline at end of file +} diff --git a/app/Entities/Queries/RecentlyViewed.php b/app/Entities/Queries/RecentlyViewed.php index d528fea44..5a29ecd72 100644 --- a/app/Entities/Queries/RecentlyViewed.php +++ b/app/Entities/Queries/RecentlyViewed.php @@ -1,4 +1,6 @@ -tagRepo = $tagRepo; @@ -27,7 +21,7 @@ class BaseRepo } /** - * Create a new entity in the system + * Create a new entity in the system. */ public function create(Entity $entity, array $input) { @@ -35,7 +29,7 @@ class BaseRepo $entity->forceFill([ 'created_by' => user()->id, 'updated_by' => user()->id, - 'owned_by' => user()->id, + 'owned_by' => user()->id, ]); $entity->refreshSlug(); $entity->save(); @@ -72,6 +66,7 @@ class BaseRepo /** * Update the given items' cover image, or clear it. + * * @throws ImageUploadException * @throws \Exception */ diff --git a/app/Entities/Repos/BookRepo.php b/app/Entities/Repos/BookRepo.php index 27d0b4075..a692bbaf7 100644 --- a/app/Entities/Repos/BookRepo.php +++ b/app/Entities/Repos/BookRepo.php @@ -1,4 +1,6 @@ -baseRepo->create($book, $input); Activity::addForEntity($book, ActivityType::BOOK_CREATE); + return $book; } @@ -101,11 +103,13 @@ class BookRepo { $this->baseRepo->update($book, $input); Activity::addForEntity($book, ActivityType::BOOK_UPDATE); + return $book; } /** * Update the given book's cover image, or clear it. + * * @throws ImageUploadException * @throws Exception */ @@ -116,6 +120,7 @@ class BookRepo /** * Remove a book from the system. + * * @throws Exception */ public function destroy(Book $book) diff --git a/app/Entities/Repos/BookshelfRepo.php b/app/Entities/Repos/BookshelfRepo.php index 649f4b0c4..3990bfbdc 100644 --- a/app/Entities/Repos/BookshelfRepo.php +++ b/app/Entities/Repos/BookshelfRepo.php @@ -1,4 +1,6 @@ -baseRepo->create($shelf, $input); $this->updateBooks($shelf, $bookIds); Activity::addForEntity($shelf, ActivityType::BOOKSHELF_CREATE); + return $shelf; } @@ -104,6 +107,7 @@ class BookshelfRepo } Activity::addForEntity($shelf, ActivityType::BOOKSHELF_UPDATE); + return $shelf; } @@ -129,6 +133,7 @@ class BookshelfRepo /** * Update the given shelf cover image, or clear it. + * * @throws ImageUploadException * @throws Exception */ @@ -164,6 +169,7 @@ class BookshelfRepo /** * Remove a bookshelf from the system. + * * @throws Exception */ public function destroy(Bookshelf $shelf) diff --git a/app/Entities/Repos/ChapterRepo.php b/app/Entities/Repos/ChapterRepo.php index d56874e0d..68330dd57 100644 --- a/app/Entities/Repos/ChapterRepo.php +++ b/app/Entities/Repos/ChapterRepo.php @@ -1,4 +1,6 @@ -priority = (new BookContents($parentBook))->getLastPriority() + 1; $this->baseRepo->create($chapter, $input); Activity::addForEntity($chapter, ActivityType::CHAPTER_CREATE); + return $chapter; } @@ -59,11 +61,13 @@ class ChapterRepo { $this->baseRepo->update($chapter, $input); Activity::addForEntity($chapter, ActivityType::CHAPTER_UPDATE); + return $chapter; } /** * Remove a chapter from the system. + * * @throws Exception */ public function destroy(Chapter $chapter) @@ -77,7 +81,8 @@ class ChapterRepo /** * Move the given chapter into a new parent book. * The $parentIdentifier must be a string of the following format: - * 'book:' (book:5) + * 'book:' (book:5). + * * @throws MoveOperationException */ public function move(Chapter $chapter, string $parentIdentifier): Book diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php index 5eb882a02..28949b2dd 100644 --- a/app/Entities/Repos/PageRepo.php +++ b/app/Entities/Repos/PageRepo.php @@ -1,14 +1,16 @@ -orderBy('created_at', 'desc') ->with('page') ->first(); + return $revision ? $revision->page : null; } @@ -119,6 +122,7 @@ class PageRepo public function getUserDraft(Page $page): ?PageRevision { $revision = $this->getUserDraftQuery($page)->first(); + return $revision; } @@ -128,11 +132,11 @@ class PageRepo public function getNewDraftPage(Entity $parent) { $page = (new Page())->forceFill([ - 'name' => trans('entities.pages_initial_name'), + 'name' => trans('entities.pages_initial_name'), 'created_by' => user()->id, - 'owned_by' => user()->id, + 'owned_by' => user()->id, 'updated_by' => user()->id, - 'draft' => true, + 'draft' => true, ]); if ($parent instanceof Chapter) { @@ -144,6 +148,7 @@ class PageRepo $page->save(); $page->refresh()->rebuildPermissions(); + return $page; } @@ -166,6 +171,7 @@ class PageRepo $draft->refresh(); Activity::addForEntity($draft, ActivityType::PAGE_CREATE); + return $draft; } @@ -190,7 +196,7 @@ class PageRepo $this->getUserDraftQuery($page)->delete(); // Save a revision after updating - $summary = trim($input['summary'] ?? ""); + $summary = trim($input['summary'] ?? ''); $htmlChanged = isset($input['html']) && $input['html'] !== $oldHtml; $nameChanged = isset($input['name']) && $input['name'] !== $oldName; $markdownChanged = isset($input['markdown']) && $input['markdown'] !== $oldMarkdown; @@ -199,6 +205,7 @@ class PageRepo } Activity::addForEntity($page, ActivityType::PAGE_UPDATE); + return $page; } @@ -234,6 +241,7 @@ class PageRepo $revision->save(); $this->deleteOldRevisions($page); + return $revision; } @@ -249,6 +257,7 @@ class PageRepo } $page->fill($input); $page->save(); + return $page; } @@ -260,11 +269,13 @@ class PageRepo } $draft->save(); + return $draft; } /** * Destroy a page from the system. + * * @throws Exception */ public function destroy(Page $page) @@ -291,7 +302,7 @@ class PageRepo } else { $content->setNewHTML($revision->html); } - + $page->updated_by = user()->id; $page->refreshSlug(); $page->save(); @@ -301,13 +312,15 @@ class PageRepo $this->savePageRevision($page, $summary); Activity::addForEntity($page, ActivityType::PAGE_RESTORE); + return $page; } /** * Move the given page into a new parent book or chapter. * The $parentIdentifier must be a string of the following format: - * 'book:' (book:5) + * 'book:' (book:5). + * * @throws MoveOperationException * @throws PermissionsException */ @@ -327,12 +340,14 @@ class PageRepo $page->rebuildPermissions(); Activity::addForEntity($page, ActivityType::PAGE_MOVE); + return $parent; } /** * Copy an existing page in the system. * Optionally providing a new parent via string identifier and a new name. + * * @throws MoveOperationException * @throws PermissionsException */ @@ -369,7 +384,8 @@ class PageRepo /** * Find a page parent entity via a identifier string in the format: * {type}:{id} - * Example: (book:5) + * Example: (book:5). + * * @throws MoveOperationException */ protected function findParentByIdentifier(string $identifier): ?Entity @@ -383,6 +399,7 @@ class PageRepo } $parentClass = $entityType === 'book' ? Book::class : Chapter::class; + return $parentClass::visible()->where('id', '=', $entityId)->first(); } @@ -420,6 +437,7 @@ class PageRepo $draft->book_slug = $page->book->slug; $draft->created_by = user()->id; $draft->type = 'update_draft'; + return $draft; } @@ -445,13 +463,14 @@ class PageRepo } /** - * Get a new priority for a page + * Get a new priority for a page. */ protected function getNewPriority(Page $page): int { $parent = $page->getParent(); if ($parent instanceof Chapter) { $lastPage = $parent->pages('desc')->first(); + return $lastPage ? $lastPage->priority + 1 : 0; } diff --git a/app/Entities/Tools/BookContents.php b/app/Entities/Tools/BookContents.php index 71c8f8393..af7746e56 100644 --- a/app/Entities/Tools/BookContents.php +++ b/app/Entities/Tools/BookContents.php @@ -1,4 +1,6 @@ -where('chapter_id', '=', 0)->max('priority'); $maxChapter = Chapter::visible()->where('book_id', '=', $this->book->id) ->max('priority'); + return max($maxChapter, $maxPage, 1); } @@ -83,6 +85,7 @@ class BookContents if (isset($entity['draft']) && $entity['draft']) { return -100; } + return $entity['priority'] ?? 0; }; } @@ -110,9 +113,10 @@ class BookContents * +"parentChapter": false (ID of parent chapter, as string, or false) * +"type": "page" (Entity type of item) * +"book": "1" (Id of book to place item in) - * } + * }. * * Returns a list of books that were involved in the operation. + * * @throws SortOperationException */ public function sortUsingMap(Collection $sortMap): Collection @@ -190,6 +194,7 @@ class BookContents /** * Get the books involved in a sort. * The given sort map should have its models loaded first. + * * @throws SortOperationException */ protected function getBooksInvolvedInSort(Collection $sortMap): Collection @@ -202,7 +207,7 @@ class BookContents $books = Book::hasPermission('update')->whereIn('id', $bookIdsInvolved)->get(); if (count($books) !== count($bookIdsInvolved)) { - throw new SortOperationException("Could not find all books requested in sort operation"); + throw new SortOperationException('Could not find all books requested in sort operation'); } return $books; diff --git a/app/Entities/Tools/ExportFormatter.php b/app/Entities/Tools/ExportFormatter.php index 9317b0431..c299f9c71 100644 --- a/app/Entities/Tools/ExportFormatter.php +++ b/app/Entities/Tools/ExportFormatter.php @@ -1,4 +1,6 @@ -html = (new PageContent($page))->render(); $pageHtml = view('pages.export', [ - 'page' => $page, + 'page' => $page, 'format' => 'html', ])->render(); + return $this->containHtml($pageHtml); } /** * Convert a chapter to a self-contained HTML file. + * * @throws Throwable */ public function chapterToContainedHtml(Chapter $chapter) @@ -50,43 +54,49 @@ class ExportFormatter }); $html = view('chapters.export', [ 'chapter' => $chapter, - 'pages' => $pages, - 'format' => 'html', + 'pages' => $pages, + 'format' => 'html', ])->render(); + return $this->containHtml($html); } /** * Convert a book to a self-contained HTML file. + * * @throws Throwable */ public function bookToContainedHtml(Book $book) { $bookTree = (new BookContents($book))->getTree(false, true); $html = view('books.export', [ - 'book' => $book, + 'book' => $book, 'bookChildren' => $bookTree, - 'format' => 'html', + 'format' => 'html', ])->render(); + return $this->containHtml($html); } /** * Convert a page to a PDF file. + * * @throws Throwable */ public function pageToPdf(Page $page) { $page->html = (new PageContent($page))->render(); $html = view('pages.export', [ - 'page' => $page, + 'page' => $page, 'format' => 'pdf', ])->render(); + return $this->htmlToPdf($html); } /** * Convert a chapter to a PDF file. + * * @throws Throwable */ public function chapterToPdf(Chapter $chapter) @@ -98,8 +108,8 @@ class ExportFormatter $html = view('chapters.export', [ 'chapter' => $chapter, - 'pages' => $pages, - 'format' => 'pdf', + 'pages' => $pages, + 'format' => 'pdf', ])->render(); return $this->htmlToPdf($html); @@ -107,21 +117,24 @@ class ExportFormatter /** * Convert a book to a PDF file. + * * @throws Throwable */ public function bookToPdf(Book $book) { $bookTree = (new BookContents($book))->getTree(false, true); $html = view('books.export', [ - 'book' => $book, + 'book' => $book, 'bookChildren' => $bookTree, - 'format' => 'pdf', + 'format' => 'pdf', ])->render(); + return $this->htmlToPdf($html); } /** * Convert normal web-page HTML to a PDF. + * * @throws Exception */ protected function htmlToPdf(string $html): string @@ -134,11 +147,13 @@ class ExportFormatter } else { $pdf = DomPDF::loadHTML($containedHtml); } + return $pdf->output(); } /** * Bundle of the contents of a html file to be self-contained. + * * @throws Exception */ protected function containHtml(string $htmlContent): string @@ -195,6 +210,7 @@ class ExportFormatter $text = html_entity_decode($text); // Add title $text = $page->name . "\n\n" . $text; + return $text; } @@ -208,6 +224,7 @@ class ExportFormatter foreach ($chapter->getVisiblePages() as $page) { $text .= $this->pageToPlainText($page); } + return $text; } @@ -225,6 +242,7 @@ class ExportFormatter $text .= $this->pageToPlainText($bookChild); } } + return $text; } @@ -234,10 +252,10 @@ class ExportFormatter public function pageToMarkdown(Page $page): string { if ($page->markdown) { - return "# " . $page->name . "\n\n" . $page->markdown; + return '# ' . $page->name . "\n\n" . $page->markdown; } - return "# " . $page->name . "\n\n" . (new HtmlToMarkdown($page->html))->convert(); + return '# ' . $page->name . "\n\n" . (new HtmlToMarkdown($page->html))->convert(); } /** @@ -245,11 +263,12 @@ class ExportFormatter */ public function chapterToMarkdown(Chapter $chapter): string { - $text = "# " . $chapter->name . "\n\n"; + $text = '# ' . $chapter->name . "\n\n"; $text .= $chapter->description . "\n\n"; foreach ($chapter->pages as $page) { $text .= $this->pageToMarkdown($page) . "\n\n"; } + return $text; } @@ -259,7 +278,7 @@ class ExportFormatter public function bookToMarkdown(Book $book): string { $bookTree = (new BookContents($book))->getTree(false, true); - $text = "# " . $book->name . "\n\n"; + $text = '# ' . $book->name . "\n\n"; foreach ($bookTree as $bookChild) { if ($bookChild instanceof Chapter) { $text .= $this->chapterToMarkdown($bookChild); @@ -267,6 +286,7 @@ class ExportFormatter $text .= $this->pageToMarkdown($bookChild); } } + return $text; } } diff --git a/app/Entities/Tools/Markdown/CustomParagraphConverter.php b/app/Entities/Tools/Markdown/CustomParagraphConverter.php index 1af770261..bd493aa03 100644 --- a/app/Entities/Tools/Markdown/CustomParagraphConverter.php +++ b/app/Entities/Tools/Markdown/CustomParagraphConverter.php @@ -1,4 +1,6 @@ -addDelimiterProcessor(new StrikethroughDelimiterProcessor()); diff --git a/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php b/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php index 7de95c263..ca9f434af 100644 --- a/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php +++ b/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php @@ -1,4 +1,6 @@ -getConverterEnvironment()); $html = $this->prepareHtml($this->html); + return $converter->convert($html); } @@ -54,19 +57,19 @@ class HtmlToMarkdown protected function getConverterEnvironment(): Environment { $environment = new Environment([ - 'header_style' => 'atx', // Set to 'atx' to output H1 and H2 headers as # Header1 and ## Header2 - 'suppress_errors' => true, // Set to false to show warnings when loading malformed HTML - 'strip_tags' => false, // Set to true to strip tags that don't have markdown equivalents. N.B. Strips tags, not their content. Useful to clean MS Word HTML output. + 'header_style' => 'atx', // Set to 'atx' to output H1 and H2 headers as # Header1 and ## Header2 + 'suppress_errors' => true, // Set to false to show warnings when loading malformed HTML + 'strip_tags' => false, // Set to true to strip tags that don't have markdown equivalents. N.B. Strips tags, not their content. Useful to clean MS Word HTML output. 'strip_placeholder_links' => false, // Set to true to remove that doesn't have href. - 'bold_style' => '**', // DEPRECATED: Set to '__' if you prefer the underlined style - 'italic_style' => '*', // DEPRECATED: Set to '_' if you prefer the underlined style - 'remove_nodes' => '', // space-separated list of dom nodes that should be removed. example: 'meta style script' - 'hard_break' => false, // Set to true to turn
into `\n` instead of ` \n` - 'list_item_style' => '-', // Set the default character for each
  • in a