From 51c5cf4dbb89323e37a3db1411f61744a9ad8541 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 25 Mar 2020 21:58:37 -0700 Subject: [PATCH 01/58] Get basic modal view for editing/creating a new subuser working --- .../Api/Client/ClientController.php | 9 +- app/Models/Permission.php | 131 ++++++++--------- resources/scripts/api/getSystemPermissions.ts | 4 +- .../dashboard/AccountApiContainer.tsx | 2 +- .../components/elements/ContentBox.tsx | 5 +- .../components/elements/TitledGreyBox.tsx | 12 +- .../server/schedules/ScheduleTaskRow.tsx | 4 +- .../server/users/AddSubuserButton.tsx | 21 +++ .../server/users/EditSubuserModal.tsx | 139 ++++++++++++++++++ .../server/users/UsersContainer.tsx | 124 +++++----------- resources/scripts/routers/ServerRouter.tsx | 5 +- resources/scripts/state/permissions.ts | 14 +- resources/styles/components/modal.css | 9 +- 13 files changed, 293 insertions(+), 186 deletions(-) create mode 100644 resources/scripts/components/server/users/AddSubuserButton.tsx create mode 100644 resources/scripts/components/server/users/EditSubuserModal.tsx diff --git a/app/Http/Controllers/Api/Client/ClientController.php b/app/Http/Controllers/Api/Client/ClientController.php index f11d1ac15..b673ac5bf 100644 --- a/app/Http/Controllers/Api/Client/ClientController.php +++ b/app/Http/Controllers/Api/Client/ClientController.php @@ -3,7 +3,6 @@ namespace Pterodactyl\Http\Controllers\Api\Client; use Pterodactyl\Models\User; -use Illuminate\Support\Collection; use Pterodactyl\Models\Permission; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Transformers\Api\Client\ServerTransformer; @@ -72,16 +71,10 @@ class ClientController extends ClientApiController */ public function permissions() { - $permissions = Permission::permissions()->map(function ($values, $key) { - return Collection::make($values)->map(function ($permission) use ($key) { - return $key . '.' . $permission; - })->values()->toArray(); - })->flatten(); - return [ 'object' => 'system_permissions', 'attributes' => [ - 'permissions' => $permissions, + 'permissions' => Permission::permissions(), ], ]; } diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 11db34545..9a834ff76 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -98,105 +98,90 @@ class Permission extends Validable */ protected static $permissions = [ 'websocket' => [ - // Allows the user to connect to the server websocket, this will give them - // access to view the console output as well as realtime server stats (CPU - // and Memory usage). - '*', + 'description' => 'Allows the user to connect to the server websocket, giving them access to view console output and realtime server stats.', + 'keys' => [ + '*' => 'Gives user full read access to the websocket.', + ], ], 'control' => [ - // Allows the user to send data to the server console process. A user with this - // permission will not be able to stop the server directly by issuing the specified - // stop command for the Egg, however depending on plugins and server configuration - // they may still be able to control the server power state. - 'console', // power.send-command - - // Allows the user to start/stop/restart/kill the server process. - 'start', // power.power-start - 'stop', // power.power-stop - 'restart', // power.power-restart - 'kill', // power.power-kill + 'description' => 'Permissions that control a user\'s ability to control the power state of a server, or send commands.', + 'keys' => [ + 'console' => 'Allows a user to send commands to the server instance via the console.', + 'start' => 'Allows a user to start the server if it is stopped.', + 'stop' => 'Allows a user to stop a server if it is running.', + 'restart' => 'Allows a user to perform a server restart. This allows them to start the server if it is offline, but not put the server in a completely stopped state.', + 'kill' => 'Allows a user to terminate a server process.', + ], ], 'user' => [ - // Allows a user to create a new user assigned to the server. They will not be able - // to assign any permissions they do not already have on their account as well. - 'create', // subuser.create-subuser - 'read', // subuser.list-subusers, subuser.view-subuser - 'update', // subuser.edit-subuser - 'delete', // subuser.delete-subuser + 'description' => 'Permissions that allow a user to manage other subusers on a server. They will never be able to edit their own account, or assign permissions they do not have themselves.', + 'keys' => [ + 'create' => 'Allows a user to create new subusers for the server.', + 'read' => 'Allows the user to view subusers and their permissions for the server.', + 'update' => 'Allows a user to modify other subusers.', + 'delete' => 'Allows a user to delete a subuser from the server.', + ], ], 'file' => [ - // Allows a user to create additional files and folders either via the Panel, - // or via a direct upload. - 'create', // files.create-files, files.upload-files, files.copy-files, files.move-files - - // Allows a user to view the contents of a directory as well as read the contents - // of a given file. A user with this permission will be able to download files - // as well. - 'read', // files.list-files, files.download-files - - // Allows a user to update the contents of an existing file or directory. - 'update', // files.edit-files, files.save-files - - // Allows a user to delete a file or directory. - 'delete', // files.delete-files - - // Allows a user to archive the contents of a directory as well as decompress existing - // archives on the system. - 'archive', // files.compress-files, files.decompress-files - - // Allows the user to connect and manage server files using their account - // credentials and a SFTP client. - 'sftp', // files.access-sftp + 'description' => 'Permissions that control a user\'s ability to modify the filesystem for this server.', + 'keys' => [ + 'create' => 'Allows a user to create additional files and folders via the Panel or direct upload.', + 'read' => 'Allows a user to view the contents of a directory and read the contents of a file. Users with this permission can also download files.', + 'update' => 'Allows a user to update the contents of an existing file or directory.', + 'delete' => 'Allows a user to delete files or directories.', + 'archive' => 'Allows a user to archive the contents of a directory as well as decompress existing archives on the system.', + 'sftp' => 'Allows a user to connect to SFTP and manage server files using the other assigned file permissions.', + ], ], // Controls permissions for editing or viewing a server's allocations. 'allocation' => [ - 'read', // server.view-allocations - 'update', // server.edit-allocation + 'description' => 'Permissions that control a user\'s ability to modify the port allocations for this server.', + 'keys' => [ + 'read' => 'Allows a user to view the allocations assigned to this server.', + 'update' => 'Allows a user to modify the allocations assigned to this server.', + ], ], // Controls permissions for editing or viewing a server's startup parameters. 'startup' => [ - 'read', // server.view-startup - 'update', // server.edit-startup + 'description' => 'Permissions that control a user\'s ability to view this server\'s startup parameters.', + 'keys' => [ + 'read' => '', + 'update' => '', + ], ], 'database' => [ - // Allows a user to create a new database for a server. - 'create', // database.create-database - - // Allows a user to view the databases associated with the server. If they do not also - // have the view_password permission they will only be able to see the connection address - // and the name of the user. - 'read', // database.view-databases - - // Allows a user to rotate the password on a database instance. If the user does not - // alow have the view_password permission they will not be able to see the updated password - // anywhere, but it will still be rotated. - 'update', // database.reset-db-password - - // Allows a user to delete a database instance. - 'delete', // database.delete-database - - // Allows a user to view the password associated with a database instance for the - // server. Note that a user without this permission may still be able to access these - // credentials by viewing files or the console. - 'view_password', // database.reset-db-password + 'description' => 'Permissions that control a user\'s access to the database management for this server.', + 'keys' => [ + 'create' => 'Allows a user to create a new database for this server.', + 'read' => 'Allows a user to view the database associated with this server.', + 'update' => 'Allows a user to rotate the password on a database instance. If the user does not have the view_password permission they will not see the updated password.', + 'delete' => 'Allows a user to remove a database instance from this server.', + 'view_password' => 'Allows a user to view the password associated with a database instance for this server.', + ], ], 'schedule' => [ - 'create', // task.create-schedule - 'read', // task.view-schedule, task.list-schedules - 'update', // task.edit-schedule, task.queue-schedule, task.toggle-schedule - 'delete', // task.delete-schedule + 'description' => 'Permissions that control a user\'s access to the schedule management for this server.', + 'keys' => [ + 'create' => '', // task.create-schedule + 'read' => '', // task.view-schedule, task.list-schedules + 'update' => '', // task.edit-schedule, task.queue-schedule, task.toggle-schedule + 'delete' => '', // task.delete-schedule + ], ], 'settings' => [ - 'rename', - 'reinstall', + 'description' => 'Permissions that control a user\'s access to the settings for this server.', + 'keys' => [ + 'rename' => '', + 'reinstall' => '', + ], ], ]; diff --git a/resources/scripts/api/getSystemPermissions.ts b/resources/scripts/api/getSystemPermissions.ts index 47016b3c9..69fb56797 100644 --- a/resources/scripts/api/getSystemPermissions.ts +++ b/resources/scripts/api/getSystemPermissions.ts @@ -1,7 +1,7 @@ -import { SubuserPermission } from '@/state/server/subusers'; +import { PanelPermissions } from '@/state/permissions'; import http from '@/api/http'; -export default (): Promise => { +export default (): Promise => { return new Promise((resolve, reject) => { http.get(`/api/client/permissions`) .then(({ data }) => resolve(data.attributes.permissions)) diff --git a/resources/scripts/components/dashboard/AccountApiContainer.tsx b/resources/scripts/components/dashboard/AccountApiContainer.tsx index 7fbe21626..b65033285 100644 --- a/resources/scripts/components/dashboard/AccountApiContainer.tsx +++ b/resources/scripts/components/dashboard/AccountApiContainer.tsx @@ -95,7 +95,7 @@ export default () => { > diff --git a/resources/scripts/components/elements/ContentBox.tsx b/resources/scripts/components/elements/ContentBox.tsx index b03503e63..94c9ad629 100644 --- a/resources/scripts/components/elements/ContentBox.tsx +++ b/resources/scripts/components/elements/ContentBox.tsx @@ -1,14 +1,16 @@ import * as React from 'react'; import classNames from 'classnames'; import FlashMessageRender from '@/components/FlashMessageRender'; +import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; type Props = Readonly, HTMLDivElement> & { title?: string; borderColor?: string; showFlashes?: string | boolean; + showLoadingOverlay?: boolean; }>; -const ContentBox = ({ title, borderColor, showFlashes, children, ...props }: Props) => ( +const ContentBox = ({ title, borderColor, showFlashes, showLoadingOverlay, children, ...props }: Props) => (
{title &&

{title}

} {showFlashes && @@ -20,6 +22,7 @@ const ContentBox = ({ title, borderColor, showFlashes, children, ...props }: Pro
+ {children}
diff --git a/resources/scripts/components/elements/TitledGreyBox.tsx b/resources/scripts/components/elements/TitledGreyBox.tsx index 6116feb2b..7e5bb1619 100644 --- a/resources/scripts/components/elements/TitledGreyBox.tsx +++ b/resources/scripts/components/elements/TitledGreyBox.tsx @@ -4,7 +4,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; interface Props { icon?: IconProp; - title: string; + title: string | React.ReactNode; className?: string; children: React.ReactNode; } @@ -12,9 +12,13 @@ interface Props { const TitledGreyBox = ({ icon, title, children, className }: Props) => (
-

- {icon && }{title} -

+ {typeof title === 'string' ? +

+ {icon && }{title} +

+ : + title + }
{children} diff --git a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx index 773308e2c..12e675ebc 100644 --- a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx +++ b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx @@ -78,7 +78,7 @@ export default ({ schedule, task, onTaskUpdated, onTaskRemoved }: Props) => { + + ); +}; diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx new file mode 100644 index 000000000..65ff2aaa2 --- /dev/null +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -0,0 +1,139 @@ +import React from 'react'; +import { Subuser } from '@/state/server/subusers'; +import { Formik, FormikHelpers, useFormikContext } from 'formik'; +import { array, object, string } from 'yup'; +import Modal, { RequiredModalProps } from '@/components/elements/Modal'; +import Field from '@/components/elements/Field'; +import { useStoreState } from 'easy-peasy'; +import { ApplicationStore } from '@/state'; +import TitledGreyBox from '@/components/elements/TitledGreyBox'; +import Checkbox from '@/components/elements/Checkbox'; +import styled from 'styled-components'; +import classNames from 'classnames'; + +type Props = { + subuser?: Subuser; +} & RequiredModalProps; + +interface Values { + email: string; + permissions: string[]; +} + +const PermissionLabel = styled.label` + ${tw`flex items-center border border-transparent rounded p-2 cursor-pointer`}; + text-transform: none; + + &:hover { + ${tw`border-neutral-500 bg-neutral-800`}; + } +`; + +const EditSubuserModal = ({ subuser, ...props }: Props) => { + const { values, isSubmitting, setFieldValue } = useFormikContext(); + const permissions = useStoreState((state: ApplicationStore) => state.permissions.data); + + return ( + +

{subuser ? 'Edit subuser' : 'Create new subuser'}

+
+ +
+
+ {Object.keys(permissions).filter(key => key !== 'websocket').map((key, index) => ( + +

{key}

+ { + if (e.currentTarget.checked) { + setFieldValue('permissions', [ + ...values.permissions, + ...Object.keys(permissions[key].keys) + .map(pkey => `${key}.${pkey}`) + .filter(permission => values.permissions.indexOf(permission) === -1), + ]); + } else { + setFieldValue('permissions', [ + ...values.permissions.filter( + permission => Object.keys(permissions[key].keys) + .map(pkey => `${key}.${pkey}`) + .indexOf(permission) < 0, + ), + ]); + } + }} + /> +
+ } + className={index !== 0 ? 'mt-4' : undefined} + > +

+ {permissions[key].description} +

+ {Object.keys(permissions[key].keys).map((pkey, index) => ( + +
+ +
+
+ + {pkey} + + {permissions[key].keys[pkey].length > 0 && +

+ {permissions[key].keys[pkey]} +

+ } +
+
+ ))} + + ))} +
+
+ +
+ + ); +}; + +export default (props: Props) => { + const submit = (values: Values, helpers: FormikHelpers) => { + }; + + return ( + + + + ); +}; diff --git a/resources/scripts/components/server/users/UsersContainer.tsx b/resources/scripts/components/server/users/UsersContainer.tsx index 01e42f741..ff685ec0b 100644 --- a/resources/scripts/components/server/users/UsersContainer.tsx +++ b/resources/scripts/components/server/users/UsersContainer.tsx @@ -1,19 +1,12 @@ import React, { useEffect, useState } from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faUserPlus } from '@fortawesome/free-solid-svg-icons/faUserPlus'; import { ServerContext } from '@/state/server'; -import Spinner from '@/components/elements/Spinner'; -import { Subuser } from '@/state/server/subusers'; -import { CSSTransition } from 'react-transition-group'; -import classNames from 'classnames'; -import PermissionEditor from '@/components/server/users/PermissionEditor'; import { Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; -import { faArrowLeft } from '@fortawesome/free-solid-svg-icons/faArrowLeft'; +import Spinner from '@/components/elements/Spinner'; +import AddSubuserButton from '@/components/server/users/AddSubuserButton'; export default () => { const [ loading, setLoading ] = useState(true); - const [ editSubuser, setEditSubuser ] = useState(null); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const subusers = ServerContext.useStoreState(state => state.subusers.data); @@ -23,10 +16,8 @@ export default () => { const getPermissions = useStoreActions((actions: Actions) => actions.permissions.getPermissions); useEffect(() => { - if (!permissions.length) { - getPermissions().catch(error => console.error(error)); - } - }, [ permissions, getPermissions ]); + getPermissions().catch(error => console.error(error)); + }, []); useEffect(() => { getSubusers(uuid) @@ -37,84 +28,47 @@ export default () => { }, [ uuid, getSubusers ]); useEffect(() => { - if (subusers.length > 0) { - setLoading(false); - } + setLoading(!subusers); }, [ subusers ]); + if (loading || !Object.keys(permissions).length) { + return ; + } + return ( -
-
-

Subusers

-
- {(loading || !permissions.length) ? -
- +
+ {!subusers.length ? +

+ It looks like you don't have any subusers. +

+ : + subusers.map(subuser => ( +
+ +
+

{subuser.email}

+
+
+ +
- : - !subusers.length ? -

It looks like you don't have any subusers.

- : - subusers.map(subuser => ( -
- -
-

{subuser.email}

-
-
- - -
-
- )) - } -
-
- -
-
- {editSubuser && - -
-

- setEditSubuser(null)}> - - - Edit {editSubuser.email} -

-
- - -
-
-
+ )) } +
+ +
); }; diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 2998c6ee3..8735c1401 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -14,6 +14,7 @@ import FileEditContainer from '@/components/server/files/FileEditContainer'; import SettingsContainer from '@/components/server/settings/SettingsContainer'; import ScheduleContainer from '@/components/server/schedules/ScheduleContainer'; import ScheduleEditContainer from '@/components/server/schedules/ScheduleEditContainer'; +import UsersContainer from '@/components/server/users/UsersContainer'; const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { const server = ServerContext.useStoreState(state => state.server.data); @@ -35,8 +36,8 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) Console File Manager Databases - {/* User Management */} Schedules + Users Settings
@@ -62,9 +63,9 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) exact /> - {/* */} + diff --git a/resources/scripts/state/permissions.ts b/resources/scripts/state/permissions.ts index e275e38f5..07a428002 100644 --- a/resources/scripts/state/permissions.ts +++ b/resources/scripts/state/permissions.ts @@ -1,15 +1,21 @@ -import { SubuserPermission } from '@/state/server/subusers'; import { action, Action, thunk, Thunk } from 'easy-peasy'; import getSystemPermissions from '@/api/getSystemPermissions'; +export interface PanelPermissions { + [key: string]: { + description: string; + keys: { [k: string]: string }; + }; +} + export interface GloablPermissionsStore { - data: SubuserPermission[]; - setPermissions: Action; + data: PanelPermissions; + setPermissions: Action; getPermissions: Thunk>; } const permissions: GloablPermissionsStore = { - data: [], + data: {}, setPermissions: action((state, payload) => { state.data = payload; diff --git a/resources/styles/components/modal.css b/resources/styles/components/modal.css index 7c3a6ae94..03e41eb4f 100644 --- a/resources/styles/components/modal.css +++ b/resources/styles/components/modal.css @@ -6,9 +6,9 @@ & > .modal-container { @apply .relative .w-full .max-w-md .m-auto .flex-col .flex; - &.top { + /*&.top { margin-top: 10%; - } + }*/ & > .modal-close-icon { @apply .absolute .pin-r .p-2 .text-white .cursor-pointer .opacity-50; @@ -22,7 +22,8 @@ } & > .modal-content { - @apply .bg-neutral-800 .rounded .shadow-md; + @apply .bg-neutral-800 .rounded .shadow-md .overflow-y-scroll; + max-height: calc(100vh - 16rem); transition: all 250ms ease; } @@ -39,7 +40,7 @@ } & > .modal-container.full-screen { - @apply .w-3/4 .mt-32; + @apply .w-3/4; height: calc(100vh - 16rem); max-width: none; } From a6f46d36ba2617a06b16ee9db57441cf7dc96117 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 27 Mar 2020 14:23:13 -0700 Subject: [PATCH 02/58] Implement basic code for creating/updating a subuser --- .../Api/Client/Servers/SubuserController.php | 89 ++++++++++++++- .../Subusers/AbstractSubuserRequest.php | 63 +++++++++++ .../Servers/Subusers/DeleteSubuserRequest.php | 16 +++ .../Servers/Subusers/StoreSubuserRequest.php | 29 +++++ .../Servers/Subusers/UpdateSubuserRequest.php | 27 +++++ app/Models/Validable.php | 2 +- .../Eloquent/SubuserRepository.php | 27 +++-- .../Subusers/PermissionCreationService.php | 63 ----------- .../Subusers/SubuserCreationService.php | 105 +++++++---------- .../Subusers/SubuserDeletionService.php | 35 ------ .../Subusers/SubuserUpdateService.php | 107 ------------------ .../Api/Client/SubuserTransformer.php | 26 +---- .../api/server/users/createOrUpdateSubuser.ts | 18 +++ .../api/server/users/getServerSubusers.ts | 6 +- .../server/users/EditSubuserModal.tsx | 50 ++++++-- resources/scripts/state/server/subusers.ts | 2 +- routes/api-client.php | 4 + 17 files changed, 347 insertions(+), 322 deletions(-) create mode 100644 app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php create mode 100644 app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php create mode 100644 app/Http/Requests/Api/Client/Servers/Subusers/StoreSubuserRequest.php create mode 100644 app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php delete mode 100644 app/Services/Subusers/PermissionCreationService.php delete mode 100644 app/Services/Subusers/SubuserDeletionService.php delete mode 100644 app/Services/Subusers/SubuserUpdateService.php create mode 100644 resources/scripts/api/server/users/createOrUpdateSubuser.ts diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php index 3b3746b04..ed4929c07 100644 --- a/app/Http/Controllers/Api/Client/Servers/SubuserController.php +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -2,11 +2,17 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; +use Illuminate\Http\Request; use Pterodactyl\Models\Server; +use Illuminate\Http\JsonResponse; use Pterodactyl\Repositories\Eloquent\SubuserRepository; +use Pterodactyl\Services\Subusers\SubuserCreationService; use Pterodactyl\Transformers\Api\Client\SubuserTransformer; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest; class SubuserController extends ClientApiController { @@ -15,16 +21,25 @@ class SubuserController extends ClientApiController */ private $repository; + /** + * @var \Pterodactyl\Services\Subusers\SubuserCreationService + */ + private $creationService; + /** * SubuserController constructor. * * @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository + * @param \Pterodactyl\Services\Subusers\SubuserCreationService $creationService */ - public function __construct(SubuserRepository $repository) - { + public function __construct( + SubuserRepository $repository, + SubuserCreationService $creationService + ) { parent::__construct(); $this->repository = $repository; + $this->creationService = $creationService; } /** @@ -36,10 +51,76 @@ class SubuserController extends ClientApiController */ public function index(GetSubuserRequest $request, Server $server) { - $server->subusers->load('user'); - return $this->fractal->collection($server->subusers) ->transformWith($this->getTransformer(SubuserTransformer::class)) ->toArray(); } + + /** + * Create a new subuser for the given server. + * + * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest $request + * @param \Pterodactyl\Models\Server $server + * @return array + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException + * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException + * @throws \Throwable + */ + public function store(StoreSubuserRequest $request, Server $server) + { + $response = $this->creationService->handle( + $server, $request->input('email'), $this->getDefaultPermissions($request) + ); + + return $this->fractal->item($response) + ->transformWith($this->getTransformer(SubuserTransformer::class)) + ->toArray(); + } + + /** + * Update a given subuser in the system for the server. + * + * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest $request + * + * @return array + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UpdateSubuserRequest $request) + { + $subuser = $request->subuser(); + $this->repository->update($subuser->id, [ + 'permissions' => $this->getDefaultPermissions($request), + ]); + + return $this->fractal->item($subuser->refresh()) + ->transformWith($this->getTransformer(SubuserTransformer::class)) + ->toArray(); + } + + /** + * Removes a subusers from a server's assignment. + * + * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function delete(DeleteSubuserRequest $request) + { + $this->repository->delete($request->subuser()->id); + + return JsonResponse::create([], JsonResponse::HTTP_NO_CONTENT); + } + + /** + * Returns the default permissions for all subusers to ensure none are ever removed wrongly. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + protected function getDefaultPermissions(Request $request): array + { + return array_merge($request->input('permissions') ?? [], ['websocket.*']); + } } diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php new file mode 100644 index 000000000..09d14545c --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php @@ -0,0 +1,63 @@ +subuser()->user_id === $this->user()->id) { + return false; + } + + return true; + } + + /** + * Return the subuser model for the given request which can then be validated. If + * required request parameters are missing a 404 error will be returned, otherwise + * a model exception will be returned if the model is not found. + * + * @return \Pterodactyl\Models\Subuser + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + */ + public function subuser() + { + /** @var \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository */ + $repository = $this->container->make(SubuserRepository::class); + + $parameters = $this->route()->parameters(); + if ( + ! isset($parameters['server'], $parameters['server']) + || ! is_string($parameters['subuser']) + || ! $parameters['server'] instanceof Server + ) { + throw new NotFoundHttpException; + } + + return $this->model ?: $this->model = $repository->getUserForServer( + $this->route()->parameter('subuser'), $this->route()->parameter('server')->id + ); + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php new file mode 100644 index 000000000..e9d05e2c0 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php @@ -0,0 +1,16 @@ + 'required|email', + 'permissions' => 'required|array', + 'permissions.*' => 'string', + ]; + } +} diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php new file mode 100644 index 000000000..41cdeb5ac --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php @@ -0,0 +1,27 @@ + 'required|array', + 'permissions.*' => 'string', + ]; + } +} diff --git a/app/Models/Validable.php b/app/Models/Validable.php index e7c5c5665..43545c3a8 100644 --- a/app/Models/Validable.php +++ b/app/Models/Validable.php @@ -140,7 +140,7 @@ abstract class Validable extends Model } return $this->getValidator()->setData( - $this->getAttributes() + $this->toArray() )->passes(); } } diff --git a/app/Repositories/Eloquent/SubuserRepository.php b/app/Repositories/Eloquent/SubuserRepository.php index 4636f7b37..e00d825e7 100644 --- a/app/Repositories/Eloquent/SubuserRepository.php +++ b/app/Repositories/Eloquent/SubuserRepository.php @@ -3,7 +3,6 @@ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Subuser; -use Illuminate\Support\Collection; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; @@ -20,19 +19,27 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI } /** - * Returns the subusers for the given server instance with the associated user - * and permission relationships pre-loaded. + * Returns a subuser model for the given user and server combination. If no record + * exists an exception will be thrown. * * @param int $server - * @return \Illuminate\Support\Collection + * @param string $uuid + * @return \Pterodactyl\Models\Subuser + * + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException */ - public function getSubusersForServer(int $server): Collection + public function getUserForServer(int $server, string $uuid): Subuser { - return $this->getBuilder() - ->with('user', 'permissions') - ->where('server_id', $server) - ->get() - ->toBase(); + /** @var \Pterodactyl\Models\Subuser $model */ + $model = $this->getBuilder() + ->with('server', 'user') + ->select('subusers.*') + ->join('users', 'users.id', '=', 'subusers.user_id') + ->where('subusers.server_id', $server) + ->where('users.uuid', $uuid) + ->firstOrFail(); + + return $model; } /** diff --git a/app/Services/Subusers/PermissionCreationService.php b/app/Services/Subusers/PermissionCreationService.php deleted file mode 100644 index 328485ee1..000000000 --- a/app/Services/Subusers/PermissionCreationService.php +++ /dev/null @@ -1,63 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Subusers; - -use Webmozart\Assert\Assert; -use Pterodactyl\Models\Permission; -use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; - -class PermissionCreationService -{ - /** - * @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface - */ - protected $repository; - - /** - * PermissionCreationService constructor. - * - * @param \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface $repository - */ - public function __construct(PermissionRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * Assign permissions to a given subuser. - * - * @param int $subuser - * @param array $permissions - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function handle($subuser, array $permissions) - { - Assert::integerish($subuser, 'First argument passed to handle must be an integer, received %s.'); - - $permissionMappings = Permission::getPermissions(true); - $insertPermissions = []; - - foreach ($permissions as $permission) { - if (array_key_exists($permission, $permissionMappings)) { - Assert::stringNotEmpty($permission, 'Permission argument provided must be a non-empty string, received %s.'); - - array_push($insertPermissions, [ - 'subuser_id' => $subuser, - 'permission' => $permission, - ]); - } - } - - if (! empty($insertPermissions)) { - $this->repository->withoutFreshModel()->insert($insertPermissions); - } - } -} diff --git a/app/Services/Subusers/SubuserCreationService.php b/app/Services/Subusers/SubuserCreationService.php index 9c6b7c42e..ea8b5c2be 100644 --- a/app/Services/Subusers/SubuserCreationService.php +++ b/app/Services/Subusers/SubuserCreationService.php @@ -10,13 +10,12 @@ namespace Pterodactyl\Services\Subusers; use Pterodactyl\Models\Server; +use Pterodactyl\Models\Subuser; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Services\Users\UserCreationService; +use Pterodactyl\Repositories\Eloquent\SubuserRepository; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; use Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException; use Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException; @@ -25,113 +24,87 @@ class SubuserCreationService /** * @var \Illuminate\Database\ConnectionInterface */ - protected $connection; + private $connection; /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService + * @var \Pterodactyl\Repositories\Eloquent\SubuserRepository */ - protected $keyCreationService; - - /** - * @var \Pterodactyl\Services\Subusers\PermissionCreationService - */ - protected $permissionService; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface - */ - protected $subuserRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; + private $subuserRepository; /** * @var \Pterodactyl\Services\Users\UserCreationService */ - protected $userCreationService; + private $userCreationService; /** * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface */ - protected $userRepository; + private $userRepository; /** * SubuserCreationService constructor. * * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService - * @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository + * @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $subuserRepository * @param \Pterodactyl\Services\Users\UserCreationService $userCreationService * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository */ public function __construct( ConnectionInterface $connection, - DaemonKeyCreationService $keyCreationService, - PermissionCreationService $permissionService, - ServerRepositoryInterface $serverRepository, - SubuserRepositoryInterface $subuserRepository, + SubuserRepository $subuserRepository, UserCreationService $userCreationService, UserRepositoryInterface $userRepository ) { $this->connection = $connection; - $this->keyCreationService = $keyCreationService; - $this->permissionService = $permissionService; - $this->serverRepository = $serverRepository; $this->subuserRepository = $subuserRepository; $this->userRepository = $userRepository; $this->userCreationService = $userCreationService; } /** - * @param int|\Pterodactyl\Models\Server $server + * Creates a new user on the system and assigns them access to the provided server. + * If the email address already belongs to a user on the system a new user will not + * be created. + * + * @param \Pterodactyl\Models\Server $server * @param string $email * @param array $permissions * @return \Pterodactyl\Models\Subuser * - * @throws \Exception * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException + * @throws \Throwable */ - public function handle($server, $email, array $permissions) + public function handle(Server $server, string $email, array $permissions): Subuser { - if (! $server instanceof Server) { - $server = $this->serverRepository->find($server); - } + return $this->connection->transaction(function () use ($server, $email, $permissions) { + try { + $user = $this->userRepository->findFirstWhere([['email', '=', $email]]); - $this->connection->beginTransaction(); - try { - $user = $this->userRepository->findFirstWhere([['email', '=', $email]]); + if ($server->owner_id === $user->id) { + throw new UserIsServerOwnerException(trans('exceptions.subusers.user_is_owner')); + } - if ($server->owner_id === $user->id) { - throw new UserIsServerOwnerException(trans('exceptions.subusers.user_is_owner')); + $subuserCount = $this->subuserRepository->findCountWhere([['user_id', '=', $user->id], ['server_id', '=', $server->id]]); + if ($subuserCount !== 0) { + throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists')); + } + } catch (RecordNotFoundException $exception) { + $user = $this->userCreationService->handle([ + 'email' => $email, + 'username' => preg_replace('/([^\w\.-]+)/', '', strtok($email, '@')) . str_random(3), + 'name_first' => 'Server', + 'name_last' => 'Subuser', + 'root_admin' => false, + ]); } - $subuserCount = $this->subuserRepository->findCountWhere([['user_id', '=', $user->id], ['server_id', '=', $server->id]]); - if ($subuserCount !== 0) { - throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists')); - } - } catch (RecordNotFoundException $exception) { - $username = preg_replace('/([^\w\.-]+)/', '', strtok($email, '@')); - $user = $this->userCreationService->handle([ - 'email' => $email, - 'username' => $username . str_random(3), - 'name_first' => 'Server', - 'name_last' => 'Subuser', - 'root_admin' => false, + return $this->subuserRepository->create([ + 'user_id' => $user->id, + 'server_id' => $server->id, + 'permissions' => $permissions, ]); - } - - $subuser = $this->subuserRepository->create(['user_id' => $user->id, 'server_id' => $server->id]); - $this->keyCreationService->handle($server->id, $user->id); - $this->permissionService->handle($subuser->id, $permissions); - $this->connection->commit(); - - return $subuser; + }); } } diff --git a/app/Services/Subusers/SubuserDeletionService.php b/app/Services/Subusers/SubuserDeletionService.php deleted file mode 100644 index 6bc35ae3b..000000000 --- a/app/Services/Subusers/SubuserDeletionService.php +++ /dev/null @@ -1,35 +0,0 @@ -repository = $repository; - } - - /** - * Delete a subuser and their associated permissions from the Panel and Daemon. - * - * @param \Pterodactyl\Models\Subuser $subuser - */ - public function handle(Subuser $subuser) - { - $this->repository->delete($subuser->id); - } -} diff --git a/app/Services/Subusers/SubuserUpdateService.php b/app/Services/Subusers/SubuserUpdateService.php deleted file mode 100644 index 64d7f0b38..000000000 --- a/app/Services/Subusers/SubuserUpdateService.php +++ /dev/null @@ -1,107 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Subusers; - -use Pterodactyl\Models\Subuser; -use GuzzleHttp\Exception\RequestException; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; -use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; - -class SubuserUpdateService -{ - /** - * @var \Illuminate\Database\ConnectionInterface - */ - private $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - private $daemonRepository; - - /** - * @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService - */ - private $keyProviderService; - - /** - * @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface - */ - private $permissionRepository; - - /** - * @var \Pterodactyl\Services\Subusers\PermissionCreationService - */ - private $permissionService; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface - */ - private $repository; - - /** - * SubuserUpdateService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository - * @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService - * @param \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface $permissionRepository - * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository - */ - public function __construct( - ConnectionInterface $connection, - DaemonKeyProviderService $keyProviderService, - DaemonServerRepositoryInterface $daemonRepository, - PermissionCreationService $permissionService, - PermissionRepositoryInterface $permissionRepository, - SubuserRepositoryInterface $repository - ) { - $this->connection = $connection; - $this->daemonRepository = $daemonRepository; - $this->keyProviderService = $keyProviderService; - $this->permissionRepository = $permissionRepository; - $this->permissionService = $permissionService; - $this->repository = $repository; - } - - /** - * Update permissions for a given subuser. - * - * @param \Pterodactyl\Models\Subuser $subuser - * @param array $permissions - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle(Subuser $subuser, array $permissions) - { - $subuser = $this->repository->loadServerAndUserRelations($subuser); - - $this->connection->beginTransaction(); - $this->permissionRepository->deleteWhere([['subuser_id', '=', $subuser->id]]); - $this->permissionService->handle($subuser->id, $permissions); - - try { - $token = $this->keyProviderService->handle($subuser->getRelation('server'), $subuser->getRelation('user'), false); - $this->daemonRepository->setServer($subuser->getRelation('server'))->revokeAccessKey($token); - } catch (RequestException $exception) { - $this->connection->rollBack(); - throw new DaemonConnectionException($exception); - } - - $this->connection->commit(); - } -} diff --git a/app/Transformers/Api/Client/SubuserTransformer.php b/app/Transformers/Api/Client/SubuserTransformer.php index e0b165553..d2e7ce0ff 100644 --- a/app/Transformers/Api/Client/SubuserTransformer.php +++ b/app/Transformers/Api/Client/SubuserTransformer.php @@ -2,16 +2,10 @@ namespace Pterodactyl\Transformers\Api\Client; -use Pterodactyl\Models\User; use Pterodactyl\Models\Subuser; class SubuserTransformer extends BaseClientTransformer { - /** - * @var array - */ - protected $defaultIncludes = ['user']; - /** * Return the resource name for the JSONAPI output. * @@ -27,23 +21,13 @@ class SubuserTransformer extends BaseClientTransformer * * @param \Pterodactyl\Models\Subuser $model * @return array|void + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ public function transform(Subuser $model) { - return [ - 'permissions' => $model->permissions->pluck('permission'), - ]; - } - - /** - * Include the permissions associated with this subuser. - * - * @param \Pterodactyl\Models\Subuser $model - * @return \League\Fractal\Resource\Item - * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException - */ - public function includeUser(Subuser $model) - { - return $this->item($model->user, $this->makeTransformer(UserTransformer::class), User::RESOURCE_NAME); + return array_merge( + $this->makeTransformer(UserTransformer::class)->transform($model->user), + ['permissions' => $model->permissions] + ); } } diff --git a/resources/scripts/api/server/users/createOrUpdateSubuser.ts b/resources/scripts/api/server/users/createOrUpdateSubuser.ts new file mode 100644 index 000000000..fbcf78fe5 --- /dev/null +++ b/resources/scripts/api/server/users/createOrUpdateSubuser.ts @@ -0,0 +1,18 @@ +import http from '@/api/http'; +import { rawDataToServerSubuser } from '@/api/server/users/getServerSubusers'; +import { Subuser } from '@/state/server/subusers'; + +interface Params { + email: string; + permissions: string[]; +} + +export default (uuid: string, params: Params, subuser?: Subuser): Promise => { + return new Promise((resolve, reject) => { + http.post(`/api/client/servers/${uuid}/users${subuser ? `/${subuser.uuid}` : ''}`, { + ...params, + }) + .then(data => resolve(rawDataToServerSubuser(data.data))) + .catch(reject); + }); +} diff --git a/resources/scripts/api/server/users/getServerSubusers.ts b/resources/scripts/api/server/users/getServerSubusers.ts index 0d290549d..177bde815 100644 --- a/resources/scripts/api/server/users/getServerSubusers.ts +++ b/resources/scripts/api/server/users/getServerSubusers.ts @@ -8,13 +8,13 @@ export const rawDataToServerSubuser = (data: FractalResponseData): Subuser => ({ image: data.attributes.image, twoFactorEnabled: data.attributes['2fa_enabled'], createdAt: new Date(data.attributes.created_at), - permissions: data.attributes.relationships!.permissions.attributes.permissions, - can: permission => data.attributes.relationships!.permissions.attributes.permissions.indexOf(permission) >= 0, + permissions: data.attributes.permissions || [], + can: permission => (data.attributes.permissions || []).indexOf(permission) >= 0, }); export default (uuid: string): Promise => { return new Promise((resolve, reject) => { - http.get(`/api/client/servers/${uuid}/users`, { params: { include: [ 'permissions' ] } }) + http.get(`/api/client/servers/${uuid}/users`) .then(({ data }) => resolve((data.data || []).map(rawDataToServerSubuser))) .catch(reject); }); diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx index 65ff2aaa2..281e52a58 100644 --- a/resources/scripts/components/server/users/EditSubuserModal.tsx +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -1,15 +1,19 @@ -import React from 'react'; +import React, { forwardRef, MutableRefObject, useRef } from 'react'; import { Subuser } from '@/state/server/subusers'; -import { Formik, FormikHelpers, useFormikContext } from 'formik'; +import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; import { array, object, string } from 'yup'; import Modal, { RequiredModalProps } from '@/components/elements/Modal'; import Field from '@/components/elements/Field'; -import { useStoreState } from 'easy-peasy'; +import { Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import Checkbox from '@/components/elements/Checkbox'; import styled from 'styled-components'; import classNames from 'classnames'; +import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser'; +import { ServerContext } from '@/state/server'; +import { httpErrorToHuman } from '@/api/http'; +import FlashMessageRender from '@/components/FlashMessageRender'; type Props = { subuser?: Subuser; @@ -29,13 +33,14 @@ const PermissionLabel = styled.label` } `; -const EditSubuserModal = ({ subuser, ...props }: Props) => { +const EditSubuserModal = forwardRef(({ subuser, ...props }, ref) => { const { values, isSubmitting, setFieldValue } = useFormikContext(); const permissions = useStoreState((state: ApplicationStore) => state.permissions.data); return ( -

{subuser ? 'Edit subuser' : 'Create new subuser'}

+

{subuser ? 'Edit subuser' : 'Create new subuser'}

+
{
); -}; +}); -export default (props: Props) => { - const submit = (values: Values, helpers: FormikHelpers) => { +export default ({ subuser, ...props }: Props) => { + const ref = useRef(null); + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser); + + const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + + const submit = (values: Values, { setSubmitting }: FormikHelpers) => { + clearFlashes('user:edit'); + createOrUpdateSubuser(uuid, values, subuser) + .then(subuser => { + appendSubuser(subuser); + props.onDismissed(); + }) + .catch(error => { + console.error(error); + setSubmitting(false); + addError({ key: 'user:edit', message: httpErrorToHuman(error) }); + + if (ref.current) { + ref.current.scrollIntoView(); + } + }); }; return ( - +
+ +
); }; diff --git a/resources/scripts/state/server/subusers.ts b/resources/scripts/state/server/subusers.ts index 5d15149b7..5cba6f3a5 100644 --- a/resources/scripts/state/server/subusers.ts +++ b/resources/scripts/state/server/subusers.ts @@ -39,7 +39,7 @@ const subusers: ServerSubuserStore = { }), appendSubuser: action((state, payload) => { - state.data = [ ...state.data, payload ]; + state.data = [ ...state.data.filter(user => user.uuid !== payload.uuid), payload ]; }), getSubusers: thunk(async (actions, payload) => { diff --git a/routes/api-client.php b/routes/api-client.php index 7aa8e6252..f623d5ec2 100644 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -81,6 +81,10 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ Route::group(['prefix' => '/users'], function () { Route::get('/', 'Servers\SubuserController@index'); + Route::post('/', 'Servers\SubuserController@store'); + Route::get('/{subuser}', 'Servers\SubuserController@view'); + Route::post('/{subuser}', 'Servers\SubuserController@update'); + Route::delete('/{subuser}', 'Servers\SubuserController@delete'); }); Route::group(['prefix' => '/settings'], function () { From 1270e51248358a99054a483b100e4e6a67ee809f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 27 Mar 2020 15:32:33 -0700 Subject: [PATCH 03/58] Add support for deleting a subuser from a server --- .../Api/Client/Servers/SubuserController.php | 8 ++- .../Subusers/AbstractSubuserRequest.php | 2 +- .../scripts/api/server/users/deleteSubuser.ts | 9 +++ .../dashboard/AccountApiContainer.tsx | 2 +- .../components/elements/ConfirmationModal.tsx | 18 +++--- .../scripts/components/elements/Modal.tsx | 28 +++++---- .../server/users/PermissionEditor.tsx | 46 -------------- .../server/users/RemoveSubuserButton.tsx | 60 +++++++++++++++++++ .../components/server/users/UserRow.tsx | 36 +++++++++++ .../server/users/UsersContainer.tsx | 45 +++++--------- resources/scripts/state/server/subusers.ts | 11 ++-- 11 files changed, 158 insertions(+), 107 deletions(-) create mode 100644 resources/scripts/api/server/users/deleteSubuser.ts delete mode 100644 resources/scripts/components/server/users/PermissionEditor.tsx create mode 100644 resources/scripts/components/server/users/RemoveSubuserButton.tsx create mode 100644 resources/scripts/components/server/users/UserRow.tsx diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php index ed4929c07..73a534341 100644 --- a/app/Http/Controllers/Api/Client/Servers/SubuserController.php +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -83,12 +83,13 @@ class SubuserController extends ClientApiController * Update a given subuser in the system for the server. * * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest $request - * + * @param \Pterodactyl\Models\Server $server * @return array + * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update(UpdateSubuserRequest $request) + public function update(UpdateSubuserRequest $request, Server $server): array { $subuser = $request->subuser(); $this->repository->update($subuser->id, [ @@ -104,9 +105,10 @@ class SubuserController extends ClientApiController * Removes a subusers from a server's assignment. * * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest $request + * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse */ - public function delete(DeleteSubuserRequest $request) + public function delete(DeleteSubuserRequest $request, Server $server) { $this->repository->delete($request->subuser()->id); diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php index 09d14545c..0782aad66 100644 --- a/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php @@ -57,7 +57,7 @@ abstract class AbstractSubuserRequest extends ClientApiRequest } return $this->model ?: $this->model = $repository->getUserForServer( - $this->route()->parameter('subuser'), $this->route()->parameter('server')->id + $parameters['server']->id, $parameters['subuser'] ); } } diff --git a/resources/scripts/api/server/users/deleteSubuser.ts b/resources/scripts/api/server/users/deleteSubuser.ts new file mode 100644 index 000000000..dccd98e69 --- /dev/null +++ b/resources/scripts/api/server/users/deleteSubuser.ts @@ -0,0 +1,9 @@ +import http from '@/api/http'; + +export default (uuid: string, userId: string): Promise => { + return new Promise((resolve, reject) => { + http.delete(`/api/client/servers/${uuid}/users/${userId}`) + .then(() => resolve()) + .catch(reject); + }); +}; diff --git a/resources/scripts/components/dashboard/AccountApiContainer.tsx b/resources/scripts/components/dashboard/AccountApiContainer.tsx index b65033285..38d7f757c 100644 --- a/resources/scripts/components/dashboard/AccountApiContainer.tsx +++ b/resources/scripts/components/dashboard/AccountApiContainer.tsx @@ -62,7 +62,7 @@ export default () => { doDeletion(deleteIdentifier); setDeleteIdentifier(''); }} - onCanceled={() => setDeleteIdentifier('')} + onDismissed={() => setDeleteIdentifier('')} > Are you sure you wish to delete this API key? All requests using it will immediately be invalidated and will fail. diff --git a/resources/scripts/components/elements/ConfirmationModal.tsx b/resources/scripts/components/elements/ConfirmationModal.tsx index 0796a5866..127a43faa 100644 --- a/resources/scripts/components/elements/ConfirmationModal.tsx +++ b/resources/scripts/components/elements/ConfirmationModal.tsx @@ -1,25 +1,25 @@ import React from 'react'; -import Modal from '@/components/elements/Modal'; +import Modal, { RequiredModalProps } from '@/components/elements/Modal'; -interface Props { +type Props = { title: string; buttonText: string; children: string; - visible: boolean; onConfirmed: () => void; - onCanceled: () => void; -} + showSpinnerOverlay?: boolean; +} & RequiredModalProps; -const ConfirmationModal = ({ title, children, visible, buttonText, onConfirmed, onCanceled }: Props) => ( +const ConfirmationModal = ({ title, appear, children, visible, buttonText, onConfirmed, showSpinnerOverlay, onDismissed }: Props) => ( onCanceled()} + showSpinnerOverlay={showSpinnerOverlay} + onDismissed={() => onDismissed()} >

{title}

{children}

- -
-
- ); -}; diff --git a/resources/scripts/components/server/users/RemoveSubuserButton.tsx b/resources/scripts/components/server/users/RemoveSubuserButton.tsx new file mode 100644 index 000000000..28f58dc49 --- /dev/null +++ b/resources/scripts/components/server/users/RemoveSubuserButton.tsx @@ -0,0 +1,60 @@ +import React, { useState } from 'react'; +import ConfirmationModal from '@/components/elements/ConfirmationModal'; +import { ServerContext } from '@/state/server'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt'; +import { Subuser } from '@/state/server/subusers'; +import deleteSubuser from '@/api/server/users/deleteSubuser'; +import { Actions, useStoreActions } from 'easy-peasy'; +import { ApplicationStore } from '@/state'; +import { httpErrorToHuman } from '@/api/http'; + +export default ({ subuser }: { subuser: Subuser }) => { + const [ loading, setLoading ] = useState(false); + const [ showConfirmation, setShowConfirmation ] = useState(false); + + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const removeSubuser = ServerContext.useStoreActions(actions => actions.subusers.removeSubuser); + const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + + const doDeletion = () => { + setLoading(true); + clearFlashes('users'); + deleteSubuser(uuid, subuser.uuid) + .then(() => { + setLoading(false); + removeSubuser(subuser.uuid); + }) + .catch(error => { + console.error(error); + addError({ key: 'users', message: httpErrorToHuman(error) }); + setShowConfirmation(false); + }); + } + + return ( + <> + {showConfirmation && + doDeletion()} + onDismissed={() => setShowConfirmation(false)} + > + Are you sure you wish to remove this subuser? They will have all access to this server revoked + immediately. + + } + + + ); +}; diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx new file mode 100644 index 000000000..635dfc42e --- /dev/null +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import { Subuser } from '@/state/server/subusers'; +import { ServerContext } from '@/state/server'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPencilAlt } from '@fortawesome/free-solid-svg-icons/faPencilAlt'; +import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt'; +import ConfirmationModal from '@/components/elements/ConfirmationModal'; +import RemoveSubuserButton from '@/components/server/users/RemoveSubuserButton'; + +interface Props { + subuser: Subuser; +} + +export default ({ subuser }: Props) => { + const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser); + + return ( +
+
+ +
+
+

{subuser.email}

+
+ + +
+ ); +}; diff --git a/resources/scripts/components/server/users/UsersContainer.tsx b/resources/scripts/components/server/users/UsersContainer.tsx index ff685ec0b..082859c3d 100644 --- a/resources/scripts/components/server/users/UsersContainer.tsx +++ b/resources/scripts/components/server/users/UsersContainer.tsx @@ -4,32 +4,38 @@ import { Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import Spinner from '@/components/elements/Spinner'; import AddSubuserButton from '@/components/server/users/AddSubuserButton'; +import UserRow from '@/components/server/users/UserRow'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import getServerSubusers from '@/api/server/users/getServerSubusers'; +import { httpErrorToHuman } from '@/api/http'; export default () => { const [ loading, setLoading ] = useState(true); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const subusers = ServerContext.useStoreState(state => state.subusers.data); - const getSubusers = ServerContext.useStoreActions(actions => actions.subusers.getSubusers); + const setSubusers = ServerContext.useStoreActions(actions => actions.subusers.setSubusers); const permissions = useStoreState((state: ApplicationStore) => state.permissions.data); const getPermissions = useStoreActions((actions: Actions) => actions.permissions.getPermissions); + const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); useEffect(() => { getPermissions().catch(error => console.error(error)); }, []); useEffect(() => { - getSubusers(uuid) - .then(() => setLoading(false)) + clearFlashes('users'); + getServerSubusers(uuid) + .then(subusers => { + setSubusers(subusers); + setLoading(false); + }) .catch(error => { console.error(error); + addError({ key: 'users', message: httpErrorToHuman(error) }); }); - }, [ uuid, getSubusers ]); - - useEffect(() => { - setLoading(!subusers); - }, [ subusers ]); + }, []); if (loading || !Object.keys(permissions).length) { return ; @@ -37,33 +43,14 @@ export default () => { return (
+ {!subusers.length ?

It looks like you don't have any subusers.

: subusers.map(subuser => ( -
- -
-

{subuser.email}

-
-
- - -
-
+ )) }
diff --git a/resources/scripts/state/server/subusers.ts b/resources/scripts/state/server/subusers.ts index 5cba6f3a5..46e69f470 100644 --- a/resources/scripts/state/server/subusers.ts +++ b/resources/scripts/state/server/subusers.ts @@ -1,5 +1,4 @@ -import { action, Action, thunk, Thunk } from 'easy-peasy'; -import getServerSubusers from '@/api/server/users/getServerSubusers'; +import { action, Action } from 'easy-peasy'; export type SubuserPermission = 'websocket.*' | @@ -28,7 +27,7 @@ export interface ServerSubuserStore { data: Subuser[]; setSubusers: Action; appendSubuser: Action; - getSubusers: Thunk>; + removeSubuser: Action; } const subusers: ServerSubuserStore = { @@ -42,10 +41,8 @@ const subusers: ServerSubuserStore = { state.data = [ ...state.data.filter(user => user.uuid !== payload.uuid), payload ]; }), - getSubusers: thunk(async (actions, payload) => { - const subusers = await getServerSubusers(payload); - - actions.setSubusers(subusers); + removeSubuser: action((state, payload) => { + state.data = [ ...state.data.filter(user => user.uuid !== payload) ]; }), }; From ee81de65341ecaa588374eab9e852e11b95a441a Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 27 Mar 2020 15:40:24 -0700 Subject: [PATCH 04/58] Add support for editing a subuser --- .../components/server/users/EditSubuserModal.tsx | 6 ++++-- .../scripts/components/server/users/UserRow.tsx | 16 +++++++++++----- resources/scripts/state/server/subusers.ts | 15 ++++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx index 281e52a58..a046528db 100644 --- a/resources/scripts/components/server/users/EditSubuserModal.tsx +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, MutableRefObject, useRef } from 'react'; +import React, { forwardRef, useRef } from 'react'; import { Subuser } from '@/state/server/subusers'; import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; import { array, object, string } from 'yup'; @@ -39,8 +39,9 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr return ( -

{subuser ? 'Edit subuser' : 'Create new subuser'}

+

{subuser ? `Modify permissions for ${subuser.email}` : 'Create new subuser'}

+ {!subuser &&
(({ subuser, ...pr description={'Enter the email address of the user you wish to invite as a subuser for this server.'} />
+ }
{Object.keys(permissions).filter(key => key !== 'websocket').map((key, index) => ( { - const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser); + const [ visible, setVisible ] = useState(false); return (
+ {visible && + setVisible(false)} + /> + }
@@ -26,7 +32,7 @@ export default ({ subuser }: Props) => { type={'button'} aria-label={'Edit subuser'} className={'block text-sm p-2 text-neutral-500 hover:text-neutral-100 transition-colors duration-150 mr-4'} - onClick={() => null} + onClick={() => setVisible(true)} > diff --git a/resources/scripts/state/server/subusers.ts b/resources/scripts/state/server/subusers.ts index 46e69f470..c859ecead 100644 --- a/resources/scripts/state/server/subusers.ts +++ b/resources/scripts/state/server/subusers.ts @@ -38,7 +38,20 @@ const subusers: ServerSubuserStore = { }), appendSubuser: action((state, payload) => { - state.data = [ ...state.data.filter(user => user.uuid !== payload.uuid), payload ]; + let matched = false; + state.data = [ + ...state.data + .map(user => { + if (user.uuid === payload.uuid) { + matched = true; + + return payload; + } + + return user; + }) + .concat(matched ? [] : [ payload ]), + ]; }), removeSubuser: action((state, payload) => { From 39f79a8f3c0c96e90e137678383d32e632f63245 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 27 Mar 2020 16:42:27 -0700 Subject: [PATCH 05/58] Finish cleaning up subuser view --- .../Api/Client/Servers/SubuserController.php | 2 +- .../Subusers/SubuserCreationService.php | 2 +- .../components/server/users/UserRow.tsx | 24 ++++++++++++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php index 73a534341..79a90d842 100644 --- a/app/Http/Controllers/Api/Client/Servers/SubuserController.php +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -123,6 +123,6 @@ class SubuserController extends ClientApiController */ protected function getDefaultPermissions(Request $request): array { - return array_merge($request->input('permissions') ?? [], ['websocket.*']); + return array_unique(array_merge($request->input('permissions') ?? [], ['websocket.*'])); } } diff --git a/app/Services/Subusers/SubuserCreationService.php b/app/Services/Subusers/SubuserCreationService.php index ea8b5c2be..b959aece8 100644 --- a/app/Services/Subusers/SubuserCreationService.php +++ b/app/Services/Subusers/SubuserCreationService.php @@ -103,7 +103,7 @@ class SubuserCreationService return $this->subuserRepository->create([ 'user_id' => $user->id, 'server_id' => $server->id, - 'permissions' => $permissions, + 'permissions' => array_unique($permissions), ]); }); } diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index c84024603..d419e8fb1 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -4,6 +4,9 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPencilAlt } from '@fortawesome/free-solid-svg-icons/faPencilAlt'; import RemoveSubuserButton from '@/components/server/users/RemoveSubuserButton'; import EditSubuserModal from '@/components/server/users/EditSubuserModal'; +import { faUnlockAlt } from '@fortawesome/free-solid-svg-icons/faUnlockAlt'; +import { faUserLock } from '@fortawesome/free-solid-svg-icons/faUserLock'; +import classNames from 'classnames'; interface Props { subuser: Subuser; @@ -28,10 +31,29 @@ export default ({ subuser }: Props) => {

{subuser.email}

+
+

+   + +   +

+

2FA Enabled

+
+
+

+ {subuser.permissions.filter(permission => permission !== 'websocket.*').length} +

+

Permissions

+
-
-
- - Return to Login - -
- -
+ + {({ isSubmitting }) => ( + + +
+ +
+
+ + Return to Login + +
+
+ )} +
); }; diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index 23c75b7cb..bebc6a059 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -2,7 +2,6 @@ import React, { useRef } from 'react'; import { Link, RouteComponentProps } from 'react-router-dom'; import login, { LoginData } from '@/api/auth/login'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; -import FlashMessageRender from '@/components/FlashMessageRender'; import { ActionCreator, Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import { FormikProps, withFormik } from 'formik'; @@ -12,33 +11,12 @@ import { httpErrorToHuman } from '@/api/http'; import { FlashMessage } from '@/state/flashes'; import ReCAPTCHA from 'react-google-recaptcha'; import Spinner from '@/components/elements/Spinner'; -import styled from 'styled-components'; -import { breakpoint } from 'styled-components-breakpoint'; type OwnProps = RouteComponentProps & { clearFlashes: ActionCreator; addFlash: ActionCreator; } -const Container = styled.div` - ${breakpoint('sm')` - ${tw`w-4/5 mx-auto`} - `}; - - ${breakpoint('md')` - ${tw`p-10`} - `}; - - ${breakpoint('lg')` - ${tw`w-3/5`} - `}; - - ${breakpoint('xl')` - ${tw`w-full`} - max-width: 660px; - `}; -`; - const LoginContainer = ({ isSubmitting, setFieldValue, values, submitForm, handleSubmit }: OwnProps & FormikProps) => { const ref = useRef(null); const { enabled: recaptchaEnabled, siteKey } = useStoreState(state => state.settings.data!.recaptcha); @@ -56,66 +34,61 @@ const LoginContainer = ({ isSubmitting, setFieldValue, values, submitForm, handl return ( {ref.current && ref.current.render()} -

- Login to Continue -

- - - - + + + +
+ -
- - -
-
- -
- {recaptchaEnabled && - { - ref.current && ref.current.reset(); - setFieldValue('recaptchaData', token); - submitForm(); - }} - onExpired={() => setFieldValue('recaptchaData', null)} - /> - } -
- - Forgot password? - -
- - +
+
+ +
+ {recaptchaEnabled && + { + ref.current && ref.current.reset(); + setFieldValue('recaptchaData', token); + submitForm(); + }} + onExpired={() => setFieldValue('recaptchaData', null)} + /> + } +
+ + Forgot password? + +
+
); }; diff --git a/resources/scripts/components/auth/LoginFormContainer.tsx b/resources/scripts/components/auth/LoginFormContainer.tsx index bfdbe6782..5e291fb29 100644 --- a/resources/scripts/components/auth/LoginFormContainer.tsx +++ b/resources/scripts/components/auth/LoginFormContainer.tsx @@ -1,17 +1,47 @@ import React, { forwardRef } from 'react'; import { Form } from 'formik'; +import styled from 'styled-components'; +import { breakpoint } from 'styled-components-breakpoint'; +import FlashMessageRender from '@/components/FlashMessageRender'; -type Props = React.DetailedHTMLProps, HTMLFormElement>; +type Props = React.DetailedHTMLProps, HTMLFormElement> & { + title?: string; +} -export default forwardRef(({ ...props }, ref) => ( -
-
-
- +const Container = styled.div` + ${breakpoint('sm')` + ${tw`w-4/5 mx-auto`} + `}; + + ${breakpoint('md')` + ${tw`p-10`} + `}; + + ${breakpoint('lg')` + ${tw`w-3/5`} + `}; + + ${breakpoint('xl')` + ${tw`w-full`} + max-width: 700px; + `}; +`; + +export default forwardRef(({ title, ...props }, ref) => ( + + {title &&

+ {title} +

} + + +
+
+ +
+
+ {props.children} +
-
- {props.children} -
-
- + + )); diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx index ec3b3ff6f..9801d9351 100644 --- a/resources/scripts/components/auth/ResetPasswordContainer.tsx +++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx @@ -5,106 +5,110 @@ import { Link } from 'react-router-dom'; import performPasswordReset from '@/api/auth/performPasswordReset'; import { httpErrorToHuman } from '@/api/http'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; -import FlashMessageRender from '@/components/FlashMessageRender'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import Spinner from '@/components/elements/Spinner'; +import { Formik, FormikHelpers } from 'formik'; +import { object, ref, string } from 'yup'; +import Field from '@/components/elements/Field'; type Props = Readonly & {}>; -export default (props: Props) => { - const [ isLoading, setIsLoading ] = useState(false); +interface Values { + password: string; + passwordConfirmation: string; +} + +export default ({ match, history, location }: Props) => { const [ email, setEmail ] = useState(''); - const [ password, setPassword ] = useState(''); - const [ passwordConfirm, setPasswordConfirm ] = useState(''); const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); - const parsed = parse(props.location.search); + const parsed = parse(location.search); if (email.length === 0 && parsed.email) { setEmail(parsed.email as string); } - const canSubmit = () => password && email && password.length >= 8 && password === passwordConfirm; - - const submit = (e: React.FormEvent) => { - e.preventDefault(); - - if (!password || !email || !passwordConfirm) { - return; - } - - setIsLoading(true); + const submit = ({ password, passwordConfirmation }: Values, { setSubmitting }: FormikHelpers) => { clearFlashes(); - - performPasswordReset(email, { - token: props.match.params.token, password, passwordConfirmation: passwordConfirm, - }) + performPasswordReset(email, { token: match.params.token, password, passwordConfirmation }) .then(() => { - addFlash({ type: 'success', message: 'Your password has been reset, please login to continue.' }); - props.history.push('/auth/login'); + // @ts-ignore + window.location = '/'; + return; }) .catch(error => { console.error(error); + + setSubmitting(false); addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); - }) - .then(() => setIsLoading(false)); + }); }; return ( -
-

- Reset Password -

- - - - -
- - setPassword(e.target.value)} - /> -

- Passwords must be at least 8 characters in length. -

-
-
- - setPasswordConfirm(e.target.value)} - /> -
-
- -
-
- - Return to Login - -
-
-
+ + {({ isSubmitting }) => ( + +
+ + +
+
+ +
+
+ +
+
+ +
+
+ + Return to Login + +
+
+ )} +
); }; diff --git a/resources/scripts/components/elements/Field.tsx b/resources/scripts/components/elements/Field.tsx index 9f2f6d64f..feb819b47 100644 --- a/resources/scripts/components/elements/Field.tsx +++ b/resources/scripts/components/elements/Field.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; interface OwnProps { name: string; + light?: boolean; label?: string; description?: string; validate?: (value: any) => undefined | string | Promise; @@ -11,19 +12,19 @@ interface OwnProps { type Props = OwnProps & Omit, 'name'>; -const Field = ({ id, name, label, description, validate, className, ...props }: Props) => ( +const Field = ({ id, name, light = false, label, description, validate, className, ...props }: Props) => ( { ({ field, form: { errors, touched } }: FieldProps) => ( {label && - + } From ff49165447ea872ca6078f6e56d5b6ffcb5d4d9e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 28 Mar 2020 15:43:06 -0700 Subject: [PATCH 08/58] Unnecessary return --- resources/scripts/components/auth/ResetPasswordContainer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx index 9801d9351..7350e725a 100644 --- a/resources/scripts/components/auth/ResetPasswordContainer.tsx +++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx @@ -35,7 +35,6 @@ export default ({ match, history, location }: Props) => { .then(() => { // @ts-ignore window.location = '/'; - return; }) .catch(error => { console.error(error); From e4e5dea6b8858219fd7cfef084423b8027d69d57 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 28 Mar 2020 16:06:36 -0700 Subject: [PATCH 09/58] Fix API key creation logic --- app/Models/ApiKey.php | 5 +- app/Models/Validable.php | 7 +- .../dashboard/AccountApiContainer.tsx | 113 +++++++++--------- 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 6fb8a0e82..53a3fa810 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -53,7 +53,7 @@ class ApiKey extends Validable * @var array */ protected $casts = [ - 'allowed_ips' => 'json', + 'allowed_ips' => 'array', 'user_id' => 'int', 'r_' . AdminAcl::RESOURCE_USERS => 'int', 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'int', @@ -99,7 +99,8 @@ class ApiKey extends Validable 'identifier' => 'required|string|size:16|unique:api_keys,identifier', 'token' => 'required|string', 'memo' => 'required|nullable|string|max:500', - 'allowed_ips' => 'nullable|json', + 'allowed_ips' => 'nullable|array', + 'allowed_ips.*' => 'string', 'last_used_at' => 'nullable|date', 'r_' . AdminAcl::RESOURCE_USERS => 'integer|min:0|max:3', 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'integer|min:0|max:3', diff --git a/app/Models/Validable.php b/app/Models/Validable.php index 43545c3a8..f11c8ad05 100644 --- a/app/Models/Validable.php +++ b/app/Models/Validable.php @@ -140,7 +140,12 @@ abstract class Validable extends Model } return $this->getValidator()->setData( - $this->toArray() + // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist + // for that model. Doing this will return all of the attributes in a format that can + // properly be validated. + $this->addCastAttributesToArray( + $this->getAttributes(), $this->getMutatedAttributes() + ) )->passes(); } } diff --git a/resources/scripts/components/dashboard/AccountApiContainer.tsx b/resources/scripts/components/dashboard/AccountApiContainer.tsx index 38d7f757c..51f1f7806 100644 --- a/resources/scripts/components/dashboard/AccountApiContainer.tsx +++ b/resources/scripts/components/dashboard/AccountApiContainer.tsx @@ -46,62 +46,67 @@ export default () => { }; return ( -
+
- - setKeys(s => ([...s!, key]))}/> - - - - {deleteIdentifier && - { - doDeletion(deleteIdentifier); - setDeleteIdentifier(''); - }} - onDismissed={() => setDeleteIdentifier('')} - > - Are you sure you wish to delete this API key? All requests using it will immediately be - invalidated and will fail. - - } - { - keys.length === 0 ? -

- {loading ? 'Loading...' : 'No API keys exist for this account.'} -

- : - keys.map(key => ( -
- -
-

{key.description}

-

- Last - used: {key.lastUsedAt ? format(key.lastUsedAt, 'MMM Do, YYYY HH:mm') : 'Never'} -

-
-

- - {key.identifier} - -

- -
- )) - } -
+ +
+

{key.description}

+

+ Last + used: {key.lastUsedAt ? format(key.lastUsedAt, 'MMM Do, YYYY HH:mm') : 'Never'} +

+
+

+ + {key.identifier} + +

+ +
+ )) + } + +
); }; From 5717a705a8a42f62b2d1b2a3febb0f08d17a9a25 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 28 Mar 2020 16:18:56 -0700 Subject: [PATCH 10/58] Fix authorization checking for subusers --- app/Models/User.php | 22 -------------------- app/Policies/ServerPolicy.php | 39 ++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/app/Models/User.php b/app/Models/User.php index 0a37311d3..e3fccad43 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -37,9 +37,7 @@ use Pterodactyl\Notifications\SendPasswordReset as ResetPasswordNotification; * * @property string $name * @property \Pterodactyl\Models\ApiKey[]|\Illuminate\Database\Eloquent\Collection $apiKeys - * @property \Pterodactyl\Models\Permission[]|\Illuminate\Database\Eloquent\Collection $permissions * @property \Pterodactyl\Models\Server[]|\Illuminate\Database\Eloquent\Collection $servers - * @property \Pterodactyl\Models\Subuser[]|\Illuminate\Database\Eloquent\Collection $subuserOf * @property \Pterodactyl\Models\DaemonKey[]|\Illuminate\Database\Eloquent\Collection $keys */ class User extends Validable implements @@ -220,16 +218,6 @@ class User extends Validable implements return trim($this->name_first . ' ' . $this->name_last); } - /** - * Returns all permissions that a user has. - * - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough - */ - public function permissions() - { - return $this->hasManyThrough(Permission::class, Subuser::class); - } - /** * Returns all servers that a user owns. * @@ -240,16 +228,6 @@ class User extends Validable implements return $this->hasMany(Server::class, 'owner_id'); } - /** - * Return all servers that user is listed as a subuser of directly. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function subuserOf() - { - return $this->hasMany(Subuser::class); - } - /** * Return all of the daemon keys that a user belongs to. * diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index ac89be673..bc3fa7aca 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -1,21 +1,29 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Policies; -use Cache; -use Carbon; +use Carbon\Carbon; use Pterodactyl\Models\User; use Pterodactyl\Models\Server; +use Illuminate\Contracts\Cache\Repository as CacheRepository; class ServerPolicy { + /** + * @var \Illuminate\Contracts\Cache\Repository + */ + private $cache; + + /** + * ServerPolicy constructor. + * + * @param \Illuminate\Contracts\Cache\Repository $cache + */ + public function __construct(CacheRepository $cache) + { + $this->cache = $cache; + } + /** * Checks if the user has the given permission on/for the server. * @@ -26,13 +34,16 @@ class ServerPolicy */ protected function checkPermission(User $user, Server $server, $permission) { - $permissions = Cache::remember('ServerPolicy.' . $user->uuid . $server->uuid, Carbon::now()->addSeconds(5), function () use ($user, $server) { - return $user->permissions()->server($server)->get()->transform(function ($item) { - return $item->permission; - })->values(); + $key = sprintf('ServerPolicy.%s.%s', $user->uuid, $server->uuid); + + $permissions = $this->cache->remember($key, Carbon::now()->addSeconds(5), function () use ($user, $server) { + /** @var \Pterodactyl\Models\Subuser|null $subuser */ + $subuser = $server->subusers()->where('user_id', $user->id)->first(); + + return $subuser ? $subuser->permissions : []; }); - return $permissions->search($permission, true) !== false; + return in_array($permission, $permissions); } /** From 1f92a7de336e7508f337e9af874c83cb422f13d5 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 28 Mar 2020 16:23:18 -0700 Subject: [PATCH 11/58] Authenticate that the request is coming from someone that should even know about the server --- .../Api/Client/Server/AuthenticateServerAccess.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php index 800add18c..fe0ca5610 100644 --- a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php +++ b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php @@ -42,6 +42,16 @@ class AuthenticateServerAccess throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); } + // At the very least, ensure that the user trying to make this request is the + // server owner, a subuser, or a root admin. We'll leave it up to the controllers + // to authenticate more detailed permissions if needed. + if ($request->user()->id !== $server->owner_id && ! $request->user()->root_admin) { + // Check for subuser status. + if (! $server->subusers->contains('user_id', $request->user()->id)) { + throw new NotFoundHttpException(trans('exceptions.api.resource_not_found')); + } + } + if ($server->suspended) { throw new AccessDeniedHttpException('Cannot access a server that is marked as being suspended.'); } From 7e0ac2c311197a150e9be83b9444a9569ba04f66 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 28 Mar 2020 16:47:32 -0700 Subject: [PATCH 12/58] Show error message when attempting to change a server's name --- .../server/settings/RenameServerBox.tsx | 97 ++++++++++--------- .../server/settings/SettingsContainer.tsx | 78 ++++++++------- 2 files changed, 91 insertions(+), 84 deletions(-) diff --git a/resources/scripts/components/server/settings/RenameServerBox.tsx b/resources/scripts/components/server/settings/RenameServerBox.tsx index 5b1ed3181..83c0b2f6b 100644 --- a/resources/scripts/components/server/settings/RenameServerBox.tsx +++ b/resources/scripts/components/server/settings/RenameServerBox.tsx @@ -1,66 +1,69 @@ import React from 'react'; import { ServerContext } from '@/state/server'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; -import { Form, FormikProps, withFormik } from 'formik'; -import { Server } from '@/api/server/getServer'; -import { ActionCreator } from 'easy-peasy'; +import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; +import { Actions, useStoreActions } from 'easy-peasy'; import renameServer from '@/api/server/renameServer'; import Field from '@/components/elements/Field'; import { object, string } from 'yup'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; - -interface OwnProps { - server: Server; - setServer: ActionCreator; -} +import { ApplicationStore } from '@/state'; +import { httpErrorToHuman } from '@/api/http'; interface Values { name: string; } -const RenameServerBox = ({ isSubmitting, ...props }: OwnProps & FormikProps) => ( - - -
- -
- -
- -
-); +const RenameServerBox = () => { + const { isSubmitting } = useFormikContext(); -const EnhancedForm = withFormik({ - displayName: 'RenameServerBoxForm', - - mapPropsToValues: props => ({ - name: props.server.name, - }), - - validationSchema: () => object().shape({ - name: string().required().min(1), - }), - - handleSubmit: (values, { props, setSubmitting }) => { - renameServer(props.server.uuid, values.name) - .then(() => props.setServer({ ...props.server, name: values.name })) - .catch(error => { - console.error(error); - }) - .then(() => setSubmitting(false)); - }, -})(RenameServerBox); + return ( + + +
+ +
+ +
+ +
+ ); +}; export default () => { const server = ServerContext.useStoreState(state => state.server.data!); const setServer = ServerContext.useStoreActions(actions => actions.server.setServer); + const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); - return ; + const submit = ({ name }: Values, { setSubmitting }: FormikHelpers) => { + clearFlashes('settings'); + renameServer(server.uuid, name) + .then(() => setServer({ ...server, name })) + .catch(error => { + console.error(error); + addError({ key: 'settings', message: httpErrorToHuman(error) }); + }) + .then(() => setSubmitting(false)); + }; + + return ( + + + + ); }; diff --git a/resources/scripts/components/server/settings/SettingsContainer.tsx b/resources/scripts/components/server/settings/SettingsContainer.tsx index 104de803f..d487bfc8f 100644 --- a/resources/scripts/components/server/settings/SettingsContainer.tsx +++ b/resources/scripts/components/server/settings/SettingsContainer.tsx @@ -5,52 +5,56 @@ import { useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import { UserData } from '@/state/user'; import RenameServerBox from '@/components/server/settings/RenameServerBox'; +import FlashMessageRender from '@/components/FlashMessageRender'; export default () => { const user = useStoreState(state => state.user.data!); const server = ServerContext.useStoreState(state => state.server.data!); return ( -
- -
- - -
-
- - -
-
-
-
-

- Your SFTP password is the same as the password you use to access this panel. -

+
+ +
+ +
+ + +
+
+ + +
+
+
+
+

+ Your SFTP password is the same as the password you use to access this panel. +

+
+
+
- +
+
+
- -
-
); From ab4c4e7e9e4b458f510ee0cf91c09b918529bcd1 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 28 Mar 2020 17:25:04 -0700 Subject: [PATCH 13/58] Add basic permissions checking logic to frontend --- resources/scripts/api/server/getServer.ts | 11 +++-- resources/scripts/components/elements/Can.tsx | 44 +++++++++++++++++++ resources/scripts/routers/ServerRouter.tsx | 21 ++++++--- resources/scripts/state/server/index.ts | 16 ++++++- 4 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 resources/scripts/components/elements/Can.tsx diff --git a/resources/scripts/api/server/getServer.ts b/resources/scripts/api/server/getServer.ts index e6a89fd1a..d3c81cefd 100644 --- a/resources/scripts/api/server/getServer.ts +++ b/resources/scripts/api/server/getServer.ts @@ -41,20 +41,23 @@ export const rawDataToServerObject = (data: any): Server => ({ port: data.sftp_details.port, }, description: data.description ? ((data.description.length > 0) ? data.description : null) : null, - allocations: [{ + allocations: [ { ip: data.allocation.ip, alias: null, port: data.allocation.port, default: true, - }], + } ], limits: { ...data.limits }, featureLimits: { ...data.feature_limits }, }); -export default (uuid: string): Promise => { +export default (uuid: string): Promise<[ Server, string[] ]> => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${uuid}`) - .then(response => resolve(rawDataToServerObject(response.data.attributes))) + .then(({ data }) => resolve([ + rawDataToServerObject(data.attributes), + data.meta?.is_server_owner ? ['*'] : (data.meta?.user_permissions || []), + ])) .catch(reject); }); }; diff --git a/resources/scripts/components/elements/Can.tsx b/resources/scripts/components/elements/Can.tsx new file mode 100644 index 000000000..87f67e0a8 --- /dev/null +++ b/resources/scripts/components/elements/Can.tsx @@ -0,0 +1,44 @@ +import React, { useMemo } from 'react'; +import { ServerContext } from '@/state/server'; + +interface Props { + action: string | string[]; + renderOnError?: React.ReactNode | null; + children: React.ReactNode; +} + +const Can = ({ action, renderOnError, children }: Props) => { + const userPermissions = ServerContext.useStoreState(state => state.server.permissions); + const actions = Array.isArray(action) ? action : [ action ]; + + const missingPermissionCount = useMemo(() => { + if (userPermissions[0] === '*') { + return 0; + } + + return actions.filter(permission => { + return !( + // Allows checking for any permission matching a name, for example files.* + // will return if the user has any permission under the file.XYZ namespace. + ( + permission.endsWith('.*') + && permission !== 'websocket.*' + && userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 + ) + // Otherwise just check if the entire permission exists in the array or not. + || userPermissions.indexOf(permission) >= 0); + }).length; + }, [ action, userPermissions ]); + + return ( + <> + {missingPermissionCount > 0 ? + renderOnError + : + children + } + + ); +}; + +export default Can; diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 8735c1401..ff7fe73ec 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -15,6 +15,7 @@ import SettingsContainer from '@/components/server/settings/SettingsContainer'; import ScheduleContainer from '@/components/server/schedules/ScheduleContainer'; import ScheduleEditContainer from '@/components/server/schedules/ScheduleEditContainer'; import UsersContainer from '@/components/server/users/UsersContainer'; +import Can from '@/components/elements/Can'; const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { const server = ServerContext.useStoreState(state => state.server.data); @@ -34,11 +35,21 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
Console - File Manager - Databases - Schedules - Users - Settings + + File Manager + + + Databases + + + Schedules + + + Users + + + Settings +
diff --git a/resources/scripts/state/server/index.ts b/resources/scripts/state/server/index.ts index 8fc23aee5..fb26a7167 100644 --- a/resources/scripts/state/server/index.ts +++ b/resources/scripts/state/server/index.ts @@ -10,18 +10,30 @@ export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running'; interface ServerDataStore { data?: Server; + permissions: string[]; + getServer: Thunk>; setServer: Action; + setPermissions: Action; } const server: ServerDataStore = { + permissions: [], + getServer: thunk(async (actions, payload) => { - const server = await getServer(payload); + const [server, permissions] = await getServer(payload); + actions.setServer(server); + actions.setPermissions(permissions); }), + setServer: action((state, payload) => { state.data = payload; }), + + setPermissions: action((state, payload) => { + state.permissions = payload; + }), }; interface ServerStatusStore { @@ -75,9 +87,9 @@ export const ServerContext = createContextStore({ subusers, clearServerState: action(state => { state.server.data = undefined; + state.server.permissions = []; state.databases.items = []; state.subusers.data = []; - state.files.directory = '/'; state.files.contents = []; From 2561e3e8d5c3e8ab2439581a9f7a7e1329be5c3d Mon Sep 17 00:00:00 2001 From: AreYouScared Date: Sun, 29 Mar 2020 14:41:55 -0400 Subject: [PATCH 14/58] Add CPU Thread assignments Added CPU Thread assignments for each server --- app/Http/Controllers/Admin/ServersController.php | 2 +- .../Api/Application/Servers/StoreServerRequest.php | 2 ++ .../Servers/UpdateServerBuildConfigurationRequest.php | 2 ++ app/Services/Servers/BuildModificationService.php | 1 + .../Servers/ServerConfigurationStructureService.php | 2 ++ app/Services/Servers/ServerCreationService.php | 1 + resources/views/admin/servers/new.blade.php | 7 +++++++ resources/views/admin/servers/view/build.blade.php | 9 ++++++++- resources/views/admin/servers/view/index.blade.php | 4 ++++ 9 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 8bcb10390..6f8e106ef 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -262,7 +262,7 @@ class ServersController extends Controller { $this->buildModificationService->handle($server, $request->only([ 'allocation_id', 'add_allocations', 'remove_allocations', - 'memory', 'swap', 'io', 'cpu', 'disk', + 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'database_limit', 'allocation_limit', 'oom_disabled', ])); $this->alert->success(trans('admin/server.alerts.build_updated'))->flash(); diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index d386f5851..32f68bd23 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -49,6 +49,7 @@ class StoreServerRequest extends ApplicationApiRequest 'limits.swap' => $rules['swap'], 'limits.disk' => $rules['disk'], 'limits.io' => $rules['io'], + 'limits.threads' => $rules['threads'], 'limits.cpu' => $rules['cpu'], // Application Resource Limits @@ -96,6 +97,7 @@ class StoreServerRequest extends ApplicationApiRequest 'disk' => array_get($data, 'limits.disk'), 'io' => array_get($data, 'limits.io'), 'cpu' => array_get($data, 'limits.cpu'), + 'threads' => array_get($data, 'limits.threads'), 'skip_scripts' => array_get($data, 'skip_scripts', false), 'allocation_id' => array_get($data, 'allocation.default'), 'allocation_additional' => array_get($data, 'allocation.additional'), diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php index 9d5a3bb0a..3f0e1c8ca 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php @@ -25,6 +25,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest 'limits.swap' => $this->requiredToOptional('swap', $rules['swap'], true), 'limits.io' => $this->requiredToOptional('io', $rules['io'], true), 'limits.cpu' => $this->requiredToOptional('cpu', $rules['cpu'], true), + 'limits.threads' => $this->requiredToOptional('threads', $rules['threads'], true), 'limits.disk' => $this->requiredToOptional('disk', $rules['disk'], true), // Legacy rules to maintain backwards compatable API support without requiring @@ -35,6 +36,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest 'swap' => $this->requiredToOptional('swap', $rules['swap']), 'io' => $this->requiredToOptional('io', $rules['io']), 'cpu' => $this->requiredToOptional('cpu', $rules['cpu']), + 'threads' => $this->requiredToOptional('threads', $rules['threads']), 'disk' => $this->requiredToOptional('disk', $rules['disk']), 'add_allocations' => 'bail|array', diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index 0e3085dc5..0a010b545 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -98,6 +98,7 @@ class BuildModificationService 'swap' => array_get($data, 'swap'), 'io' => array_get($data, 'io'), 'cpu' => array_get($data, 'cpu'), + 'threads' => array_get($data, 'threads'), 'disk' => array_get($data, 'disk'), 'allocation_id' => array_get($data, 'allocation_id'), 'database_limit' => array_get($data, 'database_limit'), diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 9984e3a98..91955892e 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -81,6 +81,7 @@ class ServerConfigurationStructureService 'swap' => $server->swap, 'io_weight' => $server->io, 'cpu_limit' => $server->cpu, + 'cpu_threads' => $server->threads, 'disk_space' => $server->disk, ], 'service' => [ @@ -130,6 +131,7 @@ class ServerConfigurationStructureService 'swap' => (int) $server->swap, 'io' => (int) $server->io, 'cpu' => (int) $server->cpu, + 'threads' => $server->threads, 'disk' => (int) $server->disk, 'image' => $server->image, ], diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index 9cc1535a4..b7b6fdb22 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -242,6 +242,7 @@ class ServerCreationService 'disk' => Arr::get($data, 'disk'), 'io' => Arr::get($data, 'io'), 'cpu' => Arr::get($data, 'cpu'), + 'threads' => Arr::get($data, 'threads'), 'oom_disabled' => Arr::get($data, 'oom_disabled', true), 'allocation_id' => Arr::get($data, 'allocation_id'), 'nest_id' => Arr::get($data, 'nest_id'), diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index f2efc2ce4..6edf9f722 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -160,6 +160,13 @@ %
+
+ +
+ + +
+
diff --git a/resources/views/admin/servers/view/build.blade.php b/resources/views/admin/servers/view/build.blade.php index b28f324ba..14c605ae8 100644 --- a/resources/views/admin/servers/view/build.blade.php +++ b/resources/views/admin/servers/view/build.blade.php @@ -26,7 +26,7 @@
-

System Resources

+

Resource Management

@@ -53,6 +53,13 @@

Each physical core on the system is considered to be 100%. Setting this value to 0 will allow a server to use CPU time without restrictions.

+
+ +
+ + +
+
diff --git a/resources/views/admin/servers/view/index.blade.php b/resources/views/admin/servers/view/index.blade.php index 3d75d4f4d..c80d90cfc 100644 --- a/resources/views/admin/servers/view/index.blade.php +++ b/resources/views/admin/servers/view/index.blade.php @@ -73,6 +73,10 @@ CPU Limit {{ $server->cpu }}% + + CPU Threads + {{ $server->threads }} + Default Connection {{ $server->allocation->ip }}:{{ $server->allocation->port }} From 2e9d327dfc2d60bed7b64eb301ddae919c1c699c Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 12:38:08 -0700 Subject: [PATCH 15/58] Fix eslint errors --- resources/scripts/components/elements/Can.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/scripts/components/elements/Can.tsx b/resources/scripts/components/elements/Can.tsx index 87f67e0a8..0906f9ff8 100644 --- a/resources/scripts/components/elements/Can.tsx +++ b/resources/scripts/components/elements/Can.tsx @@ -21,9 +21,9 @@ const Can = ({ action, renderOnError, children }: Props) => { // Allows checking for any permission matching a name, for example files.* // will return if the user has any permission under the file.XYZ namespace. ( - permission.endsWith('.*') - && permission !== 'websocket.*' - && userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 + permission.endsWith('.*') && + permission !== 'websocket.*' && + userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 ) // Otherwise just check if the entire permission exists in the array or not. || userPermissions.indexOf(permission) >= 0); From b351c07d34896f56795d1969f1819eee30c313f2 Mon Sep 17 00:00:00 2001 From: AreYouScared Date: Sun, 29 Mar 2020 16:55:06 -0400 Subject: [PATCH 16/58] Fix server transformer Properly pass the value to wings --- app/Models/Server.php | 2 ++ app/Services/Servers/ServerConfigurationStructureService.php | 2 +- app/Transformers/Api/Application/ServerTransformer.php | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Models/Server.php b/app/Models/Server.php index 9bdb19ff1..0ccd0080a 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -105,6 +105,7 @@ class Server extends Validable 'swap' => 'required|numeric|min:-1', 'io' => 'required|numeric|between:10,1000', 'cpu' => 'required|numeric|min:0', + 'threads' => 'present|nullable|regex:/^([\d-,]+)$/', 'oom_disabled' => 'sometimes|boolean', 'disk' => 'required|numeric|min:0', 'allocation_id' => 'required|bail|unique:servers|exists:allocations,id', @@ -134,6 +135,7 @@ class Server extends Validable 'disk' => 'integer', 'io' => 'integer', 'cpu' => 'integer', + 'threads' => 'string', 'oom_disabled' => 'boolean', 'allocation_id' => 'integer', 'nest_id' => 'integer', diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 91955892e..69950b230 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -81,7 +81,7 @@ class ServerConfigurationStructureService 'swap' => $server->swap, 'io_weight' => $server->io, 'cpu_limit' => $server->cpu, - 'cpu_threads' => $server->threads, + 'threads' => $server->threads, 'disk_space' => $server->disk, ], 'service' => [ diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php index 70dc185d6..4fb31212d 100644 --- a/app/Transformers/Api/Application/ServerTransformer.php +++ b/app/Transformers/Api/Application/ServerTransformer.php @@ -75,6 +75,7 @@ class ServerTransformer extends BaseTransformer 'disk' => $server->disk, 'io' => $server->io, 'cpu' => $server->cpu, + 'threads' => $server->threads, ], 'feature_limits' => [ 'databases' => $server->database_limit, From 5f90e5f1048ab20a2d2b24c54649eda48f4857a6 Mon Sep 17 00:00:00 2001 From: AreYouScared Date: Sun, 29 Mar 2020 17:00:50 -0400 Subject: [PATCH 17/58] Update Server.php Fails rules, will look at later in time. --- app/Models/Server.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Models/Server.php b/app/Models/Server.php index 0ccd0080a..9bdb19ff1 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -105,7 +105,6 @@ class Server extends Validable 'swap' => 'required|numeric|min:-1', 'io' => 'required|numeric|between:10,1000', 'cpu' => 'required|numeric|min:0', - 'threads' => 'present|nullable|regex:/^([\d-,]+)$/', 'oom_disabled' => 'sometimes|boolean', 'disk' => 'required|numeric|min:0', 'allocation_id' => 'required|bail|unique:servers|exists:allocations,id', @@ -135,7 +134,6 @@ class Server extends Validable 'disk' => 'integer', 'io' => 'integer', 'cpu' => 'integer', - 'threads' => 'string', 'oom_disabled' => 'boolean', 'allocation_id' => 'integer', 'nest_id' => 'integer', From 71292a764269e15ef1f40778f4f63a1656304326 Mon Sep 17 00:00:00 2001 From: AreYouScared Date: Sun, 29 Mar 2020 17:12:35 -0400 Subject: [PATCH 18/58] Respond with thread limit Retrun the cpu threads in the api resposne --- resources/scripts/api/server/getServer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/scripts/api/server/getServer.ts b/resources/scripts/api/server/getServer.ts index d3c81cefd..31e70fcc5 100644 --- a/resources/scripts/api/server/getServer.ts +++ b/resources/scripts/api/server/getServer.ts @@ -24,6 +24,7 @@ export interface Server { disk: number; io: number; cpu: number; + threads: string; }; featureLimits: { databases: number; From 8bc81c8c4b2fbb23f1f61eede686777480bba73d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 14:19:17 -0700 Subject: [PATCH 19/58] Update permissions checking code --- resources/scripts/components/elements/Can.tsx | 32 ++------------- .../server/users/EditSubuserModal.tsx | 40 ++++++++++++++----- .../components/server/users/UserRow.tsx | 5 ++- .../server/users/UsersContainer.tsx | 9 +++-- resources/scripts/helpers.ts | 2 +- resources/scripts/plugins/useDeepMemo.ts | 12 ++++++ resources/scripts/plugins/usePermissions.ts | 22 ++++++++++ 7 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 resources/scripts/plugins/useDeepMemo.ts create mode 100644 resources/scripts/plugins/usePermissions.ts diff --git a/resources/scripts/components/elements/Can.tsx b/resources/scripts/components/elements/Can.tsx index 0906f9ff8..ee6393719 100644 --- a/resources/scripts/components/elements/Can.tsx +++ b/resources/scripts/components/elements/Can.tsx @@ -1,5 +1,5 @@ -import React, { useMemo } from 'react'; -import { ServerContext } from '@/state/server'; +import React from 'react'; +import { usePermissions } from '@/plugins/usePermissions'; interface Props { action: string | string[]; @@ -8,35 +8,11 @@ interface Props { } const Can = ({ action, renderOnError, children }: Props) => { - const userPermissions = ServerContext.useStoreState(state => state.server.permissions); - const actions = Array.isArray(action) ? action : [ action ]; - - const missingPermissionCount = useMemo(() => { - if (userPermissions[0] === '*') { - return 0; - } - - return actions.filter(permission => { - return !( - // Allows checking for any permission matching a name, for example files.* - // will return if the user has any permission under the file.XYZ namespace. - ( - permission.endsWith('.*') && - permission !== 'websocket.*' && - userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 - ) - // Otherwise just check if the entire permission exists in the array or not. - || userPermissions.indexOf(permission) >= 0); - }).length; - }, [ action, userPermissions ]); + const can = usePermissions(action); return ( <> - {missingPermissionCount > 0 ? - renderOnError - : - children - } + {can.every(p => p) ? children : renderOnError} ); }; diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx index a046528db..5c49edb28 100644 --- a/resources/scripts/components/server/users/EditSubuserModal.tsx +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -14,6 +14,8 @@ import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser'; import { ServerContext } from '@/state/server'; import { httpErrorToHuman } from '@/api/http'; import FlashMessageRender from '@/components/FlashMessageRender'; +import Can from '@/components/elements/Can'; +import { usePermissions } from '@/plugins/usePermissions'; type Props = { subuser?: Subuser; @@ -25,21 +27,32 @@ interface Values { } const PermissionLabel = styled.label` - ${tw`flex items-center border border-transparent rounded p-2 cursor-pointer`}; + ${tw`flex items-center border border-transparent rounded p-2`}; text-transform: none; - &:hover { - ${tw`border-neutral-500 bg-neutral-800`}; + &:not(.disabled) { + ${tw`cursor-pointer`}; + + &:hover { + ${tw`border-neutral-500 bg-neutral-800`}; + } } `; const EditSubuserModal = forwardRef(({ subuser, ...props }, ref) => { const { values, isSubmitting, setFieldValue } = useFormikContext(); + const [ canEditUser ] = usePermissions([ 'user.update' ]); const permissions = useStoreState((state: ApplicationStore) => state.permissions.data); return ( -

{subuser ? `Modify permissions for ${subuser.email}` : 'Create new subuser'}

+

+ {subuser ? + `${canEditUser ? 'Modify' : 'View'} permissions for ${subuser.email}` + : + 'Create new subuser' + } +

{!subuser &&
@@ -50,13 +63,14 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr />
} -
+
{Object.keys(permissions).filter(key => key !== 'websocket').map((key, index) => (

{key}

+ {canEditUser && { @@ -78,6 +92,7 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr } }} /> + }
} className={index !== 0 ? 'mt-4' : undefined} @@ -87,9 +102,11 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr

{Object.keys(permissions[key].keys).map((pkey, index) => (
@@ -98,6 +115,7 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr name={'permissions'} value={`${key}.${pkey}`} className={'w-5 h-5 mr-2'} + disabled={!canEditUser} />
@@ -115,11 +133,13 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr ))}
-
- -
+ +
+ +
+
); }); diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index d419e8fb1..d12b7aef1 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -7,6 +7,7 @@ import EditSubuserModal from '@/components/server/users/EditSubuserModal'; import { faUnlockAlt } from '@fortawesome/free-solid-svg-icons/faUnlockAlt'; import { faUserLock } from '@fortawesome/free-solid-svg-icons/faUserLock'; import classNames from 'classnames'; +import Can from '@/components/elements/Can'; interface Props { subuser: Subuser; @@ -58,7 +59,9 @@ export default ({ subuser }: Props) => { > - + + +
); }; diff --git a/resources/scripts/components/server/users/UsersContainer.tsx b/resources/scripts/components/server/users/UsersContainer.tsx index 082859c3d..e2361684f 100644 --- a/resources/scripts/components/server/users/UsersContainer.tsx +++ b/resources/scripts/components/server/users/UsersContainer.tsx @@ -8,6 +8,7 @@ import UserRow from '@/components/server/users/UserRow'; import FlashMessageRender from '@/components/FlashMessageRender'; import getServerSubusers from '@/api/server/users/getServerSubusers'; import { httpErrorToHuman } from '@/api/http'; +import Can from '@/components/elements/Can'; export default () => { const [ loading, setLoading ] = useState(true); @@ -53,9 +54,11 @@ export default () => { )) } -
- -
+ +
+ +
+
); }; diff --git a/resources/scripts/helpers.ts b/resources/scripts/helpers.ts index 79048f778..9d531b52c 100644 --- a/resources/scripts/helpers.ts +++ b/resources/scripts/helpers.ts @@ -5,7 +5,7 @@ export function bytesToHuman (bytes: number): string { const i = Math.floor(Math.log(bytes) / Math.log(1000)); // @ts-ignore - return `${(bytes / Math.pow(1000, i)).toFixed(2) * 1} ${['Bytes', 'kB', 'MB', 'GB', 'TB'][i]}`; + return `${(bytes / Math.pow(1000, i)).toFixed(2) * 1} ${[ 'Bytes', 'kB', 'MB', 'GB', 'TB' ][i]}`; } export const bytesToMegabytes = (bytes: number) => Math.floor(bytes / 1000 / 1000); diff --git a/resources/scripts/plugins/useDeepMemo.ts b/resources/scripts/plugins/useDeepMemo.ts new file mode 100644 index 000000000..0ad6bb7df --- /dev/null +++ b/resources/scripts/plugins/useDeepMemo.ts @@ -0,0 +1,12 @@ +import { useRef } from 'react'; +import isEqual from 'lodash-es/isEqual'; + +export const useDeepMemo = (fn: () => T, key: K): T => { + const ref = useRef<{ key: K, value: T }>(); + + if (!ref.current || !isEqual(key, ref.current.key)) { + ref.current = { key, value: fn() }; + } + + return ref.current.value; +}; diff --git a/resources/scripts/plugins/usePermissions.ts b/resources/scripts/plugins/usePermissions.ts new file mode 100644 index 000000000..02f57ebde --- /dev/null +++ b/resources/scripts/plugins/usePermissions.ts @@ -0,0 +1,22 @@ +import { ServerContext } from '@/state/server'; +import { useDeepMemo } from '@/plugins/useDeepMemo'; + +export const usePermissions = (action: string | string[]): boolean[] => { + const userPermissions = ServerContext.useStoreState(state => state.server.permissions); + + return useDeepMemo(() => { + return (Array.isArray(action) ? action : [ action ]) + .map(permission => ( + // Allows checking for any permission matching a name, for example files.* + // will return if the user has any permission under the file.XYZ namespace. + ( + permission.endsWith('.*') && + permission !== 'websocket.*' && + userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 + ) || + // Otherwise just check if the entire permission exists in the array or not. + userPermissions.indexOf(permission) >= 0 + ), + ); + }, [ action, userPermissions ]); +}; From 9347ee8d78cb1c2080edb987b76d8ddf42361e5e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 21:30:33 -0700 Subject: [PATCH 20/58] Fix permissions handling logic for admins/owners --- resources/scripts/plugins/usePermissions.ts | 27 ++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/resources/scripts/plugins/usePermissions.ts b/resources/scripts/plugins/usePermissions.ts index 02f57ebde..974e09434 100644 --- a/resources/scripts/plugins/usePermissions.ts +++ b/resources/scripts/plugins/usePermissions.ts @@ -5,18 +5,23 @@ export const usePermissions = (action: string | string[]): boolean[] => { const userPermissions = ServerContext.useStoreState(state => state.server.permissions); return useDeepMemo(() => { + if (userPermissions[0] === '*') { + return ([] as boolean[]).fill( + true, 0, Array.isArray(action) ? action.length : 1, + ); + } + return (Array.isArray(action) ? action : [ action ]) .map(permission => ( - // Allows checking for any permission matching a name, for example files.* - // will return if the user has any permission under the file.XYZ namespace. - ( - permission.endsWith('.*') && - permission !== 'websocket.*' && - userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 - ) || - // Otherwise just check if the entire permission exists in the array or not. - userPermissions.indexOf(permission) >= 0 - ), - ); + // Allows checking for any permission matching a name, for example files.* + // will return if the user has any permission under the file.XYZ namespace. + ( + permission.endsWith('.*') && + permission !== 'websocket.*' && + userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 + ) || + // Otherwise just check if the entire permission exists in the array or not. + userPermissions.indexOf(permission) >= 0 + )); }, [ action, userPermissions ]); }; From 9b4f2deb78c8ac17aea5561ee5bbd86063b38cc9 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 21:42:02 -0700 Subject: [PATCH 21/58] Update permissions handling for file manager; ensure errors are shown --- .../server/files/FileDropdownMenu.tsx | 70 +++++++++++-------- .../server/files/FileEditContainer.tsx | 40 +++++++---- .../server/files/FileManagerContainer.tsx | 19 +++-- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/resources/scripts/components/server/files/FileDropdownMenu.tsx b/resources/scripts/components/server/files/FileDropdownMenu.tsx index 07bec2912..5db5d90e6 100644 --- a/resources/scripts/components/server/files/FileDropdownMenu.tsx +++ b/resources/scripts/components/server/files/FileDropdownMenu.tsx @@ -13,7 +13,8 @@ import { join } from 'path'; import deleteFile from '@/api/server/files/deleteFile'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import copyFile from '@/api/server/files/copyFile'; -import http, { httpErrorToHuman } from '@/api/http'; +import { httpErrorToHuman } from '@/api/http'; +import Can from '@/components/elements/Can'; type ModalType = 'rename' | 'move'; @@ -118,30 +119,37 @@ export default ({ uuid }: { uuid: string }) => {
{ e.stopPropagation(); setMenuVisible(false); }} + onClick={e => { + e.stopPropagation(); + setMenuVisible(false); + }} className={'absolute bg-white p-2 rounded border border-neutral-700 shadow-lg text-neutral-500 min-w-48'} > -
setModal('rename')} - className={'hover:text-neutral-700 p-2 flex items-center hover:bg-neutral-100 rounded'} - > - - Rename -
-
setModal('move')} - className={'hover:text-neutral-700 p-2 flex items-center hover:bg-neutral-100 rounded'} - > - - Move -
-
doCopy()} - className={'hover:text-neutral-700 p-2 flex items-center hover:bg-neutral-100 rounded'} - > - - Copy -
+ +
setModal('rename')} + className={'hover:text-neutral-700 p-2 flex items-center hover:bg-neutral-100 rounded'} + > + + Rename +
+
setModal('move')} + className={'hover:text-neutral-700 p-2 flex items-center hover:bg-neutral-100 rounded'} + > + + Move +
+
+ +
doCopy()} + className={'hover:text-neutral-700 p-2 flex items-center hover:bg-neutral-100 rounded'} + > + + Copy +
+
doDownload()} @@ -149,13 +157,15 @@ export default ({ uuid }: { uuid: string }) => { Download
-
doDeletion()} - className={'hover:text-red-700 p-2 flex items-center hover:bg-red-100 rounded'} - > - - Delete -
+ +
doDeletion()} + className={'hover:text-red-700 p-2 flex items-center hover:bg-red-100 rounded'} + > + + Delete +
+
diff --git a/resources/scripts/components/server/files/FileEditContainer.tsx b/resources/scripts/components/server/files/FileEditContainer.tsx index 2d0de6576..a44698fd9 100644 --- a/resources/scripts/components/server/files/FileEditContainer.tsx +++ b/resources/scripts/components/server/files/FileEditContainer.tsx @@ -2,7 +2,7 @@ import React, { lazy, useEffect, useState } from 'react'; import { ServerContext } from '@/state/server'; import getFileContents from '@/api/server/files/getFileContents'; import useRouter from 'use-react-router'; -import { Actions, useStoreState } from 'easy-peasy'; +import { Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import { httpErrorToHuman } from '@/api/http'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; @@ -10,6 +10,8 @@ import saveFileContents from '@/api/server/files/saveFileContents'; import FileManagerBreadcrumbs from '@/components/server/files/FileManagerBreadcrumbs'; import { useParams } from 'react-router'; import FileNameModal from '@/components/server/files/FileNameModal'; +import Can from '@/components/elements/Can'; +import FlashMessageRender from '@/components/FlashMessageRender'; const LazyAceEditor = lazy(() => import(/* webpackChunkName: "editor" */'@/components/elements/AceEditor')); @@ -21,15 +23,20 @@ export default () => { const [ modalVisible, setModalVisible ] = useState(false); const { id, uuid } = ServerContext.useStoreState(state => state.server.data!); - const addError = useStoreState((state: Actions) => state.flashes.addError); + const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); let fetchFileContent: null | (() => Promise) = null; if (action !== 'new') { useEffect(() => { + setLoading(true); + clearFlashes('files:view'); getFileContents(uuid, hash.replace(/^#/, '')) .then(setContent) - .catch(error => console.error(error)) + .catch(error => { + console.error(error); + addError({ key: 'files:view', message: httpErrorToHuman(error) }); + }) .then(() => setLoading(false)); }, [ uuid, hash ]); } @@ -40,10 +47,10 @@ export default () => { } setLoading(true); - fetchFileContent() - .then(content => { - return saveFileContents(uuid, name || hash.replace(/^#/, ''), content); - }) + clearFlashes('files:view'); + fetchFileContent().then(content => { + return saveFileContents(uuid, name || hash.replace(/^#/, ''), content); + }) .then(() => { if (name) { history.push(`/server/${id}/files/edit#/${name}`); @@ -54,13 +61,14 @@ export default () => { }) .catch(error => { console.error(error); - addError({ message: httpErrorToHuman(error), key: 'files' }); + addError({ message: httpErrorToHuman(error), key: 'files:view' }); }) .then(() => setLoading(false)); }; return (
+ {
{action === 'edit' ? - + + + : - + + + }
diff --git a/resources/scripts/components/server/files/FileManagerContainer.tsx b/resources/scripts/components/server/files/FileManagerContainer.tsx index bec0c2a31..41adcd8e2 100644 --- a/resources/scripts/components/server/files/FileManagerContainer.tsx +++ b/resources/scripts/components/server/files/FileManagerContainer.tsx @@ -11,6 +11,7 @@ import FileManagerBreadcrumbs from '@/components/server/files/FileManagerBreadcr import { FileObject } from '@/api/server/files/loadDirectory'; import NewDirectoryButton from '@/components/server/files/NewDirectoryButton'; import { Link } from 'react-router-dom'; +import Can from '@/components/elements/Can'; const sortFiles = (files: FileObject[]): FileObject[] => { return files.sort((a, b) => a.name.localeCompare(b.name)) @@ -34,7 +35,6 @@ export default () => { console.error(error.message, { error }); addError({ message: httpErrorToHuman(error), key: 'files' }); }); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ directory ]); return ( @@ -78,12 +78,17 @@ export default () => { } -
- - - New File - -
+ +
+ + + New File + +
+
} From 5d5a5c2afc7e067f3b2630c97d6cb2dd504ddf4b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 21:52:18 -0700 Subject: [PATCH 22/58] Fix array fill logicl; allow matching on any permissions --- resources/scripts/components/elements/Can.tsx | 14 ++++++++++++-- resources/scripts/plugins/usePermissions.ts | 4 +--- resources/scripts/routers/ServerRouter.tsx | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/resources/scripts/components/elements/Can.tsx b/resources/scripts/components/elements/Can.tsx index ee6393719..2990f2489 100644 --- a/resources/scripts/components/elements/Can.tsx +++ b/resources/scripts/components/elements/Can.tsx @@ -3,16 +3,26 @@ import { usePermissions } from '@/plugins/usePermissions'; interface Props { action: string | string[]; + matchAny?: boolean; renderOnError?: React.ReactNode | null; children: React.ReactNode; } -const Can = ({ action, renderOnError, children }: Props) => { +const Can = ({ action, matchAny = false, renderOnError, children }: Props) => { const can = usePermissions(action); + if (matchAny) { + console.log('Can.tsx', can); + } + return ( <> - {can.every(p => p) ? children : renderOnError} + { + ((matchAny && can.filter(p => p).length > 0) || (!matchAny && can.every(p => p))) ? + children + : + renderOnError + } ); }; diff --git a/resources/scripts/plugins/usePermissions.ts b/resources/scripts/plugins/usePermissions.ts index 974e09434..89dbd64bc 100644 --- a/resources/scripts/plugins/usePermissions.ts +++ b/resources/scripts/plugins/usePermissions.ts @@ -6,9 +6,7 @@ export const usePermissions = (action: string | string[]): boolean[] => { return useDeepMemo(() => { if (userPermissions[0] === '*') { - return ([] as boolean[]).fill( - true, 0, Array.isArray(action) ? action.length : 1, - ); + return Array(Array.isArray(action) ? action.length : 1).fill(true); } return (Array.isArray(action) ? action : [ action ]) diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index ff7fe73ec..241337b79 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -47,7 +47,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) Users - + Settings
From 3df134b5587fdd18c7de2aa5e33013be2c97094b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 22:04:38 -0700 Subject: [PATCH 23/58] Make sizing across margins/padding/width/height the same --- .../server/users/UsersContainer.tsx | 2 +- resources/styles/components/modal.css | 2 +- tailwind.js | 209 +++++------------- 3 files changed, 57 insertions(+), 156 deletions(-) diff --git a/resources/scripts/components/server/users/UsersContainer.tsx b/resources/scripts/components/server/users/UsersContainer.tsx index e2361684f..a6c863ea0 100644 --- a/resources/scripts/components/server/users/UsersContainer.tsx +++ b/resources/scripts/components/server/users/UsersContainer.tsx @@ -43,7 +43,7 @@ export default () => { } return ( -
+
{!subusers.length ?

diff --git a/resources/styles/components/modal.css b/resources/styles/components/modal.css index 03e41eb4f..5fdceaa87 100644 --- a/resources/styles/components/modal.css +++ b/resources/styles/components/modal.css @@ -4,7 +4,7 @@ transition: opacity 250ms ease; & > .modal-container { - @apply .relative .w-full .max-w-md .m-auto .flex-col .flex; + @apply .relative .w-full .max-w-1/2 .m-auto .flex-col .flex; /*&.top { margin-top: 10%; diff --git a/tailwind.js b/tailwind.js index e83e6d675..90a7e5782 100644 --- a/tailwind.js +++ b/tailwind.js @@ -120,6 +120,52 @@ let colors = { 'green-900': 'hsl(125, 97%, 14%)', }; +/* +|------------------------------------------------------------------------------- +| Sizes +|------------------------------------------------------------------------------- +| +| Here you can specify the sizes that should be available to all of the height, +| width, padding, and margin utilities. +| +*/ + +let sizes = { + 'auto': 'auto', + '0': '0', + 'px': '1px', + '1': '0.25rem', + '2': '0.5rem', + '3': '0.75rem', + '4': '1rem', + '5': '1.25rem', + '6': '1.5rem', + '8': '2rem', + '10': '2.5rem', + '12': '3rem', + '16': '4rem', + '20': '5rem', + '24': '6rem', + '28': '7rem', + '32': '8rem', + '48': '12rem', + '64': '16rem', + '96': '24rem', + '1/2': '50%', + '1/3': '33.33333%', + '2/3': '66.66667%', + '1/4': '25%', + '3/4': '75%', + '1/5': '20%', + '2/5': '40%', + '3/5': '60%', + '4/5': '80%', + '1/6': '16.66667%', + '5/6': '83.33333%', + 'full': '100%', + 'screen': '100vw', +}; + module.exports = { /* @@ -452,38 +498,7 @@ module.exports = { | */ - width: { - 'auto': 'auto', - 'px': '1px', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '5': '1.25rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '24': '6rem', - '32': '8rem', - '48': '12rem', - '64': '16rem', - '96': '24rem', - '1/2': '50%', - '1/3': '33.33333%', - '2/3': '66.66667%', - '1/4': '25%', - '3/4': '75%', - '1/5': '20%', - '2/5': '40%', - '3/5': '60%', - '4/5': '80%', - '1/6': '16.66667%', - '5/6': '83.33333%', - 'full': '100%', - 'screen': '100vw', - }, + width: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -500,26 +515,7 @@ module.exports = { | */ - height: { - 'auto': 'auto', - 'px': '1px', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '5': '1.25rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '24': '6rem', - '32': '8rem', - '48': '12rem', - '64': '16rem', - 'full': '100%', - 'screen': '100vh', - }, + height: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -535,25 +531,7 @@ module.exports = { | */ - minWidth: { - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '5': '1.25rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '24': '6rem', - '32': '8rem', - '48': '12rem', - '64': '16rem', - '96': '24rem', - 'full': '100%', - }, + minWidth: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -569,11 +547,7 @@ module.exports = { | */ - minHeight: { - '0': '0', - 'full': '100%', - 'screen': '100vh', - }, + minHeight: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -590,19 +564,7 @@ module.exports = { | */ - maxWidth: { - 'xxs': '10rem', - 'xs': '20rem', - 'sm': '30rem', - 'md': '40rem', - 'lg': '50rem', - 'xl': '60rem', - '2xl': '70rem', - '3xl': '80rem', - '4xl': '90rem', - '5xl': '100rem', - 'full': '100%', - }, + maxWidth: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -618,10 +580,7 @@ module.exports = { | */ - maxHeight: { - 'full': '100%', - 'screen': '100vh', - }, + maxHeight: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -638,23 +597,7 @@ module.exports = { | */ - padding: { - 'px': '1px', - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '5': '1.25rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '20': '5rem', - '24': '6rem', - '32': '8rem', - }, + padding: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -671,33 +614,7 @@ module.exports = { | */ - margin: { - 'n-12': '-3rem', - 'n-10': '-2.5rem', - 'n-8': '-2rem', - 'n-6': '-1.5rem', - 'n-4': '-1rem', - 'n-3': '-0.75rem', - 'n-2': '-0.5rem', - 'n-1': '-0.25rem', - 'n-px': '-1px', - 'auto': 'auto', - 'px': '1px', - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '5': '1.25rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '20': '5rem', - '24': '6rem', - '32': '8rem', - }, + margin: { ...sizes }, /* |----------------------------------------------------------------------------- @@ -714,23 +631,7 @@ module.exports = { | */ - negativeMargin: { - 'px': '1px', - '0': '0', - '1': '0.25rem', - '2': '0.5rem', - '3': '0.75rem', - '4': '1rem', - '5': '1.25rem', - '6': '1.5rem', - '8': '2rem', - '10': '2.5rem', - '12': '3rem', - '16': '4rem', - '20': '5rem', - '24': '6rem', - '32': '8rem', - }, + negativeMargin: { ...sizes }, /* |----------------------------------------------------------------------------- From 79095b526c1c44c9a7a765530ba45e0f8e3f58eb Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 22:05:30 -0700 Subject: [PATCH 24/58] Add permissions checking to the settings page --- .../server/settings/SettingsContainer.tsx | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/resources/scripts/components/server/settings/SettingsContainer.tsx b/resources/scripts/components/server/settings/SettingsContainer.tsx index d487bfc8f..ffdad1208 100644 --- a/resources/scripts/components/server/settings/SettingsContainer.tsx +++ b/resources/scripts/components/server/settings/SettingsContainer.tsx @@ -6,6 +6,7 @@ import { ApplicationStore } from '@/state'; import { UserData } from '@/state/user'; import RenameServerBox from '@/components/server/settings/RenameServerBox'; import FlashMessageRender from '@/components/FlashMessageRender'; +import Can from '@/components/elements/Can'; export default () => { const user = useStoreState(state => state.user.data!); @@ -15,46 +16,50 @@ export default () => {

- -
- - -
-
- - -
-
-
-
-

- Your SFTP password is the same as the password you use to access this panel. -

+ + +
+ + +
+
+ + +
+
+
+
+

+ Your SFTP password is the same as the password you use to access this panel. +

+
+
+
- +
+
+ +
+
- -
- -
+
); From 171b21e7ee219989daf87de7131d7644e527f4c2 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 22:12:50 -0700 Subject: [PATCH 25/58] Add permissions handling to the console; remove kill permission (wrapped in with stop) --- .../Api/Client/Servers/SendPowerRequest.php | 3 +- app/Models/Permission.php | 2 - .../scripts/components/server/Console.tsx | 10 +++- .../components/server/ServerConsole.tsx | 53 +++++++++++-------- resources/scripts/state/server/subusers.ts | 2 +- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php index 8686b4f67..ea7e00fcc 100644 --- a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php +++ b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php @@ -18,11 +18,10 @@ class SendPowerRequest extends ClientApiRequest case 'start': return Permission::ACTION_CONTROL_START; case 'stop': + case 'kill': return Permission::ACTION_CONTROL_STOP; case 'restart': return Permission::ACTION_CONTROL_RESTART; - case 'kill': - return Permission::ACTION_CONTROL_KILL; } return '__invalid'; diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 9a834ff76..df19f595c 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -20,7 +20,6 @@ class Permission extends Validable const ACTION_CONTROL_START = 'control.start'; const ACTION_CONTROL_STOP = 'control.stop'; const ACTION_CONTROL_RESTART = 'control.restart'; - const ACTION_CONTROL_KILL = 'control.kill'; const ACTION_DATABASE_READ = 'database.read'; const ACTION_DATABASE_CREATE = 'database.create'; @@ -111,7 +110,6 @@ class Permission extends Validable 'start' => 'Allows a user to start the server if it is stopped.', 'stop' => 'Allows a user to stop a server if it is running.', 'restart' => 'Allows a user to perform a server restart. This allows them to start the server if it is offline, but not put the server in a completely stopped state.', - 'kill' => 'Allows a user to terminate a server process.', ], ], diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 099943fe9..54d6e9620 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -4,6 +4,9 @@ import * as TerminalFit from 'xterm/lib/addons/fit/fit'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ServerContext } from '@/state/server'; import styled from 'styled-components'; +import Can from '@/components/elements/Can'; +import { usePermissions } from '@/plugins/usePermissions'; +import classNames from 'classnames'; const theme = { background: 'transparent', @@ -52,6 +55,7 @@ export default () => { const useRef = useCallback(node => setTerminalElement(node), []); const terminal = useMemo(() => new Terminal({ ...terminalProps }), []); const { connected, instance } = ServerContext.useStoreState(state => state.socket); + const [ canSendCommands ] = usePermissions([ 'control.console']); const handleConsoleOutput = (line: string, prelude = false) => terminal.writeln( (prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m', @@ -121,7 +125,9 @@ export default () => {
{ >
+ {canSendCommands &&
$
@@ -140,6 +147,7 @@ export default () => { />
+ }
); }; diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index 55668ffbd..434589f10 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -9,6 +9,7 @@ import { faMicrochip } from '@fortawesome/free-solid-svg-icons/faMicrochip'; import { bytesToHuman } from '@/helpers'; import SuspenseSpinner from '@/components/elements/SuspenseSpinner'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; +import Can from '@/components/elements/Can'; type PowerAction = 'start' | 'stop' | 'restart' | 'kill'; @@ -109,28 +110,36 @@ export default () => {  {cpu.toFixed(2)} %

-
- - - sendPowerCommand(action)}/> -
+ +
+ + + + + + + + sendPowerCommand(action)}/> + +
+
diff --git a/resources/scripts/state/server/subusers.ts b/resources/scripts/state/server/subusers.ts index c859ecead..5a6224853 100644 --- a/resources/scripts/state/server/subusers.ts +++ b/resources/scripts/state/server/subusers.ts @@ -2,7 +2,7 @@ import { action, Action } from 'easy-peasy'; export type SubuserPermission = 'websocket.*' | - 'control.console' | 'control.start' | 'control.stop' | 'control.restart' | 'control.kill' | + 'control.console' | 'control.start' | 'control.stop' | 'control.restart' | 'user.create' | 'user.read' | 'user.update' | 'user.delete' | 'file.create' | 'file.read' | 'file.update' | 'file.delete' | 'file.archive' | 'file.sftp' | 'allocation.read' | 'allocation.update' | From 7f0a05c192fd52b2793124147ef722b3e283b9ee Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Mar 2020 22:20:27 -0700 Subject: [PATCH 26/58] UPdate remainder of screens with basic permissions checking --- .../server/databases/DatabaseRow.tsx | 35 ++++++++++------- .../server/databases/DatabasesContainer.tsx | 21 +++++----- .../server/schedules/ScheduleContainer.tsx | 38 ++++++++++--------- .../schedules/ScheduleEditContainer.tsx | 34 +++++++++-------- .../server/schedules/ScheduleTaskRow.tsx | 37 ++++++++++-------- 5 files changed, 94 insertions(+), 71 deletions(-) diff --git a/resources/scripts/components/server/databases/DatabaseRow.tsx b/resources/scripts/components/server/databases/DatabaseRow.tsx index 4b4b1d3b0..1fd07774d 100644 --- a/resources/scripts/components/server/databases/DatabaseRow.tsx +++ b/resources/scripts/components/server/databases/DatabaseRow.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import { ServerDatabase } from '@/api/server/getServerDatabases'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faDatabase } from '@fortawesome/free-solid-svg-icons/faDatabase'; import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt'; @@ -16,6 +15,7 @@ import { ServerContext } from '@/state/server'; import deleteServerDatabase from '@/api/server/deleteServerDatabase'; import { httpErrorToHuman } from '@/api/http'; import RotatePasswordButton from '@/components/server/databases/RotatePasswordButton'; +import Can from '@/components/elements/Can'; interface Props { databaseId: string | number; @@ -24,10 +24,10 @@ interface Props { } export default ({ databaseId, className, onDelete }: Props) => { - const [visible, setVisible] = useState(false); + const [ visible, setVisible ] = useState(false); const database = ServerContext.useStoreState(state => state.databases.items.find(item => item.id === databaseId)); const appendDatabase = ServerContext.useStoreActions(actions => actions.databases.appendDatabase); - const [connectionVisible, setConnectionVisible] = useState(false); + const [ connectionVisible, setConnectionVisible ] = useState(false); const { addFlash, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); const server = ServerContext.useStoreState(state => state.server.data!); @@ -38,7 +38,7 @@ export default ({ databaseId, className, onDelete }: Props) => { const schema = object().shape({ confirm: string() .required('The database name must be provided.') - .oneOf([database.name.split('_', 2)[1], database.name], 'The database name must be provided.'), + .oneOf([ database.name.split('_', 2)[1], database.name ], 'The database name must be provided.'), }); const submit = (values: { confirm: string }, { setSubmitting }: FormikHelpers<{ confirm: string }>) => { @@ -73,7 +73,10 @@ export default ({ databaseId, className, onDelete }: Props) => { visible={visible} dismissable={!isSubmitting} showSpinnerOverlay={isSubmitting} - onDismissed={() => { setVisible(false); resetForm(); }} + onDismissed={() => { + setVisible(false); + resetForm(); + }} >

Confirm database deletion

@@ -113,10 +116,12 @@ export default ({ databaseId, className, onDelete }: Props) => { setConnectionVisible(false)}>

Database connection details

-
- - -
+ +
+ + +
+
{ />
- + + + @@ -156,9 +163,11 @@ export default ({ databaseId, className, onDelete }: Props) => { - + + +
diff --git a/resources/scripts/components/server/databases/DatabasesContainer.tsx b/resources/scripts/components/server/databases/DatabasesContainer.tsx index 30136423f..a5335f5c0 100644 --- a/resources/scripts/components/server/databases/DatabasesContainer.tsx +++ b/resources/scripts/components/server/databases/DatabasesContainer.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import getServerDatabases, { ServerDatabase } from '@/api/server/getServerDatabases'; +import getServerDatabases from '@/api/server/getServerDatabases'; import { ServerContext } from '@/state/server'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationStore } from '@/state'; @@ -9,6 +9,7 @@ import DatabaseRow from '@/components/server/databases/DatabaseRow'; import Spinner from '@/components/elements/Spinner'; import { CSSTransition } from 'react-transition-group'; import CreateDatabaseButton from '@/components/server/databases/CreateDatabaseButton'; +import Can from '@/components/elements/Can'; export default () => { const [ loading, setLoading ] = useState(true); @@ -41,7 +42,7 @@ export default () => { : - + <> {databases.length > 0 ? databases.map((database, index) => ( { :

{server.featureLimits.databases > 0 ? - `It looks like you have no databases. Click the button below to create one now.` + `It looks like you have no databases.` : `Databases cannot be created for this server.` }

} - {server.featureLimits.databases > 0 && -
- -
- } -
+ + {server.featureLimits.databases > 0 && +
+ +
+ } +
+
}
diff --git a/resources/scripts/components/server/schedules/ScheduleContainer.tsx b/resources/scripts/components/server/schedules/ScheduleContainer.tsx index 63f174a4c..2e771b1b9 100644 --- a/resources/scripts/components/server/schedules/ScheduleContainer.tsx +++ b/resources/scripts/components/server/schedules/ScheduleContainer.tsx @@ -9,6 +9,7 @@ import { httpErrorToHuman } from '@/api/http'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import EditScheduleModal from '@/components/server/schedules/EditScheduleModal'; +import Can from '@/components/elements/Can'; export default ({ match, history }: RouteComponentProps) => { const { uuid } = ServerContext.useStoreState(state => state.server.data!); @@ -35,9 +36,8 @@ export default ({ match, history }: RouteComponentProps) => { <> { schedules.length === 0 ? -

- There are no schedules configured for this server. Click the button below to get - started. +

+ There are no schedules configured for this server.

: schedules.map(schedule => ( @@ -54,21 +54,23 @@ export default ({ match, history }: RouteComponentProps) => { )) } -
- {visible && setSchedules(s => [...(s || []), schedule])} - onDismissed={() => setVisible(false)} - />} - -
+ +
+ {visible && setSchedules(s => [ ...(s || []), schedule ])} + onDismissed={() => setVisible(false)} + />} + +
+
}
diff --git a/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx b/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx index 77cfb9ee3..e601e8250 100644 --- a/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx +++ b/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx @@ -13,6 +13,7 @@ import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow'; import EditScheduleModal from '@/components/server/schedules/EditScheduleModal'; import NewTaskButton from '@/components/server/schedules/NewTaskButton'; import DeleteScheduleButton from '@/components/server/schedules/DeleteScheduleButton'; +import Can from '@/components/elements/Can'; interface Params { id: string; @@ -93,24 +94,27 @@ export default ({ match, history, location: { state } }: RouteComponentProps :

- There are no tasks configured for this schedule. Consider adding a new one using the - button below. + There are no tasks configured for this schedule.

}
- history.push(`/server/${id}/schedules`)} - /> - - setSchedule(s => ({ - ...s!, tasks: [ ...s!.tasks, task ], - }))} - /> + + history.push(`/server/${id}/schedules`)} + /> + + + + setSchedule(s => ({ + ...s!, tasks: [ ...s!.tasks, task ], + }))} + /> +
} diff --git a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx index 12e675ebc..93bd4d326 100644 --- a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx +++ b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx @@ -13,6 +13,7 @@ import { httpErrorToHuman } from '@/api/http'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal'; import { faPencilAlt } from '@fortawesome/free-solid-svg-icons/faPencilAlt'; +import Can from '@/components/elements/Can'; interface Props { schedule: number; @@ -75,22 +76,26 @@ export default ({ schedule, task, onTaskUpdated, onTaskRemoved }: Props) => {

} - - + + + + + +
); }; From 37c7ba336521eba2673cc9692c7e9a3917b2273a Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 3 Apr 2020 12:58:20 -0700 Subject: [PATCH 27/58] Update dependencies --- .php_cs | 1 + composer.json | 30 +- composer.lock | 2323 ++++++++++++++++++++++++++++++------------------- 3 files changed, 1445 insertions(+), 909 deletions(-) diff --git a/.php_cs b/.php_cs index 1e37fcb13..fdb94601d 100644 --- a/.php_cs +++ b/.php_cs @@ -33,6 +33,7 @@ return PhpCsFixer\Config::create() 'new_with_braces' => false, 'no_alias_functions' => true, 'no_multiline_whitespace_before_semicolons' => true, + 'no_superfluous_phpdoc_tags' => false, 'no_unreachable_default_argument_value' => true, 'no_useless_return' => true, 'not_operator_with_successor_space' => true, diff --git a/composer.json b/composer.json index 0bae14398..f582e060a 100644 --- a/composer.json +++ b/composer.json @@ -15,16 +15,16 @@ "ext-mbstring": "*", "ext-pdo_mysql": "*", "ext-zip": "*", - "appstract/laravel-blade-directives": "^1.6", - "aws/aws-sdk-php": "^3.110", - "cakephp/chronos": "^1.2", - "doctrine/dbal": "^2.9", + "appstract/laravel-blade-directives": "^1.8", + "aws/aws-sdk-php": "^3.134", + "cakephp/chronos": "^1.3", + "doctrine/dbal": "^2.10", "fideloper/proxy": "^4.2", - "guzzlehttp/guzzle": "^6.3", + "guzzlehttp/guzzle": "^6.5", "hashids/hashids": "^4.0", - "laracasts/utilities": "^3.0", - "laravel/framework": "^6.0.0", - "laravel/helpers": "^1.1", + "laracasts/utilities": "^3.1", + "laravel/framework": "^6.18", + "laravel/helpers": "^1.2", "laravel/tinker": "^1.0", "lcobucci/jwt": "^3.3", "matriphe/iso-639": "^1.2", @@ -32,18 +32,18 @@ "predis/predis": "^1.1", "prologue/alerts": "^0.4", "s1lentium/iptools": "^1.1", - "spatie/laravel-fractal": "^5.6", - "staudenmeir/belongs-to-through": "^2.6", - "symfony/yaml": "^4.0", - "webmozart/assert": "^1.5" + "spatie/laravel-fractal": "^5.7", + "staudenmeir/belongs-to-through": "^2.9", + "symfony/yaml": "^4.4", + "webmozart/assert": "^1.7" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.2", "barryvdh/laravel-ide-helper": "^2.6", "codedungeon/phpunit-result-printer": "0.25.1", - "friendsofphp/php-cs-fixer": "^2.15.1", - "laravel/dusk": "^5.5", - "php-mock/php-mock-phpunit": "^2.4", + "friendsofphp/php-cs-fixer": "^2.16.1", + "laravel/dusk": "^5.11", + "php-mock/php-mock-phpunit": "^2.6", "phpunit/phpunit": "^7" }, "autoload": { diff --git a/composer.lock b/composer.lock index 4c8759161..6cc218722 100644 --- a/composer.lock +++ b/composer.lock @@ -4,24 +4,24 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "39fbdca3eac026ce6a99684706ffa03b", + "content-hash": "1d03ca0a7151c6594255643eaaa8f527", "packages": [ { "name": "appstract/laravel-blade-directives", - "version": "1.6.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/appstract/laravel-blade-directives.git", - "reference": "72dbfce8c0a8f421293e211b17cef13e7fa17fc7" + "reference": "ac9958e5499d21b12c317a43d96e41c00eaa3fba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appstract/laravel-blade-directives/zipball/72dbfce8c0a8f421293e211b17cef13e7fa17fc7", - "reference": "72dbfce8c0a8f421293e211b17cef13e7fa17fc7", + "url": "https://api.github.com/repos/appstract/laravel-blade-directives/zipball/ac9958e5499d21b12c317a43d96e41c00eaa3fba", + "reference": "ac9958e5499d21b12c317a43d96e41c00eaa3fba", "shasum": "" }, "require": { - "laravel/framework": "^5.7|^6.0", + "laravel/framework": "^5.7|^6.0|^7.0", "php": "^7.1.3" }, "require-dev": { @@ -48,8 +48,8 @@ "authors": [ { "name": "Gijs Jorissen", - "email": "hello@appstract.team", - "homepage": "https://appstract.team", + "email": "gijs@appstract.nl", + "homepage": "https://appstract.nl", "role": "Developer" } ], @@ -59,30 +59,30 @@ "appstract", "laravel-blade-directives" ], - "time": "2019-09-04T09:05:25+00:00" + "time": "2020-03-04T08:57:34+00:00" }, { "name": "aws/aws-sdk-php", - "version": "3.110.9", + "version": "3.134.3", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "25fb32317e4dbd2b34caca01ed940cce12f7362c" + "reference": "3de2711a47e7c3f5e93a5c83f019188fd23f852f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/25fb32317e4dbd2b34caca01ed940cce12f7362c", - "reference": "25fb32317e4dbd2b34caca01ed940cce12f7362c", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3de2711a47e7c3f5e93a5c83f019188fd23f852f", + "reference": "3de2711a47e7c3f5e93a5c83f019188fd23f852f", "shasum": "" }, "require": { "ext-json": "*", "ext-pcre": "*", "ext-simplexml": "*", - "guzzlehttp/guzzle": "^5.3.3|^6.2.1", - "guzzlehttp/promises": "~1.0", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "~2.2", + "mtdowling/jmespath.php": "^2.5", "php": ">=5.5" }, "require-dev": { @@ -97,7 +97,8 @@ "nette/neon": "^2.3", "phpunit/phpunit": "^4.8.35|^5.4.3", "psr/cache": "^1.0", - "psr/simple-cache": "^1.0" + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" }, "suggest": { "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", @@ -142,30 +143,29 @@ "s3", "sdk" ], - "time": "2019-09-04T18:17:50+00:00" + "time": "2020-04-03T18:11:51+00:00" }, { "name": "cakephp/chronos", - "version": "1.2.8", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/cakephp/chronos.git", - "reference": "0292f06e8cc23fc82f0574889da2d8bf27b613c1" + "reference": "ba2bab98849e7bf29b02dd634ada49ab36472959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/0292f06e8cc23fc82f0574889da2d8bf27b613c1", - "reference": "0292f06e8cc23fc82f0574889da2d8bf27b613c1", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/ba2bab98849e7bf29b02dd634ada49ab36472959", + "reference": "ba2bab98849e7bf29b02dd634ada49ab36472959", "shasum": "" }, "require": { - "php": "^5.5.9|^7" + "php": ">=5.6" }, "require-dev": { "athletic/athletic": "~0.1", "cakephp/cakephp-codesniffer": "^3.0", "phpbench/phpbench": "@dev", - "phpstan/phpstan": "^0.6.4", "phpunit/phpunit": "<6.0 || ^7.0" }, "type": "library", @@ -199,29 +199,29 @@ "datetime", "time" ], - "time": "2019-06-17T15:19:18+00:00" + "time": "2019-11-30T02:33:19+00:00" }, { "name": "dnoegel/php-xdg-base-dir", - "version": "0.1", + "version": "v0.1.1", "source": { "type": "git", "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", "shasum": "" }, "require": { "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "@stable" + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" }, - "type": "project", + "type": "library", "autoload": { "psr-4": { "XdgBaseDir\\": "src/" @@ -232,20 +232,20 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", - "time": "2014-10-24T07:27:01+00:00" + "time": "2019-12-04T15:06:13+00:00" }, { "name": "doctrine/cache", - "version": "v1.8.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "url": "https://api.github.com/repos/doctrine/cache/zipball/382e7f4db9a12dc6c19431743a2b096041bcdd62", + "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62", "shasum": "" }, "require": { @@ -256,7 +256,7 @@ }, "require-dev": { "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^4.0", + "doctrine/coding-standard": "^6.0", "mongodb/mongodb": "^1.1", "phpunit/phpunit": "^7.0", "predis/predis": "~1.0" @@ -267,7 +267,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.9.x-dev" } }, "autoload": { @@ -280,6 +280,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -288,10 +292,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -301,41 +301,47 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "https://www.doctrine-project.org", + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", "keywords": [ + "abstraction", + "apcu", "cache", - "caching" + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" ], - "time": "2018-08-21T18:01:43+00:00" + "time": "2019-11-29T15:36:20+00:00" }, { "name": "doctrine/dbal", - "version": "v2.9.2", + "version": "v2.10.1", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9" + "reference": "c2b8e6e82732a64ecde1cddf9e1e06cb8556e3d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9", - "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/c2b8e6e82732a64ecde1cddf9e1e06cb8556e3d8", + "reference": "c2b8e6e82732a64ecde1cddf9e1e06cb8556e3d8", "shasum": "" }, "require": { "doctrine/cache": "^1.0", "doctrine/event-manager": "^1.0", "ext-pdo": "*", - "php": "^7.1" + "php": "^7.2" }, "require-dev": { - "doctrine/coding-standard": "^5.0", - "jetbrains/phpstorm-stubs": "^2018.1.2", - "phpstan/phpstan": "^0.10.1", - "phpunit/phpunit": "^7.4", - "symfony/console": "^2.0.5|^3.0|^4.0", - "symfony/phpunit-bridge": "^3.4.5|^4.0.5" + "doctrine/coding-standard": "^6.0", + "jetbrains/phpstorm-stubs": "^2019.1", + "phpstan/phpstan": "^0.11.3", + "phpunit/phpunit": "^8.4.1", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -346,7 +352,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", + "dev-master": "2.10.x-dev", "dev-develop": "3.0.x-dev" } }, @@ -360,6 +366,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -368,10 +378,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -382,27 +388,38 @@ "keywords": [ "abstraction", "database", + "db2", "dbal", + "mariadb", + "mssql", "mysql", - "persistence", + "oci8", + "oracle", + "pdo", "pgsql", - "php", - "queryobject" + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlanywhere", + "sqlite", + "sqlserver", + "sqlsrv" ], - "time": "2018-12-31T03:27:51+00:00" + "time": "2020-01-04T12:56:21+00:00" }, { "name": "doctrine/event-manager", - "version": "v1.0.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" + "reference": "629572819973f13486371cb611386eb17851e85c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", - "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/629572819973f13486371cb611386eb17851e85c", + "reference": "629572819973f13486371cb611386eb17851e85c", "shasum": "" }, "require": { @@ -412,7 +429,7 @@ "doctrine/common": "<2.9@dev" }, "require-dev": { - "doctrine/coding-standard": "^4.0", + "doctrine/coding-standard": "^6.0", "phpunit/phpunit": "^7.0" }, "type": "library", @@ -431,6 +448,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -439,10 +460,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -456,27 +473,29 @@ "email": "ocramius@gmail.com" } ], - "description": "Doctrine Event Manager component", + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", "homepage": "https://www.doctrine-project.org/projects/event-manager.html", "keywords": [ "event", - "eventdispatcher", - "eventmanager" + "event dispatcher", + "event manager", + "event system", + "events" ], - "time": "2018-06-11T11:59:03+00:00" + "time": "2019-11-10T09:48:07+00:00" }, { "name": "doctrine/inflector", - "version": "v1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/ec3a55242203ffa6a4b27c58176da97ff0a7aec1", + "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1", "shasum": "" }, "require": { @@ -501,6 +520,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -509,10 +532,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -530,20 +549,20 @@ "singularize", "string" ], - "time": "2018-01-09T20:05:19+00:00" + "time": "2019-10-30T19:59:35+00:00" }, { "name": "doctrine/lexer", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea" + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea", - "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", "shasum": "" }, "require": { @@ -557,7 +576,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -592,7 +611,7 @@ "parser", "php" ], - "time": "2019-07-30T19:33:28+00:00" + "time": "2019-10-30T14:39:59+00:00" }, { "name": "dragonmantank/cron-expression", @@ -650,27 +669,27 @@ }, { "name": "egulias/email-validator", - "version": "2.1.11", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23" + "reference": "ade6887fd9bd74177769645ab5c474824f8a418a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23", - "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ade6887fd9bd74177769645ab5c474824f8a418a", + "reference": "ade6887fd9bd74177769645ab5c474824f8a418a", "shasum": "" }, "require": { "doctrine/lexer": "^1.0.1", - "php": ">= 5.5" + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.10" }, "require-dev": { - "dominicsayers/isemail": "dev-master", - "phpunit/phpunit": "^4.8.35||^5.7||^6.0", - "satooshi/php-coveralls": "^1.0.1", - "symfony/phpunit-bridge": "^4.4@dev" + "dominicsayers/isemail": "^3.0.7", + "phpunit/phpunit": "^4.8.36|^7.5.15", + "satooshi/php-coveralls": "^1.0.1" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -704,74 +723,28 @@ "validation", "validator" ], - "time": "2019-08-13T17:33:27+00:00" - }, - { - "name": "erusev/parsedown", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/erusev/parsedown.git", - "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7", - "reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35" - }, - "type": "library", - "autoload": { - "psr-0": { - "Parsedown": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Emanuil Rusev", - "email": "hello@erusev.com", - "homepage": "http://erusev.com" - } - ], - "description": "Parser for Markdown.", - "homepage": "http://parsedown.org", - "keywords": [ - "markdown", - "parser" - ], - "time": "2019-03-17T18:48:37+00:00" + "time": "2020-02-13T22:36:52+00:00" }, { "name": "fideloper/proxy", - "version": "4.2.1", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/fideloper/TrustedProxy.git", - "reference": "03085e58ec7bee24773fa5a8850751a6e61a7e8a" + "reference": "ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/03085e58ec7bee24773fa5a8850751a6e61a7e8a", - "reference": "03085e58ec7bee24773fa5a8850751a6e61a7e8a", + "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a", + "reference": "ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a", "shasum": "" }, "require": { - "illuminate/contracts": "^5.0|^6.0|^7.0", + "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0", "php": ">=5.4.0" }, "require-dev": { - "illuminate/http": "^5.0|^6.0|^7.0", + "illuminate/http": "^5.0|^6.0|^7.0|^8.0", "mockery/mockery": "^1.0", "phpunit/phpunit": "^6.0" }, @@ -804,48 +777,50 @@ "proxy", "trusted proxy" ], - "time": "2019-09-03T16:45:42+00:00" + "time": "2020-02-22T01:51:47+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "version": "6.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", "shasum": "" }, "require": { + "ext-json": "*", "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", + "guzzlehttp/psr7": "^1.6.1", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" + "psr/log": "^1.1" }, "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.5-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -869,7 +844,7 @@ "rest", "web service" ], - "time": "2018-04-22T15:46:56+00:00" + "time": "2019-12-23T11:57:10+00:00" }, { "name": "guzzlehttp/promises", @@ -1100,6 +1075,7 @@ "email": "jakub.onderka@gmail.com" } ], + "abandoned": "php-parallel-lint/php-console-color", "time": "2018-09-29T17:23:10+00:00" }, { @@ -1146,25 +1122,26 @@ } ], "description": "Highlight PHP code in terminal", + "abandoned": "php-parallel-lint/php-console-highlighter", "time": "2018-09-29T18:48:56+00:00" }, { "name": "laracasts/utilities", - "version": "3.0.1", + "version": "3.1", "source": { "type": "git", "url": "https://github.com/laracasts/PHP-Vars-To-Js-Transformer.git", - "reference": "490c6cf4d23c26c94e01867265c84921863a5f19" + "reference": "7c5eb11221de608eef8c70c2d3540c8cd80466e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laracasts/PHP-Vars-To-Js-Transformer/zipball/490c6cf4d23c26c94e01867265c84921863a5f19", - "reference": "490c6cf4d23c26c94e01867265c84921863a5f19", + "url": "https://api.github.com/repos/laracasts/PHP-Vars-To-Js-Transformer/zipball/7c5eb11221de608eef8c70c2d3540c8cd80466e3", + "reference": "7c5eb11221de608eef8c70c2d3540c8cd80466e3", "shasum": "" }, "require": { - "illuminate/support": "^5.0|^6.0", - "php": ">=5.5.0" + "illuminate/support": "^5.0|^6.0|^7.0", + "php": ">=5.5.0|>=7.2.5" }, "require-dev": { "phpspec/phpspec": "~2.0" @@ -1203,30 +1180,30 @@ "javascript", "laravel" ], - "time": "2019-09-04T12:55:27+00:00" + "time": "2020-03-03T16:07:08+00:00" }, { "name": "laravel/framework", - "version": "v6.0.0", + "version": "v6.18.3", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "89c81d4dc37714d82521d05dc003b26b2a86defc" + "reference": "4e48acfaba87f08320a2764d36c3b6a4a4112ccf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/89c81d4dc37714d82521d05dc003b26b2a86defc", - "reference": "89c81d4dc37714d82521d05dc003b26b2a86defc", + "url": "https://api.github.com/repos/laravel/framework/zipball/4e48acfaba87f08320a2764d36c3b6a4a4112ccf", + "reference": "4e48acfaba87f08320a2764d36c3b6a4a4112ccf", "shasum": "" }, "require": { "doctrine/inflector": "^1.1", "dragonmantank/cron-expression": "^2.0", "egulias/email-validator": "^2.1.10", - "erusev/parsedown": "^1.7", "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", + "league/commonmark": "^1.3", "league/flysystem": "^1.0.8", "monolog/monolog": "^1.12|^2.0", "nesbot/carbon": "^2.0", @@ -1284,16 +1261,15 @@ "aws/aws-sdk-php": "^3.0", "doctrine/dbal": "^2.6", "filp/whoops": "^2.4", - "guzzlehttp/guzzle": "^6.3", + "guzzlehttp/guzzle": "^6.3|^7.0", "league/flysystem-cached-adapter": "^1.0", - "mockery/mockery": "^1.2.3", + "mockery/mockery": "^1.3.1", "moontoast/math": "^1.1", "orchestra/testbench-core": "^4.0", "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8.3", + "phpunit/phpunit": "^7.5.15|^8.4|^9.0", "predis/predis": "^1.1.1", - "symfony/cache": "^4.3", - "true/punycode": "^2.1" + "symfony/cache": "^4.3.4" }, "suggest": { "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.0).", @@ -1302,19 +1278,21 @@ "ext-memcached": "Required to use the memcache cache driver.", "ext-pcntl": "Required to use all features of the queue worker.", "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", "filp/whoops": "Required for friendly error pages in development (^2.4).", - "fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).", - "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.0).", - "laravel/tinker": "Required to use the tinker console command (^1.0).", + "fzaninotto/faker": "Required to use the eloquent factory builder (^1.9.1).", + "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.0|^7.0).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", "moontoast/math": "Required to use ordered UUIDs (^1.1).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^4.3).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.1).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^4.3.4).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.2).", "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." }, "type": "library", @@ -1348,24 +1326,24 @@ "framework", "laravel" ], - "time": "2019-09-03T13:09:57+00:00" + "time": "2020-03-24T16:37:50+00:00" }, { "name": "laravel/helpers", - "version": "v1.1.1", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/laravel/helpers.git", - "reference": "b8eae9ddd461e89d0296f74fd069c413bf83b6fa" + "reference": "1f978fc5dad9f7f906b18242c654252615201de4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/helpers/zipball/b8eae9ddd461e89d0296f74fd069c413bf83b6fa", - "reference": "b8eae9ddd461e89d0296f74fd069c413bf83b6fa", + "url": "https://api.github.com/repos/laravel/helpers/zipball/1f978fc5dad9f7f906b18242c654252615201de4", + "reference": "1f978fc5dad9f7f906b18242c654252615201de4", "shasum": "" }, "require": { - "illuminate/support": "~5.8.0|^6.0", + "illuminate/support": "~5.8.0|^6.0|^7.0", "php": ">=7.1.3" }, "require-dev": { @@ -1401,7 +1379,7 @@ "helpers", "laravel" ], - "time": "2019-07-30T15:25:31+00:00" + "time": "2020-03-03T13:52:16+00:00" }, { "name": "laravel/tinker", @@ -1522,17 +1500,91 @@ "time": "2019-05-24T18:30:49+00:00" }, { - "name": "league/flysystem", - "version": "1.0.55", + "name": "league/commonmark", + "version": "1.3.2", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6" + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "75542a366ccbe1896ed79fcf3e8e68206d6c4257" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6", - "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/75542a366ccbe1896ed79fcf3e8e68206d6c4257", + "reference": "75542a366ccbe1896ed79fcf3e8e68206d6c4257", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1" + }, + "conflict": { + "scrutinizer/ocular": "1.7.*" + }, + "require-dev": { + "cebe/markdown": "~1.0", + "commonmark/commonmark.js": "0.29.1", + "erusev/parsedown": "~1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "~1.4", + "mikehaertl/php-shellcommand": "^1.4", + "phpstan/phpstan-shim": "^0.11.5", + "phpunit/phpunit": "^7.5", + "scrutinizer/ocular": "^1.5", + "symfony/finder": "^4.2" + }, + "bin": [ + "bin/commonmark" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "time": "2020-03-25T19:55:28+00:00" + }, + { + "name": "league/flysystem", + "version": "1.0.66", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/021569195e15f8209b1c4bebb78bd66aa4f08c21", + "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21", "shasum": "" }, "require": { @@ -1544,7 +1596,7 @@ }, "require-dev": { "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.10" + "phpunit/phpunit": "^5.7.26" }, "suggest": { "ext-fileinfo": "Required for MimeType", @@ -1603,20 +1655,26 @@ "sftp", "storage" ], - "time": "2019-08-24T11:17:19+00:00" + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2020-03-17T18:58:12+00:00" }, { "name": "league/fractal", - "version": "0.18.0", + "version": "0.19.2", "source": { "type": "git", "url": "https://github.com/thephpleague/fractal.git", - "reference": "4e553dae1a9402adbe11c81430a64675dc97b4fc" + "reference": "06dc15f6ba38f2dde2f919d3095d13b571190a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/fractal/zipball/4e553dae1a9402adbe11c81430a64675dc97b4fc", - "reference": "4e553dae1a9402adbe11c81430a64675dc97b4fc", + "url": "https://api.github.com/repos/thephpleague/fractal/zipball/06dc15f6ba38f2dde2f919d3095d13b571190a7c", + "reference": "06dc15f6ba38f2dde2f919d3095d13b571190a7c", "shasum": "" }, "require": { @@ -1628,7 +1686,7 @@ "mockery/mockery": "~0.9", "pagerfanta/pagerfanta": "~1.0.0", "phpunit/phpunit": "^4.8.35 || ^7.5", - "squizlabs/php_codesniffer": "~1.5", + "squizlabs/php_codesniffer": "~1.5|~2.0|~3.4", "zendframework/zend-paginator": "~2.3" }, "suggest": { @@ -1667,7 +1725,7 @@ "league", "rest" ], - "time": "2019-05-10T02:16:43+00:00" + "time": "2020-01-24T23:17:29+00:00" }, { "name": "matriphe/iso-639", @@ -1715,16 +1773,16 @@ }, { "name": "monolog/monolog", - "version": "2.0.0", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "68545165e19249013afd1d6f7485aecff07a2d22" + "reference": "c861fcba2ca29404dc9e617eedd9eff4616986b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/68545165e19249013afd1d6f7485aecff07a2d22", - "reference": "68545165e19249013afd1d6f7485aecff07a2d22", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c861fcba2ca29404dc9e617eedd9eff4616986b8", + "reference": "c861fcba2ca29404dc9e617eedd9eff4616986b8", "shasum": "" }, "require": { @@ -1792,27 +1850,29 @@ "logging", "psr-3" ], - "time": "2019-08-30T09:56:44+00:00" + "time": "2019-12-20T14:22:59+00:00" }, { "name": "mtdowling/jmespath.php", - "version": "2.4.0", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac" + "reference": "52168cb9472de06979613d365c7f1ab8798be895" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/adcc9531682cf87dfda21e1fd5d0e7a41d292fac", - "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" }, "bin": [ "bin/jp.php" @@ -1820,7 +1880,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -1847,31 +1907,32 @@ "json", "jsonpath" ], - "time": "2016-12-03T22:08:25+00:00" + "time": "2019-12-30T18:03:34+00:00" }, { "name": "nesbot/carbon", - "version": "2.24.0", + "version": "2.32.2", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29" + "reference": "f10e22cf546704fab1db4ad4b9dedbc5c797a0dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/934459c5ac0658bc765ad1e53512c7c77adcac29", - "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/f10e22cf546704fab1db4ad4b9dedbc5c797a0dc", + "reference": "f10e22cf546704fab1db4ad4b9dedbc5c797a0dc", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.1.8 || ^8.0", - "symfony/translation": "^3.4 || ^4.0" + "symfony/translation": "^3.4 || ^4.0 || ^5.0" }, "require-dev": { + "doctrine/orm": "^2.7", "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", "kylekatarnls/multi-tester": "^1.1", - "phpmd/phpmd": "dev-php-7.1-compatibility", + "phpmd/phpmd": "^2.8", "phpstan/phpstan": "^0.11", "phpunit/phpunit": "^7.5 || ^8.0", "squizlabs/php_codesniffer": "^3.4" @@ -1881,6 +1942,9 @@ ], "type": "library", "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -1907,27 +1971,37 @@ "homepage": "http://github.com/kylekatarnls" } ], - "description": "A API extension for DateTime that supports 281 different languages.", + "description": "An API extension for DateTime that supports 281 different languages.", "homepage": "http://carbon.nesbot.com", "keywords": [ "date", "datetime", "time" ], - "time": "2019-08-31T16:37:55+00:00" + "funding": [ + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2020-03-31T13:43:19+00:00" }, { "name": "nikic/php-parser", - "version": "v4.2.4", + "version": "v4.3.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "97e59c7a16464196a8b9c77c47df68e4a39a45c4" + "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/97e59c7a16464196a8b9c77c47df68e4a39a45c4", - "reference": "97e59c7a16464196a8b9c77c47df68e4a39a45c4", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9a9981c347c5c49d6dfe5cf826bb882b824080dc", + "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc", "shasum": "" }, "require": { @@ -1935,6 +2009,7 @@ "php": ">=7.0" }, "require-dev": { + "ircmaxell/php-yacc": "0.0.5", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" }, "bin": [ @@ -1943,7 +2018,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -1965,20 +2040,20 @@ "parser", "php" ], - "time": "2019-09-01T07:51:21+00:00" + "time": "2019-11-08T13:50:10+00:00" }, { "name": "opis/closure", - "version": "3.4.0", + "version": "3.5.1", "source": { "type": "git", "url": "https://github.com/opis/closure.git", - "reference": "60a97fff133b1669a5b1776aa8ab06db3f3962b7" + "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/60a97fff133b1669a5b1776aa8ab06db3f3962b7", - "reference": "60a97fff133b1669a5b1776aa8ab06db3f3962b7", + "url": "https://api.github.com/repos/opis/closure/zipball/93ebc5712cdad8d5f489b500c59d122df2e53969", + "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969", "shasum": "" }, "require": { @@ -1991,7 +2066,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3.x-dev" + "dev-master": "3.5.x-dev" } }, "autoload": { @@ -2026,28 +2101,28 @@ "serialization", "serialize" ], - "time": "2019-09-02T21:07:33+00:00" + "time": "2019-11-29T22:36:02+00:00" }, { "name": "paragonie/constant_time_encoding", - "version": "v2.2.3", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "55af0dc01992b4d0da7f6372e2eac097bbbaffdb" + "reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/55af0dc01992b4d0da7f6372e2eac097bbbaffdb", - "reference": "55af0dc01992b4d0da7f6372e2eac097bbbaffdb", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2", + "reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2", "shasum": "" }, "require": { - "php": "^7" + "php": "^7|^8" }, "require-dev": { "phpunit/phpunit": "^6|^7", - "vimeo/psalm": "^1|^2" + "vimeo/psalm": "^1|^2|^3" }, "type": "library", "autoload": { @@ -2088,7 +2163,7 @@ "hex2bin", "rfc4648" ], - "time": "2019-01-03T20:26:31+00:00" + "time": "2019-11-06T19:20:29+00:00" }, { "name": "paragonie/random_compat", @@ -2137,43 +2212,48 @@ }, { "name": "phpoption/phpoption", - "version": "1.5.0", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/4acfd6a4b33a509d8c88f50e5222f734b6aeebae", + "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "4.7.*" + "bamarni/composer-bin-plugin": "^1.3", + "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.7-dev" } }, "autoload": { - "psr-0": { - "PhpOption\\": "src/" + "psr-4": { + "PhpOption\\": "src/PhpOption/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "Apache-2.0" ], "authors": [ { "name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" } ], "description": "Option Type for PHP", @@ -2183,7 +2263,7 @@ "php", "type" ], - "time": "2015-07-25T16:39:46+00:00" + "time": "2020-03-21T18:07:53+00:00" }, { "name": "pragmarx/google2fa", @@ -2293,22 +2373,22 @@ }, { "name": "prologue/alerts", - "version": "0.4.5", + "version": "0.4.6", "source": { "type": "git", "url": "https://github.com/prologuephp/alerts.git", - "reference": "b4357dfa8abd5c58e41b8bb1fddc0781184d35da" + "reference": "4c621a541a7f16631deda9d1b2b075c182d251d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/prologuephp/alerts/zipball/b4357dfa8abd5c58e41b8bb1fddc0781184d35da", - "reference": "b4357dfa8abd5c58e41b8bb1fddc0781184d35da", + "url": "https://api.github.com/repos/prologuephp/alerts/zipball/4c621a541a7f16631deda9d1b2b075c182d251d1", + "reference": "4c621a541a7f16631deda9d1b2b075c182d251d1", "shasum": "" }, "require": { - "illuminate/config": "~5|~6", - "illuminate/session": "~5|~6", - "illuminate/support": "~5|~6", + "illuminate/config": "~5|~6|~7", + "illuminate/session": "~5|~6|~7", + "illuminate/support": "~5|~6|~7", "php": ">=5.4.0" }, "require-dev": { @@ -2355,7 +2435,7 @@ "laravel", "messages" ], - "time": "2019-09-04T04:10:03+00:00" + "time": "2020-03-03T08:33:38+00:00" }, { "name": "psr/container", @@ -2458,16 +2538,16 @@ }, { "name": "psr/log", - "version": "1.1.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, "require": { @@ -2476,7 +2556,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -2501,7 +2581,7 @@ "psr", "psr-3" ], - "time": "2018-11-20T15:27:04+00:00" + "time": "2020-03-23T09:12:05+00:00" }, { "name": "psr/simple-cache", @@ -2553,27 +2633,27 @@ }, { "name": "psy/psysh", - "version": "v0.9.9", + "version": "v0.9.12", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "9aaf29575bb8293206bb0420c1e1c87ff2ffa94e" + "reference": "90da7f37568aee36b116a030c5f99c915267edd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/9aaf29575bb8293206bb0420c1e1c87ff2ffa94e", - "reference": "9aaf29575bb8293206bb0420c1e1c87ff2ffa94e", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/90da7f37568aee36b116a030c5f99c915267edd4", + "reference": "90da7f37568aee36b116a030c5f99c915267edd4", "shasum": "" }, "require": { - "dnoegel/php-xdg-base-dir": "0.1", + "dnoegel/php-xdg-base-dir": "0.1.*", "ext-json": "*", "ext-tokenizer": "*", "jakub-onderka/php-console-highlighter": "0.3.*|0.4.*", "nikic/php-parser": "~1.3|~2.0|~3.0|~4.0", "php": ">=5.4.0", - "symfony/console": "~2.3.10|^2.4.2|~3.0|~4.0", - "symfony/var-dumper": "~2.7|~3.0|~4.0" + "symfony/console": "~2.3.10|^2.4.2|~3.0|~4.0|~5.0", + "symfony/var-dumper": "~2.7|~3.0|~4.0|~5.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.2", @@ -2623,7 +2703,7 @@ "interactive", "shell" ], - "time": "2018-10-13T15:16:03+00:00" + "time": "2019-12-06T14:19:43+00:00" }, { "name": "ralouphie/getallheaders", @@ -2667,44 +2747,46 @@ }, { "name": "ramsey/uuid", - "version": "3.8.0", + "version": "3.9.3", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3" + "reference": "7e1633a6964b48589b142d60542f9ed31bd37a92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/d09ea80159c1929d75b3f9c60504d613aeb4a1e3", - "reference": "d09ea80159c1929d75b3f9c60504d613aeb4a1e3", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/7e1633a6964b48589b142d60542f9ed31bd37a92", + "reference": "7e1633a6964b48589b142d60542f9ed31bd37a92", "shasum": "" }, "require": { - "paragonie/random_compat": "^1.0|^2.0|9.99.99", - "php": "^5.4 || ^7.0", + "ext-json": "*", + "paragonie/random_compat": "^1 | ^2 | 9.99.99", + "php": "^5.4 | ^7 | ^8", "symfony/polyfill-ctype": "^1.8" }, "replace": { "rhumsaa/uuid": "self.version" }, "require-dev": { - "codeception/aspect-mock": "^1.0 | ~2.0.0", - "doctrine/annotations": "~1.2.0", - "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ~2.1.0", - "ircmaxell/random-lib": "^1.1", - "jakub-onderka/php-parallel-lint": "^0.9.0", - "mockery/mockery": "^0.9.9", + "codeception/aspect-mock": "^1 | ^2", + "doctrine/annotations": "^1.2", + "goaop/framework": "1.0.0-alpha.2 | ^1 | ^2.1", + "jakub-onderka/php-parallel-lint": "^1", + "mockery/mockery": "^0.9.11 | ^1", "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", - "phpunit/phpunit": "^4.7|^5.0|^6.5", - "squizlabs/php_codesniffer": "^2.3" + "paragonie/random-lib": "^2", + "php-mock/php-mock-phpunit": "^0.3 | ^1.1", + "phpunit/phpunit": "^4.8 | ^5.4 | ^6.5", + "squizlabs/php_codesniffer": "^3.5" }, "suggest": { "ext-ctype": "Provides support for PHP Ctype functions", "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", + "ext-openssl": "Provides the OpenSSL extension for use with the OpenSslGenerator", "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, @@ -2717,13 +2799,21 @@ "autoload": { "psr-4": { "Ramsey\\Uuid\\": "src/" - } + }, + "files": [ + "src/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + }, { "name": "Marijn Huizendveld", "email": "marijn.huizendveld@gmail.com" @@ -2731,11 +2821,6 @@ { "name": "Thibaud Fabre", "email": "thibaud@aztech.io" - }, - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" } ], "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", @@ -2745,7 +2830,7 @@ "identifier", "uuid" ], - "time": "2018-07-19T23:38:55+00:00" + "time": "2020-02-21T04:36:14+00:00" }, { "name": "s1lentium/iptools", @@ -2800,20 +2885,20 @@ }, { "name": "spatie/fractalistic", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/spatie/fractalistic.git", - "reference": "9d29952f4552d6fc117e1f44f89d8dfdd36c393d" + "reference": "9d7594c4e9ed2657eb017db93ab6dbb58f56e049" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/fractalistic/zipball/9d29952f4552d6fc117e1f44f89d8dfdd36c393d", - "reference": "9d29952f4552d6fc117e1f44f89d8dfdd36c393d", + "url": "https://api.github.com/repos/spatie/fractalistic/zipball/9d7594c4e9ed2657eb017db93ab6dbb58f56e049", + "reference": "9d7594c4e9ed2657eb017db93ab6dbb58f56e049", "shasum": "" }, "require": { - "league/fractal": "^0.18.0", + "league/fractal": "^0.19.0", "php": "^7.0" }, "require-dev": { @@ -2833,9 +2918,9 @@ "authors": [ { "name": "Freek Van der Herten", - "role": "Developer", "email": "freek@spatie.be", - "homepage": "https://spatie.be" + "homepage": "https://spatie.be", + "role": "Developer" } ], "description": "A developer friendly wrapper around Fractal", @@ -2847,31 +2932,31 @@ "spatie", "transform" ], - "time": "2019-05-13T07:08:06+00:00" + "time": "2020-01-31T11:54:45+00:00" }, { "name": "spatie/laravel-fractal", - "version": "5.6.0", + "version": "5.7.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-fractal.git", - "reference": "21704e4a2cf62d9c2b981395fa5b15bd06848ed5" + "reference": "482b73a7942d14087167b32c6681351c81afaeb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/21704e4a2cf62d9c2b981395fa5b15bd06848ed5", - "reference": "21704e4a2cf62d9c2b981395fa5b15bd06848ed5", + "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/482b73a7942d14087167b32c6681351c81afaeb3", + "reference": "482b73a7942d14087167b32c6681351c81afaeb3", "shasum": "" }, "require": { - "illuminate/contracts": "~5.7.0|~5.8.0|^6.0", - "illuminate/support": "~5.7.0|~5.8.0|^6.0", + "illuminate/contracts": "~5.8.0|^6.0|^7.0", + "illuminate/support": "~5.8.0|^6.0|^7.0", "php": "^7.2", "spatie/fractalistic": "^2.5" }, "require-dev": { "dms/phpunit-arraysubset-asserts": "^0.1.0", - "orchestra/testbench": "~3.7.0|~3.8.0|^4.0" + "orchestra/testbench": "~3.8.0|^4.0|^5.0" }, "type": "library", "extra": { @@ -2915,20 +3000,26 @@ "spatie", "transform" ], - "time": "2019-09-04T07:02:37+00:00" + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2020-03-02T18:40:49+00:00" }, { "name": "staudenmeir/belongs-to-through", - "version": "v2.6", + "version": "v2.9", "source": { "type": "git", "url": "https://github.com/staudenmeir/belongs-to-through.git", - "reference": "1f961dcc05e23aba291eb9f3da33b405b48a06ce" + "reference": "8f16bb7b51d081d90d9b093ba6f380f71a96d79f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/belongs-to-through/zipball/1f961dcc05e23aba291eb9f3da33b405b48a06ce", - "reference": "1f961dcc05e23aba291eb9f3da33b405b48a06ce", + "url": "https://api.github.com/repos/staudenmeir/belongs-to-through/zipball/8f16bb7b51d081d90d9b093ba6f380f71a96d79f", + "reference": "8f16bb7b51d081d90d9b093ba6f380f71a96d79f", "shasum": "" }, "require": { @@ -2959,20 +3050,20 @@ } ], "description": "Laravel Eloquent BelongsToThrough relationship", - "time": "2019-08-25T23:38:38+00:00" + "time": "2019-12-29T10:58:12+00:00" }, { "name": "swiftmailer/swiftmailer", - "version": "v6.2.1", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a" + "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a", - "reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9", + "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9", "shasum": "" }, "require": { @@ -3021,31 +3112,32 @@ "mail", "mailer" ], - "time": "2019-04-21T09:21:45+00:00" + "time": "2019-11-12T09:31:26+00:00" }, { "name": "symfony/console", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "de63799239b3881b8a08f8481b22348f77ed7b36" + "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/de63799239b3881b8a08f8481b22348f77ed7b36", - "reference": "de63799239b3881b8a08f8481b22348f77ed7b36", + "url": "https://api.github.com/repos/symfony/console/zipball/10bb3ee3c97308869d53b3e3d03f6ac23ff985f7", + "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1" + "symfony/service-contracts": "^1.1|^2" }, "conflict": { "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", "symfony/process": "<3.3" }, "provide": { @@ -3053,12 +3145,12 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/event-dispatcher": "^4.3", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/var-dumper": "^4.3" + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" }, "suggest": { "psr/log": "For using the console logger", @@ -3069,7 +3161,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -3096,29 +3188,43 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-08-26T08:26:39+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T11:41:10+00:00" }, { "name": "symfony/css-selector", - "version": "v4.3.4", + "version": "v5.0.7", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "c6e5e2a00db768c92c3ae131532af4e1acc7bd03" + "reference": "5f8d5271303dad260692ba73dfa21777d38e124e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/c6e5e2a00db768c92c3ae131532af4e1acc7bd03", - "reference": "c6e5e2a00db768c92c3ae131532af4e1acc7bd03", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/5f8d5271303dad260692ba73dfa21777d38e124e", + "reference": "5f8d5271303dad260692ba73dfa21777d38e124e", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -3149,20 +3255,34 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-08-20T14:07:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:56:45+00:00" }, { "name": "symfony/debug", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced" + "reference": "346636d2cae417992ecfd761979b2ab98b339a45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/afcdea44a2e399c1e4b52246ec8d54c715393ced", - "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced", + "url": "https://api.github.com/repos/symfony/debug/zipball/346636d2cae417992ecfd761979b2ab98b339a45", + "reference": "346636d2cae417992ecfd761979b2ab98b339a45", "shasum": "" }, "require": { @@ -3173,12 +3293,12 @@ "symfony/http-kernel": "<3.4" }, "require-dev": { - "symfony/http-kernel": "~3.4|~4.0" + "symfony/http-kernel": "^3.4|^4.0|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -3205,20 +3325,104 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-08-20T14:27:59+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v4.3.4", + "name": "symfony/error-handler", + "version": "v4.4.7", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2" + "url": "https://github.com/symfony/error-handler.git", + "reference": "7e9828fc98aa1cf27b422fe478a84f5b0abb7358" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/429d0a1451d4c9c4abe1959b2986b88794b9b7d2", - "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/7e9828fc98aa1cf27b422fe478a84f5b0abb7358", + "reference": "7e9828fc98aa1cf27b422fe478a84f5b0abb7358", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0", + "symfony/debug": "^4.4.5", + "symfony/var-dumper": "^4.4|^5.0" + }, + "require-dev": { + "symfony/http-kernel": "^4.4|^5.0", + "symfony/serializer": "^4.4|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ErrorHandler Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T14:07:33+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v4.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abc8e3618bfdb55e44c8c6a00abd333f831bbfed", + "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed", "shasum": "" }, "require": { @@ -3234,12 +3438,12 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "^3.4|^4.0", - "symfony/service-contracts": "^1.1", - "symfony/stopwatch": "~3.4|~4.0" + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/stopwatch": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/dependency-injection": "", @@ -3248,7 +3452,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -3275,20 +3479,34 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-08-26T08:55:16+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.5", + "version": "v1.1.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "c61766f4440ca687de1084a5c00b08e167a2575c" + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c61766f4440ca687de1084a5c00b08e167a2575c", - "reference": "c61766f4440ca687de1084a5c00b08e167a2575c", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", + "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", "shasum": "" }, "require": { @@ -3333,20 +3551,20 @@ "interoperability", "standards" ], - "time": "2019-06-20T06:46:26+00:00" + "time": "2019-09-17T09:54:03+00:00" }, { "name": "symfony/finder", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2" + "reference": "5729f943f9854c5781984ed4907bbb817735776b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/86c1c929f0a4b24812e1eb109262fc3372c8e9f2", - "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2", + "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b", + "reference": "5729f943f9854c5781984ed4907bbb817735776b", "shasum": "" }, "require": { @@ -3355,7 +3573,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -3382,35 +3600,49 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-08-14T12:26:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "d804bea118ff340a12e22a79f9c7e7eb56b35adc" + "reference": "62f92509c9abfd1f73e17b8cf1b72c0bdac6611b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d804bea118ff340a12e22a79f9c7e7eb56b35adc", - "reference": "d804bea118ff340a12e22a79f9c7e7eb56b35adc", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/62f92509c9abfd1f73e17b8cf1b72c0bdac6611b", + "reference": "62f92509c9abfd1f73e17b8cf1b72c0bdac6611b", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/mime": "^4.3", + "symfony/mime": "^4.3|^5.0", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { "predis/predis": "~1.0", - "symfony/expression-language": "~3.4|~4.0" + "symfony/expression-language": "^3.4|^4.0|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -3437,37 +3669,51 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-08-26T08:55:16+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T14:07:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "5e0fc71be03d52cd00c423061cfd300bd6f92a52" + "reference": "f356a489e51856b99908005eb7f2c51a1dfc95dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5e0fc71be03d52cd00c423061cfd300bd6f92a52", - "reference": "5e0fc71be03d52cd00c423061cfd300bd6f92a52", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f356a489e51856b99908005eb7f2c51a1dfc95dc", + "reference": "f356a489e51856b99908005eb7f2c51a1dfc95dc", "shasum": "" }, "require": { "php": "^7.1.3", "psr/log": "~1.0", - "symfony/debug": "~3.4|~4.0", - "symfony/event-dispatcher": "^4.3", - "symfony/http-foundation": "^4.1.1", - "symfony/polyfill-ctype": "~1.8", + "symfony/error-handler": "^4.4", + "symfony/event-dispatcher": "^4.4", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-php73": "^1.9" }, "conflict": { "symfony/browser-kit": "<4.3", "symfony/config": "<3.4", + "symfony/console": ">=5", "symfony/dependency-injection": "<4.3", "symfony/translation": "<4.2", - "symfony/var-dumper": "<4.1.1", "twig/twig": "<1.34|<2.4,>=2" }, "provide": { @@ -3475,34 +3721,32 @@ }, "require-dev": { "psr/cache": "~1.0", - "symfony/browser-kit": "^4.3", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~3.4|~4.0", - "symfony/dependency-injection": "^4.3", - "symfony/dom-crawler": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~3.4|~4.0", - "symfony/translation": "~4.2", - "symfony/translation-contracts": "^1.1", - "symfony/var-dumper": "^4.1.1", - "twig/twig": "^1.34|^2.4" + "symfony/browser-kit": "^4.3|^5.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0", + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^4.3|^5.0", + "symfony/dom-crawler": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/routing": "^3.4|^4.0|^5.0", + "symfony/stopwatch": "^3.4|^4.0|^5.0", + "symfony/templating": "^3.4|^4.0|^5.0", + "symfony/translation": "^4.2|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "twig/twig": "^1.34|^2.4|^3.0" }, "suggest": { "symfony/browser-kit": "", "symfony/config": "", "symfony/console": "", - "symfony/dependency-injection": "", - "symfony/var-dumper": "" + "symfony/dependency-injection": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -3529,35 +3773,52 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2019-08-26T16:47:42+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T14:59:15+00:00" }, { "name": "symfony/mime", - "version": "v4.3.4", + "version": "v5.0.7", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "987a05df1c6ac259b34008b932551353f4f408df" + "reference": "481b7d6da88922fb1e0d86a943987722b08f3955" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/987a05df1c6ac259b34008b932551353f4f408df", - "reference": "987a05df1c6ac259b34008b932551353f4f408df", + "url": "https://api.github.com/repos/symfony/mime/zipball/481b7d6da88922fb1e0d86a943987722b08f3955", + "reference": "481b7d6da88922fb1e0d86a943987722b08f3955", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.2.5", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, + "conflict": { + "symfony/mailer": "<4.4" + }, "require-dev": { "egulias/email-validator": "^2.1.10", - "symfony/dependency-injection": "~3.4|^4.1" + "symfony/dependency-injection": "^4.4|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -3588,20 +3849,34 @@ "mime", "mime-type" ], - "time": "2019-08-22T08:16:11+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:56:45+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", "shasum": "" }, "require": { @@ -3613,7 +3888,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3646,20 +3921,34 @@ "polyfill", "portable" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "685968b11e61a347c18bf25db32effa478be610f" + "reference": "ad6d62792bfbcfc385dd34b424d4fcf9712a32c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f", - "reference": "685968b11e61a347c18bf25db32effa478be610f", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/ad6d62792bfbcfc385dd34b424d4fcf9712a32c8", + "reference": "ad6d62792bfbcfc385dd34b424d4fcf9712a32c8", "shasum": "" }, "require": { @@ -3671,7 +3960,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3705,26 +3994,40 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2" + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6af626ae6fa37d396dc90a399c0ff08e5cfc45b2", - "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", "shasum": "" }, "require": { "php": ">=5.3.3", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php72": "^1.9" + "symfony/polyfill-php72": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -3732,7 +4035,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3767,20 +4070,34 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", "shasum": "" }, "require": { @@ -3792,7 +4109,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3826,20 +4143,34 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-php56", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php56.git", - "reference": "0e3b212e96a51338639d8ce175c046d7729c3403" + "reference": "d51ec491c8ddceae7dca8dd6c7e30428f543f37d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/0e3b212e96a51338639d8ce175c046d7729c3403", - "reference": "0e3b212e96a51338639d8ce175c046d7729c3403", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/d51ec491c8ddceae7dca8dd6c7e30428f543f37d", + "reference": "d51ec491c8ddceae7dca8dd6c7e30428f543f37d", "shasum": "" }, "require": { @@ -3849,7 +4180,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3882,20 +4213,34 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "04ce3335667451138df4307d6a9b61565560199e" + "reference": "37b0976c78b94856543260ce09b460a7bc852747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", - "reference": "04ce3335667451138df4307d6a9b61565560199e", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", "shasum": "" }, "require": { @@ -3904,7 +4249,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3937,20 +4282,34 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188" + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/2ceb49eaccb9352bff54d22570276bb75ba4a188", - "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", "shasum": "" }, "require": { @@ -3959,7 +4318,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -3995,20 +4354,34 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-util", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-util.git", - "reference": "4317de1386717b4c22caed7725350a8887ab205c" + "reference": "d8e76c104127675d0ea3df3be0f2ae24a8619027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4317de1386717b4c22caed7725350a8887ab205c", - "reference": "4317de1386717b4c22caed7725350a8887ab205c", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/d8e76c104127675d0ea3df3be0f2ae24a8619027", + "reference": "d8e76c104127675d0ea3df3be0f2ae24a8619027", "shasum": "" }, "require": { @@ -4017,7 +4390,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -4047,20 +4420,34 @@ "polyfill", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-02T11:55:35+00:00" }, { "name": "symfony/process", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "e89969c00d762349f078db1128506f7f3dcc0d4a" + "reference": "3e40e87a20eaf83a1db825e1fa5097ae89042db3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/e89969c00d762349f078db1128506f7f3dcc0d4a", - "reference": "e89969c00d762349f078db1128506f7f3dcc0d4a", + "url": "https://api.github.com/repos/symfony/process/zipball/3e40e87a20eaf83a1db825e1fa5097ae89042db3", + "reference": "3e40e87a20eaf83a1db825e1fa5097ae89042db3", "shasum": "" }, "require": { @@ -4069,7 +4456,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -4096,20 +4483,34 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-08-26T08:26:39+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/routing", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "ff1049f6232dc5b6023b1ff1c6de56f82bcd264f" + "reference": "0f562fa613e288d7dbae6c63abbc9b33ed75a8f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/ff1049f6232dc5b6023b1ff1c6de56f82bcd264f", - "reference": "ff1049f6232dc5b6023b1ff1c6de56f82bcd264f", + "url": "https://api.github.com/repos/symfony/routing/zipball/0f562fa613e288d7dbae6c63abbc9b33ed75a8f8", + "reference": "0f562fa613e288d7dbae6c63abbc9b33ed75a8f8", "shasum": "" }, "require": { @@ -4123,11 +4524,11 @@ "require-dev": { "doctrine/annotations": "~1.2", "psr/log": "~1.0", - "symfony/config": "~4.2", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "doctrine/annotations": "For using the annotation loader", @@ -4139,7 +4540,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -4172,24 +4573,38 @@ "uri", "url" ], - "time": "2019-08-26T08:26:39+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T11:41:10+00:00" }, { "name": "symfony/service-contracts", - "version": "v1.1.6", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3" + "reference": "144c5e51266b281231e947b51223ba14acf1a749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ea7263d6b6d5f798b56a45a5b8d686725f2719a3", - "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", + "reference": "144c5e51266b281231e947b51223ba14acf1a749", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.2.5", "psr/container": "^1.0" }, "suggest": { @@ -4198,7 +4613,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -4230,30 +4645,31 @@ "interoperability", "standards" ], - "time": "2019-08-20T14:44:19+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { "name": "symfony/translation", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "28498169dd334095fa981827992f3a24d50fed0f" + "reference": "4e54d336f2eca5facad449d0b0118bb449375b76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/28498169dd334095fa981827992f3a24d50fed0f", - "reference": "28498169dd334095fa981827992f3a24d50fed0f", + "url": "https://api.github.com/repos/symfony/translation/zipball/4e54d336f2eca5facad449d0b0118bb449375b76", + "reference": "4e54d336f2eca5facad449d0b0118bb449375b76", "shasum": "" }, "require": { "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^1.1.6" + "symfony/translation-contracts": "^1.1.6|^2" }, "conflict": { "symfony/config": "<3.4", "symfony/dependency-injection": "<3.4", + "symfony/http-kernel": "<4.4", "symfony/yaml": "<3.4" }, "provide": { @@ -4261,15 +4677,14 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/intl": "~3.4|~4.0", - "symfony/service-contracts": "^1.1.2", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/finder": "~2.8|~3.0|~4.0|^5.0", + "symfony/http-kernel": "^4.4", + "symfony/intl": "^3.4|^4.0|^5.0", + "symfony/service-contracts": "^1.1.2|^2", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "psr/log-implementation": "To use logging capability in translator", @@ -4279,7 +4694,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -4306,24 +4721,38 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2019-08-26T08:55:16+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v1.1.6", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a" + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/325b17c24f3ee23cbecfa63ba809c6d89b5fa04a", - "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/8cc682ac458d75557203b2f2f14b0b92e1c744ed", + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.2.5" }, "suggest": { "symfony/translation-implementation": "" @@ -4331,7 +4760,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -4363,20 +4792,20 @@ "interoperability", "standards" ], - "time": "2019-08-02T12:15:04+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.3.4", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "641043e0f3e615990a0f29479f9c117e8a6698c6" + "reference": "5a0c2d93006131a36cf6f767d10e2ca8333b0d4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/641043e0f3e615990a0f29479f9c117e8a6698c6", - "reference": "641043e0f3e615990a0f29479f9c117e8a6698c6", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/5a0c2d93006131a36cf6f767d10e2ca8333b0d4a", + "reference": "5a0c2d93006131a36cf6f767d10e2ca8333b0d4a", "shasum": "" }, "require": { @@ -4390,9 +4819,9 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "twig/twig": "~1.34|~2.4" + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^4.4|^5.0", + "twig/twig": "^1.34|^2.4|^3.0" }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", @@ -4405,7 +4834,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -4439,20 +4868,34 @@ "debug", "dump" ], - "time": "2019-08-26T08:26:39+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/yaml", - "version": "v4.4.1", + "version": "v4.4.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "76de473358fe802578a415d5bb43c296cf09d211" + "reference": "ef166890d821518106da3560086bfcbeb4fadfec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/76de473358fe802578a415d5bb43c296cf09d211", - "reference": "76de473358fe802578a415d5bb43c296cf09d211", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ef166890d821518106da3560086bfcbeb4fadfec", + "reference": "ef166890d821518106da3560086bfcbeb4fadfec", "shasum": "" }, "require": { @@ -4498,25 +4941,41 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-11-12T14:51:11+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T11:41:10+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.1", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757" + "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757", - "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/dda2ee426acd6d801d5b7fd1001cde9b5f790e15", + "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-libxml": "*", "php": "^5.5 || ^7.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0" + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" @@ -4545,20 +5004,20 @@ ], "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", - "time": "2017-11-27T11:13:29+00:00" + "time": "2019-10-24T08:53:34+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v3.5.0", + "version": "v3.6.2", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "95cb0fa6c025f7f0db7fc60f81e9fb231eb2d222" + "reference": "786a947e57086cf236cefdee80784634224b99fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/95cb0fa6c025f7f0db7fc60f81e9fb231eb2d222", - "reference": "95cb0fa6c025f7f0db7fc60f81e9fb231eb2d222", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/786a947e57086cf236cefdee80784634224b99fa", + "reference": "786a947e57086cf236cefdee80784634224b99fa", "shasum": "" }, "require": { @@ -4567,12 +5026,18 @@ "symfony/polyfill-ctype": "^1.9" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0" + "ext-filter": "*", + "ext-pcre": "*", + "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator.", + "ext-pcre": "Required to use most of the library." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.5-dev" + "dev-master": "3.6-dev" } }, "autoload": { @@ -4602,35 +5067,39 @@ "env", "environment" ], - "time": "2019-08-27T17:00:38+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2020-03-27T23:36:02+00:00" }, { "name": "webmozart/assert", - "version": "1.5.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", + "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "vimeo/psalm": "<3.6.0" + }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -4652,7 +5121,7 @@ "check", "validate" ], - "time": "2019-08-24T08:43:50+00:00" + "time": "2020-02-14T12:15:55+00:00" } ], "packages-dev": [ @@ -4703,26 +5172,26 @@ }, { "name": "barryvdh/laravel-debugbar", - "version": "v3.2.8", + "version": "v3.2.9", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "18208d64897ab732f6c04a19b319fe8f1d57a9c0" + "reference": "42d5da5379a7860093f8e4032167e4cb5ebec180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/18208d64897ab732f6c04a19b319fe8f1d57a9c0", - "reference": "18208d64897ab732f6c04a19b319fe8f1d57a9c0", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/42d5da5379a7860093f8e4032167e4cb5ebec180", + "reference": "42d5da5379a7860093f8e4032167e4cb5ebec180", "shasum": "" }, "require": { - "illuminate/routing": "^5.5|^6", - "illuminate/session": "^5.5|^6", - "illuminate/support": "^5.5|^6", - "maximebf/debugbar": "~1.15.0", + "illuminate/routing": "^5.5|^6|^7", + "illuminate/session": "^5.5|^6|^7", + "illuminate/support": "^5.5|^6|^7", + "maximebf/debugbar": "^1.15.1", "php": ">=7.0", - "symfony/debug": "^3|^4", - "symfony/finder": "^3|^4" + "symfony/debug": "^3|^4|^5", + "symfony/finder": "^3|^4|^5" }, "require-dev": { "laravel/framework": "5.5.x" @@ -4767,42 +5236,39 @@ "profiler", "webprofiler" ], - "time": "2019-08-29T07:01:03+00:00" + "time": "2020-02-25T20:42:23+00:00" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.6.4", + "version": "v2.6.7", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "16eb4f65ee0d51b1f1182d56ae28ee00a70ce75a" + "reference": "edd69c5e0508972c81f1f7173236de2459c45814" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/16eb4f65ee0d51b1f1182d56ae28ee00a70ce75a", - "reference": "16eb4f65ee0d51b1f1182d56ae28ee00a70ce75a", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/edd69c5e0508972c81f1f7173236de2459c45814", + "reference": "edd69c5e0508972c81f1f7173236de2459c45814", "shasum": "" }, "require": { "barryvdh/reflection-docblock": "^2.0.6", "composer/composer": "^1.6", - "illuminate/console": "^5.5|^6", - "illuminate/filesystem": "^5.5|^6", - "illuminate/support": "^5.5|^6", - "php": ">=7" + "doctrine/dbal": "~2.3", + "illuminate/console": "^5.5|^6|^7", + "illuminate/filesystem": "^5.5|^6|^7", + "illuminate/support": "^5.5|^6|^7", + "php": ">=7.2" }, "require-dev": { - "doctrine/dbal": "~2.3", - "illuminate/config": "^5.5|^6", - "illuminate/view": "^5.5|^6", - "phpro/grumphp": "^0.14", - "phpunit/phpunit": "4.*", - "scrutinizer/ocular": "~1.1", + "illuminate/config": "^5.5|^6|^7", + "illuminate/view": "^5.5|^6|^7", + "mockery/mockery": "^1.3", + "orchestra/testbench": "^3|^4", + "phpro/grumphp": "^0.17.1", "squizlabs/php_codesniffer": "^3" }, - "suggest": { - "doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)" - }, "type": "library", "extra": { "branch-alias": { @@ -4841,7 +5307,7 @@ "phpstorm", "sublime" ], - "time": "2019-09-03T17:51:13+00:00" + "time": "2020-02-25T20:41:32+00:00" }, { "name": "barryvdh/reflection-docblock", @@ -4894,16 +5360,16 @@ }, { "name": "codedungeon/php-cli-colors", - "version": "1.10.7", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/mikeerickson/php-cli-colors.git", - "reference": "5649ef76ec0c9ed626e95bf40fdfaf4b8efcf79b" + "reference": "9f60ac692cc790755dad47b01c1d607fe5f43b94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mikeerickson/php-cli-colors/zipball/5649ef76ec0c9ed626e95bf40fdfaf4b8efcf79b", - "reference": "5649ef76ec0c9ed626e95bf40fdfaf4b8efcf79b", + "url": "https://api.github.com/repos/mikeerickson/php-cli-colors/zipball/9f60ac692cc790755dad47b01c1d607fe5f43b94", + "reference": "9f60ac692cc790755dad47b01c1d607fe5f43b94", "shasum": "" }, "require-dev": { @@ -4934,7 +5400,7 @@ "package", "php" ], - "time": "2018-05-17T01:34:14+00:00" + "time": "2019-12-29T22:29:29+00:00" }, { "name": "codedungeon/phpunit-result-printer", @@ -4991,16 +5457,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.4", + "version": "1.2.6", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527" + "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527", - "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/47fe531de31fca4a1b997f87308e7d7804348f7e", + "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e", "shasum": "" }, "require": { @@ -5011,7 +5477,7 @@ "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" }, "type": "library", "extra": { @@ -5043,20 +5509,20 @@ "ssl", "tls" ], - "time": "2019-08-30T08:44:50+00:00" + "time": "2020-01-13T10:02:55+00:00" }, { "name": "composer/composer", - "version": "1.9.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5" + "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5", - "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5", + "url": "https://api.github.com/repos/composer/composer/zipball/b912a45da3e2b22f5cb5a23e441b697a295ba011", + "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011", "shasum": "" }, "require": { @@ -5069,17 +5535,17 @@ "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0" + "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "conflict": { "symfony/console": "2.8.38" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7", - "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" + "phpspec/prophecy": "^1.10", + "symfony/phpunit-bridge": "^3.4" }, "suggest": { "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", @@ -5092,7 +5558,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -5123,28 +5589,37 @@ "dependency", "package" ], - "time": "2019-08-02T18:55:33+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-03-13T19:34:27+00:00" }, { "name": "composer/semver", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e" + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e", - "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e", + "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + "phpunit/phpunit": "^4.5 || ^5.0.5" }, "type": "library", "extra": { @@ -5185,20 +5660,20 @@ "validation", "versioning" ], - "time": "2019-03-19T17:25:45+00:00" + "time": "2020-01-13T12:06:48+00:00" }, { "name": "composer/spdx-licenses", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5" + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", "shasum": "" }, "require": { @@ -5245,28 +5720,28 @@ "spdx", "validator" ], - "time": "2019-07-29T10:31:59+00:00" + "time": "2020-02-14T07:44:31+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.3.3", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f" + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f", - "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", + "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" }, "type": "library", "autoload": { @@ -5284,39 +5759,46 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2019-05-27T17:52:04+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + } + ], + "time": "2020-03-01T12:26:26+00:00" }, { "name": "doctrine/annotations", - "version": "v1.7.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "fa4c4e861e809d6a1103bd620cce63ed91aedfeb" + "reference": "5eb79f3dbdffed6544e1fc287572c0f462bd29bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/fa4c4e861e809d6a1103bd620cce63ed91aedfeb", - "reference": "fa4c4e861e809d6a1103bd620cce63ed91aedfeb", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5eb79f3dbdffed6544e1fc287572c0f462bd29bb", + "reference": "5eb79f3dbdffed6544e1fc287572c0f462bd29bb", "shasum": "" }, "require": { "doctrine/lexer": "1.*", + "ext-tokenizer": "*", "php": "^7.1" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^7.5@dev" + "phpunit/phpunit": "^7.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.9.x-dev" } }, "autoload": { @@ -5357,20 +5839,20 @@ "docblock", "parser" ], - "time": "2019-08-08T18:11:40+00:00" + "time": "2020-04-02T12:33:25+00:00" }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { @@ -5413,80 +5895,20 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" - }, - { - "name": "facebook/webdriver", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/facebook/php-webdriver.git", - "reference": "e43de70f3c7166169d0f14a374505392734160e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/e43de70f3c7166169d0f14a374505392734160e5", - "reference": "e43de70f3c7166169d0f14a374505392734160e5", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", - "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "^5.7", - "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", - "squizlabs/php_codesniffer": "^2.6", - "symfony/var-dumper": "^3.3 || ^4.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-community": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A PHP client for Selenium WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "time": "2019-06-13T08:02:18+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.15.3", + "version": "v2.16.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "705490b0f282f21017d73561e9498d2b622ee34c" + "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/705490b0f282f21017d73561e9498d2b622ee34c", - "reference": "705490b0f282f21017d73561e9498d2b622ee34c", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c8afb599858876e95e8ebfcd97812d383fa23f02", + "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02", "shasum": "" }, "require": { @@ -5497,15 +5919,15 @@ "ext-tokenizer": "*", "php": "^5.6 || ^7.0", "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6", - "symfony/event-dispatcher": "^3.0 || ^4.0", - "symfony/filesystem": "^3.0 || ^4.0", - "symfony/finder": "^3.0 || ^4.0", - "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^3.0 || ^4.0 || ^5.0", + "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", "symfony/polyfill-php70": "^1.0", "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0", - "symfony/stopwatch": "^3.0 || ^4.0" + "symfony/process": "^3.0 || ^4.0 || ^5.0", + "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" }, "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", @@ -5518,8 +5940,8 @@ "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", "phpunitgoodpractices/traits": "^1.8", - "symfony/phpunit-bridge": "^4.3", - "symfony/yaml": "^3.0 || ^4.0" + "symfony/phpunit-bridge": "^4.3 || ^5.0", + "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", @@ -5562,7 +5984,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2019-08-31T12:51:54+00:00" + "time": "2019-11-25T22:10:32+00:00" }, { "name": "hassankhan/config", @@ -5623,23 +6045,23 @@ }, { "name": "justinrainbow/json-schema", - "version": "5.2.8", + "version": "5.2.9", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4" + "reference": "44c6787311242a979fa15c704327c20e7221a0e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4", - "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", + "reference": "44c6787311242a979fa15c704327c20e7221a0e4", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20", + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", "json-schema/json-schema-test-suite": "1.2.0", "phpunit/phpunit": "^4.8.35" }, @@ -5685,39 +6107,42 @@ "json", "schema" ], - "time": "2019-01-14T23:55:14+00:00" + "time": "2019-09-25T14:49:45+00:00" }, { "name": "laravel/dusk", - "version": "v5.5.0", + "version": "v5.11.0", "source": { "type": "git", "url": "https://github.com/laravel/dusk.git", - "reference": "525076aa34f6935b1f54d4c5a155f84182194bbb" + "reference": "e07cc46a1e39767739e8197189780b4c2639806d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/dusk/zipball/525076aa34f6935b1f54d4c5a155f84182194bbb", - "reference": "525076aa34f6935b1f54d4c5a155f84182194bbb", + "url": "https://api.github.com/repos/laravel/dusk/zipball/e07cc46a1e39767739e8197189780b4c2639806d", + "reference": "e07cc46a1e39767739e8197189780b4c2639806d", "shasum": "" }, "require": { "ext-json": "*", "ext-zip": "*", - "facebook/webdriver": "^1.7", - "illuminate/console": "~5.7.0|~5.8.0|^6.0", - "illuminate/support": "~5.7.0|~5.8.0|^6.0", + "illuminate/console": "~5.7.0|~5.8.0|^6.0|^7.0", + "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0", "nesbot/carbon": "^1.20|^2.0", "php": ">=7.1.0", - "symfony/console": "^4.0", - "symfony/finder": "^4.0", - "symfony/process": "^4.0", - "vlucas/phpdotenv": "^2.2|^3.3" + "php-webdriver/webdriver": "^1.8.1", + "symfony/console": "^4.0|^5.0", + "symfony/finder": "^4.0|^5.0", + "symfony/process": "^4.0|^5.0", + "vlucas/phpdotenv": "^2.2|^3.0|^4.0" }, "require-dev": { "mockery/mockery": "^1.0", "phpunit/phpunit": "^7.5|^8.0" }, + "suggest": { + "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." + }, "type": "library", "extra": { "branch-alias": { @@ -5750,29 +6175,29 @@ "testing", "webdriver" ], - "time": "2019-08-06T17:53:56+00:00" + "time": "2020-03-24T16:21:49+00:00" }, { "name": "maximebf/debugbar", - "version": "v1.15.0", + "version": "v1.16.1", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07" + "reference": "58998b818c6567fac01e35b8a4b70c1a64530556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e7d60937ee5f1320975ca9bc7bcdd44d500f07", - "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/58998b818c6567fac01e35b8a4b70c1a64530556", + "reference": "58998b818c6567fac01e35b8a4b70c1a64530556", "shasum": "" }, "require": { - "php": ">=5.3.0", + "php": "^7.1", "psr/log": "^1.0", - "symfony/var-dumper": "^2.6|^3.0|^4.0" + "symfony/var-dumper": "^2.6|^3|^4|^5" }, "require-dev": { - "phpunit/phpunit": "^4.0|^5.0" + "phpunit/phpunit": "^5" }, "suggest": { "kriswallsmith/assetic": "The best way to manage assets", @@ -5782,7 +6207,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.16-dev" } }, "autoload": { @@ -5811,20 +6236,20 @@ "debug", "debugbar" ], - "time": "2017-12-15T11:13:46+00:00" + "time": "2019-11-24T09:46:11+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.3", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", "shasum": "" }, "require": { @@ -5859,7 +6284,7 @@ "object", "object graph" ], - "time": "2019-08-09T12:45:53+00:00" + "time": "2020-01-17T21:11:47+00:00" }, { "name": "phar-io/manifest", @@ -6016,33 +6441,36 @@ }, { "name": "php-mock/php-mock", - "version": "2.1.2", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/php-mock/php-mock.git", - "reference": "35379d7b382b787215617f124662d9ead72c15e3" + "reference": "8ca7205ad5e73fbbffa9bde9f6bc90daf5e49702" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock/zipball/35379d7b382b787215617f124662d9ead72c15e3", - "reference": "35379d7b382b787215617f124662d9ead72c15e3", + "url": "https://api.github.com/repos/php-mock/php-mock/zipball/8ca7205ad5e73fbbffa9bde9f6bc90daf5e49702", + "reference": "8ca7205ad5e73fbbffa9bde9f6bc90daf5e49702", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1" + "phpunit/php-text-template": "^1 || ^2" }, "replace": { "malkusch/php-mock": "*" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0" + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0 || ^9.0" }, "suggest": { "php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock." }, "type": "library", "autoload": { + "files": [ + "autoload.php" + ], "psr-4": { "phpmock\\": [ "classes/", @@ -6057,9 +6485,9 @@ "authors": [ { "name": "Markus Malkusch", - "role": "Developer", "email": "markus@malkusch.de", - "homepage": "http://markus.malkusch.de" + "homepage": "http://markus.malkusch.de", + "role": "Developer" } ], "description": "PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.", @@ -6073,29 +6501,29 @@ "test", "test double" ], - "time": "2019-06-05T20:10:01+00:00" + "time": "2020-02-08T14:50:32+00:00" }, { "name": "php-mock/php-mock-integration", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/php-mock/php-mock-integration.git", - "reference": "5a0d7d7755f823bc2a230cfa45058b40f9013bc4" + "reference": "003d585841e435958a02e9b986953907b8b7609b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/5a0d7d7755f823bc2a230cfa45058b40f9013bc4", - "reference": "5a0d7d7755f823bc2a230cfa45058b40f9013bc4", + "url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/003d585841e435958a02e9b986953907b8b7609b", + "reference": "003d585841e435958a02e9b986953907b8b7609b", "shasum": "" }, "require": { "php": ">=5.6", - "php-mock/php-mock": "^2", - "phpunit/php-text-template": "^1" + "php-mock/php-mock": "^2.2", + "phpunit/php-text-template": "^1 || ^2" }, "require-dev": { - "phpunit/phpunit": "^4|^5" + "phpunit/phpunit": "^5.7.27 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "autoload": { @@ -6126,26 +6554,26 @@ "test", "test double" ], - "time": "2017-02-17T21:31:34+00:00" + "time": "2020-02-08T14:40:25+00:00" }, { "name": "php-mock/php-mock-phpunit", - "version": "2.4.0", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/php-mock/php-mock-phpunit.git", - "reference": "04f78fe83df4855654373188aca8cccf8bf472ce" + "reference": "2877a0e58f12e91b64bf36ccd080a209dcbf6c30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/04f78fe83df4855654373188aca8cccf8bf472ce", - "reference": "04f78fe83df4855654373188aca8cccf8bf472ce", + "url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/2877a0e58f12e91b64bf36ccd080a209dcbf6c30", + "reference": "2877a0e58f12e91b64bf36ccd080a209dcbf6c30", "shasum": "" }, "require": { "php": ">=7", - "php-mock/php-mock-integration": "^2", - "phpunit/phpunit": "^6 || ^7 || ^8" + "php-mock/php-mock-integration": "^2.1", + "phpunit/phpunit": "^6 || ^7 || ^8 || ^9" }, "type": "library", "autoload": { @@ -6163,9 +6591,9 @@ "authors": [ { "name": "Markus Malkusch", - "role": "Developer", "email": "markus@malkusch.de", - "homepage": "http://markus.malkusch.de" + "homepage": "http://markus.malkusch.de", + "role": "Developer" } ], "description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.", @@ -6180,39 +6608,102 @@ "test", "test double" ], - "time": "2019-06-07T12:26:51+00:00" + "time": "2020-02-08T15:44:47+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "name": "php-webdriver/webdriver", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", + "reference": "3308a70be084d6d7fd1ee5787b4c2e6eb4b70aab", "shasum": "" }, "require": { - "php": ">=5.5" + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "friendsofphp/php-cs-fixer": "^2.0", + "jakub-onderka/php-parallel-lint": "^1.0", + "php-coveralls/php-coveralls": "^2.0", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "sminnee/phpunit-mock-objects": "^3.4", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "Facebook\\WebDriver\\": "lib/" + }, + "files": [ + "lib/Exception/TimeoutException.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "time": "2020-03-04T14:40:12+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -6234,44 +6725,42 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2018-08-07T13:53:10+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.1", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "ext-filter": "^7.1", + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0", + "phpdocumentor/type-resolver": "^1.0", + "webmozart/assert": "^1" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -6282,44 +6771,46 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-04-30T17:48:53+00:00" + "time": "2020-02-22T12:28:44+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -6332,37 +6823,38 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-02-18T18:59:58+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.1", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { @@ -6395,7 +6887,7 @@ "spy", "stub" ], - "time": "2019-06-13T12:50:23+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6602,16 +7094,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a" + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a", - "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { @@ -6647,20 +7139,20 @@ "keywords": [ "tokenizer" ], - "time": "2019-07-25T05:29:42+00:00" + "time": "2019-09-17T06:23:10+00:00" }, { "name": "phpunit/phpunit", - "version": "7.5.15", + "version": "7.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d79c053d972856b8b941bb233e39dc521a5093f0" + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d79c053d972856b8b941bb233e39dc521a5093f0", - "reference": "d79c053d972856b8b941bb233e39dc521a5093f0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", "shasum": "" }, "require": { @@ -6731,7 +7223,7 @@ "testing", "xunit" ], - "time": "2019-08-21T07:05:16+00:00" + "time": "2020-01-08T08:45:45+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -6900,16 +7392,16 @@ }, { "name": "sebastian/environment", - "version": "4.2.2", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", "shasum": "" }, "require": { @@ -6949,20 +7441,20 @@ "environment", "hhvm" ], - "time": "2019-05-05T09:05:15+00:00" + "time": "2019-11-20T08:46:58+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "06a9a5947f47b3029d76118eb5c22802e5869687" + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687", - "reference": "06a9a5947f47b3029d76118eb5c22802e5869687", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "shasum": "" }, "require": { @@ -7016,7 +7508,7 @@ "export", "exporter" ], - "time": "2019-08-11T12:43:14+00:00" + "time": "2019-09-14T09:02:43+00:00" }, { "name": "sebastian/global-state", @@ -7301,16 +7793,16 @@ }, { "name": "seld/jsonlint", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38" + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", "shasum": "" }, "require": { @@ -7346,20 +7838,20 @@ "parser", "validator" ], - "time": "2018-01-24T12:46:19+00:00" + "time": "2019-10-24T14:27:39+00:00" }, { "name": "seld/phar-utils", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", "shasum": "" }, "require": { @@ -7388,32 +7880,32 @@ ], "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "phra" + "phar" ], - "time": "2015-10-13T18:44:15+00:00" + "time": "2020-02-14T15:25:33+00:00" }, { "name": "symfony/filesystem", - "version": "v4.3.4", + "version": "v5.0.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" + "reference": "ca3b87dd09fff9b771731637f5379965fbfab420" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ca3b87dd09fff9b771731637f5379965fbfab420", + "reference": "ca3b87dd09fff9b771731637f5379965fbfab420", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.2.5", "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -7440,29 +7932,43 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2019-08-20T14:07:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:56:45+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.3.4", + "version": "v5.0.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "81c2e120522a42f623233968244baebd6b36cb6a" + "reference": "09dccfffd24b311df7f184aa80ee7b61ad61ed8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/81c2e120522a42f623233968244baebd6b36cb6a", - "reference": "81c2e120522a42f623233968244baebd6b36cb6a", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/09dccfffd24b311df7f184aa80ee7b61ad61ed8d", + "reference": "09dccfffd24b311df7f184aa80ee7b61ad61ed8d", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -7494,20 +8000,34 @@ "configuration", "options" ], - "time": "2019-08-08T09:29:19+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:56:45+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.12.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "54b4c428a0054e254223797d2713c31e08610831" + "reference": "2a18e37a489803559284416df58c71ccebe50bf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", - "reference": "54b4c428a0054e254223797d2713c31e08610831", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", + "reference": "2a18e37a489803559284416df58c71ccebe50bf0", "shasum": "" }, "require": { @@ -7517,7 +8037,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -7553,30 +8073,30 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.3.4", + "version": "v5.0.7", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71" + "reference": "a1d86d30d4522423afc998f32404efa34fcf5a73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/1e4ff456bd625be5032fac9be4294e60442e9b71", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/a1d86d30d4522423afc998f32404efa34fcf5a73", + "reference": "a1d86d30d4522423afc998f32404efa34fcf5a73", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/service-contracts": "^1.0" + "php": "^7.2.5", + "symfony/service-contracts": "^1.0|^2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -7603,7 +8123,21 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-08-07T11:52:19+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:56:45+00:00" }, { "name": "theseer/tokenizer", @@ -7657,5 +8191,6 @@ "ext-pdo_mysql": "*", "ext-zip": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } From dfefd8845bf08698a8713739e811341bbd9d5f42 Mon Sep 17 00:00:00 2001 From: AreYouScared Date: Fri, 3 Apr 2020 16:46:24 -0400 Subject: [PATCH 28/58] Fixed textboxes (#1886) Text boxes on the user view page were set to readonly not allowing admins to change user values Co-Authored-By: Lance Pioch --- resources/views/admin/users/view.blade.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/views/admin/users/view.blade.php b/resources/views/admin/users/view.blade.php index e1a49ef0a..d1e6ba9aa 100644 --- a/resources/views/admin/users/view.blade.php +++ b/resources/views/admin/users/view.blade.php @@ -30,25 +30,25 @@
- +
- +
- +
- +
@@ -80,7 +80,7 @@
- +

Leave blank to keep this user's password the same. User will not receive any notification if password is changed.

From 7d45379f3173ca245bde8d18544eb0525f964774 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 3 Apr 2020 13:46:45 -0700 Subject: [PATCH 29/58] Add back support for reinstalling a server --- app/Models/Server.php | 4 +++ .../Wings/DaemonServerRepository.php | 14 ++++++-- .../Servers/ReinstallServerService.php | 33 ++++++++----------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/app/Models/Server.php b/app/Models/Server.php index 9bdb19ff1..ebe6d2ee7 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -61,6 +61,10 @@ class Server extends Validable */ const RESOURCE_NAME = 'server'; + const STATUS_INSTALLING = 0; + const STATUS_INSTALLED = 1; + const STATUS_INSTALL_FAILED = 2; + /** * The table associated with the model. * diff --git a/app/Repositories/Wings/DaemonServerRepository.php b/app/Repositories/Wings/DaemonServerRepository.php index ae4d1333a..3461e4cc9 100644 --- a/app/Repositories/Wings/DaemonServerRepository.php +++ b/app/Repositories/Wings/DaemonServerRepository.php @@ -2,7 +2,6 @@ namespace Pterodactyl\Repositories\Wings; -use BadMethodCallException; use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use GuzzleHttp\Exception\TransferException; @@ -13,7 +12,6 @@ class DaemonServerRepository extends DaemonRepository /** * Returns details about a server from the Daemon instance. * - * @return array * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function getDetails(): array @@ -89,10 +87,20 @@ class DaemonServerRepository extends DaemonRepository /** * Reinstall a server on the daemon. + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function reinstall(): void { - throw new BadMethodCallException('Method is not implemented.'); + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient()->post(sprintf( + '/api/servers/%s/reinstall', $this->server->uuid + )); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } /** diff --git a/app/Services/Servers/ReinstallServerService.php b/app/Services/Servers/ReinstallServerService.php index 6242eeea2..27955c475 100644 --- a/app/Services/Servers/ReinstallServerService.php +++ b/app/Services/Servers/ReinstallServerService.php @@ -3,11 +3,9 @@ namespace Pterodactyl\Services\Servers; use Pterodactyl\Models\Server; -use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class ReinstallServerService { @@ -44,28 +42,23 @@ class ReinstallServerService } /** - * @param int|\Pterodactyl\Models\Server $server + * Reinstall a server on the remote daemon. * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @param \Pterodactyl\Models\Server $server + * @return \Pterodactyl\Models\Server + * + * @throws \Throwable */ - public function reinstall($server) + public function reinstall(Server $server) { - if (! $server instanceof Server) { - $server = $this->repository->find($server); - } + $this->database->transaction(function () use ($server) { + $this->repository->withoutFreshModel()->update($server->id, [ + 'installed' => Server::STATUS_INSTALLING, + ]); - $this->database->beginTransaction(); - $this->repository->withoutFreshModel()->update($server->id, [ - 'installed' => 0, - ], true, true); - - try { $this->daemonServerRepository->setServer($server)->reinstall(); - $this->database->commit(); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } + }); + + return $server->refresh(); } } From 86de7372a867ed9bda19d933302f67580f7a4650 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 3 Apr 2020 14:43:15 -0700 Subject: [PATCH 30/58] Add notes about IO/CPU Threads --- resources/views/admin/servers/new.blade.php | 8 ++++---- resources/views/admin/servers/view/build.blade.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index 6edf9f722..8c7011709 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -162,17 +162,17 @@
-
+
-
+

Advanced: Enter the specific CPU cores that this process can run on, or leave blank to allow all cores. This can be a single number, or a comma seperated list. Example: 0, 0-1,3, or 0,1,3,4.

-
+
- I/O
+

Advanced: The IO performance of this server relative to other running containers on the system. Value should be between 10 and 1000.

-
+
-
+

Advanced: Enter the specific CPU cores that this process can run on, or leave blank to allow all cores. This can be a single number, or a comma seperated list. Example: 0, 0-1,3, or 0,1,3,4.

-

Changing this value can have negative effects on all containers on the system. We strongly recommend leaving this value as 500.

+

Advanced: The IO performance of this server relative to other running containers on the system. Value should be between 10 and 1000.

From 85e3945cd7373764dc61405bb361cec10395a549 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 3 Apr 2020 14:43:24 -0700 Subject: [PATCH 31/58] Add support for client-side server reinstallation --- .../Api/Client/Servers/SettingsController.php | 36 +++++++- .../Settings/ReinstallServerRequest.php | 17 ++++ .../scripts/api/server/reinstallServer.ts | 9 ++ resources/scripts/components/elements/Can.tsx | 4 - .../server/settings/ReinstallServerBox.tsx | 63 ++++++++++++++ .../server/settings/SettingsContainer.tsx | 86 ++++++++++--------- routes/api-client.php | 1 + 7 files changed, 169 insertions(+), 47 deletions(-) create mode 100644 app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php create mode 100644 resources/scripts/api/server/reinstallServer.ts create mode 100644 resources/scripts/components/server/settings/ReinstallServerBox.tsx diff --git a/app/Http/Controllers/Api/Client/Servers/SettingsController.php b/app/Http/Controllers/Api/Client/Servers/SettingsController.php index 5b457fa9c..64c73a8de 100644 --- a/app/Http/Controllers/Api/Client/Servers/SettingsController.php +++ b/app/Http/Controllers/Api/Client/Servers/SettingsController.php @@ -4,9 +4,12 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Illuminate\Http\Response; use Pterodactyl\Models\Server; +use Illuminate\Http\JsonResponse; use Pterodactyl\Repositories\Eloquent\ServerRepository; +use Pterodactyl\Services\Servers\ReinstallServerService; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\Settings\RenameServerRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Settings\ReinstallServerRequest; class SettingsController extends ClientApiController { @@ -15,16 +18,25 @@ class SettingsController extends ClientApiController */ private $repository; + /** + * @var \Pterodactyl\Services\Servers\ReinstallServerService + */ + private $reinstallServerService; + /** * SettingsController constructor. * * @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository + * @param \Pterodactyl\Services\Servers\ReinstallServerService $reinstallServerService */ - public function __construct(ServerRepository $repository) - { + public function __construct( + ServerRepository $repository, + ReinstallServerService $reinstallServerService + ) { parent::__construct(); $this->repository = $repository; + $this->reinstallServerService = $reinstallServerService; } /** @@ -32,7 +44,7 @@ class SettingsController extends ClientApiController * * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Settings\RenameServerRequest $request * @param \Pterodactyl\Models\Server $server - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException @@ -43,6 +55,22 @@ class SettingsController extends ClientApiController 'name' => $request->input('name'), ]); - return Response::create('', Response::HTTP_NO_CONTENT); + return JsonResponse::create([], Response::HTTP_NO_CONTENT); + } + + /** + * Reinstalls the server on the daemon. + * + * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Settings\ReinstallServerRequest $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Http\JsonResponse + * + * @throws \Throwable + */ + public function reinstall(ReinstallServerRequest $request, Server $server) + { + $this->reinstallServerService->reinstall($server); + + return JsonResponse::create([], Response::HTTP_ACCEPTED); } } diff --git a/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php b/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php new file mode 100644 index 000000000..9edc8ad1c --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Settings/ReinstallServerRequest.php @@ -0,0 +1,17 @@ + => { + return new Promise((resolve, reject) => { + http.post(`/api/client/servers/${uuid}/settings/reinstall`) + .then(() => resolve()) + .catch(reject); + }); +} diff --git a/resources/scripts/components/elements/Can.tsx b/resources/scripts/components/elements/Can.tsx index 2990f2489..4ea140d3e 100644 --- a/resources/scripts/components/elements/Can.tsx +++ b/resources/scripts/components/elements/Can.tsx @@ -11,10 +11,6 @@ interface Props { const Can = ({ action, matchAny = false, renderOnError, children }: Props) => { const can = usePermissions(action); - if (matchAny) { - console.log('Can.tsx', can); - } - return ( <> { diff --git a/resources/scripts/components/server/settings/ReinstallServerBox.tsx b/resources/scripts/components/server/settings/ReinstallServerBox.tsx new file mode 100644 index 000000000..895901215 --- /dev/null +++ b/resources/scripts/components/server/settings/ReinstallServerBox.tsx @@ -0,0 +1,63 @@ +import React, { useState } from 'react'; +import { ServerContext } from '@/state/server'; +import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; +import TitledGreyBox from '@/components/elements/TitledGreyBox'; +import ConfirmationModal from '@/components/elements/ConfirmationModal'; +import reinstallServer from '@/api/server/reinstallServer'; +import { Actions, useStoreActions } from 'easy-peasy'; +import { ApplicationStore } from '@/state'; +import { httpErrorToHuman } from '@/api/http'; + +export default () => { + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const [ isSubmitting, setIsSubmitting ] = useState(false); + const [ modalVisible, setModalVisible ] = useState(false); + const { addFlash, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + + const reinstall = () => { + clearFlashes('settings'); + setIsSubmitting(true); + reinstallServer(uuid) + .then(() => { + addFlash({ key: 'settings', type: 'success', message: 'Your server has begun the reinstallation process.' }); + }) + .catch(error => { + console.error(error); + + addFlash({ key: 'settings', type: 'error', message: httpErrorToHuman(error) }); + }) + .then(() => { + setIsSubmitting(false); + setModalVisible(false); + }); + } + + return ( + + reinstall()} + showSpinnerOverlay={isSubmitting} + visible={modalVisible} + onDismissed={() => setModalVisible(false)} + > + Your server will be stopped and some files may be deleted or modified during this process, are you sure you wish to continue? + +

+ Reinstalling your server will stop it, and then re-run the installation script that initially + set it up.Some files may be deleted or modified during this process, + please back up your data before continuing. +

+
+ +
+
+ ); +}; diff --git a/resources/scripts/components/server/settings/SettingsContainer.tsx b/resources/scripts/components/server/settings/SettingsContainer.tsx index ffdad1208..25766e80e 100644 --- a/resources/scripts/components/server/settings/SettingsContainer.tsx +++ b/resources/scripts/components/server/settings/SettingsContainer.tsx @@ -7,6 +7,7 @@ import { UserData } from '@/state/user'; import RenameServerBox from '@/components/server/settings/RenameServerBox'; import FlashMessageRender from '@/components/FlashMessageRender'; import Can from '@/components/elements/Can'; +import ReinstallServerBox from '@/components/server/settings/ReinstallServerBox'; export default () => { const user = useStoreState(state => state.user.data!); @@ -17,49 +18,56 @@ export default () => {
- -
- - -
-
- - -
-
-
-
-

- Your SFTP password is the same as the password you use to access this panel. -

+
+ +
+ + +
+
+ + +
+
+
+
+

+ Your SFTP password is the same as the password you use to access this panel. +

+
+
+
- -
- - - -
- +
+
+ +
+ +
+
+ + + +
); diff --git a/routes/api-client.php b/routes/api-client.php index f623d5ec2..0a6fa39e9 100644 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -89,5 +89,6 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ Route::group(['prefix' => '/settings'], function () { Route::post('/rename', 'Servers\SettingsController@rename'); + Route::post('/reinstall', 'Servers\SettingsController@reinstall'); }); }); From 807cd815eae575d7b3d6bec1470c8d166057bdc8 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 3 Apr 2020 16:39:55 -0700 Subject: [PATCH 32/58] Fix modal layout positioning --- resources/scripts/components/elements/Modal.tsx | 6 ++++-- .../scripts/components/server/users/EditSubuserModal.tsx | 2 +- resources/styles/components/modal.css | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/scripts/components/elements/Modal.tsx b/resources/scripts/components/elements/Modal.tsx index 88cc78f0c..c37669b02 100644 --- a/resources/scripts/components/elements/Modal.tsx +++ b/resources/scripts/components/elements/Modal.tsx @@ -3,11 +3,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes'; import { CSSTransition } from 'react-transition-group'; import Spinner from '@/components/elements/Spinner'; +import classNames from 'classnames'; export interface RequiredModalProps { visible: boolean; onDismissed: () => void; appear?: boolean; + top?: boolean; } type Props = RequiredModalProps & { @@ -18,7 +20,7 @@ type Props = RequiredModalProps & { children: React.ReactNode; } -export default ({ visible, appear, dismissable, showSpinnerOverlay, closeOnBackground = true, closeOnEscape = true, onDismissed, children }: Props) => { +export default ({ visible, appear, dismissable, showSpinnerOverlay, top = true, closeOnBackground = true, closeOnEscape = true, onDismissed, children }: Props) => { const [render, setRender] = useState(visible); const isDismissable = useMemo(() => { @@ -58,7 +60,7 @@ export default ({ visible, appear, dismissable, showSpinnerOverlay, closeOnBackg } } }}> -
+
{isDismissable &&
setRender(false)}> diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx index 5c49edb28..bd6c0dfdb 100644 --- a/resources/scripts/components/server/users/EditSubuserModal.tsx +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -45,7 +45,7 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr const permissions = useStoreState((state: ApplicationStore) => state.permissions.data); return ( - +

{subuser ? `${canEditUser ? 'Modify' : 'View'} permissions for ${subuser.email}` diff --git a/resources/styles/components/modal.css b/resources/styles/components/modal.css index 5fdceaa87..550191c6b 100644 --- a/resources/styles/components/modal.css +++ b/resources/styles/components/modal.css @@ -6,9 +6,9 @@ & > .modal-container { @apply .relative .w-full .max-w-1/2 .m-auto .flex-col .flex; - /*&.top { + &.top { margin-top: 10%; - }*/ + } & > .modal-close-icon { @apply .absolute .pin-r .p-2 .text-white .cursor-pointer .opacity-50; From 829f05a2c75218d69b24b3678a542410710635ea Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 3 Apr 2020 18:50:07 -0600 Subject: [PATCH 33/58] Add migration for 'threads' column, fix errors on some admin pages, add validation rule for 'threads' column --- app/Models/Server.php | 2 ++ ...24_add_threads_column_to_servers_table.php | 32 +++++++++++++++++++ .../views/admin/eggs/variables.blade.php | 2 +- resources/views/admin/eggs/view.blade.php | 2 +- resources/views/admin/packs/view.blade.php | 2 +- .../views/admin/servers/view/index.blade.php | 8 ++++- 6 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php diff --git a/app/Models/Server.php b/app/Models/Server.php index ebe6d2ee7..6ad7b9f05 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -23,6 +23,7 @@ use Znck\Eloquent\Traits\BelongsToThrough; * @property int $disk * @property int $io * @property int $cpu + * @property string $threads * @property bool $oom_disabled * @property int $allocation_id * @property int $nest_id @@ -109,6 +110,7 @@ class Server extends Validable 'swap' => 'required|numeric|min:-1', 'io' => 'required|numeric|between:10,1000', 'cpu' => 'required|numeric|min:0', + 'threads' => 'sometimes|regex:/^[0-9-,]+$/', 'oom_disabled' => 'sometimes|boolean', 'disk' => 'required|numeric|min:0', 'allocation_id' => 'required|bail|unique:servers|exists:allocations,id', diff --git a/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php new file mode 100644 index 000000000..5440d0c58 --- /dev/null +++ b/database/migrations/2020_04_03_203624_add_threads_column_to_servers_table.php @@ -0,0 +1,32 @@ +string('threads')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('threads'); + }); + } +} diff --git a/resources/views/admin/eggs/variables.blade.php b/resources/views/admin/eggs/variables.blade.php index f8463f4bd..2441c725f 100644 --- a/resources/views/admin/eggs/variables.blade.php +++ b/resources/views/admin/eggs/variables.blade.php @@ -48,7 +48,7 @@

{{ $variable->name }}

-
+
diff --git a/resources/views/admin/eggs/view.blade.php b/resources/views/admin/eggs/view.blade.php index 64c3183e9..d483f049f 100644 --- a/resources/views/admin/eggs/view.blade.php +++ b/resources/views/admin/eggs/view.blade.php @@ -160,7 +160,7 @@