diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9ff2b8a0..8377d3939 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ name : CI -on : [push, pull_request] +on : [pull_request] jobs : client-linting: name : "Client: Linting" @@ -40,7 +40,7 @@ jobs : runs-on : ubuntu-latest strategy : matrix: - php: [7.4, 7.3, 7.2, 7.1] + php: [7.4, 7.3] defaults : { run: { working-directory: ./server }} services : mysql: diff --git a/CHANGELOG.md b/CHANGELOG.md index caac88484..50e5af479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Ce projet adhère au principe du [Semantic Versioning](https://semver.org/spec/v - Corrige un problème avec le nom de la base de données de test (#128 et #129). - Ajoute la création / suppression de devis pour les événements (#5). +- __[CHANGEMENT CRITIQUE]__ Robert requiert maintenant au minimum PHP 7.3 pour fonctionner (#78). ## 0.12.0 (2021-03-29) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83ae05b49..5ad0efc33 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ Cette commande vous permet de lancer un serveur de développement front-end, ave qui servira les sources JS, CSS et les assets, à l'adresse `http://localhost:8081/`. Pour travailler, créez un fichier `.env` dans le dossier `server/` qui contient la variable `APP_ENV=development`, -puis ouvrez l'application sur son serveur back-end (par ex. `http://robert.local`). +puis ouvrez l'application sur son serveur back-end (par ex. `http://robert2.local`). #### `yarn build` @@ -90,7 +90,7 @@ _(Pensez à exécuter cette commande et à commiter le résultat dans votre PR l ## URL de l'API en développement -En développement, l'hôte par défaut utilisé par la partie client pour communiquer avec l'API est `http://robert.local`. +En développement, l'hôte par défaut utilisé par la partie client pour communiquer avec l'API est `http://robert2.local`. Si vous souhaitez modifier ceci, vous pouvez créer un fichier `.env.development.local` à la racine du dossier client et surcharger la variable d'environnement `VUE_APP_API_URL` avec votre propre URL d'API (par diff --git a/client/.env.development b/client/.env.development index d23150e0f..da429c19b 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1 +1 @@ -VUE_APP_API_URL='http://robert.local' +VUE_APP_API_URL='http://robert2.local' diff --git a/client/package.json b/client/package.json index 7d917350a..677d9bb1d 100644 --- a/client/package.json +++ b/client/package.json @@ -7,44 +7,41 @@ "test": "vue-cli-service test:unit" }, "dependencies": { - "@fortawesome/fontawesome-free": "~5.15.3", - "axios": "~0.21.1", - "core-js": "~3.10.1", - "debounce": "~1.2.1", - "deep-freeze-strict": "~1.1.1", - "js-cookie": "~2.2.1", - "moment": "~2.29.1", - "sweetalert2": "~10.15.5", - "v-tooltip": "~2.1.3", - "vue": "~2.6.12", - "vue-click-outside": "~1.1.0", - "vue-js-modal": "~2.0.0-rc.6", - "vue-router": "~3.5.1", - "vue-select": "~3.11.2", - "vue-slim-tabs": "~0.4.0", - "vue-tables-2": "~2.2.1", - "vue-visjs": "~0.4.2", - "vuejs-datepicker": "~1.6.2", - "vuex": "~3.6.2", - "vuex-i18n": "~1.13.1" + "@fortawesome/fontawesome-free": "5.15.3", + "@robert2/vis-timeline": "file:./vendors/vis-timeline", + "axios": "0.21.1", + "debounce": "1.2.1", + "deep-freeze-strict": "1.1.1", + "js-cookie": "2.2.1", + "moment": "2.29.1", + "sweetalert2": "10.16.7", + "v-tooltip": "2.1.3", + "vue": "2.6.12", + "vue-click-outside": "1.1.0", + "vue-js-modal": "2.0.0-rc.6", + "vue-router": "3.5.1", + "vue-select": "3.11.2", + "vue-slim-tabs": "0.4.0", + "vue-tables-2": "2.2.2", + "vuejs-datepicker": "1.6.2", + "vuex": "3.6.2", + "vuex-i18n": "1.13.1" }, "devDependencies": { - "@vue/cli-plugin-babel": "^4.5.12", - "@vue/cli-plugin-eslint": "^4.5.12", - "@vue/cli-plugin-unit-jest": "^4.5.12", - "@vue/cli-service": "^4.5.12", - "@vue/eslint-config-airbnb": "^5.3.0", - "@vue/test-utils": "1.1.4", + "@babel/core": "7.14.0", + "@vue/cli-plugin-babel": "4.5.12", + "@vue/cli-plugin-eslint": "4.5.12", + "@vue/cli-plugin-unit-jest": "4.5.12", + "@vue/cli-service": "4.5.12", + "@vue/eslint-config-airbnb": "5.3.0", + "@vue/test-utils": "1.2.0", "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.1.0", - "babel-jest": "^26.6.3", - "eslint": "^7.24.0", - "eslint-plugin-vue": "^7.9.0", - "node-sass": "^5.0.0", - "sass-loader": "^10.1.1", - "vue-template-compiler": "^2.6.12" - }, - "resolutions": { - "vis-timeline": "7.3.9" + "babel-eslint": "10.1.0", + "babel-jest": "26.6.3", + "eslint": "7.25.0", + "eslint-plugin-vue": "7.9.0", + "node-sass": "5.0.0", + "sass-loader": "10.1.1", + "vue-template-compiler": "2.6.12" } } diff --git a/client/src/components/ErrorDetails/ErrorDetails.vue b/client/src/components/ErrorDetails/ErrorDetails.vue index 76438f9c4..aa3cffb93 100644 --- a/client/src/components/ErrorDetails/ErrorDetails.vue +++ b/client/src/components/ErrorDetails/ErrorDetails.vue @@ -18,18 +18,18 @@ {{ $t('errors.details-intro3') }} Github.

-

+

{{ $t('errors.details-intro-not-detailed') }}


-
+

#### {{ $t('errors.details-request') }}

- `{{ data.requested }}` + `{{ requested }}`

@@ -111,16 +111,21 @@ export default { }; }, computed: { + requested() { + return this.data.debug?.requested; + }, + file() { - return cleanFilePath(this.data.file); + return cleanFilePath(this.data.debug?.file); }, trace() { - if (!this.data.stackTrace) { + const stackTrace = this.data.debug?.stackTrace; + if (!stackTrace) { return []; } - return this.data.stackTrace.map((traceItem) => { + return stackTrace.map((traceItem) => { const { file } = traceItem; return { ...traceItem, file: cleanFilePath(file) }; }); diff --git a/client/src/components/Help/Help.vue b/client/src/components/Help/Help.vue index 814b9c9c6..e15e4b52c 100644 --- a/client/src/components/Help/Help.vue +++ b/client/src/components/Help/Help.vue @@ -86,11 +86,12 @@ export default { } const defaultError = { - requested: '', code: 500, message: 'Unknown error', - file: '', - stackTrace: [], + debug: { + file: '', + stackTrace: [], + }, }; return response.data?.error || defaultError; diff --git a/client/src/components/Timeline/_utils.js b/client/src/components/Timeline/_utils.js new file mode 100644 index 000000000..ea252309d --- /dev/null +++ b/client/src/components/Timeline/_utils.js @@ -0,0 +1,28 @@ +// Copyright (c) 2019 Steve Mallon +// @see https://github.com/sjmallon/vue-visjs/blob/v0.4.2/src/utils.js +/* eslint-disable import/prefer-default-export */ + +import { DataSet, DataView } from '@robert2/vis-timeline'; + +const arrayDiff = (arr1, arr2) => arr1.filter((x) => arr2.indexOf(x) === -1); + +export const mountVisData = (vm) => { + if (vm.items instanceof DataSet || vm.items instanceof DataView) { + return vm.items; + } + + // We attach deep watcher on the prop to propagate changes in the DataSet + const callback = (value) => { + if (!Array.isArray(value)) { + return; + } + + const newIds = new DataSet(value).getIds(); + const diff = arrayDiff(vm.data.getIds(), newIds); + vm.data.update(value); + vm.data.remove(diff); + }; + vm.$watch('items', callback, { deep: true }); + + return new DataSet(vm.items); +}; diff --git a/client/src/components/Timeline/index.js b/client/src/components/Timeline/index.js new file mode 100644 index 000000000..95bc208bd --- /dev/null +++ b/client/src/components/Timeline/index.js @@ -0,0 +1,102 @@ +import '@robert2/vis-timeline/index.scss'; +import moment from 'moment'; +import { Timeline as TimelineCore, DataSet, DataView } from '@robert2/vis-timeline'; +import { mountVisData } from './_utils'; + +const Timeline = { + props: { + items: { + type: [Array, DataSet, DataView], + default: () => [], + }, + options: { + type: Object, + }, + }, + data: () => ({ + data: null, + }), + created() { + // @see https://github.com/almende/vis/issues/2524 + this.timeline = null; + }, + mounted() { + this.data = mountVisData(this); + this.timeline = new TimelineCore( + this.$refs.visualization, + this.data, + this.fullOptions, + ); + + // - Binding des événements globaux. + const globalsEvents = { + itemover: 'item-over', + itemout: 'item-out', + rangechanged: 'range-changed', + doubleClick: 'double-click', + }; + Object.entries(globalsEvents).forEach(([originalName, name]) => { + this.timeline.on(originalName, (props) => { this.$emit(name, props); }); + }); + + // - Binding des événements liés aux données. + const dataEvents = { + add: 'item-added', + update: 'item-updated', + remove: 'item-removed', + }; + Object.entries(dataEvents).forEach(([originalName, name]) => { + this.data.on(originalName, (event, properties, senderId) => { + this.$emit(name, { event, properties, senderId }); + }); + }); + }, + computed: { + fullOptions() { + return { + xss: { + filterOptions: { + whiteList: { + i: 'class', + strong: 'class', + em: 'class', + }, + }, + }, + tooltip: { + followMouse: true, + overflowMethod: 'flip', + }, + moment: (date) => moment(date), + ...(this.options || {}), + onMove: (item, callback) => { + this.$emit('item-moved', item, callback); + }, + onRemove: (item, callback) => { + this.$emit('item-remove', item, callback); + }, + }; + }, + }, + watch: { + fullOptions: { + deep: true, + handler() { + this.timeline.setOptions(this.fullOptions); + }, + }, + }, + beforeDestroy() { + this.timeline.destroy(); + }, + methods: { + moveTo(time, options) { + this.timeline.moveTo(time, options); + }, + }, + render() { + return
; + }, +}; + +export default Timeline; diff --git a/client/src/pages/Calendar/Calendar.vue b/client/src/pages/Calendar/Calendar.vue index ce12dcecf..564a57b7c 100644 --- a/client/src/pages/Calendar/Calendar.vue +++ b/client/src/pages/Calendar/Calendar.vue @@ -17,17 +17,15 @@ class="Calendar__timeline" :items="events" :options="timelineOptions" - :events="['itemover', 'itemout', 'doubleClick', 'items-remove', 'rangechanged']" - @itemover="onItemOver" - @itemout="onItemOut" + @item-over="onItemOver" + @item-out="onItemOut" + @item-moved="onItemMoved" + @item-remove="onItemRemove" + @item-removed="onItemRemoved" @double-click="onDoubleClick" - @items-remove="onRemoved" - @rangechanged="onRangeChanged" - /> - +

diff --git a/client/src/pages/Calendar/index.js b/client/src/pages/Calendar/index.js index 3ab6684f0..902ff0d80 100644 --- a/client/src/pages/Calendar/index.js +++ b/client/src/pages/Calendar/index.js @@ -1,10 +1,10 @@ import moment from 'moment'; -import { Timeline } from 'vue-visjs'; import { DATE_DB_FORMAT, DATE_QUERY_FORMAT } from '@/config/constants'; import ModalConfig from '@/config/modalConfig'; import Alert from '@/components/Alert'; import Help from '@/components/Help/Help.vue'; import EventDetails from '@/components/EventDetails/EventDetails.vue'; +import Timeline from '@/components/Timeline'; import CalendarHeader from './Header/Header.vue'; import formatEvent from './utils'; @@ -59,55 +59,6 @@ export default { orientation: 'top', zoomMin: ONE_DAY * 7, zoomMax: ONE_DAY * 6 * 30, - tooltip: { followMouse: true, overflowMethod: 'flip' }, - moment: (date) => moment(date), - onMove: (item, callback) => { - if (isVisitor) { - return; - } - - const url = `${this.$route.meta.resource}/${item.id}`; - const itemEnd = moment(item.end); - if (itemEnd.hour() === 0) { - itemEnd.subtract(1, 'day').endOf('day'); - } - const data = { - start_date: moment(item.start).format(DATE_DB_FORMAT), - end_date: itemEnd.format(DATE_DB_FORMAT), - }; - - this.error = null; - this.isLoading = true; - this.$http.put(url, data) - .then(() => { - this.isLoading = false; - this.help = { type: 'success', text: 'page-calendar.event-saved' }; - callback(item); - this.getEventsData(); - }) - .catch((error) => { - callback(null); // - Needed to cancel the move in timeline - this.showError(error); - }); - }, - onRemove: (item, callback) => { - if (isVisitor) { - return; - } - - Alert.ConfirmDelete(this.$t, 'calendar') - .then((result) => { - if (!result.value) { - callback(null); // - Needed to cancel the deletion in timeline - return; - } - - this.error = null; - this.isLoading = true; - const url = `${this.$route.meta.resource}/${item.id}`; - this.$http.delete(url).then(() => { callback(item); }); - }); - }, }, }; }, @@ -157,38 +108,77 @@ export default { }); }, - onRangeChanged(newPeriod) { - localStorage.setItem('calendarStart', newPeriod.start); - localStorage.setItem('calendarEnd', newPeriod.end); + setCenterDate(date) { + this.$refs.Timeline.moveTo(date); + }, - this.$refs.Header.changePeriod(newPeriod); + onItemOver() { + this.help = 'page-calendar.help-timeline-event-operations'; + }, - let needFetch = false; - if (this.fetchStart.isAfter(newPeriod.start)) { - this.fetchStart = moment(newPeriod.start).subtract(8, 'days').startOf('day'); - needFetch = true; - } + onItemOut() { + this.help = 'page-calendar.help'; + }, - if (this.fetchEnd.isBefore(newPeriod.end)) { - this.fetchEnd = moment(newPeriod.end).add(1, 'months').endOf('month'); - needFetch = true; + onItemMoved(item, callback) { + const isVisitor = this.$store.getters['auth/is']('visitor'); + if (isVisitor) { + return; } - if (needFetch) { - this.getEventsData(); + const url = `${this.$route.meta.resource}/${item.id}`; + const itemEnd = moment(item.end); + if (itemEnd.hour() === 0) { + itemEnd.subtract(1, 'day').endOf('day'); } - }, + const data = { + start_date: moment(item.start).format(DATE_DB_FORMAT), + end_date: itemEnd.format(DATE_DB_FORMAT), + }; - setCenterDate(date) { - this.$refs.Timeline.moveTo(date); + this.error = null; + this.isLoading = true; + this.$http.put(url, data) + .then(() => { + this.isLoading = false; + this.help = { type: 'success', text: 'page-calendar.event-saved' }; + callback(item); + this.getEventsData(); + }) + .catch((error) => { + callback(null); // - Needed to cancel the move in timeline + this.showError(error); + }); }, - onItemOver() { - this.help = 'page-calendar.help-timeline-event-operations'; + onItemRemove(item, callback) { + const isVisitor = this.$store.getters['auth/is']('visitor'); + if (isVisitor) { + return; + } + + Alert.ConfirmDelete(this.$t, 'calendar') + .then((result) => { + if (!result.value) { + callback(null); // - Needed to cancel the deletion in timeline + return; + } + + this.error = null; + this.isLoading = true; + const url = `${this.$route.meta.resource}/${item.id}`; + this.$http.delete(url).then(() => { callback(item); }); + }); }, - onItemOut() { - this.help = 'page-calendar.help'; + onItemRemoved() { + if (!this.isLoading) { + return; + } + + this.help = { type: 'success', text: 'page-calendar.event-deleted' }; + this.error = null; + this.isLoading = false; }, onDoubleClick(e) { @@ -212,6 +202,31 @@ export default { }); }, + onRangeChanged(newPeriod) { + const dates = Object.fromEntries(['start', 'end'].map( + (type) => [type, newPeriod[type].getTime()], + )); + + localStorage.setItem('calendarStart', dates.start); + localStorage.setItem('calendarEnd', dates.end); + this.$refs.Header.changePeriod(dates); + + let needFetch = false; + if (this.fetchStart.isAfter(dates.start)) { + this.fetchStart = moment(dates.start).subtract(8, 'days').startOf('day'); + needFetch = true; + } + + if (this.fetchEnd.isBefore(dates.end)) { + this.fetchEnd = moment(dates.end).add(1, 'months').endOf('month'); + needFetch = true; + } + + if (needFetch) { + this.getEventsData(); + } + }, + openEventModal(eventId) { this.$modal.show( EventDetails, @@ -225,16 +240,6 @@ export default { ); }, - onRemoved() { - if (!this.isLoading) { - return; - } - - this.help = { type: 'success', text: 'page-calendar.event-deleted' }; - this.error = null; - this.isLoading = false; - }, - showError(error) { this.error = error; this.isLoading = false; diff --git a/client/src/pages/UserProfile/index.js b/client/src/pages/UserProfile/index.js index e836af565..5a14c6b24 100644 --- a/client/src/pages/UserProfile/index.js +++ b/client/src/pages/UserProfile/index.js @@ -48,7 +48,8 @@ export default { }, computed: { groupId() { - return this.$store.state.auth.user.groupId; + const { user } = this.$store.state.auth; + return user ? user.groupId : ''; }, }, mounted() { diff --git a/client/src/style/vendors/_vis-timeline.scss b/client/src/style/vendors/_vis-timeline.scss index 4da22482e..242a3716b 100644 --- a/client/src/style/vendors/_vis-timeline.scss +++ b/client/src/style/vendors/_vis-timeline.scss @@ -1,8 +1,8 @@ $items-border-radius: 10px; // - L'obligation d'utiliser des !important est due au fait que lors du build, -// l'ordre final du CSS semble être inversé : le CSS de vis-timeline (importé d'office -// par vue-visjs) se retrouve après celui-ci +// l'ordre final du CSS semble être inversé : le CSS de vis-timeline se +// retrouve après celui-ci. .vis-timeline { border:none !important; background-color: $calendar-main-background-color; diff --git a/client/vendors/vis-timeline/index.js b/client/vendors/vis-timeline/index.js new file mode 100644 index 000000000..bbb859833 --- /dev/null +++ b/client/vendors/vis-timeline/index.js @@ -0,0 +1,6 @@ +/* eslint-disable */ + +import { Timeline } from 'vis-timeline'; +import { DataSet, DataView } from 'vis-data'; + +export { Timeline, DataSet, DataView }; diff --git a/client/vendors/vis-timeline/index.scss b/client/vendors/vis-timeline/index.scss new file mode 100644 index 000000000..e9ddf1091 --- /dev/null +++ b/client/vendors/vis-timeline/index.scss @@ -0,0 +1 @@ +@import '~vis-timeline/styles/vis-timeline-graph2d.min.css'; diff --git a/client/vendors/vis-timeline/package.json b/client/vendors/vis-timeline/package.json new file mode 100644 index 000000000..9b444c215 --- /dev/null +++ b/client/vendors/vis-timeline/package.json @@ -0,0 +1,16 @@ +{ + "name": "@robert2/vis-timeline", + "version": "0.0.0", + "main": "./index.js", + "dependencies": { + "@egjs/hammerjs": "2.0.17", + "component-emitter": "1.3.0", + "keycharm": "0.4.0", + "propagating-hammerjs": "2.0.1", + "uuid": "7.0.0", + "vis-data": "7.1.2", + "vis-timeline": "7.4.7", + "vis-util": "5.0.2", + "xss": "1.0.8" + } +} diff --git a/client/yarn.lock b/client/yarn.lock index 7b7212d34..c455622b0 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -16,25 +16,25 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.13.8": - version "7.13.15" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.15.tgz#7e8eea42d0b64fda2b375b22d06c605222e848f4" - integrity sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" + integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== -"@babel/core@^7.1.0", "@babel/core@^7.11.0", "@babel/core@^7.7.5": - version "7.13.15" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.15.tgz#a6d40917df027487b54312202a06812c4f7792d0" - integrity sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ== +"@babel/core@7.14.0", "@babel/core@^7.1.0", "@babel/core@^7.11.0", "@babel/core@^7.7.5": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.0.tgz#47299ff3ec8d111b493f1a9d04bf88c04e728d88" + integrity sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-compilation-targets" "^7.13.13" - "@babel/helper-module-transforms" "^7.13.14" - "@babel/helpers" "^7.13.10" - "@babel/parser" "^7.13.15" + "@babel/generator" "^7.14.0" + "@babel/helper-compilation-targets" "^7.13.16" + "@babel/helper-module-transforms" "^7.14.0" + "@babel/helpers" "^7.14.0" + "@babel/parser" "^7.14.0" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.15" - "@babel/types" "^7.13.14" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -42,12 +42,12 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.13.9", "@babel/generator@^7.4.0": - version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" - integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== +"@babel/generator@^7.14.0", "@babel/generator@^7.4.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.0.tgz#0f35d663506c43e4f10898fbda0d752ec75494be" + integrity sha512-C6u00HbmsrNPug6A+CiNl8rEys7TsdcXwg12BHi2ca5rUfAs3+UwZsuDQSXnc+wCElCXMB8gMaJ3YXDdh8fAlg== dependencies: - "@babel/types" "^7.13.0" + "@babel/types" "^7.14.0" jsesc "^2.5.1" source-map "^0.5.0" @@ -66,25 +66,26 @@ "@babel/helper-explode-assignable-expression" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.13", "@babel/helper-compilation-targets@^7.13.8", "@babel/helper-compilation-targets@^7.9.6": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5" - integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.16", "@babel/helper-compilation-targets@^7.13.8", "@babel/helper-compilation-targets@^7.9.6": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" + integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== dependencies: - "@babel/compat-data" "^7.13.12" + "@babel/compat-data" "^7.13.15" "@babel/helper-validator-option" "^7.12.17" browserslist "^4.14.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.13.11": - version "7.13.11" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" - integrity sha512-ays0I7XYq9xbjCSvT+EvysLgfc3tOkwCULHjrnscGT3A9qD4sk3wXnJ3of0MAWsWGjdinFvajHU2smYuqXKMrw== +"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.13.11", "@babel/helper-create-class-features-plugin@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.0.tgz#38367d3dab125b12f94273de418f4df23a11a15e" + integrity sha512-6pXDPguA5zC40Y8oI5mqr+jEUpjMJonKvknvA+vD8CYDz5uuXEwWBK8sRAsE/t3gfb1k15AQb9RhwpscC4nUJQ== dependencies: + "@babel/helper-annotate-as-pure" "^7.12.13" "@babel/helper-function-name" "^7.12.13" - "@babel/helper-member-expression-to-functions" "^7.13.0" + "@babel/helper-member-expression-to-functions" "^7.13.12" "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/helper-replace-supers" "^7.13.0" + "@babel/helper-replace-supers" "^7.13.12" "@babel/helper-split-export-declaration" "^7.12.13" "@babel/helper-create-regexp-features-plugin@^7.12.13": @@ -133,14 +134,14 @@ "@babel/types" "^7.12.13" "@babel/helper-hoist-variables@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz#5d5882e855b5c5eda91e0cadc26c6e7a2c8593d8" - integrity sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g== + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz#1b1651249e94b51f8f0d33439843e33e39775b30" + integrity sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg== dependencies: - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/traverse" "^7.13.15" + "@babel/types" "^7.13.16" -"@babel/helper-member-expression-to-functions@^7.13.0", "@babel/helper-member-expression-to-functions@^7.13.12": +"@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== @@ -154,19 +155,19 @@ dependencies: "@babel/types" "^7.13.12" -"@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.13.14": - version "7.13.14" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef" - integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== +"@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz#8fcf78be220156f22633ee204ea81f73f826a8ad" + integrity sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw== dependencies: "@babel/helper-module-imports" "^7.13.12" "@babel/helper-replace-supers" "^7.13.12" "@babel/helper-simple-access" "^7.13.12" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.13" - "@babel/types" "^7.13.14" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" @@ -199,7 +200,7 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.12" -"@babel/helper-simple-access@^7.12.13", "@babel/helper-simple-access@^7.13.12": +"@babel/helper-simple-access@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== @@ -220,10 +221,10 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== +"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== "@babel/helper-validator-option@^7.12.17": version "7.12.17" @@ -240,28 +241,28 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.0" -"@babel/helpers@^7.13.10": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" - integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== +"@babel/helpers@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.0.tgz#ea9b6be9478a13d6f961dbb5f36bf75e2f3b8f62" + integrity sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg== dependencies: "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" - integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.15", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0": - version "7.13.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8" - integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.0.tgz#2f0ebfed92bcddcc8395b91f1895191ce2760380" + integrity sha512-AHbfoxesfBALg33idaTBVUkLnfXtsgvJREf93p4p0Lwsz4ppfE7g1tpEXVm4vrxUcH4DVhAa9Z1m1zqf9WUC7Q== "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" @@ -289,6 +290,14 @@ "@babel/helper-create-class-features-plugin" "^7.13.0" "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-proposal-class-static-block@^7.13.11": + version "7.13.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz#6fcbba4a962702c17e5371a0c7b39afde186d703" + integrity sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-class-static-block" "^7.12.13" + "@babel/plugin-proposal-decorators@^7.8.3": version "7.13.15" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.13.15.tgz#e91ccfef2dc24dd5bd5dcc9fc9e2557c684ecfb8" @@ -382,6 +391,16 @@ "@babel/helper-create-class-features-plugin" "^7.13.0" "@babel/helper-plugin-utils" "^7.13.0" +"@babel/plugin-proposal-private-property-in-object@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz#b1a1f2030586b9d3489cc26179d2eb5883277636" + integrity sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.12.13" + "@babel/helper-create-class-features-plugin" "^7.14.0" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-private-property-in-object" "^7.14.0" + "@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba" @@ -411,6 +430,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-class-static-block@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz#8e3d674b0613e67975ceac2776c97b60cafc5c9c" + integrity sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-decorators@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.13.tgz#fac829bf3c7ef4a1bc916257b403e58c6bdaf648" @@ -495,6 +521,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-private-property-in-object@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz#762a4babec61176fec6c88480dec40372b140c0b" + integrity sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-top-level-await@^7.12.13", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" @@ -525,12 +558,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-block-scoping@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" - integrity sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ== +"@babel/plugin-transform-block-scoping@^7.13.16": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.13.16.tgz#a9c0f10794855c63b1d629914c7dcfeddd185892" + integrity sha512-ad3PHUxGnfWF4Efd3qFuznEtZKoBp0spS+DgqzVzRPV7urEBvPLue3y2j80w4Jf2YLzZHj8TOv/Lmvdmh3b2xg== dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-transform-classes@^7.13.0": version "7.13.0" @@ -552,10 +585,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-destructuring@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz#c5dce270014d4e1ebb1d806116694c12b7028963" - integrity sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA== +"@babel/plugin-transform-destructuring@^7.13.17": + version "7.13.17" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz#678d96576638c19d5b36b332504d3fd6e06dea27" + integrity sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA== dependencies: "@babel/helper-plugin-utils" "^7.13.0" @@ -611,23 +644,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-modules-amd@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz#19f511d60e3d8753cc5a6d4e775d3a5184866cc3" - integrity sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ== +"@babel/plugin-transform-modules-amd@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.0.tgz#589494b5b290ff76cf7f59c798011f6d77026553" + integrity sha512-CF4c5LX4LQ03LebQxJ5JZes2OYjzBuk1TdiF7cG7d5dK4lAdw9NZmaxq5K/mouUdNeqwz3TNjnW6v01UqUNgpQ== dependencies: - "@babel/helper-module-transforms" "^7.13.0" + "@babel/helper-module-transforms" "^7.14.0" "@babel/helper-plugin-utils" "^7.13.0" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.9.6": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b" - integrity sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw== +"@babel/plugin-transform-modules-commonjs@^7.14.0", "@babel/plugin-transform-modules-commonjs@^7.9.6": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz#52bc199cb581e0992edba0f0f80356467587f161" + integrity sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ== dependencies: - "@babel/helper-module-transforms" "^7.13.0" + "@babel/helper-module-transforms" "^7.14.0" "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-simple-access" "^7.12.13" + "@babel/helper-simple-access" "^7.13.12" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.13.8": @@ -641,12 +674,12 @@ "@babel/helper-validator-identifier" "^7.12.11" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz#8a3d96a97d199705b9fd021580082af81c06e70b" - integrity sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw== +"@babel/plugin-transform-modules-umd@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz#2f8179d1bbc9263665ce4a65f305526b2ea8ac34" + integrity sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw== dependencies: - "@babel/helper-module-transforms" "^7.13.0" + "@babel/helper-module-transforms" "^7.14.0" "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-transform-named-capturing-groups-regex@^7.12.13": @@ -763,17 +796,18 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/preset-env@^7.11.0": - version "7.13.15" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.15.tgz#c8a6eb584f96ecba183d3d414a83553a599f478f" - integrity sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.0.tgz#236f88cd5da625e625dd40500d4824523f50e6c5" + integrity sha512-GWRCdBv2whxqqaSi7bo/BEXf070G/fWFMEdCnmoRg2CZJy4GK06ovFuEjJrZhDRXYgBsYtxVbG8GUHvw+UWBkQ== dependencies: - "@babel/compat-data" "^7.13.15" - "@babel/helper-compilation-targets" "^7.13.13" + "@babel/compat-data" "^7.14.0" + "@babel/helper-compilation-targets" "^7.13.16" "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-validator-option" "^7.12.17" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12" "@babel/plugin-proposal-async-generator-functions" "^7.13.15" "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-class-static-block" "^7.13.11" "@babel/plugin-proposal-dynamic-import" "^7.13.8" "@babel/plugin-proposal-export-namespace-from" "^7.12.13" "@babel/plugin-proposal-json-strings" "^7.13.8" @@ -784,9 +818,11 @@ "@babel/plugin-proposal-optional-catch-binding" "^7.13.8" "@babel/plugin-proposal-optional-chaining" "^7.13.12" "@babel/plugin-proposal-private-methods" "^7.13.0" + "@babel/plugin-proposal-private-property-in-object" "^7.14.0" "@babel/plugin-proposal-unicode-property-regex" "^7.12.13" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.12.13" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.3" @@ -796,14 +832,15 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.0" "@babel/plugin-syntax-top-level-await" "^7.12.13" "@babel/plugin-transform-arrow-functions" "^7.13.0" "@babel/plugin-transform-async-to-generator" "^7.13.0" "@babel/plugin-transform-block-scoped-functions" "^7.12.13" - "@babel/plugin-transform-block-scoping" "^7.12.13" + "@babel/plugin-transform-block-scoping" "^7.13.16" "@babel/plugin-transform-classes" "^7.13.0" "@babel/plugin-transform-computed-properties" "^7.13.0" - "@babel/plugin-transform-destructuring" "^7.13.0" + "@babel/plugin-transform-destructuring" "^7.13.17" "@babel/plugin-transform-dotall-regex" "^7.12.13" "@babel/plugin-transform-duplicate-keys" "^7.12.13" "@babel/plugin-transform-exponentiation-operator" "^7.12.13" @@ -811,10 +848,10 @@ "@babel/plugin-transform-function-name" "^7.12.13" "@babel/plugin-transform-literals" "^7.12.13" "@babel/plugin-transform-member-expression-literals" "^7.12.13" - "@babel/plugin-transform-modules-amd" "^7.13.0" - "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/plugin-transform-modules-amd" "^7.14.0" + "@babel/plugin-transform-modules-commonjs" "^7.14.0" "@babel/plugin-transform-modules-systemjs" "^7.13.8" - "@babel/plugin-transform-modules-umd" "^7.13.0" + "@babel/plugin-transform-modules-umd" "^7.14.0" "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.13" "@babel/plugin-transform-new-target" "^7.12.13" "@babel/plugin-transform-object-super" "^7.12.13" @@ -830,7 +867,7 @@ "@babel/plugin-transform-unicode-escapes" "^7.12.13" "@babel/plugin-transform-unicode-regex" "^7.12.13" "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.13.14" + "@babel/types" "^7.14.0" babel-plugin-polyfill-corejs2 "^0.2.0" babel-plugin-polyfill-corejs3 "^0.2.0" babel-plugin-polyfill-regenerator "^0.2.0" @@ -849,9 +886,9 @@ esutils "^2.0.2" "@babel/runtime@^7.11.0", "@babel/runtime@^7.13.10", "@babel/runtime@^7.8.4": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" - integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" + integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== dependencies: regenerator-runtime "^0.13.4" @@ -864,27 +901,26 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.13.15", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0": - version "7.13.15" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.15.tgz#c38bf7679334ddd4028e8e1f7b3aa5019f0dada7" - integrity sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef" + integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" + "@babel/generator" "^7.14.0" "@babel/helper-function-name" "^7.12.13" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.15" - "@babel/types" "^7.13.14" + "@babel/parser" "^7.14.0" + "@babel/types" "^7.14.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - version "7.13.14" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" - integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== +"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.0.tgz#3fc3fc74e0cdad878182e5f66cc6bcab1915a802" + integrity sha512-O2LVLdcnWplaGxiPBz12d0HcdN8QdxdsWYhz5LSeuukV/5mn2xUUc3gBeU4QBYPJ18g/UToe8F532XJ608prmg== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" + "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" "@cnakazawa/watch@^1.0.3": @@ -895,7 +931,7 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@egjs/hammerjs@^2.0.17": +"@egjs/hammerjs@2.0.17": version "2.0.17" resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124" integrity sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A== @@ -917,7 +953,7 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@fortawesome/fontawesome-free@~5.15.3": +"@fortawesome/fontawesome-free@5.15.3": version "5.15.3" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz#c36ffa64a2a239bf948541a97b6ae8d729e09a9a" integrity sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w== @@ -1172,6 +1208,19 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@robert2/vis-timeline@file:./vendors/vis-timeline": + version "0.0.0" + dependencies: + "@egjs/hammerjs" "2.0.17" + component-emitter "1.3.0" + keycharm "0.4.0" + propagating-hammerjs "2.0.1" + uuid "7.0.0" + vis-data "7.1.2" + vis-timeline "7.4.7" + vis-util "5.0.2" + xss "1.0.8" + "@soda/friendly-errors-webpack-plugin@^1.7.1": version "1.8.0" resolved "https://registry.yarnpkg.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.0.tgz#84751d82a93019d5c92c0cf0e45ac59087cd2240" @@ -1354,9 +1403,9 @@ integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== "@types/node@*": - version "14.14.41" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" - integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== + version "15.0.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.1.tgz#ef34dea0881028d11398be5bf4e856743e3dc35a" + integrity sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1480,9 +1529,9 @@ integrity sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA== "@vue/babel-plugin-jsx@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.0.4.tgz#077826ca0eccd77cb6ad698254f5821ded5c5189" - integrity sha512-Vu5gsabUdsiWc4vQarg46xWJGs8pMEJyyMQAKA1vO+F4+aR4/jaxWxPCOvZ7XvVyy+ecSbwQp/qIyDVje360UQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.0.6.tgz#184bf3541ab6efdbe5079ab8b20c19e2af100bfb" + integrity sha512-RzYsvBhzKUmY2YG6LoV+W5PnlnkInq0thh1AzCmewwctAgGN6e9UFon6ZrQQV1CO5G5PeME7MqpB+/vvGg0h4g== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/plugin-syntax-jsx" "^7.0.0" @@ -1596,7 +1645,7 @@ resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-4.5.12.tgz#d5ae353abb187672204197dcd077a4367d4d4a24" integrity sha512-dCN0RzVpA8fp+MfjuVBROgM483MPObAb/je+APE/JhpCJyPQORYQEvNpmaorpN+9Cp6mrESVSzhh0qD4SFrlzg== -"@vue/cli-plugin-babel@^4.5.12": +"@vue/cli-plugin-babel@4.5.12": version "4.5.12" resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.12.tgz#c9737d4079485ce9be07c463c81e1e33886c6219" integrity sha512-PhiNDhlGydsRR0F00OJqG/Q3Mz2G1ko8XqS7CJ0l1GVVGmklUEBy2dW/S8ntEgHpSkFa6h49PgYP3WE2OM3CEg== @@ -1609,7 +1658,7 @@ thread-loader "^2.1.3" webpack "^4.0.0" -"@vue/cli-plugin-eslint@^4.5.12": +"@vue/cli-plugin-eslint@4.5.12": version "4.5.12" resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.12.tgz#7fc2a1d0a490fa300ef4e94518c2cc49ba7a292f" integrity sha512-nbjGJkWxo/xdD32DwvnEAUwkWYsObpqNk9NuU7T62ehdzHPzz58o3j03YZ7a7T7Le8bYyOWMYsdNfz63F+XiZQ== @@ -1628,7 +1677,7 @@ dependencies: "@vue/cli-shared-utils" "^4.5.12" -"@vue/cli-plugin-unit-jest@^4.5.12": +"@vue/cli-plugin-unit-jest@4.5.12": version "4.5.12" resolved "https://registry.yarnpkg.com/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-4.5.12.tgz#883d43f9a4bb6088a3b8125bd492511d05e1073e" integrity sha512-hZibVfMDGTANN7QENbE7eEAlk8adTW8fEpuGXA4IV+eDqLDPUPVUOwcw8f9d7Rx3KVHES3GyFQ9yVK/KeI9NGw== @@ -1654,7 +1703,7 @@ resolved "https://registry.yarnpkg.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.12.tgz#f7fbe177ee7176f055b546e9e74472f9d9177626" integrity sha512-STgbvNv/3iHAKArc18b/qjN7RX1FTrfxPeHH26GOr/A8lJes7+CSluZZ8E5R7Zr/vL0zOqOkUVDAjFXVf4zWQA== -"@vue/cli-service@^4.5.12": +"@vue/cli-service@4.5.12": version "4.5.12" resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-4.5.12.tgz#483aef7dc4e2a7b02b7f224f0a2ef7cea910e033" integrity sha512-Di/dFw72HIvUrpTgnnPQkPq07mdd7z3GPeCH/o+6fv4bxOD+gwK9z7P6RkG4lGv2QdLz+qjim9f7xw5w+9ENkg== @@ -1751,7 +1800,7 @@ optionalDependencies: prettier "^1.18.2" -"@vue/eslint-config-airbnb@^5.3.0": +"@vue/eslint-config-airbnb@5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@vue/eslint-config-airbnb/-/eslint-config-airbnb-5.3.0.tgz#896551d600816a06dff13fdd7d04fd5153379817" integrity sha512-m9ldRhbqaODbcc9mQZjPgnTzyNweZblLMTqMfC2kHWY68dYd3kwG/hvENeZWXJnKKo+eGnoptk+7Zq/c1519ZQ== @@ -1766,10 +1815,10 @@ resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz#ceb924b4ecb3b9c43871c7a429a02f8423e621ab" integrity sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ== -"@vue/test-utils@1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.1.4.tgz#a9acb32ea1fa4535b2e1ce5ca100bceb4fade2db" - integrity sha512-9BeL8IqGvJKy553lq/07rhYURQkpS/k+j19rJ/4eDpGJk7z872M0YrBWFhjS14yMKlvYVYOCfWnVIXyrAx0xNw== +"@vue/test-utils@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.2.0.tgz#3bc8c17ed549157275f0aec6b95da40887f7297f" + integrity sha512-poBTLqeJYNq1TXVhtVfnY8vELUVOFdJY8KZZoUuaAkIqPTWsxonU1M8nMWpZT+xEMrM+49+YcuEqtMHVD9Q9gw== dependencies: dom-event-types "^1.0.0" lodash "^4.17.15" @@ -2025,9 +2074,9 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" ajv@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.1.0.tgz#45d5d3d36c7cdd808930cc3e603cf6200dbeb736" - integrity sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ== + version "8.2.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.2.0.tgz#c89d3380a784ce81b2085f48811c4c101df4c602" + integrity sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2338,7 +2387,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axios@~0.21.1: +axios@0.21.1: version "0.21.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== @@ -2359,7 +2408,7 @@ babel-core@7.0.0-bridge.0, babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-eslint@^10.1.0: +babel-eslint@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== @@ -2376,20 +2425,7 @@ babel-helper-vue-jsx-merge-props@^2.0.3: resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg== -babel-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== - dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.0" - chalk "^2.4.2" - slash "^2.0.0" - -babel-jest@^26.6.3: +babel-jest@26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== @@ -2403,6 +2439,19 @@ babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + babel-loader@^8.1.0: version "8.2.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" @@ -2824,14 +2873,14 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.3: - version "4.16.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.4.tgz#7ebf913487f40caf4637b892b268069951c35d58" - integrity sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ== +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.5: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: - caniuse-lite "^1.0.30001208" + caniuse-lite "^1.0.30001219" colorette "^1.2.2" - electron-to-chromium "^1.3.712" + electron-to-chromium "^1.3.723" escalade "^3.1.1" node-releases "^1.1.71" @@ -3048,10 +3097,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001208: - version "1.0.30001209" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001209.tgz#1bb4be0bd118e98e21cfb7ef617b1ef2164622f4" - integrity sha512-2Ktt4OeRM7EM/JaOZjuLzPYAIqmbwQMNnYbgooT+icoRGrKOyAxA1xhlnotBD1KArRSPsuJp3TdYcZYrL7qNxA== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219: + version "1.0.30001220" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001220.tgz#c080e1c8eefb99f6cc9685da6313840bdbaf4c36" + integrity sha512-pjC2T4DIDyGAKTL4dMvGUQaMUHRmhvPpAgNNTa14jaBWHu+bLQgvpFqElxh9L4829Fdx0PlKiMp3wnYldRtECA== capture-exit@^2.0.0: version "2.0.0" @@ -3091,9 +3140,9 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4 supports-color "^5.3.0" chalk@^4.0.0, chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -3363,7 +3412,7 @@ commander@2.17.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.18.0, commander@^2.19.0, commander@^2.20.0: +commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3378,7 +3427,7 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -component-emitter@^1.2.1: +component-emitter@1.3.0, component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== @@ -3537,11 +3586,11 @@ copy-webpack-plugin@^5.1.1: webpack-log "^2.0.0" core-js-compat@^3.6.5, core-js-compat@^3.9.0, core-js-compat@^3.9.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.10.1.tgz#62183a3a77ceeffcc420d907a3e6fc67d9b27f1c" - integrity sha512-ZHQTdTPkqvw2CeHiZC970NNJcnwzT6YIueDMASKt+p3WbZsLXOcoD392SkcWhkC0wBBHhlfhqGKKsNCQUozYtg== + version "3.11.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.11.1.tgz#57a91e9b02d3bb8cf37f82eceaf44a3d646fa614" + integrity sha512-aZ0e4tmlG/aOBHj92/TuOuZwp6jFvn1WNabU5VOVixzhu5t5Ao+JZkQOPlgNXu6ynwLrwJxklT4Gw1G1VGEh+g== dependencies: - browserslist "^4.16.3" + browserslist "^4.16.5" semver "7.0.0" core-js@^2.4.0: @@ -3549,10 +3598,10 @@ core-js@^2.4.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== -core-js@^3.6.5, core-js@~3.10.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.1.tgz#e683963978b6806dcc6c0a4a8bd4ab0bdaf3f21a" - integrity sha512-pwCxEXnj27XG47mu7SXAwhLP3L5CrlvCB91ANUkIz40P27kUcvNfSdvyZJ9CLHiVoKSp+TTChMQMSKQEH/IQxA== +core-js@^3.6.5: + version "3.11.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.11.1.tgz#f920392bf8ed63a0ec8e4e729857bfa3d121c525" + integrity sha512-k93Isqg7e4txZWMGNYwevZL9MiogLk8pd1PtwrmFmi8IBq4GXqUaVW/a33Llt6amSI36uSjd0GWwc9pTT9ALlQ== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3729,6 +3778,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssfilter@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" + integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4= + cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" @@ -3869,7 +3923,7 @@ deasync@^0.1.15: bindings "^1.5.0" node-addon-api "^1.7.1" -debounce@^1.1, debounce@~1.2.1: +debounce@1.2.1, debounce@^1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== @@ -3917,7 +3971,7 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-freeze-strict@~1.1.1: +deep-freeze-strict@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz#77d0583ca24a69be4bbd9ac2fae415d55523e5b0" integrity sha1-d9BYPKJKab5LvZrC+uQV1VUj5bA= @@ -4217,10 +4271,10 @@ ejs@^2.6.1: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== -electron-to-chromium@^1.3.712: - version "1.3.717" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz#78d4c857070755fb58ab64bcc173db1d51cbc25f" - integrity sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ== +electron-to-chromium@^1.3.723: + version "1.3.725" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.725.tgz#04fc83f9189169aff50f0a00c6b4090b910cba85" + integrity sha512-2BbeAESz7kc6KBzs7WVrMc1BY5waUphk4D4DX5dSQXJhsc3tP5ZFaiyuL0AB7vUKzDYpIeYwTYlEfxyjsGUrhw== elliptic@^6.5.3: version "6.5.4" @@ -4462,7 +4516,7 @@ eslint-plugin-import@^2.21.2: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-plugin-vue@^7.9.0: +eslint-plugin-vue@7.9.0: version "7.9.0" resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.9.0.tgz#f8e83a2a908f4c43fc8304f5401d4ff671f3d560" integrity sha512-2Q0qQp5+5h+pZvJKCbG1/jCRUYrdgAz5BYKGyTlp2NU8mx09u3Hp7PsH6d5qef6ojuPoCXMnrbbDxeoplihrSw== @@ -4505,10 +4559,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.24.0: - version "7.24.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.24.0.tgz#2e44fa62d93892bfdb100521f17345ba54b8513a" - integrity sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ== +eslint@7.25.0: + version "7.25.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67" + integrity sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -5008,9 +5062,9 @@ flush-write-stream@^1.0.0: readable-stream "^2.3.6" follow-redirects@^1.0.0, follow-redirects@^1.10.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== + version "1.14.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.0.tgz#f5d260f95c5f8c105894491feee5dc8993b402fe" + integrity sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg== for-each@^0.3.3: version "0.3.3" @@ -5318,11 +5372,6 @@ gzip-size@^5.0.0: duplexer "^0.1.1" pify "^4.0.1" -hammerjs@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" - integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= - handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -5610,9 +5659,9 @@ http-proxy-middleware@0.19.1: micromatch "^3.1.10" http-proxy-middleware@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.1.2.tgz#38d062ce4182b2931442efc2d9a0c429cab634f8" - integrity sha512-YRFUeOG3q85FJjAaYVJUoNRW9a73SDlOtAyQOS5PHLr18QeZ/vEhxywNoOPiEO8BxCegz4RXzTHcvyLEGB78UA== + version "1.3.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665" + integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg== dependencies: "@types/http-proxy" "^1.17.5" http-proxy "^1.18.1" @@ -5933,9 +5982,9 @@ is-color-stop@^1.0.0: rgba-regex "^1.0.0" is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.3.0.tgz#d341652e3408bca69c4671b79a0954a3d349f887" + integrity sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw== dependencies: has "^1.0.3" @@ -6736,7 +6785,7 @@ js-beautify@^1.6.12, js-beautify@^1.6.14: mkdirp "^1.0.4" nopt "^5.0.0" -js-cookie@~2.2.1: +js-cookie@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== @@ -6926,10 +6975,10 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -keycharm@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/keycharm/-/keycharm-0.3.1.tgz#1de258425454752b95c4d8a6cab9ec83218670de" - integrity sha512-zn47Ti4FJT9zdF+YBBLWJsfKF/fYQHkrYlBeB5Ez5e2PjW7SoIxr43yehAne2HruulIoid4NKZZxO0dHBygCtQ== +keycharm@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/keycharm/-/keycharm-0.4.0.tgz#8d684ea9cc01379a07fbddee33ff32d97f5ae2a7" + integrity sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ== killable@^1.0.1: version "1.0.1" @@ -7533,7 +7582,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -moment@^2.27.0, moment@~2.29.1: +moment@2.29.1: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -7744,7 +7793,7 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== -node-sass@^5.0.0: +node-sass@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-5.0.0.tgz#4e8f39fbef3bac8d2dc72ebe3b539711883a78d2" integrity sha512-opNgmlu83ZCF792U281Ry7tak9IbVC+AKnXGovcQ8LG8wFaJv6cLnRlc6DIHlmNxWEexB5bZxi9SZ9JyUuOYjw== @@ -7891,9 +7940,9 @@ object-hash@^1.1.4: integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + version "1.10.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.2.tgz#b6385a3e2b7cae0b5eafcf90cddf85d128767f30" + integrity sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA== object-is@^1.0.1: version "1.1.5" @@ -8741,13 +8790,11 @@ postcss-selector-parser@^3.0.0: uniq "^1.0.1" postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: - version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" - integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + version "6.0.5" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz#042d74e137db83e6f294712096cb413f5aa612c4" + integrity sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg== dependencies: cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" util-deprecate "^1.0.2" postcss-svgo@^4.0.3: @@ -8862,12 +8909,10 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -propagating-hammerjs@^1.4.7: - version "1.5.0" - resolved "https://registry.yarnpkg.com/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz#223d58465489b64879fb0cef2c99ba92b294c239" - integrity sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw== - dependencies: - hammerjs "^2.0.8" +propagating-hammerjs@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagating-hammerjs/-/propagating-hammerjs-2.0.1.tgz#2ba5d491d8734944cc0ace9ad2ae9660232cde5d" + integrity sha512-PH3zG5whbSxMocphXJzVtvKr+vWAgfkqVvtuwjSJ/apmEACUoiw6auBAT5HYXpZOR0eGcTAfYG5Yl8h91O5Elg== proto-list@~1.2.1: version "1.2.4" @@ -9484,7 +9529,7 @@ sass-graph@2.2.5: scss-tokenizer "^0.2.3" yargs "^13.3.2" -sass-loader@^10.1.1: +sass-loader@10.1.1: version "10.1.1" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw== @@ -10234,10 +10279,10 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -sweetalert2@~10.15.5: - version "10.15.7" - resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.15.7.tgz#ad4c8f08432952d3283adbaa9a309c534f5a863d" - integrity sha512-imY0MR03NGefPcGzSYjWYz1GMIlniusEBXilswvKrHD/GMiTxA5jnsdjtK2IoRyXfEaqV7GWt6DM4YVjAZU8gw== +sweetalert2@10.16.7: + version "10.16.7" + resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.16.7.tgz#beea4accd616c93545ab16ee40c41a3e79ac866b" + integrity sha512-8w7UMU+joJro3r7CyyqLOK5Agc0/TI2OqC2T8odOOYRKbPvq2Bq9GF3HoQT2tFnjiqP5QEpVugayowoIl2ZlWw== symbol-tree@^3.2.2: version "3.2.4" @@ -10245,19 +10290,17 @@ symbol-tree@^3.2.2: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.4: - version "6.1.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.1.0.tgz#676a0cfb206008b59e783fcd94ef8ba7d67d966c" - integrity sha512-T4G5KMmqIk6X87gLKWyU5exPpTjLjY5KyrFWaIjv3SvgaIUGXV7UEzGEnZJdTA38/yUS6f9PlKezQ0bYXG3iIQ== + version "6.6.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.6.0.tgz#905654b79df98d9e9a973de1dd58682532c40e8e" + integrity sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg== dependencies: ajv "^8.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" lodash.clonedeep "^4.5.0" lodash.flatten "^4.4.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" + strip-ansi "^6.0.0" tapable@^0.1.8: version "0.1.10" @@ -10837,12 +10880,17 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.0.tgz#1833d4b9ce50b732bfaa271f9cb74e974d303c79" + integrity sha512-LNUrNsXdI/fUsypJbWM8Jt4DgQdFAZh41p9C7WE9Cn+CULOEkoG2lgQyH68v3wnIy5K3fN4jdSt270K6IFA3MQ== + uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v-tooltip@~2.1.3: +v-tooltip@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/v-tooltip/-/v-tooltip-2.1.3.tgz#281c2015d1e73787f13c8956aa295b8c3a73f261" integrity sha512-xXngyxLQTOx/yUEy50thb8te7Qo4XU6h4LZB6cvEfVd9mnysUxLEoYwGWDdqR+l69liKsy3IPkdYff3J1gAJ5w== @@ -10884,32 +10932,27 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vis-data@^6.6.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/vis-data/-/vis-data-6.6.1.tgz#2aa52e46c305ad46bb7abe6e7634e2eecd743b15" - integrity sha512-xmujDB2Dzf8T04rGFJ9OP4OA6zRVrz8R9hb0CVKryBrZRCljCga9JjSfgctA8S7wdZu7otDtUIwX4ZOgfV/57w== - -vis-network@^7.8.0: - version "7.10.2" - resolved "https://registry.yarnpkg.com/vis-network/-/vis-network-7.10.2.tgz#b318f1907cf006d9640c4c31a262e0782405a3cf" - integrity sha512-KDx2agbDnaiE0Bye4AcCRqTn5mxzDKhdUNpKkzSn0AOLBmdhNtPGjxAFluAmvFVyiSK5R6Q5KIWdLjeIMu/PAQ== +vis-data@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/vis-data/-/vis-data-7.1.2.tgz#b7d076ac79cb54f7c5e9c80f5b03b93cc8cc1fda" + integrity sha512-RPSegFxEcnp3HUEJSzhS2vBdbJ2PSsrYYuhRlpHp2frO/MfRtTYbIkkLZmPkA/Sg3pPfBlR235gcoKbtdm4mbw== -vis-timeline@7.3.9, vis-timeline@^7.3.7: - version "7.3.9" - resolved "https://registry.yarnpkg.com/vis-timeline/-/vis-timeline-7.3.9.tgz#39235724d3dc66689dacc998b19af233b74509a3" - integrity sha512-VJTTygTMmzfx9nyogaCUrGey/fPKWROnH7UJdKAoajTr/5mn0fPDSAlVhyLhZXBQpIEwnldrhGjnCk3w5UWMKQ== +vis-timeline@7.4.7: + version "7.4.7" + resolved "https://registry.yarnpkg.com/vis-timeline/-/vis-timeline-7.4.7.tgz#ff1e7383e530bc317160d30e860190f93c732224" + integrity sha512-wB/mPBVH3ClgzcM8eawwdBMdVYKcSONAEzhvdrrfjar8x9BAGi8C6JA/5Dhy+/rJwhXQJutwsaABMASMzUzxUA== -vis-util@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/vis-util/-/vis-util-4.3.4.tgz#02319fbd909f82782b96a36d1224f1beea67f8b2" - integrity sha512-hJIZNrwf4ML7FYjs+m+zjJfaNvhjk3/1hbMdQZVnwwpOFJS/8dMG8rdbOHXcKoIEM6U5VOh3HNpaDXxGkOZGpw== +vis-util@5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/vis-util/-/vis-util-5.0.2.tgz#47e8a31580c0805680c43d253ac7da21501990b9" + integrity sha512-oPDmPc4o0uQLoKpKai2XD1DjrhYsA7MRz75Wx9KmfX84e9LLgsbno7jVL5tR0K9eNVQkD6jf0Ei8NtbBHDkF1A== vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -vue-click-outside@~1.1.0: +vue-click-outside@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vue-click-outside/-/vue-click-outside-1.1.0.tgz#48b7680b518923e701643cccb3e165854aad99eb" integrity sha512-pNyvAA9mRXJwPHlHJyjMb4IONSc7khS5lxGcMyE2EIKgNMAO279PWM9Hyq0d5J4FkiSRdmFLwnbjDd5UtPizHQ== @@ -10948,7 +10991,7 @@ vue-jest@^3.0.5: tsconfig "^7.0.0" vue-template-es2015-compiler "^1.6.0" -vue-js-modal@~2.0.0-rc.6: +vue-js-modal@2.0.0-rc.6: version "2.0.0-rc.6" resolved "https://registry.yarnpkg.com/vue-js-modal/-/vue-js-modal-2.0.0-rc.6.tgz#2fd596c79a713d2cbf447150abb5fefce65efd2d" integrity sha512-bJOm7Yhrl0ur/QyXjoC3gMMmE7UxiVEcS2rl8v9iPXIe9QLvjiCSZElSOvvyps8LNuG1X0rPifZGxI/CWKCFaw== @@ -10992,22 +11035,17 @@ vue-resize@^1.0.1: dependencies: "@babel/runtime" "^7.13.10" -vue-router@~3.5.1: +vue-router@3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.1.tgz#edf3cf4907952d1e0583e079237220c5ff6eb6c9" integrity sha512-RRQNLT8Mzr8z7eL4p7BtKvRaTSGdCbTy2+Mm5HTJvLGYSSeG9gDzNasJPP/yOYKLy+/cLG/ftrqq5fvkFwBJEw== -vue-runtime-helpers@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vue-runtime-helpers/-/vue-runtime-helpers-1.1.2.tgz#446b7b820888ab0c5264d2c3a32468e72e4100f3" - integrity sha512-pZfGp+PW/IXEOyETE09xQHR1CKkR9HfHZdnMD/FVLUNI+HxYTa82evx5WrF6Kz4s82qtqHvMZ8MZpbk2zT2E1Q== - -vue-select@~3.11.2: +vue-select@3.11.2: version "3.11.2" resolved "https://registry.yarnpkg.com/vue-select/-/vue-select-3.11.2.tgz#3ef93e3f2707e133c2df0b2920a05eea78764d18" integrity sha512-pIOcY8ajWNSwg8Ns4eHVr5ZWwqKCSZeQRymTnlUI8i+3QiQXF6JIM4lylK6mVfbccs4S6vOyxB7zmJBpp7tDUg== -vue-slim-tabs@~0.4.0: +vue-slim-tabs@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/vue-slim-tabs/-/vue-slim-tabs-0.4.0.tgz#b07face4ad54aaa227f5706b60c17767783d66be" integrity sha512-19bwuKs2HICja0E039QmE7N7PMQcjdZXcpigmXlJTtTYGZy40C112tZF6q7bS18XNZGFcd/sP3jOJRGC80gVVw== @@ -11020,10 +11058,10 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.2: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue-tables-2@~2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/vue-tables-2/-/vue-tables-2-2.2.1.tgz#e7bcc5819f3e8ac3b04e537d4da46c2dab64f0e4" - integrity sha512-N0kBCEmIILjHx6FU92eeX3qD4zCCji4zq9iXFG+bo6YnRK9LsHYbcYVdfrzVhKTUAc0+8q89ocJXwWSphy2fEA== +vue-tables-2@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/vue-tables-2/-/vue-tables-2-2.2.2.tgz#7b060d3a29d02b0a1fc375aff2bb71e0ca6f6b16" + integrity sha512-u+V8jbuLKxz4M7MPw5RoeB5I2z8ao893tq5hd4gg2yDbJy06ByL2qODFi1FVSyP+9lYJtFLFhJvvnH+6n82HAQ== dependencies: array-intersect "^0.0.1" babel-helper-vue-jsx-merge-props "^2.0.3" @@ -11033,7 +11071,7 @@ vue-tables-2@~2.2.1: vue "^2.6.10" vue-pagination-2 "^3.0" -vue-template-compiler@^2.6.12: +vue-template-compiler@2.6.12: version "2.6.12" resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e" integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg== @@ -11046,49 +11084,31 @@ vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0: resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== -vue-visjs@~0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/vue-visjs/-/vue-visjs-0.4.2.tgz#4e30cfedec3a4166ffee38024306eb23a430c525" - integrity sha512-Bjo1hYMoBQkBDQc/KoWzx4cmD34ATaXLQBHL8EPg6SjSZ0Xb8JzqHr9xc5GBbqf199k5i8ybB3Q5poKd2nYSPw== - dependencies: - "@egjs/hammerjs" "^2.0.17" - core-js "^3.6.5" - keycharm "^0.3.1" - moment "^2.27.0" - propagating-hammerjs "^1.4.7" - uuid "^3.4.0" - vis-data "^6.6.1" - vis-network "^7.8.0" - vis-timeline "^7.3.7" - vis-util "^4.3.2" - vue "^2.6.11" - vue-runtime-helpers "^1.1.2" - -vue@>=2.0.0, vue@^2.6.10, vue@^2.6.11, vue@~2.6.12: +vue@2.6.12, vue@>=2.0.0, vue@^2.6.10: version "2.6.12" resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123" integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg== -vuejs-datepicker@~1.6.2: +vuejs-datepicker@1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/vuejs-datepicker/-/vuejs-datepicker-1.6.2.tgz#83c1e8fd4108e7f1d01c061a7e344918f25e47ae" integrity sha512-PkC4vxzFBo7i6FSCUAJfnaWOx6VkKbOqxijSGHHlWxh8FIUKEZVtFychkonVWtK3iwWfhmYtqHcwsmgxefLpLQ== -vuex-i18n@~1.13.1: +vuex-i18n@1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.13.1.tgz#f9f6bf5de44f85929ff684170ed9bb5724cbba88" integrity sha512-VTy5QAyMI6BJwpRfN5qncWQT0ohKiAYK+iTRW4JxgV9dkNoPMuKKDqExbOm1fzpitdrIoIipC3Zqr5fJ706VQg== +vuex@3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71" + integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw== + vuex@^2.0.0: version "2.5.0" resolved "https://registry.yarnpkg.com/vuex/-/vuex-2.5.0.tgz#20f0265ade6c9a5ac6724a405d3ffdb4726c9741" integrity sha512-5oJPOJySBgSgSzoeO+gZB/BbN/XsapgIF6tz34UwJqnGZMQurzIO3B4KIBf862gfc9ya+oduY5sSkq+5/oOilQ== -vuex@~3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71" - integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw== - w3c-hr-time@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -11437,9 +11457,9 @@ ws@^6.0.0, ws@^6.2.1: async-limiter "~1.0.0" ws@^7.0.0: - version "7.4.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" - integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== + version "7.4.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" + integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== xml-name-validator@^3.0.0: version "3.0.0" @@ -11451,6 +11471,14 @@ xmlchars@^2.1.1: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xss@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.8.tgz#32feb87feb74b3dcd3d404b7a68ababf10700535" + integrity sha512-3MgPdaXV8rfQ/pNn16Eio6VXYPTkqwa0vc7GkiymmY/DqR1SE/7VPAAVZz1GJsJFrllMYO3RHfEaiUGjab6TNw== + dependencies: + commander "^2.20.3" + cssfilter "0.0.10" + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" diff --git a/server/.gitignore b/server/.gitignore index 05bb3d288..85805e3aa 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -2,5 +2,6 @@ src/vendor/ +/.phpunit* tests/coverage/ tests/Fixtures/tmp/* diff --git a/server/composer.json b/server/composer.json index fa5af78bc..eaa9b0ea4 100644 --- a/server/composer.json +++ b/server/composer.json @@ -1,4 +1,5 @@ { + "type": "project", "private": true, "authors": [ { @@ -24,32 +25,36 @@ }, "config": { "vendor-dir": "src/vendor", + "sort-packages": true, "platform": { - "php": "7.1.17" + "php": "7.3" } }, "require": { - "php": ">=7.1.17", - "slim/slim": "^3.12.3", - "slim/http-cache": "^0.4.0", - "slim/twig-view": "^2.5.1", - "projek-xyz/slim-monolog" : "^0.1.6", - "tuupola/slim-jwt-auth": "^3.4.0", - "respect/validation": "^1.1.31", - "illuminate/database": "^5.8.36", - "illuminate/pagination": "^5.8.35", - "robmorgan/phinx": "^0.11.4", - "delfimov/translate": "^2.6.0", - "dompdf/dompdf": "^1.0.2", - "twig/intl-extra": "^3.3.0", - "cocur/slugify": "^4.0.0", - "twig/extensions": "^1.5.4", - "vlucas/phpdotenv": "^5.3.0" + "php": ">=7.3", + "cocur/slugify": "4.0.0", + "delfimov/translate": "2.6.0", + "dompdf/dompdf": "1.0.2", + "firebase/php-jwt": "5.2.1", + "illuminate/database": "8.40.0", + "illuminate/pagination": "8.40.0", + "monolog/monolog": "2.2.0", + "php-di/php-di": "6.3.3", + "respect/validation": "1.1.31", + "robmorgan/phinx": "0.12.6", + "slim/http": "1.2.0", + "slim/http-cache": "1.1.0", + "slim/psr7": "1.3.0", + "slim/slim": "4.7.1", + "slim/twig-view": "3.2.0", + "twig/intl-extra": "3.3.0", + "twig/string-extra": "3.3.0", + "vlucas/phpdotenv": "5.3.0" }, "require-dev": { - "there4/slim-test-helpers": "^2.1.3", - "squizlabs/php_codesniffer": "^3.5.4", - "ifsnop/mysqldump-php": "^2.8.0" + "ifsnop/mysqldump-php": "2.9.0", + "phpunit/phpunit": "9.5.4", + "squizlabs/php_codesniffer": "3.6.0" }, "autoload": { "psr-4": { diff --git a/server/composer.lock b/server/composer.lock index 0920db8ad..13831eab2 100644 --- a/server/composer.lock +++ b/server/composer.lock @@ -4,131 +4,30 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f0c6522675d6b4096c84a42bbd769692", + "content-hash": "477ce248385c997e7bb98096a5133666", "packages": [ - { - "name": "cakephp/cache", - "version": "3.9.6", - "source": { - "type": "git", - "url": "https://github.com/cakephp/cache.git", - "reference": "1cdd2dcdce1cb069398d96fd4f70dd1acf7f1f10" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cakephp/cache/zipball/1cdd2dcdce1cb069398d96fd4f70dd1acf7f1f10", - "reference": "1cdd2dcdce1cb069398d96fd4f70dd1acf7f1f10", - "shasum": "" - }, - "require": { - "cakephp/core": "^3.6.0", - "php": ">=5.6.0,<8.0.0", - "psr/simple-cache": "^1.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cake\\Cache\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "CakePHP Community", - "homepage": "https://github.com/cakephp/cache/graphs/contributors" - } - ], - "description": "Easy to use Caching library with support for multiple caching backends", - "homepage": "https://cakephp.org", - "keywords": [ - "cache", - "caching", - "cakephp" - ], - "support": { - "forum": "https://stackoverflow.com/tags/cakephp", - "irc": "irc://irc.freenode.org/cakephp", - "issues": "https://github.com/cakephp/cakephp/issues", - "source": "https://github.com/cakephp/cache" - }, - "time": "2020-10-28T18:13:14+00:00" - }, - { - "name": "cakephp/collection", - "version": "3.9.6", - "source": { - "type": "git", - "url": "https://github.com/cakephp/collection.git", - "reference": "125154fec507d0f1afd0c8913b1cf919dff1da92" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cakephp/collection/zipball/125154fec507d0f1afd0c8913b1cf919dff1da92", - "reference": "125154fec507d0f1afd0c8913b1cf919dff1da92", - "shasum": "" - }, - "require": { - "php": ">=5.6.0,<8.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cake\\Collection\\": "." - }, - "files": [ - "functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "CakePHP Community", - "homepage": "https://github.com/cakephp/collection/graphs/contributors" - } - ], - "description": "Work easily with arrays and iterators by having a battery of utility traversal methods", - "homepage": "https://cakephp.org", - "keywords": [ - "arrays", - "cakephp", - "collections", - "iterators" - ], - "support": { - "forum": "https://stackoverflow.com/tags/cakephp", - "irc": "irc://irc.freenode.org/cakephp", - "issues": "https://github.com/cakephp/cakephp/issues", - "source": "https://github.com/cakephp/collection" - }, - "time": "2020-10-28T18:13:14+00:00" - }, { "name": "cakephp/core", - "version": "3.9.6", + "version": "4.2.5", "source": { "type": "git", "url": "https://github.com/cakephp/core.git", - "reference": "1768817b61dbb500b7dd4b0a9e603479b7b00cc2" + "reference": "c0165c3a64d11f4f48ee61aaffd4a93c4b467927" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/core/zipball/1768817b61dbb500b7dd4b0a9e603479b7b00cc2", - "reference": "1768817b61dbb500b7dd4b0a9e603479b7b00cc2", + "url": "https://api.github.com/repos/cakephp/core/zipball/c0165c3a64d11f4f48ee61aaffd4a93c4b467927", + "reference": "c0165c3a64d11f4f48ee61aaffd4a93c4b467927", "shasum": "" }, "require": { - "cakephp/utility": "^3.6.0", - "php": ">=5.6.0,<8.0.0" + "cakephp/utility": "^4.0", + "php": ">=7.2.0" }, "suggest": { "cakephp/cache": "To use Configure::store() and restore().", - "cakephp/event": "To use PluginApplicationInterface or plugin applications." + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "league/container": "To use Container and ServiceProvider classes" }, "type": "library", "autoload": { @@ -162,28 +61,29 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/core" }, - "time": "2020-10-28T18:13:14+00:00" + "time": "2021-03-30T17:17:30+00:00" }, { "name": "cakephp/database", - "version": "3.9.6", + "version": "4.2.5", "source": { "type": "git", "url": "https://github.com/cakephp/database.git", - "reference": "a2ce3b5471f8c9dd794cefd89f4ce6c3ccc79753" + "reference": "b2935208a9b15318a2dd497010ff28c2b73cdd2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/database/zipball/a2ce3b5471f8c9dd794cefd89f4ce6c3ccc79753", - "reference": "a2ce3b5471f8c9dd794cefd89f4ce6c3ccc79753", + "url": "https://api.github.com/repos/cakephp/database/zipball/b2935208a9b15318a2dd497010ff28c2b73cdd2d", + "reference": "b2935208a9b15318a2dd497010ff28c2b73cdd2d", "shasum": "" }, "require": { - "cakephp/cache": "^3.6.0", - "cakephp/core": "^3.6.0", - "cakephp/datasource": "^3.6.0", - "cakephp/log": "^3.6.0", - "php": ">=5.6.0,<8.0.0" + "cakephp/core": "^4.0", + "cakephp/datasource": "^4.0", + "php": ">=7.2.0" + }, + "suggest": { + "cakephp/i18n": "If you are using locale-aware datetime formats or Chronos types." }, "type": "library", "autoload": { @@ -216,25 +116,27 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/database" }, - "time": "2020-12-09T02:43:02+00:00" + "time": "2021-03-29T16:51:56+00:00" }, { "name": "cakephp/datasource", - "version": "3.9.6", + "version": "4.2.5", "source": { "type": "git", "url": "https://github.com/cakephp/datasource.git", - "reference": "9d1a7e5acc2773fc29febb7d001bd15f0d065789" + "reference": "47aaa8b796dce35e2fe2547db4f8eb591220acd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/datasource/zipball/9d1a7e5acc2773fc29febb7d001bd15f0d065789", - "reference": "9d1a7e5acc2773fc29febb7d001bd15f0d065789", + "url": "https://api.github.com/repos/cakephp/datasource/zipball/47aaa8b796dce35e2fe2547db4f8eb591220acd8", + "reference": "47aaa8b796dce35e2fe2547db4f8eb591220acd8", "shasum": "" }, "require": { - "cakephp/core": "^3.6.0", - "php": ">=5.6.0,<8.0.0" + "cakephp/core": "^4.0", + "php": ">=7.2.0", + "psr/log": "^1.1", + "psr/simple-cache": "^1.0" }, "suggest": { "cakephp/cache": "If you decide to use Query caching.", @@ -272,76 +174,25 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/datasource" }, - "time": "2020-10-28T18:13:14+00:00" - }, - { - "name": "cakephp/log", - "version": "3.9.6", - "source": { - "type": "git", - "url": "https://github.com/cakephp/log.git", - "reference": "cf10bdcce4e78430a780edb2cc5b4b2b4719a65d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cakephp/log/zipball/cf10bdcce4e78430a780edb2cc5b4b2b4719a65d", - "reference": "cf10bdcce4e78430a780edb2cc5b4b2b4719a65d", - "shasum": "" - }, - "require": { - "cakephp/core": "^3.6.0", - "php": ">=5.6.0,<8.0.0", - "psr/log": "^1.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cake\\Log\\": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "CakePHP Community", - "homepage": "https://github.com/cakephp/log/graphs/contributors" - } - ], - "description": "CakePHP logging library with support for multiple different streams", - "homepage": "https://cakephp.org", - "keywords": [ - "Streams", - "cakephp", - "log", - "logging" - ], - "support": { - "forum": "https://stackoverflow.com/tags/cakephp", - "irc": "irc://irc.freenode.org/cakephp", - "issues": "https://github.com/cakephp/cakephp/issues", - "source": "https://github.com/cakephp/log" - }, - "time": "2020-10-28T18:13:14+00:00" + "time": "2021-03-29T16:51:56+00:00" }, { "name": "cakephp/utility", - "version": "3.9.6", + "version": "4.2.5", "source": { "type": "git", "url": "https://github.com/cakephp/utility.git", - "reference": "51b0af31af3239f6141006bbd7cbc7b16aba40d6" + "reference": "4259ae4154e639557af751ae719d58253a79282a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/utility/zipball/51b0af31af3239f6141006bbd7cbc7b16aba40d6", - "reference": "51b0af31af3239f6141006bbd7cbc7b16aba40d6", + "url": "https://api.github.com/repos/cakephp/utility/zipball/4259ae4154e639557af751ae719d58253a79282a", + "reference": "4259ae4154e639557af751ae719d58253a79282a", "shasum": "" }, "require": { - "cakephp/core": "^3.6.0", - "php": ">=5.6.0,<8.0.0" + "cakephp/core": "^4.0", + "php": ">=7.2.0" }, "suggest": { "ext-intl": "To use Text::transliterate() or Text::slug()", @@ -382,7 +233,7 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/utility" }, - "time": "2020-12-09T02:43:02+00:00" + "time": "2021-03-29T16:08:09+00:00" }, { "name": "cocur/slugify", @@ -452,6 +303,10 @@ "slug", "slugify" ], + "support": { + "issues": "https://github.com/cocur/slugify/issues", + "source": "https://github.com/cocur/slugify/tree/master" + }, "time": "2019-12-14T13:04:14+00:00" }, { @@ -507,37 +362,45 @@ "plural", "translate" ], + "support": { + "issues": "https://github.com/delfimov/Translate/issues", + "source": "https://github.com/delfimov/Translate/tree/v2.6.0" + }, "time": "2019-02-13T12:48:20+00:00" }, { "name": "doctrine/inflector", - "version": "1.3.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1" + "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/ec3a55242203ffa6a4b27c58176da97ff0a7aec1", - "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/9cf661f4eb38f7c881cac67c75ea9b00bf97b210", + "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "doctrine/coding-standard": "^7.0", + "phpstan/phpstan": "^0.11", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-strict-rules": "^0.11", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" } }, "notification-url": "https://packagist.org/downloads/", @@ -566,15 +429,39 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", "keywords": [ "inflection", - "pluralize", - "singularize", - "string" + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.x" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } ], - "time": "2019-10-30T19:59:35+00:00" + "time": "2020-05-29T15:13:26+00:00" }, { "name": "dompdf/dompdf", @@ -648,6 +535,62 @@ }, "time": "2021-01-08T14:18:52+00:00" }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, { "name": "firebase/php-jwt", "version": "v5.2.1", @@ -752,6 +695,10 @@ "Result-Type", "result" ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -764,30 +711,86 @@ ], "time": "2020-04-13T13:17:36+00:00" }, + { + "name": "illuminate/collections", + "version": "v8.40.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "deccb956d38710f3f8baf36dd876c3fa1585ec22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/deccb956d38710f3f8baf36dd876c3fa1585ec22", + "reference": "deccb956d38710f3f8baf36dd876c3fa1585ec22", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "php": "^7.3|^8.0" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^5.1.4)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-04-22T21:08:09+00:00" + }, { "name": "illuminate/container", - "version": "v5.8.36", + "version": "v8.40.0", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", - "reference": "b42e5ef939144b77f78130918da0ce2d9ee16574" + "reference": "0e38ee1632d470e56aece0079e6e22d13e6bea8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/b42e5ef939144b77f78130918da0ce2d9ee16574", - "reference": "b42e5ef939144b77f78130918da0ce2d9ee16574", + "url": "https://api.github.com/repos/illuminate/container/zipball/0e38ee1632d470e56aece0079e6e22d13e6bea8e", + "reference": "0e38ee1632d470e56aece0079e6e22d13e6bea8e", "shasum": "" }, "require": { - "illuminate/contracts": "5.8.*", - "illuminate/support": "5.8.*", - "php": "^7.1.3", + "illuminate/contracts": "^8.0", + "php": "^7.3|^8.0", "psr/container": "^1.0" }, + "provide": { + "psr/container-implementation": "1.0" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.8-dev" + "dev-master": "8.x-dev" } }, "autoload": { @@ -807,31 +810,35 @@ ], "description": "The Illuminate Container package.", "homepage": "https://laravel.com", - "time": "2019-08-20T02:00:23+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-03-16T19:42:20+00:00" }, { "name": "illuminate/contracts", - "version": "v5.8.36", + "version": "v8.40.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "00fc6afee788fa07c311b0650ad276585f8aef96" + "reference": "5152041a5c4ac4dbebb3c8ee72d05666c592ae08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/00fc6afee788fa07c311b0650ad276585f8aef96", - "reference": "00fc6afee788fa07c311b0650ad276585f8aef96", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/5152041a5c4ac4dbebb3c8ee72d05666c592ae08", + "reference": "5152041a5c4ac4dbebb3c8ee72d05666c592ae08", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.3|^8.0", "psr/container": "^1.0", "psr/simple-cache": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.8-dev" + "dev-master": "8.x-dev" } }, "autoload": { @@ -851,41 +858,49 @@ ], "description": "The Illuminate Contracts package.", "homepage": "https://laravel.com", - "time": "2019-07-30T13:57:21+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-04-23T13:31:10+00:00" }, { "name": "illuminate/database", - "version": "v5.8.36", + "version": "v8.40.0", "source": { "type": "git", "url": "https://github.com/illuminate/database.git", - "reference": "ac9ae2d82b8a6137400f17b3eea258be3518daa9" + "reference": "742c062a6447278f6b6f8622bd649173ed51fa3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/database/zipball/ac9ae2d82b8a6137400f17b3eea258be3518daa9", - "reference": "ac9ae2d82b8a6137400f17b3eea258be3518daa9", + "url": "https://api.github.com/repos/illuminate/database/zipball/742c062a6447278f6b6f8622bd649173ed51fa3a", + "reference": "742c062a6447278f6b6f8622bd649173ed51fa3a", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/container": "5.8.*", - "illuminate/contracts": "5.8.*", - "illuminate/support": "5.8.*", - "php": "^7.1.3" + "illuminate/collections": "^8.0", + "illuminate/container": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3|^8.0", + "symfony/console": "^5.1.4" }, "suggest": { - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).", - "fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).", - "illuminate/console": "Required to use the database commands (5.8.*).", - "illuminate/events": "Required to use the observers with Eloquent (5.8.*).", - "illuminate/filesystem": "Required to use the migrations (5.8.*).", - "illuminate/pagination": "Required to paginate the result set (5.8.*)." + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "illuminate/console": "Required to use the database commands (^8.0).", + "illuminate/events": "Required to use the observers with Eloquent (^8.0).", + "illuminate/filesystem": "Required to use the migrations (^8.0).", + "illuminate/pagination": "Required to paginate the result set (^8.0).", + "symfony/finder": "Required to use Eloquent model factories (^5.1.4)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.8-dev" + "dev-master": "8.x-dev" } }, "autoload": { @@ -911,37 +926,38 @@ "orm", "sql" ], - "time": "2019-10-03T16:22:57+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-04-28T14:34:49+00:00" }, { - "name": "illuminate/pagination", - "version": "v5.8.36", + "name": "illuminate/macroable", + "version": "v8.40.0", "source": { "type": "git", - "url": "https://github.com/illuminate/pagination.git", - "reference": "391134bc87a47b3dfe5cf60df73e5e0080aec220" + "url": "https://github.com/illuminate/macroable.git", + "reference": "300aa13c086f25116b5f3cde3ca54ff5c822fb05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/pagination/zipball/391134bc87a47b3dfe5cf60df73e5e0080aec220", - "reference": "391134bc87a47b3dfe5cf60df73e5e0080aec220", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/300aa13c086f25116b5f3cde3ca54ff5c822fb05", + "reference": "300aa13c086f25116b5f3cde3ca54ff5c822fb05", "shasum": "" }, "require": { - "ext-json": "*", - "illuminate/contracts": "5.8.*", - "illuminate/support": "5.8.*", - "php": "^7.1.3" + "php": "^7.3|^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.8-dev" + "dev-master": "8.x-dev" } }, "autoload": { "psr-4": { - "Illuminate\\Pagination\\": "" + "Illuminate\\Support\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -954,56 +970,45 @@ "email": "taylor@laravel.com" } ], - "description": "The Illuminate Pagination package.", + "description": "The Illuminate Macroable package.", "homepage": "https://laravel.com", - "time": "2019-03-18T14:45:00+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2020-10-27T15:20:30+00:00" }, { - "name": "illuminate/support", - "version": "v5.8.36", + "name": "illuminate/pagination", + "version": "v8.40.0", "source": { "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "df4af6a32908f1d89d74348624b57e3233eea247" + "url": "https://github.com/illuminate/pagination.git", + "reference": "11839988f114cb80d48b3ca088d65f19388d5a8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/df4af6a32908f1d89d74348624b57e3233eea247", - "reference": "df4af6a32908f1d89d74348624b57e3233eea247", + "url": "https://api.github.com/repos/illuminate/pagination/zipball/11839988f114cb80d48b3ca088d65f19388d5a8d", + "reference": "11839988f114cb80d48b3ca088d65f19388d5a8d", "shasum": "" }, "require": { - "doctrine/inflector": "^1.1", "ext-json": "*", - "ext-mbstring": "*", - "illuminate/contracts": "5.8.*", - "nesbot/carbon": "^1.26.3 || ^2.0", - "php": "^7.1.3" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "suggest": { - "illuminate/filesystem": "Required to use the composer class (5.8.*).", - "moontoast/math": "Required to use ordered UUIDs (^1.1).", - "ramsey/uuid": "Required to use Str::uuid() (^3.7).", - "symfony/process": "Required to use the composer class (^4.2).", - "symfony/var-dumper": "Required to use the dd function (^4.2).", - "vlucas/phpdotenv": "Required to use the env helper (^3.3)." + "illuminate/collections": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3|^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.8-dev" + "dev-master": "8.x-dev" } }, "autoload": { "psr-4": { - "Illuminate\\Support\\": "" - }, - "files": [ - "helpers.php" - ] + "Illuminate\\Pagination\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1015,27 +1020,99 @@ "email": "taylor@laravel.com" } ], - "description": "The Illuminate Support package.", + "description": "The Illuminate Pagination package.", "homepage": "https://laravel.com", - "time": "2019-12-12T14:16:47+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-04-13T12:54:24+00:00" }, { - "name": "monolog/monolog", - "version": "1.26.0", + "name": "illuminate/support", + "version": "v8.40.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "ce1682ef73ab28a61be01c24ec5b090bdf2c3256" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/ce1682ef73ab28a61be01c24ec5b090bdf2c3256", + "reference": "ce1682ef73ab28a61be01c24ec5b090bdf2c3256", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^1.4|^2.0", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/collections": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "nesbot/carbon": "^2.31", + "php": "^7.3|^8.0", + "voku/portable-ascii": "^1.4.8" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "suggest": { + "illuminate/filesystem": "Required to use the composer class (^8.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^1.3).", + "ramsey/uuid": "Required to use Str::uuid() (^4.0).", + "symfony/process": "Required to use the composer class (^5.1.4).", + "symfony/var-dumper": "Required to use the dd function (^5.1.4).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-04-28T12:56:25+00:00" + }, + { + "name": "monolog/monolog", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33" + "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/2209ddd84e7ef1256b7af205d0717fb62cfc9c33", - "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084", + "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" + "php": ">=7.2", + "psr/log": "^1.0.1" }, "provide": { "psr/log-implementation": "1.0.0" @@ -1043,29 +1120,39 @@ "require-dev": { "aws/aws-sdk-php": "^2.4.9 || ^3.0", "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", + "elasticsearch/elasticsearch": "^7", + "graylog2/gelf-php": "^1.4.2", + "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4", "php-console/php-console": "^3.1.3", + "phpspec/prophecy": "^1.6.1", "phpstan/phpstan": "^0.12.59", - "phpunit/phpunit": "~4.5", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", + "phpunit/phpunit": "^8.5", + "predis/predis": "^1.1", + "rollbar/rollbar": "^1.3", + "ruflin/elastica": ">=0.90 <7.0.1", "swiftmailer/swiftmailer": "^5.3|^6.0" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, "autoload": { "psr-4": { "Monolog\\": "src/Monolog" @@ -1079,16 +1166,20 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "homepage": "https://github.com/Seldaek/monolog", "keywords": [ "log", "logging", "psr-3" ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.2.0" + }, "funding": [ { "url": "https://github.com/Seldaek", @@ -1099,20 +1190,20 @@ "type": "tidelift" } ], - "time": "2020-12-14T12:56:38+00:00" + "time": "2020-12-14T13:15:25+00:00" }, { "name": "nesbot/carbon", - "version": "2.45.1", + "version": "2.47.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d" + "reference": "606262fd8888b75317ba9461825a24fc34001e1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/606262fd8888b75317ba9461825a24fc34001e1e", + "reference": "606262fd8888b75317ba9461825a24fc34001e1e", "shasum": "" }, "require": { @@ -1192,7 +1283,7 @@ "type": "tidelift" } ], - "time": "2021-02-11T18:30:17+00:00" + "time": "2021-04-13T21:54:02+00:00" }, { "name": "nikic/fast-route", @@ -1238,8 +1329,77 @@ "router", "routing" ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, "time": "2018-02-13T20:26:39+00:00" }, + { + "name": "opis/closure", + "version": "3.6.2", + "source": { + "type": "git", + "url": "https://github.com/opis/closure.git", + "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/closure/zipball/06e2ebd25f2869e54a306dda991f7db58066f7f6", + "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0" + }, + "require-dev": { + "jeremeamia/superclosure": "^2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opis\\Closure\\": "src/" + }, + "files": [ + "functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", + "homepage": "https://opis.io/closure", + "keywords": [ + "anonymous functions", + "closure", + "function", + "serializable", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/opis/closure/issues", + "source": "https://github.com/opis/closure/tree/3.6.2" + }, + "time": "2021-04-09T13:42:10+00:00" + }, { "name": "phenx/php-font-lib", "version": "0.5.2", @@ -1275,6 +1435,10 @@ ], "description": "A library to read, parse, export and make subsets of different types of font files.", "homepage": "https://github.com/PhenX/php-font-lib", + "support": { + "issues": "https://github.com/PhenX/php-font-lib/issues", + "source": "https://github.com/PhenX/php-font-lib/tree/0.5.2" + }, "time": "2020-03-08T15:31:32+00:00" }, { @@ -1315,201 +1479,272 @@ ], "description": "A library to read, parse and export to PDF SVG files.", "homepage": "https://github.com/PhenX/php-svg-lib", + "support": { + "issues": "https://github.com/PhenX/php-svg-lib/issues", + "source": "https://github.com/PhenX/php-svg-lib/tree/master" + }, "time": "2019-09-11T20:02:13+00:00" }, { - "name": "phpoption/phpoption", - "version": "1.7.5", + "name": "php-di/invoker", + "version": "2.3.0", "source": { "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "992fec6c56f2d1ad1ad5fee28267867c85bfb8f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", - "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/992fec6c56f2d1ad1ad5fee28267867c85bfb8f9", + "reference": "992fec6c56f2d1ad1ad5fee28267867c85bfb8f9", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0" + "php": ">=7.3", + "psr/container": "~1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, "autoload": { "psr-4": { - "PhpOption\\": "src/PhpOption/" + "Invoker\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.0" + }, + "funding": [ { - "name": "Graham Campbell", - "email": "graham@alt-three.com" + "url": "https://github.com/mnapoli", + "type": "github" } ], - "description": "Option Type for PHP", + "time": "2021-01-15T10:25:40+00:00" + }, + { + "name": "php-di/php-di", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "da8e476cafc8011477e2ec9fd2e4706947758af2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/da8e476cafc8011477e2ec9fd2e4706947758af2", + "reference": "da8e476cafc8011477e2ec9fd2e4706947758af2", + "shasum": "" + }, + "require": { + "opis/closure": "^3.5.5", + "php": ">=7.2.0", + "php-di/invoker": "^2.0", + "php-di/phpdoc-reader": "^2.0.1", + "psr/container": "^1.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "doctrine/annotations": "~1.2", + "friendsofphp/php-cs-fixer": "^2.4", + "mnapoli/phpunit-easymock": "^1.2", + "ocramius/proxy-manager": "^2.0.2", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5|^9.0" + }, + "suggest": { + "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", + "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~2.0)" + }, + "type": "library", + "autoload": { + "psr-4": { + "DI\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", "keywords": [ - "language", - "option", - "php", - "type" + "PSR-11", + "container", + "container-interop", + "dependency injection", + "di", + "ioc", + "psr11" ], + "support": { + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/6.3.3" + }, "funding": [ { - "url": "https://github.com/GrahamCampbell", + "url": "https://github.com/mnapoli", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", "type": "tidelift" } ], - "time": "2020-07-20T17:29:33+00:00" + "time": "2021-05-01T16:26:47+00:00" }, { - "name": "pimple/pimple", - "version": "v3.2.3", + "name": "php-di/phpdoc-reader", + "version": "2.2.1", "source": { "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32" + "url": "https://github.com/PHP-DI/PhpDocReader.git", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32", - "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32", + "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/66daff34cbd2627740ffec9469ffbac9f8c8185c", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/container": "^1.0" + "php": ">=7.2.0" }, "require-dev": { - "symfony/phpunit-bridge": "^3.2" + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^8.5|^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev" - } - }, "autoload": { - "psr-0": { - "Pimple": "src/" + "psr-4": { + "PhpDocReader\\": "src/PhpDocReader" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple, a simple Dependency Injection Container", - "homepage": "http://pimple.sensiolabs.org", + "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)", "keywords": [ - "container", - "dependency injection" + "phpdoc", + "reflection" ], - "time": "2018-01-21T07:42:36+00:00" + "support": { + "issues": "https://github.com/PHP-DI/PhpDocReader/issues", + "source": "https://github.com/PHP-DI/PhpDocReader/tree/2.2.1" + }, + "time": "2020-10-12T12:39:22+00:00" }, { - "name": "projek-xyz/slim-monolog", - "version": "v0.1.6", + "name": "phpoption/phpoption", + "version": "1.7.5", "source": { "type": "git", - "url": "https://github.com/projek-xyz/slim-monolog.git", - "reference": "f83ca2a04c8e9e764e7d4a0e5798a9e7f4505961" + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/projek-xyz/slim-monolog/zipball/f83ca2a04c8e9e764e7d4a0e5798a9e7f4505961", - "reference": "f83ca2a04c8e9e764e7d4a0e5798a9e7f4505961", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", "shasum": "" }, "require": { - "monolog/monolog": "~1.21", - "php": ">=5.5.0", - "slim/slim": "~3.0" + "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.7" + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.1-dev" + "dev-master": "1.7-dev" } }, "autoload": { "psr-4": { - "Projek\\Slim\\": "src/" + "PhpOption\\": "src/PhpOption/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Fery Wardiyanto", - "email": "hallo@feryardiant.me", - "homepage": "http://feryardiant.me" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" } ], - "description": "Slim Framework 3 log helper built on top of the Monolog logger component", - "homepage": "http://www.projek.xyz/slim-monolog", + "description": "Option Type for PHP", "keywords": [ - "framework", - "logger", - "monolog", - "slim" + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.7.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } ], - "time": "2016-10-09T23:22:12+00:00" + "time": "2020-07-20T17:29:33+00:00" }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1522,7 +1757,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -1534,7 +1769,11 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/http-factory", @@ -1586,6 +1825,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, "time": "2019-04-30T12:38:16+00:00" }, { @@ -1636,6 +1878,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, "time": "2016-08-06T14:39:51+00:00" }, { @@ -1689,6 +1934,10 @@ "response", "server" ], + "support": { + "issues": "https://github.com/php-fig/http-server-handler/issues", + "source": "https://github.com/php-fig/http-server-handler/tree/master" + }, "time": "2018-10-30T16:46:14+00:00" }, { @@ -1742,20 +1991,24 @@ "request", "response" ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/master" + }, "time": "2018-10-30T17:12:04+00:00" }, { "name": "psr/log", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { @@ -1779,7 +2032,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -1789,7 +2042,10 @@ "psr", "psr-3" ], - "time": "2020-03-23T09:12:05+00:00" + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" }, { "name": "psr/simple-cache", @@ -1837,19 +2093,66 @@ "psr-16", "simple-cache" ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, "time": "2017-10-23T01:57:42+00:00" }, { - "name": "respect/validation", - "version": "1.1.31", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/Respect/Validation.git", - "reference": "45d109fc830644fecc1145200d6351ce4f2769d0" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Respect/Validation/zipball/45d109fc830644fecc1145200d6351ce4f2769d0", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "respect/validation", + "version": "1.1.31", + "source": { + "type": "git", + "url": "https://github.com/Respect/Validation.git", + "reference": "45d109fc830644fecc1145200d6351ce4f2769d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Respect/Validation/zipball/45d109fc830644fecc1145200d6351ce4f2769d0", "reference": "45d109fc830644fecc1145200d6351ce4f2769d0", "shasum": "" }, @@ -1900,38 +2203,44 @@ "validation", "validator" ], + "support": { + "issues": "https://github.com/Respect/Validation/issues", + "source": "https://github.com/Respect/Validation/tree/1.1.31" + }, "time": "2019-05-28T06:10:06+00:00" }, { "name": "robmorgan/phinx", - "version": "0.11.7", + "version": "0.12.6", "source": { "type": "git", "url": "https://github.com/cakephp/phinx.git", - "reference": "3cdde73e0c33c410e076108b3e1603fabb5b330d" + "reference": "3519c5c8d36ec9d41915a9416d2d434adaa79690" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/phinx/zipball/3cdde73e0c33c410e076108b3e1603fabb5b330d", - "reference": "3cdde73e0c33c410e076108b3e1603fabb5b330d", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/3519c5c8d36ec9d41915a9416d2d434adaa79690", + "reference": "3519c5c8d36ec9d41915a9416d2d434adaa79690", "shasum": "" }, "require": { - "cakephp/collection": "^3.7", - "cakephp/database": "^3.7", - "php": ">=5.6", + "cakephp/database": "^4.0", + "php": ">=7.2", + "psr/container": "^1.0", "symfony/config": "^3.4|^4.0|^5.0", - "symfony/console": "^3.4|^4.0|^5.0", - "symfony/yaml": "^3.4|^4.0|^5.0" + "symfony/console": "^3.4|^4.0|^5.0" }, "require-dev": { "cakephp/cakephp-codesniffer": "^3.0", "ext-json": "*", - "phpunit/phpunit": ">=5.7,<8.0", - "sebastian/comparator": ">=1.2.3" + "ext-pdo": "*", + "phpunit/phpunit": "^8.5|^9.3", + "sebastian/comparator": ">=1.2.3", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "ext-json": "Install if using JSON configuration format", + "ext-pdo": "PDO extension is needed", "symfony/yaml": "Install if using YAML configuration format" }, "bin": [ @@ -1980,7 +2289,11 @@ "migrations", "phinx" ], - "time": "2020-05-09T13:59:05+00:00" + "support": { + "issues": "https://github.com/cakephp/phinx/issues", + "source": "https://github.com/cakephp/phinx/tree/0.12.6" + }, + "time": "2021-02-23T09:52:59+00:00" }, { "name": "sabberworm/php-css-parser", @@ -2025,29 +2338,113 @@ "parser", "stylesheet" ], + "support": { + "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", + "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.3.1" + }, "time": "2020-06-01T09:10:00+00:00" }, + { + "name": "slim/http", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-Http.git", + "reference": "3bc9d61b5243cab0d75c89d778bd69464de07354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-Http/zipball/3bc9d61b5243cab0d75c89d778bd69464de07354", + "reference": "3bc9d61b5243cab0d75c89d778bd69464de07354", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "php": "^7.2 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.2", + "laminas/laminas-diactoros": "^2.4", + "nyholm/psr7": "^1.3", + "php-http/psr7-integration-tests": "dev-master", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5 || ^9.3", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Http\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Slim PSR-7 Object Decorators", + "homepage": "http://slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Http/issues", + "source": "https://github.com/slimphp/Slim-Http/tree/1.2.0" + }, + "time": "2020-11-20T06:43:10+00:00" + }, { "name": "slim/http-cache", - "version": "0.4.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-HttpCache.git", - "reference": "1ebc8984718d500e50c410a75063484ceebde468" + "reference": "d1a091aca45695a2159194132872f4a544416bc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-HttpCache/zipball/1ebc8984718d500e50c410a75063484ceebde468", - "reference": "1ebc8984718d500e50c410a75063484ceebde468", + "url": "https://api.github.com/repos/slimphp/Slim-HttpCache/zipball/d1a091aca45695a2159194132872f4a544416bc9", + "reference": "d1a091aca45695a2159194132872f4a544416bc9", "shasum": "" }, "require": { - "php": ">=5.5.0", - "psr/http-message": "^1.0" + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.0", - "slim/slim": "^3.0" + "phpstan/phpstan": "^0.12.28", + "phpunit/phpunit": "^8.5.13 || ^9.3.8", + "slim/psr7": "^1.1", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "autoload": { @@ -2067,45 +2464,146 @@ } ], "description": "Slim Framework HTTP cache middleware and service provider", - "homepage": "http://slimframework.com", + "homepage": "https://www.slimframework.com", "keywords": [ "cache", "framework", "middleware", "slim" ], - "time": "2017-09-20T19:36:50+00:00" + "support": { + "issues": "https://github.com/slimphp/Slim-HttpCache/issues", + "source": "https://github.com/slimphp/Slim-HttpCache/tree/1.1.0" + }, + "time": "2020-12-08T17:32:05+00:00" + }, + { + "name": "slim/psr7", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-Psr7.git", + "reference": "235d2e5a5ee1ad4b97b96870f37f3091b22fffd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/235d2e5a5ee1ad4b97b96870f37f3091b22fffd7", + "reference": "235d2e5a5ee1ad4b97b96870f37f3091b22fffd7", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.4", + "php": "^7.2 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3", + "symfony/polyfill-php80": "^1.18" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.2", + "ext-json": "*", + "http-interop/http-factory-tests": "^0.7.0", + "php-http/psr7-integration-tests": "dev-master", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5 || ^9.3", + "squizlabs/php_codesniffer": "^3.5", + "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Psr7\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Strict PSR-7 implementation", + "homepage": "https://www.slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Psr7/issues", + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.3.0" + }, + "time": "2020-11-28T06:28:46+00:00" }, { "name": "slim/slim", - "version": "3.12.3", + "version": "4.7.1", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "1c9318a84ffb890900901136d620b4f03a59da38" + "reference": "0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/1c9318a84ffb890900901136d620b4f03a59da38", - "reference": "1c9318a84ffb890900901136d620b4f03a59da38", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f", + "reference": "0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f", "shasum": "" }, "require": { "ext-json": "*", - "ext-libxml": "*", - "ext-simplexml": "*", - "nikic/fast-route": "^1.0", - "php": ">=5.5.0", - "pimple/pimple": "^3.0", + "nikic/fast-route": "^1.3", + "php": "^7.2 || ^8.0", "psr/container": "^1.0", - "psr/http-message": "^1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^4.0", - "squizlabs/php_codesniffer": "^2.5" + "adriansuter/php-autoload-override": "^1.2", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^1.7", + "http-interop/http-factory-guzzle": "^1.0", + "laminas/laminas-diactoros": "^2.4", + "nyholm/psr7": "^1.3", + "nyholm/psr7-server": "^1.0.1", + "phpspec/prophecy": "^1.12", + "phpstan/phpstan": "^0.12.58", + "phpunit/phpunit": "^8.5.13", + "slim/http": "^1.2", + "slim/psr7": "^1.3", + "squizlabs/php_codesniffer": "^3.5", + "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." }, "type": "library", "autoload": { @@ -2133,6 +2631,11 @@ "email": "rob@akrabat.com", "homepage": "http://akrabat.com" }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, { "name": "Gabriel Manricks", "email": "gmanricks@me.com", @@ -2140,37 +2643,61 @@ } ], "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", - "homepage": "https://slimframework.com", + "homepage": "https://www.slimframework.com", "keywords": [ "api", "framework", "micro", "router" ], - "time": "2019-11-28T17:40:33+00:00" + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } + ], + "time": "2020-12-01T19:41:22+00:00" }, { "name": "slim/twig-view", - "version": "2.5.1", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/slimphp/Twig-View.git", - "reference": "47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9" + "reference": "9ceaff0764ab8e70f9eeee825a9efd0b4e1dfc85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9", - "reference": "47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9", + "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/9ceaff0764ab8e70f9eeee825a9efd0b4e1dfc85", + "reference": "9ceaff0764ab8e70f9eeee825a9efd0b4e1dfc85", "shasum": "" }, "require": { - "php": ">=5.5.0", + "php": "^7.2 || ^8.0", "psr/http-message": "^1.0", - "twig/twig": "^1.38|^2.7|^3.0" + "slim/slim": "^4.7", + "twig/twig": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^4.8|^5.7", - "slim/slim": "^3.10" + "phpstan/phpstan": "^0.12.58", + "phpunit/phpunit": "^8.5.13 || ^9.3.8", + "psr/http-factory": "^1.0", + "squizlabs/php_codesniffer": "^3.5", + "weirdan/prophecy-shim": "^1.0 || ^2.0.2" }, "type": "library", "autoload": { @@ -2187,10 +2714,15 @@ "name": "Josh Lockhart", "email": "hello@joshlockhart.com", "homepage": "http://joshlockhart.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" } ], - "description": "Slim Framework 3 view helper built on top of the Twig 2 templating component", - "homepage": "http://slimframework.com", + "description": "Slim Framework 4 view helper built on top of the Twig 3 templating component", + "homepage": "https://www.slimframework.com", "keywords": [ "framework", "slim", @@ -2198,36 +2730,42 @@ "twig", "view" ], - "time": "2019-11-28T18:03:50+00:00" + "support": { + "issues": "https://github.com/slimphp/Twig-View/issues", + "source": "https://github.com/slimphp/Twig-View/tree/3.2.0" + }, + "time": "2020-12-08T17:04:14+00:00" }, { "name": "symfony/config", - "version": "v4.4.19", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "2c4c7827a7e143f5cf375666641b0f448eab8802" + "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/2c4c7827a7e143f5cf375666641b0f448eab8802", - "reference": "2c4c7827a7e143f5cf375666641b0f448eab8802", + "url": "https://api.github.com/repos/symfony/config/zipball/3817662ada105c8c4d1afdb4ec003003efd1d8d8", + "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/filesystem": "^3.4|^4.0|^5.0", - "symfony/polyfill-ctype": "~1.8" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/filesystem": "^4.4|^5.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.15" }, "conflict": { - "symfony/finder": "<3.4" + "symfony/finder": "<4.4" }, "require-dev": { - "symfony/event-dispatcher": "^3.4|^4.0|^5.0", - "symfony/finder": "^3.4|^4.0|^5.0", - "symfony/messenger": "^4.1|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/finder": "^4.4|^5.0", + "symfony/messenger": "^4.4|^5.0", "symfony/service-contracts": "^1.1|^2", - "symfony/yaml": "^3.4|^4.0|^5.0" + "symfony/yaml": "^4.4|^5.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -2258,7 +2796,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v4.4.19" + "source": "https://github.com/symfony/config/tree/v5.2.7" }, "funding": [ { @@ -2274,46 +2812,48 @@ "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" + "time": "2021-04-07T16:07:52+00:00" }, { "name": "symfony/console", - "version": "v4.4.19", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "24026c44fc37099fa145707fecd43672831b837a" + "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/24026c44fc37099fa145707fecd43672831b837a", - "reference": "24026c44fc37099fa145707fecd43672831b837a", + "url": "https://api.github.com/repos/symfony/console/zipball/90374b8ed059325b49a29b55b3f8bb4062c87629", + "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", "symfony/polyfill-php80": "^1.15", - "symfony/service-contracts": "^1.1|^2" + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" }, "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3|>=5", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", "symfony/lock": "<4.4", - "symfony/process": "<3.3" + "symfony/process": "<4.4" }, "provide": { "psr/log-implementation": "1.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/event-dispatcher": "^4.3", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", "symfony/lock": "^4.4|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/var-dumper": "^4.3|^5.0" + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" }, "suggest": { "psr/log": "For using the console logger", @@ -2346,8 +2886,81 @@ ], "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.7" + }, + "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": "2021-04-19T14:07:32+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/console/tree/v4.4.19" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" }, "funding": [ { @@ -2363,24 +2976,24 @@ "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" + "time": "2021-03-23T23:28:01+00:00" }, { "name": "symfony/filesystem", - "version": "v4.4.19", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "83a6feed14846d2d9f3916adbaf838819e4e3380" + "reference": "056e92acc21d977c37e6ea8e97374b2a6c8551b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/83a6feed14846d2d9f3916adbaf838819e4e3380", - "reference": "83a6feed14846d2d9f3916adbaf838819e4e3380", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/056e92acc21d977c37e6ea8e97374b2a6c8551b0", + "reference": "056e92acc21d977c37e6ea8e97374b2a6c8551b0", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8" }, "type": "library", @@ -2409,7 +3022,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v4.4.19" + "source": "https://github.com/symfony/filesystem/tree/v5.2.7" }, "funding": [ { @@ -2425,28 +3038,29 @@ "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" + "time": "2021-04-01T10:42:13+00:00" }, { "name": "symfony/intl", - "version": "v4.4.19", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "8a15571146066c39536fd7c3734aa36281d0e788" + "reference": "6d40be5e4331041aa14add5633986d95667ae624" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/8a15571146066c39536fd7c3734aa36281d0e788", - "reference": "8a15571146066c39536fd7c3734aa36281d0e788", + "url": "https://api.github.com/repos/symfony/intl/zipball/6d40be5e4331041aa14add5633986d95667ae624", + "reference": "6d40be5e4331041aa14add5633986d95667ae624", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/polyfill-intl-icu": "~1.0" + "php": ">=7.2.5", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-php80": "^1.15" }, "require-dev": { - "symfony/filesystem": "^3.4|^4.0|^5.0" + "symfony/filesystem": "^4.4|^5.0" }, "suggest": { "ext-intl": "to use the component with locales other than \"en\"" @@ -2496,7 +3110,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v4.4.19" + "source": "https://github.com/symfony/intl/tree/v5.2.7" }, "funding": [ { @@ -2512,7 +3126,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" + "time": "2021-04-24T14:39:13+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2594,7 +3208,88 @@ "time": "2021-01-07T16:49:33+00:00" }, { - "name": "symfony/polyfill-intl-icu", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + }, + "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": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-intl-icu", "version": "v1.22.1", "source": { "type": "git", @@ -2680,6 +3375,90 @@ ], "time": "2021-01-22T09:19:47+00:00" }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, + "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": "2021-01-22T09:19:47+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.22.1", @@ -2924,21 +3703,21 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.9", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26" + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26", - "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", "shasum": "" }, "require": { - "php": ">=7.1.3", - "psr/container": "^1.0" + "php": ">=7.2.5", + "psr/container": "^1.1" }, "suggest": { "symfony/service-implementation": "" @@ -2946,7 +3725,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "2.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -2982,6 +3761,92 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" + }, + "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": "2021-04-01T10:43:52+00:00" + }, + { + "name": "symfony/string", + "version": "v5.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.6" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2996,46 +3861,48 @@ "type": "tidelift" } ], - "time": "2020-07-06T13:19:58+00:00" + "time": "2021-03-17T17:12:15+00:00" }, { "name": "symfony/translation", - "version": "v4.4.19", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "e1d0c67167a553556d9f974b5fa79c2448df317a" + "reference": "e37ece5242564bceea54d709eafc948377ec9749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e1d0c67167a553556d9f974b5fa79c2448df317a", - "reference": "e1d0c67167a553556d9f974b5fa79c2448df317a", + "url": "https://api.github.com/repos/symfony/translation/zipball/e37ece5242564bceea54d709eafc948377ec9749", + "reference": "e37ece5242564bceea54d709eafc948377ec9749", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^1.1.6|^2" + "symfony/polyfill-php80": "^1.15", + "symfony/translation-contracts": "^2.3" }, "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/http-kernel": "<4.4", - "symfony/yaml": "<3.4" + "symfony/config": "<4.4", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" }, "provide": { - "symfony/translation-implementation": "1.0" + "symfony/translation-implementation": "2.3" }, "require-dev": { "psr/log": "~1.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/config": "^4.4|^5.0", + "symfony/console": "^4.4|^5.0", + "symfony/dependency-injection": "^5.0", + "symfony/finder": "^4.4|^5.0", + "symfony/http-kernel": "^5.0", + "symfony/intl": "^4.4|^5.0", "symfony/service-contracts": "^1.1.2|^2", - "symfony/yaml": "^3.4|^4.0|^5.0" + "symfony/yaml": "^4.4|^5.0" }, "suggest": { "psr/log-implementation": "To use logging capability in translator", @@ -3044,6 +3911,9 @@ }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Symfony\\Component\\Translation\\": "" }, @@ -3068,7 +3938,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v4.4.19" + "source": "https://github.com/symfony/translation/tree/v5.2.7" }, "funding": [ { @@ -3084,24 +3954,24 @@ "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" + "time": "2021-04-01T08:15:21+00:00" }, { "name": "symfony/translation-contracts", - "version": "v1.1.10", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "84180a25fad31e23bebd26ca09d89464f082cacc" + "reference": "95c812666f3e91db75385749fe219c5e494c7f95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/84180a25fad31e23bebd26ca09d89464f082cacc", - "reference": "84180a25fad31e23bebd26ca09d89464f082cacc", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/95c812666f3e91db75385749fe219c5e494c7f95", + "reference": "95c812666f3e91db75385749fe219c5e494c7f95", "shasum": "" }, "require": { - "php": ">=7.1.3" + "php": ">=7.2.5" }, "suggest": { "symfony/translation-implementation": "" @@ -3109,7 +3979,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "2.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -3145,6 +4015,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.4.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3159,39 +4032,39 @@ "type": "tidelift" } ], - "time": "2020-09-02T16:08:58+00:00" + "time": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/yaml", - "version": "v4.4.19", + "name": "twig/intl-extra", + "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "17ed9f14c1aa05b1a5cf2e2c5ef2d0be28058ef9" + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "919e8f945c30bd3efeb6a4d79722cda538116658" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/17ed9f14c1aa05b1a5cf2e2c5ef2d0be28058ef9", - "reference": "17ed9f14c1aa05b1a5cf2e2c5ef2d0be28058ef9", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/919e8f945c30bd3efeb6a4d79722cda538116658", + "reference": "919e8f945c30bd3efeb6a4d79722cda538116658", "shasum": "" }, "require": { "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" + "symfony/intl": "^4.3|^5.0", + "twig/twig": "^2.4|^3.0" }, "require-dev": { - "symfony/console": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Twig\\Extra\\Intl\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3204,301 +4077,50 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" } ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", + "keywords": [ + "intl", + "twig" + ], "support": { - "source": "https://github.com/symfony/yaml/tree/v4.4.19" + "source": "https://github.com/twigphp/intl-extra/tree/v3.3.0" }, "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, { "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/twig/twig", "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" - }, - { - "name": "tuupola/callable-handler", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/tuupola/callable-handler.git", - "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tuupola/callable-handler/zipball/0bc7b88630ca753de9aba8f411046856f5ca6f8c", - "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0", - "psr/http-server-middleware": "^1.0" - }, - "require-dev": { - "overtrue/phplint": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.2", - "tuupola/http-factory": "^0.4.0|^1.0", - "zendframework/zend-diactoros": "^1.6.0|^2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Tuupola\\Middleware\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mika Tuupola", - "email": "tuupola@appelsiini.net", - "homepage": "https://appelsiini.net/", - "role": "Developer" - } - ], - "description": "Compatibility layer for PSR-7 double pass and PSR-15 middlewares.", - "homepage": "https://github.com/tuupola/callable-handler", - "keywords": [ - "middleware", - "psr-15", - "psr-7" - ], - "funding": [ - { - "url": "https://github.com/tuupola", - "type": "github" - } - ], - "time": "2020-09-09T08:31:54+00:00" - }, - { - "name": "tuupola/http-factory", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/tuupola/http-factory.git", - "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tuupola/http-factory/zipball/aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", - "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0", - "psr/http-factory": "^1.0" - }, - "conflict": { - "nyholm/psr7": "<1.0" - }, - "provide": { - "psr/http-factory-implementation": "^1.0" - }, - "require-dev": { - "http-interop/http-factory-tests": "^0.7.0", - "overtrue/phplint": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Tuupola\\Http\\Factory\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mika Tuupola", - "email": "tuupola@appelsiini.net", - "homepage": "https://appelsiini.net/", - "role": "Developer" - } - ], - "description": "Lightweight autodiscovering PSR-17 HTTP factories", - "homepage": "https://github.com/tuupola/http-factory", - "keywords": [ - "http", - "psr-17", - "psr-7" - ], - "funding": [ - { - "url": "https://github.com/tuupola", - "type": "github" - } - ], - "time": "2020-10-01T07:46:32+00:00" - }, - { - "name": "tuupola/slim-jwt-auth", - "version": "3.5.1", - "source": { - "type": "git", - "url": "https://github.com/tuupola/slim-jwt-auth.git", - "reference": "1c3668deebbcbdc49c9abe6cce4f6e0c8c5e09b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tuupola/slim-jwt-auth/zipball/1c3668deebbcbdc49c9abe6cce4f6e0c8c5e09b5", - "reference": "1c3668deebbcbdc49c9abe6cce4f6e0c8c5e09b5", - "shasum": "" - }, - "require": { - "firebase/php-jwt": "^3.0|^4.0|^5.0", - "php": "^7.1|^8.0", - "psr/http-message": "^1.0", - "psr/http-server-middleware": "^1.0", - "psr/log": "^1.0", - "tuupola/callable-handler": "^0.3.0|^0.4.0|^1.0", - "tuupola/http-factory": "^0.4.0|^1.0.2" - }, - "require-dev": { - "codedungeon/phpunit-result-printer": "^0.23.4", - "equip/dispatch": "^2.0", - "overtrue/phplint": "^1.0", - "phpstan/phpstan": "^0.12.43", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.4", - "zendframework/zend-diactoros": "^1.3|^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-3.x": "3.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Tuupola\\Middleware\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mika Tuupola", - "email": "tuupola@appelsiini.net", - "homepage": "https://appelsiini.net/", - "role": "Developer" - } - ], - "description": "PSR-7 and PSR-15 JWT Authentication Middleware", - "homepage": "https://github.com/tuupola/slim-jwt-auth", - "keywords": [ - "auth", - "json", - "jwt", - "middleware", - "psr-15", - "psr-7" - ], - "funding": [ - { - "url": "https://github.com/tuupola", - "type": "github" - } - ], - "time": "2020-10-28T09:53:52+00:00" - }, - { - "name": "twig/extensions", - "version": "v1.5.4", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig-extensions.git", - "reference": "57873c8b0c1be51caa47df2cdb824490beb16202" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/57873c8b0c1be51caa47df2cdb824490beb16202", - "reference": "57873c8b0c1be51caa47df2cdb824490beb16202", - "shasum": "" - }, - "require": { - "twig/twig": "^1.27|^2.0" - }, - "require-dev": { - "symfony/phpunit-bridge": "^3.4", - "symfony/translation": "^2.7|^3.4" - }, - "suggest": { - "symfony/translation": "Allow the time_diff output to be translated" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_Extensions_": "lib/" - }, - "psr-4": { - "Twig\\Extensions\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Common additional features for Twig that do not directly belong in core", - "keywords": [ - "i18n", - "text" - ], - "abandoned": true, - "time": "2018-12-05T18:34:18+00:00" + "time": "2021-01-01T14:58:18+00:00" }, { - "name": "twig/intl-extra", + "name": "twig/string-extra", "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/twigphp/intl-extra.git", - "reference": "919e8f945c30bd3efeb6a4d79722cda538116658" + "url": "https://github.com/twigphp/string-extra.git", + "reference": "8a8bb5631e8e27573fae741211b9c752b9b0b428" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/919e8f945c30bd3efeb6a4d79722cda538116658", - "reference": "919e8f945c30bd3efeb6a4d79722cda538116658", + "url": "https://api.github.com/repos/twigphp/string-extra/zipball/8a8bb5631e8e27573fae741211b9c752b9b0b428", + "reference": "8a8bb5631e8e27573fae741211b9c752b9b0b428", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/intl": "^4.3|^5.0", + "php": ">=7.2.5", + "symfony/string": "^5.0", + "symfony/translation-contracts": "^1.1|^2", "twig/twig": "^2.4|^3.0" }, "require-dev": { @@ -3512,7 +4134,7 @@ }, "autoload": { "psr-4": { - "Twig\\Extra\\Intl\\": "" + "Twig\\Extra\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3530,14 +4152,16 @@ "role": "Lead Developer" } ], - "description": "A Twig extension for Intl", + "description": "A Twig extension for Symfony String", "homepage": "https://twig.symfony.com", "keywords": [ - "intl", - "twig" + "html", + "string", + "twig", + "unicode" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.3.0" + "source": "https://github.com/twigphp/string-extra/tree/v3.3.0" }, "funding": [ { @@ -3549,24 +4173,24 @@ "type": "tidelift" } ], - "time": "2021-01-01T14:58:18+00:00" + "time": "2021-01-29T14:39:43+00:00" }, { "name": "twig/twig", - "version": "v2.13.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "57e96259776ddcacf1814885fc3950460c8e18ef" + "reference": "1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/57e96259776ddcacf1814885fc3950460c8e18ef", - "reference": "57e96259776ddcacf1814885fc3950460c8e18ef", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5", + "reference": "1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3" }, @@ -3577,13 +4201,10 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.13-dev" + "dev-master": "3.3-dev" } }, "autoload": { - "psr-0": { - "Twig_": "lib/" - }, "psr-4": { "Twig\\": "src/" } @@ -3614,6 +4235,10 @@ "keywords": [ "templating" ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.3.0" + }, "funding": [ { "url": "https://github.com/fabpot", @@ -3624,7 +4249,7 @@ "type": "tidelift" } ], - "time": "2020-08-05T15:09:04+00:00" + "time": "2021-02-08T09:54:36+00:00" }, { "name": "vlucas/phpdotenv", @@ -3690,6 +4315,10 @@ "env", "environment" ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.3.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -3701,6 +4330,80 @@ } ], "time": "2021-01-20T15:23:13+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "1.5.6", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "80953678b19901e5165c56752d087fc11526017c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c", + "reference": "80953678b19901e5165c56752d087fc11526017c", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/1.5.6" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2020-11-12T00:07:28+00:00" } ], "packages-dev": [ @@ -3753,6 +4456,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -3822,6 +4529,10 @@ "php5", "sql" ], + "support": { + "issues": "https://github.com/ifsnop/mysqldump-php/issues", + "source": "https://github.com/ifsnop/mysqldump-php/tree/master" + }, "time": "2020-04-03T14:40:40+00:00" }, { @@ -3870,6 +4581,10 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", @@ -3878,30 +4593,87 @@ ], "time": "2020-11-13T09:40:50+00:00" }, + { + "name": "nikic/php-parser", + "version": "v4.10.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + }, + "time": "2020-12-20T10:01:03+00:00" + }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -3931,24 +4703,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, + "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -3978,29 +4754,33 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -4027,45 +4807,45 @@ "reflection", "static analysis" ], - "time": "2020-04-27T09:25:28+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.4", + "version": "5.2.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", - "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", - "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", - "webmozart/assert": "^1.0" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "^1.0.5", - "mockery/mockery": "^1.0", - "phpdocumentor/type-resolver": "0.4.*", - "phpunit/phpunit": "^6.4" + "mockery/mockery": "~1.3.2" }, "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/", @@ -4076,38 +4856,44 @@ { "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-12-28T18:55:12+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, + "time": "2020-09-03T19:13:55+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.0.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "shasum": "" }, "require": { - "php": "^7.1", + "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.1", - "mockery/mockery": "~1", - "phpunit/phpunit": "^7.0" + "ext-tokenizer": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { @@ -4126,37 +4912,41 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-08-22T18:11:29+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + }, + "time": "2020-09-17T18:55:26+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.3", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.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" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -4189,33 +4979,52 @@ "spy", "stub" ], - "time": "2020-03-05T15:02:03+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + }, + "time": "2021-03-17T13:42:18+00:00" }, { - "name": "phpunit/dbunit", - "version": "3.0.3", + "name": "phpunit/php-code-coverage", + "version": "9.2.6", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/dbunit.git", - "reference": "0fa4329e490480ab957fe7b1185ea0996ca11f44" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f6293e1b30a2354e8428e004689671b83871edde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/0fa4329e490480ab957fe7b1185ea0996ca11f44", - "reference": "0fa4329e490480ab957fe7b1185ea0996ca11f44", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", + "reference": "f6293e1b30a2354e8428e004689671b83871edde", "shasum": "" }, "require": { - "ext-pdo": "*", - "ext-simplexml": "*", - "php": "^7.0", - "phpunit/phpunit": "^6.0", - "symfony/yaml": "^3.0 || ^4.0" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.10.2", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -4234,52 +5043,49 @@ "role": "lead" } ], - "description": "PHPUnit extension for database interaction testing", - "homepage": "https://github.com/sebastianbergmann/dbunit/", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "database", + "coverage", "testing", "xunit" ], - "abandoned": true, - "time": "2018-01-23T13:32:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-03-28T07:26:59+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", + "name": "phpunit/php-file-iterator", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -4298,36 +5104,52 @@ "role": "lead" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "coverage", - "testing", - "xunit" + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-04-06T15:36:58+00:00" + "time": "2020-09-28T05:57:25+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -4342,36 +5164,53 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "filesystem", - "iterator" + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2020-09-28T05:58:55+00:00" }, { "name": "phpunit/php-text-template", - "version": "1.2.1", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -4393,32 +5232,42 @@ "keywords": [ "template" ], - "time": "2015-06-21T13:50:34+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -4433,7 +5282,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -4442,38 +5291,85 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "2.0.2", + "name": "phpunit/phpunit", + "version": "9.5.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "c73c6737305e779771147af66c96ca6a7ed8a741" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741", + "reference": "c73c6737305e779771147af66c96ca6a7ed8a741", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.0" + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.3", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3", + "sebastian/version": "^3.0.2" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "9.5-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -4483,74 +5379,57 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "tokenizer" + "phpunit", + "testing", + "xunit" ], - "abandoned": true, - "time": "2017-11-27T05:48:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4" + }, + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-03-23T07:16:29+00:00" }, { - "name": "phpunit/phpunit", - "version": "6.5.14", + "name": "sebastian/cli-parser", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" + "php": ">=7.3" }, "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/phpunit": "^9.3" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -4569,48 +5448,44 @@ "role": "lead" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2019-02-01T05:22:47+00:00" + "time": "2020-09-28T06:08:49+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", + "name": "sebastian/code-unit", + "version": "1.0.8", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -4629,39 +5504,101 @@ "role": "lead" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" + "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { @@ -4675,46 +5612,66 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T08:15:22+00:00" + "time": "2020-10-26T15:49:45+00:00" }, { - "name": "sebastian/comparator", - "version": "2.1.3", + "name": "sebastian/complexity", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" + "nikic/php-parser": "^4.7", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -4727,56 +5684,51 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2020-10-26T15:52:27+00:00" }, { "name": "sebastian/diff", - "version": "2.0.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4789,46 +5741,62 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -4853,34 +5821,44 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.3", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4920,33 +5898,40 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:47:53+00:00" + "time": "2020-09-28T05:24:23+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -4954,7 +5939,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -4977,34 +5962,101 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:55:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.4", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -5024,38 +6076,42 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:40:27+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -5075,38 +6131,42 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:37:18+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -5134,35 +6194,42 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:34:24+00:00" + "time": "2020-10-26T13:17:30+00:00" }, { "name": "sebastian/resource-operations", - "version": "1.0.0", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -5182,29 +6249,42 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "sebastian/type", + "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -5223,131 +6303,148 @@ "role": "lead" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:18:59+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "3.5.8", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", - "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" + "php": ">=7.3" }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { - "name": "Greg Sherwood", + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards" + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2020-10-23T02:01:07+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "there4/slim-test-helpers", - "version": "v2.1.3", + "name": "squizlabs/php_codesniffer", + "version": "3.6.0", "source": { "type": "git", - "url": "https://github.com/there4/slim-test-helpers.git", - "reference": "f44d2c4b2f8a08031b2a15837e8b29231cbbc1bf" + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/there4/slim-test-helpers/zipball/f44d2c4b2f8a08031b2a15837e8b29231cbbc1bf", - "reference": "f44d2c4b2f8a08031b2a15837e8b29231cbbc1bf", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625", + "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625", "shasum": "" }, "require": { - "illuminate/database": ">=4.0", - "phpunit/dbunit": "2.*|3.*", - "phpunit/phpunit": "^4.8|5.*|6.*", - "slim/slim": "~3.1" + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, "require-dev": { - "codeclimate/php-test-reporter": "^0.3", - "squizlabs/php_codesniffer": "2.*" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], "type": "library", - "autoload": { - "psr-0": { - "There4": "src/" + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Craig Davis", - "email": "craig@there4development.com" - }, - { - "name": "Guillermo A. Fisher", - "homepage": "http://guillermoandraefisher.com" + "name": "Greg Sherwood", + "role": "lead" } ], - "description": "Integration testing helpers for the Slim Framework", - "homepage": "https://github.com/there4/slim-test-helpers", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ - "slim" + "phpcs", + "standards" ], - "time": "2017-09-27T13:20:14+00:00" + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2021-04-09T00:54:41+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -5367,34 +6464,49 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2020-07-12T23:59:07+00:00" }, { "name": "webmozart/assert", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -5416,7 +6528,11 @@ "check", "validate" ], - "time": "2020-07-08T17:02:28+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], @@ -5425,11 +6541,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.1.17" + "php": ">=7.3" }, "platform-dev": [], "platform-overrides": { - "php": "7.1.17" + "php": "7.3" }, - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.1.0" } diff --git a/server/phpunit.xml b/server/phpunit.xml index 7cc532274..b2ad1585c 100644 --- a/server/phpunit.xml +++ b/server/phpunit.xml @@ -1,4 +1,21 @@ - + + + + + src/App + + + src/App/Config/database.dist.php + src/App/Config/settings.dist.php + src/App/Config/constants.php + src/App/Controllers/EntryController.php + src/App/Controllers/SetupController.php + + tests/other @@ -13,15 +30,4 @@ tests/endpoints - - - src/App - - src/App/Config/database.dist.php - src/App/Config/settings.dist.php - src/App/Config/constants.php - src/App/Controllers/HomeController.php - - - diff --git a/server/src/App/ApiRouter.php b/server/src/App/ApiRouter.php deleted file mode 100644 index 558ad77de..000000000 --- a/server/src/App/ApiRouter.php +++ /dev/null @@ -1,100 +0,0 @@ - [ - '/session[/]' => 'AuthController:getSelf', - '/attributes[/]' => 'AttributeController:getAll', - '/users[/]' => 'UserController:getAll', - '/users/{id:[0-9]+}[/]' => 'UserController:getOne', - '/users/{id:[0-9]+}/settings[/]' => 'UserController:getSettings', - '/tags[/]' => 'TagController:getAll', - '/tags/{id:[0-9]+}/persons[/]' => 'TagController:getPersons', - '/tags/{id:[0-9]+}/materials[/]' => 'TagController:getMaterials', - '/categories[/]' => 'CategoryController:getAll', - '/categories/{id:[0-9]+}[/]' => 'CategoryController:getOne', - '/persons[/]' => 'PersonController:getAll', - '/persons/{id:[0-9]+}[/]' => 'PersonController:getOne', - '/persons/{id:[0-9]+}/tags[/]' => 'PersonController:getTags', - '/countries[/]' => 'CountryController:getAll', - '/countries/{id:[0-9]+}[/]' => 'CountryController:getOne', - '/companies[/]' => 'CompanyController:getAll', - '/companies/{id:[0-9]+}[/]' => 'CompanyController:getOne', - '/companies/{id:[0-9]+}/persons[/]' => 'CompanyController:getPersons', - '/parks[/]' => 'ParkController:getAll', - '/parks/{id:[0-9]+}[/]' => 'ParkController:getOne', - '/materials[/]' => 'MaterialController:getAll', - '/materials/{id:[0-9]+}[/]' => 'MaterialController:getOne', - '/materials/{id:[0-9]+}/tags[/]' => 'MaterialController:getTags', - '/materials/{id:[0-9]+}/documents[/]' => 'MaterialController:getAllDocuments', - '/materials/while-event/{eventId:[0-9]+}[/]' => 'MaterialController:getAllWhileEvent', - '/events[/]' => 'EventController:getAll', - '/events/{id:[0-9]+}[/]' => 'EventController:getOne', - '/events/{id:[0-9]+}/missing-materials[/]' => 'EventController:getMissingMaterials', - '/bills/{id:[0-9]+}[/]' => 'BillController:getOne', - '/estimates/{id:[0-9]+}[/]' => 'EstimateController:getOne', - ], - 'post' => [ - '/session[/]' => 'AuthController:loginWithForm', - '/users/signup[/]' => 'UserController:create', - '/categories[/]' => 'CategoryController:create', - '/subcategories[/]' => 'SubCategoryController:create', - '/tags[/]' => 'TagController:create', - '/persons[/]' => 'PersonController:create', - '/companies[/]' => 'CompanyController:create', - '/parks[/]' => 'ParkController:create', - '/materials[/]' => 'MaterialController:create', - '/materials/{id:[0-9]+}/documents[/]' => 'MaterialController:handleUploadDocuments', - '/attributes[/]' => 'AttributeController:create', - '/events[/]' => 'EventController:create', - '/events/{eventId:[0-9]+}/bill[/]' => 'BillController:create', - '/events/{eventId:[0-9]+}/estimate[/]' => 'EstimateController:create', - ], - 'put' => [ - '/users/{id:[0-9]+}[/]' => 'UserController:update', - '/users/restore/{id:[0-9]+}[/]' => 'UserController:restore', - '/users/{id:[0-9]+}/settings[/]' => 'UserController:updateSettings', - '/categories/{id:[0-9]+}[/]' => 'CategoryController:update', - '/categories/restore/{id:[0-9]+}[/]' => 'CategoryController:restore', - '/subcategories/{id:[0-9]+}[/]' => 'SubCategoryController:update', - '/subcategories/restore/{id:[0-9]+}[/]' => 'SubCategoryController:restore', - '/tags/{id:[0-9]+}[/]' => 'TagController:update', - '/tags/restore/{id:[0-9]+}[/]' => 'TagController:restore', - '/persons/{id:[0-9]+}[/]' => 'PersonController:update', - '/persons/restore/{id:[0-9]+}[/]' => 'PersonController:restore', - '/companies/{id:[0-9]+}[/]' => 'CompanyController:update', - '/companies/restore/{id:[0-9]+}[/]' => 'CompanyController:restore', - '/parks/{id:[0-9]+}[/]' => 'ParkController:update', - '/parks/restore/{id:[0-9]+}[/]' => 'ParkController:restore', - '/materials/{id:[0-9]+}[/]' => 'MaterialController:update', - '/materials/restore/{id:[0-9]+}[/]' => 'MaterialController:restore', - '/attributes/{id:[0-9]+}[/]' => 'AttributeController:update', - '/events/{id:[0-9]+}[/]' => 'EventController:update', - '/events/restore/{id:[0-9]+}[/]' => 'EventController:restore', - ], - 'delete' => [ - '/users/{id:[0-9]+}[/]' => 'UserController:delete', - '/categories/{id:[0-9]+}[/]' => 'CategoryController:delete', - '/subcategories/{id:[0-9]+}[/]' => 'SubCategoryController:delete', - '/tags/{id:[0-9]+}[/]' => 'TagController:delete', - '/persons/{id:[0-9]+}[/]' => 'PersonController:delete', - '/companies/{id:[0-9]+}[/]' => 'CompanyController:delete', - '/parks/{id:[0-9]+}[/]' => 'ParkController:delete', - '/materials/{id:[0-9]+}[/]' => 'MaterialController:delete', - '/attributes/{id:[0-9]+}[/]' => 'AttributeController:delete', - '/events/{id:[0-9]+}[/]' => 'EventController:delete', - '/bills/{id:[0-9]+}[/]' => 'BillController:delete', - '/documents/{id:[0-9]+}[/]' => 'DocumentController:delete', - '/estimates/{id:[0-9]+}[/]' => 'EstimateController:delete', - ], - ]; - - public function getRoutes(): Array - { - return $this->_routes; - } -} diff --git a/server/src/App/App.php b/server/src/App/App.php index 065fa4977..bab43af92 100644 --- a/server/src/App/App.php +++ b/server/src/App/App.php @@ -3,37 +3,44 @@ namespace Robert2\API; -use Robert2\API\Config as Config; -use Robert2\API\Errors as Errors; -use Robert2\API\Middlewares as Middlewares; - +use DI\ContainerBuilder; +use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\RequestHandlerInterface as RequestHandler; +use Robert2\API\Config\Config; +use Robert2\API\Errors\ErrorHandler; +use Slim\Exception\HttpNotFoundException; +use Slim\Factory\AppFactory; +use Slim\Http\Response; +use Slim\Routing\RouteCollectorProxy; + +/** + * App. + * + * @method self add(\Psr\Http\Server\MiddlewareInterface|string|callable $middleware) + * @method Response handle(Request $request) + * @method void run(Request|null $request = null) + */ class App { + private $container; private $app; public function __construct() { - $this->app = new \Slim\App([ - 'settings' => Config\Config::getSettings(), - 'response' => buildResponse(200), - ]); - - $this->_setContainer(); - $this->_setAppRoutes(); - $this->_setMiddlewares(); + $this->container = static::createContainer(); + + $this->app = AppFactory::create(null, $this->container); + $this->app->addBodyParsingMiddleware(); + + $this->configureRouter(); + $this->configureMiddlewares(); + $this->configureErrorHandlers(); + $this->configureCors(); } - public function configureAndGet(): \Slim\App + public function __call($name, $arguments) { - if (isTestMode()) { - $settings = $this->app->getContainer()->get('settings'); - $settings->replace([ - 'displayErrorDetails' => true, - 'routerCacheFile' => false, - ]); - } - - return $this->app; + return \call_user_func_array([$this->app, $name], $arguments); } // ------------------------------------------------------ @@ -42,130 +49,144 @@ public function configureAndGet(): \Slim\App // - // ------------------------------------------------------ - private function _setContainer(): void + protected function configureCors() { - $container = $this->app->getContainer(); + $isCORSEnabled = (bool)$this->container->get('settings')['enableCORS']; + if (isTestMode() || !$isCORSEnabled) { + return; + } + + $this->app->add(function (Request $request, RequestHandler $handler): Response { + /** @var \Slim\Http\Response */ + $response = $handler->handle($request); - $container->register(new \Projek\Slim\MonologProvider()); + $response = $response->withHeader('Access-Control-Allow-Origin', '*'); + $response = $response->withHeader('Access-Control-Allow-Credentials', 'true'); + $response = $response->withHeader( + 'Access-Control-Allow-Headers', + 'X-Requested-With, Content-Type, Accept, Origin, Authorization' + ); - $container = $this->_setHttpCachePovider($container); - $container = $this->_setErrorHandlers($container); - $container = $this->_setControllers($container); - $container = $this->_setServices($container); + return $response; + }); } - private function _setHttpCachePovider(\Slim\Container $container): \Slim\Container + protected function configureRouter() { - $container['cache'] = function () { - return new \Slim\HttpCache\CacheProvider(); - }; + $settings = $this->container->get('settings'); + $useRouterCache = (bool)$settings['useRouterCache'] && !isTestMode(); + $isCORSEnabled = (bool)$settings['enableCORS'] && !isTestMode(); + + // - Route cache + if ($useRouterCache) { + $routeCollector = $this->app->getRouteCollector(); + $routeCollector->setCacheFile(VAR_FOLDER . DS . 'cache' . DS . 'routes.php'); + } - return $container; - } + // - Middleware + $this->app->addRoutingMiddleware(); - private function _setErrorHandlers(\Slim\Container $container): \Slim\Container - { - // - Error (500) handler - $container['errorHandler'] = function ($c) use ($container) { - return new Errors\ErrorHandler($c); - }; - $container['phpErrorHandler'] = function ($c) use ($container) { - // @codeCoverageIgnoreStart - return new Errors\ErrorHandler($c); - // @codeCoverageIgnoreEnd - }; - // - Method Not Allowed (405) handler - $container['notAllowedHandler'] = function ($c) { - return new Errors\MethodNotAllowedHandler(); - }; - // - Not Found (404) handler - $container['notFoundHandler'] = function ($c) { - return new Errors\NotFoundHandler(); - }; + // + // -- Routes: Api + // - return $container; - } + $getActionFqdn = function ($action) { + return sprintf('Robert2\\API\\Controllers\\%s', $action); + }; - private function _setControllers(\Slim\Container $container): \Slim\Container - { - foreach (glob(__DIR__ . '/Controllers/*Controller.php') as $controller) { - $controllerPath = pathinfo($controller); - $controllerName = $controllerPath['filename']; + $this->app->group('/api', function (RouteCollectorProxy $group) use ($isCORSEnabled, $getActionFqdn) { + // - Autorise les requêtes de type OPTIONS sur les routes d'API. + if ($isCORSEnabled) { + $group->options('/{routes:.+}', function (Request $request, Response $response) { + return $response; + }); + } - if ($controllerName === "BaseController") { - continue; + // - Toutes les routes d'API sont définies dans le fichier `Config/routes.php`. + $routeMethods = include CONFIG_FOLDER . DS . 'routes.php'; + foreach ($routeMethods as $method => $routes) { + foreach ($routes as $route => $action) { + $group->$method($route, $getActionFqdn($action)); + } } - $container[$controllerName] = function ($container) use ($controllerName) { - $controllerClass = "Robert2\\API\\Controllers\\$controllerName"; - return new $controllerClass($container); - }; - } + // - Not found API + $group->any('/[{path:.*}]', function (Request $request) { + throw new HttpNotFoundException($request); + }); + }); - return $container; - } + // + // -- Routes: "statics" + // - private function _setServices(\Slim\Container $container): \Slim\Container - { - $container['auth'] = new Services\Auth([ - new Services\Auth\JWT, - ]); + $this->app->map(['GET', 'POST'], '/install', $getActionFqdn('SetupController:index')) + ->setName('install'); - return $container; + // - Download files + $this->app->get('/bills/{id:[0-9]+}/pdf[/]', $getActionFqdn('BillController:getOnePdf')) + ->setName('getBillPdf'); + $this->app->get('/estimates/{id:[0-9]+}/pdf[/]', $getActionFqdn('EstimateController:getOnePdf')) + ->setName('getEstimatePdf'); + $this->app->get('/events/{id:[0-9]+}/pdf[/]', $getActionFqdn('EventController:getOnePdf')) + ->setName('getEventPdf'); + $this->app->get('/documents/{id:[0-9]+}/download[/]', $getActionFqdn('DocumentController:getOne')) + ->setName('getDocumentFile'); + + // - Login services + $this->app->get('/logout', $getActionFqdn('AuthController:logout')); + + // - All remaining non-API routes should be handled by Front-End Router + $this->app->get('/[{path:.*}]', $getActionFqdn('EntryController:index')); } - private function _setMiddlewares(): void + protected function configureMiddlewares() { - $container = $this->app->getContainer(); - + $this->app->add(new Middlewares\Acl); + $this->app->add([$this->container->get('auth'), 'middleware']); $this->app->add(new Middlewares\Pagination); - - // - On passe l'identification si c'est une requête OPTIONS. - $request = $this->app->getContainer()->get('request'); - if (!$request->isOptions()) { - $this->app->add(new Middlewares\Acl); - $this->app->add([$container->get('auth'), 'middleware']); - } } - private function _setAppRoutes() + protected function configureErrorHandlers() { - // - "Static" routes - $this->app->map(['GET', 'POST'], '/install', 'HomeController:install')->setName('install'); - - $settings = $this->app->getContainer()->get('settings'); - if ($settings['enableCORS']) { - $this->app->options('/api/{routes:.+}', function ($request, $response, $args) { - return $response; - }); + $shouldLog = true; + $displayErrorDetails = (bool)$this->container->get('settings')['displayErrorDetails']; + if (isTestMode()) { + $shouldLog = false; + $displayErrorDetails = true; } - // - All API routes are defined in `ApiRouter.php` - $this->app->group('/api', function () { - $routeMethods = (new ApiRouter())->getRoutes(); + $logger = $this->container->get('logger')->createLogger('error'); + $errorMiddleware = $this->app->addErrorMiddleware($displayErrorDetails, $shouldLog, $shouldLog, $logger); - foreach ($routeMethods as $method => $routes) { - foreach ($routes as $route => $controller) { - $this->$method($route, $controller); - } - } - }); + // - Default error handler. + $defaultErrorHandler = new ErrorHandler( + $this->app->getCallableResolver(), + $this->app->getResponseFactory(), + $logger + ); + $errorMiddleware->setDefaultErrorHandler($defaultErrorHandler); + } - // - Not found API - $this->app->any('/api/[{path:.*}]', function () { - throw new Errors\NotFoundException(); - }); + // ------------------------------------------------------ + // - + // - Internal static Methods + // - + // ------------------------------------------------------ - // - Download files - $this->app->get('/bills/{id:[0-9]+}/pdf[/]', 'BillController:getOnePdf')->setName('getBillPdf'); - $this->app->get('/estimates/{id:[0-9]+}/pdf[/]', 'EstimateController:getOnePdf')->setName('getEstimatePdf'); - $this->app->get('/events/{id:[0-9]+}/pdf[/]', 'EventController:getOnePdf')->setName('getEventPdf'); - $this->app->get('/documents/{id:[0-9]+}/download[/]', 'DocumentController:getOne')->setName('getDocumentFile'); + protected static function createContainer() + { + $builder = new ContainerBuilder(); + $builder->addDefinitions(CONFIG_FOLDER . DS . 'definitions.php'); - // - Login services - $this->app->get('/logout', 'AuthController:logout'); + $container = $builder->build(); - // - All remaining non-API routes should be handled by Front-End Router - $this->app->get('/[{path:.*}]', 'HomeController:webclient'); + // + // - Settings + // + + $container->set('settings', Config::getSettings()); + + return $container; } } diff --git a/server/src/App/Config/Config.php b/server/src/App/Config/Config.php index f03b3cec8..7007a0d0c 100644 --- a/server/src/App/Config/Config.php +++ b/server/src/App/Config/Config.php @@ -25,7 +25,6 @@ class Config 'displayErrorDetails' => false, 'useRouterCache' => true, 'useHTTPS' => USE_SSL, - 'routerCacheFile' => VAR_FOLDER . DS . 'cache' . DS . 'routes.php', 'JWTSecret' => 'super_secret_key_you_should_not_commit', 'httpAuthHeader' => 'Authorization', 'sessionExpireHours' => 12, @@ -90,11 +89,8 @@ class Config 'vatRate' => 0.0, ], 'logger' => [ - 'directory' => VAR_FOLDER . DS . 'logs', - 'filename' => 'robert2-api.log', 'timezone' => 'Europe/Paris', 'level' => 'debug', - 'handlers' => [], 'max_files' => 10, ], 'authorizedFileTypes' => [ @@ -141,6 +137,9 @@ class Config 'companyData' => 'array', ]; + /** @var string|null La version de l'application, mise en "cache". */ + private static $versionCached; + public static function getSettings(?string $setting = null) { $settings = self::DEFAULT_SETTINGS; @@ -155,11 +154,6 @@ public static function getSettings(?string $setting = null) return $settings[$setting] ?? null; } - public static function customConfigExists(): bool - { - return file_exists(self::SETTINGS_FILE); - } - public static function getEnv() { $env = $_ENV['APP_ENV'] ?? static::getSettings('env') ?? 'production'; @@ -172,6 +166,14 @@ public static function getEnv() return $env; } + public static function getVersion() + { + if (!static::$versionCached) { + static::$versionCached = trim(file_get_contents(SRC_FOLDER . DS . 'VERSION')); + } + return static::$versionCached; + } + public static function getDbConfig(array $options = []): array { $options = array_merge(['noCharset' => false], $options); @@ -253,6 +255,11 @@ public static function getPDO(): \PDO // @codeCoverageIgnoreEnd } + public static function customConfigExists(): bool + { + return file_exists(self::SETTINGS_FILE); + } + /** * @codeCoverageIgnore */ @@ -326,21 +333,6 @@ private static function _readSettingsFile(): array throw new \RuntimeException("JSON settings file cannot be decoded. It may be malformed or corrupted."); } - return self::_normalizeSettings($settings); - } - - /** - * @codeCoverageIgnore - */ - private static function _normalizeSettings(array $settings): array - { - $settings = array_replace_recursive(self::DEFAULT_SETTINGS, $settings); - - $settings['routerCacheFile'] = false; - if ($settings['useRouterCache']) { - $settings['routerCacheFile'] = VAR_FOLDER . DS . 'cache' . DS . 'routes.php'; - } - - return $settings; + return array_replace_recursive(self::DEFAULT_SETTINGS, $settings); } } diff --git a/server/src/App/Config/constants.php b/server/src/App/Config/constants.php index 45a8eaee0..6ed343446 100644 --- a/server/src/App/Config/constants.php +++ b/server/src/App/Config/constants.php @@ -15,6 +15,9 @@ if (!defined('SRC_FOLDER')) { define('SRC_FOLDER', ROOT_FOLDER . DS . 'src'); } +if (!defined('LOCALES_FOLDER')) { + define('LOCALES_FOLDER', SRC_FOLDER . DS . 'locales'); +} if (!defined('PUBLIC_FOLDER')) { define('PUBLIC_FOLDER', SRC_FOLDER . DS . 'public'); } @@ -24,6 +27,9 @@ if (!defined('VIEWS_FOLDER')) { define('VIEWS_FOLDER', SRC_FOLDER . DS . 'views'); } +if (!defined('CONFIG_FOLDER')) { + define('CONFIG_FOLDER', SRC_FOLDER . DS . 'App' . DS . 'Config'); +} // - Errors codes define('ERROR_VALIDATION', 400); diff --git a/server/src/App/Config/definitions.php b/server/src/App/Config/definitions.php new file mode 100644 index 000000000..ee4cc5535 --- /dev/null +++ b/server/src/App/Config/definitions.php @@ -0,0 +1,30 @@ + function (ContainerInterface $container) { + $settings = $container->get('settings')['logger'] ?? []; + return new Services\Logger($settings); + }, + + 'auth' => function () { + return new Services\Auth([ + new Services\Auth\JWT + ]); + }, + + // + // - Aliases + // + + 'cache' => get(\Slim\HttpCache\CacheProvider::class), + 'i18n' => get(Services\I18n::class), + 'view' => get(Services\View::class), + + Services\Auth::class => get('auth'), + Services\Logger::class => get('logger'), +]; diff --git a/server/src/App/Config/functions.php b/server/src/App/Config/functions.php index 7c7790e5c..a33e28771 100644 --- a/server/src/App/Config/functions.php +++ b/server/src/App/Config/functions.php @@ -1,14 +1,14 @@ toSql() . "\n", FILE_APPEND); @@ -80,7 +84,8 @@ function logSql($builder): void /** * Get elapsed time since php script first launched, or a custom start microtime * - * @param start float: A custom start microtime + * @param float $start A custom start microtime. + * * @return string The elapsed time, in seconds. */ function getExecutionTime(?float $start = null): string @@ -94,8 +99,9 @@ function getExecutionTime(?float $start = null): string /** * Transform any snake_case string into camelCase string * - * @param str string: The string to transform - * @param capitalizeFirstLetter bool: Wether to capitalize the first letter or not + * @param string $str The string to transform. + * @param bool $capitalizeFirstLetter Wether to capitalize the first letter or not. + * * @return string */ function snakeToCamelCase(string $str, bool $capitalizeFirstLetter = false): string @@ -109,10 +115,27 @@ function snakeToCamelCase(string $str, bool $capitalizeFirstLetter = false): str return $string; } +/** + * Transforme une chaîne de caractère en snake_case. + * + * @return string + */ +function snakeCase(string $value): string +{ + if (ctype_lower($value)) { + return $value; + } + + $value = preg_replace('/\s+/u', '', ucwords($value)); + $value = preg_replace('/(.)(?=[A-Z])/u', '$1_', $value); + return mb_strtolower($value, 'UTF-8'); +} + /** * Transform any spaces (normal & unbreakable) in a string into underscores * - * @param str string: The string to transform + * @param string $str The string to transform. + * * @return string */ function slugify(string $str): string @@ -123,7 +146,8 @@ function slugify(string $str): string /** * Set all empty fields of an array to null * - * @param data array: The array to clean + * @param array $data The array to clean. + * * @return array */ function cleanEmptyFields(array $data): array @@ -145,20 +169,27 @@ function normalizePhone(string $phone): string return preg_replace('/ /', '', $phone); } +function alphanumericalize(string $string): string +{ + return (new Cocur\Slugify\Slugify()) + ->slugify($string, ['separator' => '-']); +} + /** * Set all empty fields of an array to null * - * @param e Illuminate\Database\QueryException: The PDO Exception thrown + * @param QueryException $e The PDO Exception thrown + * * @return bool */ -function isDuplicateException(Illuminate\Database\QueryException $e): bool +function isDuplicateException(QueryException $e): bool { if ($e->getCode() != '23000') { return false; } $details = $e->getMessage(); - $subCode = explode(" ", explode(": ", $details)[2]); + $subCode = explode(' ', explode(': ', $details)[2]); return $subCode[0] == '1062'; } @@ -213,11 +244,12 @@ function splitPeriods(array $slots): array /** * Déplace un fichier uploaded dans le dossier des data en sécurisant son nom * - * @param string $directory dossier dans lequel placer le fichier (sera créé si inexistant) - * @param UploadedFile $uploadedFile le fichier à placer - * @return string le nom du fichier résultant + * @param string $directory Dossier dans lequel placer le fichier (sera créé si inexistant) + * @param UploadedFileInterface $uploadedFile Le fichier à placer + * + * @return string Le nom du fichier résultant. */ -function moveUploadedFile($directory, Slim\Http\UploadedFile $uploadedFile) +function moveUploadedFile($directory, UploadedFileInterface $uploadedFile) { $name = $uploadedFile->getClientFilename(); @@ -232,26 +264,3 @@ function moveUploadedFile($directory, Slim\Http\UploadedFile $uploadedFile) return $nameSecure; } - -function alphanumericalize(string $string): string -{ - return (new Cocur\Slugify\Slugify()) - ->slugify($string, ['separator' => '-']); -} - -function buildResponse(int $status = 200): Response -{ - $headers = ['Content-Type' => 'text/html; charset=UTF-8']; - - $isCORSEnabled = Config::getSettings('enableCORS'); - if (!isTestMode() && $isCORSEnabled) { - $headers = array_merge($headers, [ - 'Access-Control-Allow-Origin' => '*', - 'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, Accept, Origin, Authorization', - 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS' - ]); - } - - return (new Response($status, new Headers($headers))) - ->withProtocolVersion('1.1'); -} diff --git a/server/src/App/Config/routes.php b/server/src/App/Config/routes.php new file mode 100644 index 000000000..6213d2095 --- /dev/null +++ b/server/src/App/Config/routes.php @@ -0,0 +1,88 @@ + [ + '/session[/]' => 'AuthController:getSelf', + '/attributes[/]' => 'AttributeController:getAll', + '/users[/]' => 'UserController:getAll', + '/users/{id:[0-9]+}[/]' => 'UserController:getOne', + '/users/{id:[0-9]+}/settings[/]' => 'UserController:getSettings', + '/tags[/]' => 'TagController:getAll', + '/tags/{id:[0-9]+}/persons[/]' => 'TagController:getPersons', + '/tags/{id:[0-9]+}/materials[/]' => 'TagController:getMaterials', + '/categories[/]' => 'CategoryController:getAll', + '/categories/{id:[0-9]+}[/]' => 'CategoryController:getOne', + '/persons[/]' => 'PersonController:getAll', + '/persons/{id:[0-9]+}[/]' => 'PersonController:getOne', + '/persons/{id:[0-9]+}/tags[/]' => 'PersonController:getTags', + '/countries[/]' => 'CountryController:getAll', + '/countries/{id:[0-9]+}[/]' => 'CountryController:getOne', + '/companies[/]' => 'CompanyController:getAll', + '/companies/{id:[0-9]+}[/]' => 'CompanyController:getOne', + '/companies/{id:[0-9]+}/persons[/]' => 'CompanyController:getPersons', + '/parks[/]' => 'ParkController:getAll', + '/parks/{id:[0-9]+}[/]' => 'ParkController:getOne', + '/materials[/]' => 'MaterialController:getAll', + '/materials/{id:[0-9]+}[/]' => 'MaterialController:getOne', + '/materials/{id:[0-9]+}/tags[/]' => 'MaterialController:getTags', + '/materials/{id:[0-9]+}/documents[/]' => 'MaterialController:getAllDocuments', + '/materials/while-event/{eventId:[0-9]+}[/]' => 'MaterialController:getAllWhileEvent', + '/events[/]' => 'EventController:getAll', + '/events/{id:[0-9]+}[/]' => 'EventController:getOne', + '/events/{id:[0-9]+}/missing-materials[/]' => 'EventController:getMissingMaterials', + '/bills/{id:[0-9]+}[/]' => 'BillController:getOne', + '/estimates/{id:[0-9]+}[/]' => 'EstimateController:getOne', + ], + 'post' => [ + '/session[/]' => 'AuthController:loginWithForm', + '/users/signup[/]' => 'UserController:create', + '/categories[/]' => 'CategoryController:create', + '/subcategories[/]' => 'SubCategoryController:create', + '/tags[/]' => 'TagController:create', + '/persons[/]' => 'PersonController:create', + '/companies[/]' => 'CompanyController:create', + '/parks[/]' => 'ParkController:create', + '/materials[/]' => 'MaterialController:create', + '/materials/{id:[0-9]+}/documents[/]' => 'MaterialController:handleUploadDocuments', + '/attributes[/]' => 'AttributeController:create', + '/events[/]' => 'EventController:create', + '/events/{eventId:[0-9]+}/bill[/]' => 'BillController:create', + '/events/{eventId:[0-9]+}/estimate[/]' => 'EstimateController:create', + ], + 'put' => [ + '/users/{id:[0-9]+}[/]' => 'UserController:update', + '/users/restore/{id:[0-9]+}[/]' => 'UserController:restore', + '/users/{id:[0-9]+}/settings[/]' => 'UserController:updateSettings', + '/categories/{id:[0-9]+}[/]' => 'CategoryController:update', + '/categories/restore/{id:[0-9]+}[/]' => 'CategoryController:restore', + '/subcategories/{id:[0-9]+}[/]' => 'SubCategoryController:update', + '/subcategories/restore/{id:[0-9]+}[/]' => 'SubCategoryController:restore', + '/tags/{id:[0-9]+}[/]' => 'TagController:update', + '/tags/restore/{id:[0-9]+}[/]' => 'TagController:restore', + '/persons/{id:[0-9]+}[/]' => 'PersonController:update', + '/persons/restore/{id:[0-9]+}[/]' => 'PersonController:restore', + '/companies/{id:[0-9]+}[/]' => 'CompanyController:update', + '/companies/restore/{id:[0-9]+}[/]' => 'CompanyController:restore', + '/parks/{id:[0-9]+}[/]' => 'ParkController:update', + '/parks/restore/{id:[0-9]+}[/]' => 'ParkController:restore', + '/materials/{id:[0-9]+}[/]' => 'MaterialController:update', + '/materials/restore/{id:[0-9]+}[/]' => 'MaterialController:restore', + '/attributes/{id:[0-9]+}[/]' => 'AttributeController:update', + '/events/{id:[0-9]+}[/]' => 'EventController:update', + '/events/restore/{id:[0-9]+}[/]' => 'EventController:restore', + ], + 'delete' => [ + '/users/{id:[0-9]+}[/]' => 'UserController:delete', + '/categories/{id:[0-9]+}[/]' => 'CategoryController:delete', + '/subcategories/{id:[0-9]+}[/]' => 'SubCategoryController:delete', + '/tags/{id:[0-9]+}[/]' => 'TagController:delete', + '/persons/{id:[0-9]+}[/]' => 'PersonController:delete', + '/companies/{id:[0-9]+}[/]' => 'CompanyController:delete', + '/parks/{id:[0-9]+}[/]' => 'ParkController:delete', + '/materials/{id:[0-9]+}[/]' => 'MaterialController:delete', + '/attributes/{id:[0-9]+}[/]' => 'AttributeController:delete', + '/events/{id:[0-9]+}[/]' => 'EventController:delete', + '/bills/{id:[0-9]+}[/]' => 'BillController:delete', + '/documents/{id:[0-9]+}[/]' => 'DocumentController:delete', + '/estimates/{id:[0-9]+}[/]' => 'EstimateController:delete', + ], +]; diff --git a/server/src/App/Controllers/AttributeController.php b/server/src/App/Controllers/AttributeController.php index 0592f84e6..daa98fe83 100644 --- a/server/src/App/Controllers/AttributeController.php +++ b/server/src/App/Controllers/AttributeController.php @@ -3,16 +3,20 @@ namespace Robert2\API\Controllers; -use Slim\Http\Request; +use Robert2\API\Controllers\Traits\WithCrud; +use Robert2\API\Models\Attribute; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; class AttributeController extends BaseController { + use WithCrud; + public function getAll(Request $request, Response $response): Response { - $categoryId = $request->getQueryParam('category', null); + $attributes = Attribute::orderBy('name', 'asc'); - $attributes = $this->model->orderBy('name', 'asc'); + $categoryId = $request->getQueryParam('category', null); if (!empty($categoryId)) { $attributes ->whereDoesntHave('categories') @@ -22,13 +26,12 @@ public function getAll(Request $request, Response $response): Response } $results = $attributes->with('categories')->get()->toArray(); - return $response->withJson($results); } public function create(Request $request, Response $response): Response { - $postData = $request->getParsedBody(); + $postData = (array)$request->getParsedBody(); if (empty($postData)) { throw new \InvalidArgumentException( "Missing request data to process validation", @@ -36,8 +39,7 @@ public function create(Request $request, Response $response): Response ); } - $attribute = $this->model->edit(null, $postData); - + $attribute = Attribute::new($postData); if (isset($postData['categories'])) { $attribute->Categories()->sync($postData['categories']); } diff --git a/server/src/App/Controllers/AuthController.php b/server/src/App/Controllers/AuthController.php index d810bb857..b377c543c 100644 --- a/server/src/App/Controllers/AuthController.php +++ b/server/src/App/Controllers/AuthController.php @@ -3,24 +3,24 @@ namespace Robert2\API\Controllers; -use Slim\Http\Request; -use Slim\Http\Response; -use Robert2\API\Validation\Validator as V; +use DI\Container; use Robert2\API\Errors\ValidationException; -use Robert2\API\Services\Auth; use Robert2\API\Models\User; +use Robert2\API\Services\Auth; +use Robert2\API\Validation\Validator as V; +use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; -class AuthController +class AuthController extends BaseController { - /** @var User */ - protected $user; + /** @var Auth */ + protected $auth; - public function __construct($container) + public function __construct(Container $container, Auth $auth) { - $this->container = $container; - $this->config = $container->get('settings'); + parent::__construct($container); - $this->user = new User; + $this->auth = $auth; } public function getSelf(Request $request, Response $response): Response @@ -30,11 +30,10 @@ public function getSelf(Request $request, Response $response): Response public function loginWithForm(Request $request, Response $response): Response { - $data = $request->getParsedBody(); - + $data = (array)$request->getParsedBody(); $this->_validateAuthRequest($data); - $user = $this->user->getLogin($data['identifier'], $data['password']); + $user = User::fromLogin($data['identifier'], $data['password']); $responseData['user'] = $user->toArray(); $responseData['token'] = Auth\JWT::generateToken($user); @@ -44,8 +43,7 @@ public function loginWithForm(Request $request, Response $response): Response public function logout(Request $request, Response $response) { - $auth = $this->container->get('auth'); - if (!$auth->logout()) { + if (!$this->auth->logout()) { // TODO: Ajouter un message d'erreur passé au client (lorsqu'on aura un moyen de le faire) // l'informant du fait qu'il n'a pas été complétement // déconnécté. @@ -62,7 +60,6 @@ public function logout(Request $request, Response $response) protected function _validateAuthRequest(array $data): void { - $ex = new ValidationException; $valid = true; $errors = ['identifier' => [], 'password' => []]; @@ -82,8 +79,8 @@ protected function _validateAuthRequest(array $data): void } if (!$valid) { - $ex->setValidationErrors($errors); - throw $ex; + throw (new ValidationException) + ->setValidationErrors($errors); } } } diff --git a/server/src/App/Controllers/BaseController.php b/server/src/App/Controllers/BaseController.php index a2139c6ff..1508861b0 100644 --- a/server/src/App/Controllers/BaseController.php +++ b/server/src/App/Controllers/BaseController.php @@ -3,141 +3,46 @@ namespace Robert2\API\Controllers; -use Slim\Http\Request; -use Slim\Http\Response; -use Robert2\API\Errors; -use Illuminate\Pagination\LengthAwarePaginator as Paginator; +use DI\Container; +use Illuminate\Database\Eloquent\Builder; +use Slim\Http\ServerRequest as Request; abstract class BaseController { + /** @var Container */ protected $container; - protected $model; - protected $itemsCount; - public function __construct($container) + public function __construct(Container $container) { - if ($this->model === null) { - $modelName = preg_replace('/Controller$/', '', class_basename($this)); - $modelFullName = sprintf('\\Robert2\\API\\Models\\%s', $modelName); - if (class_exists($modelFullName, true)) { - $this->model = new $modelFullName(); - } - } - - $this->container = $container; - $this->itemsCount = $container->settings['maxItemsPerPage']; + $this->container = $container; } - // —————————————————————————————————————————————————————— - // — - // — Getters - // — - // —————————————————————————————————————————————————————— - - public function getAll(Request $request, Response $response): Response + /** + * @param Request $request + * @param Builder $query + * @param int|null $limit + * + * @return array + */ + protected function paginate(Request $request, $query, ?int $limit = null): array { - $searchTerm = $request->getQueryParam('search', null); - $searchField = $request->getQueryParam('searchBy', null); - $orderBy = $request->getQueryParam('orderBy', null); - $limit = $request->getQueryParam('limit', null); - $ascending = (bool)$request->getQueryParam('ascending', true); - $withDeleted = (bool)$request->getQueryParam('deleted', false); - - $results = $this->model - ->setOrderBy($orderBy, $ascending) - ->setSearch($searchTerm, $searchField) - ->getAll($withDeleted) - ->paginate($limit ? (int)$limit : $this->itemsCount); + $maxItemsPerPage = $this->container->get('settings')['maxItemsPerPage'] ?? 100; + $limit = min($limit ? (int)$limit : $maxItemsPerPage, $maxItemsPerPage); + $paginated = $query->paginate($limit); $basePath = $request->getUri()->getPath(); $params = $request->getQueryParams(); - $results = $results->withPath($basePath)->appends($params); - $results = $this->_formatPagination($results); - - return $response->withJson($results); - } - - public function getOne(Request $request, Response $response): Response - { - $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); - if (!$model) { - throw new Errors\NotFoundException; - } - - return $response->withJson($model->toArray()); - } - - // —————————————————————————————————————————————————————— - // — - // — Setters - // — - // —————————————————————————————————————————————————————— - - public function create(Request $request, Response $response): Response - { - $postData = $request->getParsedBody(); - if (empty($postData)) { - throw new \InvalidArgumentException( - "Missing request data to process validation", - ERROR_VALIDATION - ); - } - - $result = $this->model->edit(null, $postData); - - return $response->withJson($result->toArray(), SUCCESS_CREATED); - } - public function update(Request $request, Response $response): Response - { - $postData = $request->getParsedBody(); - if (empty($postData)) { - throw new \InvalidArgumentException( - "Missing request data to process validation", - ERROR_VALIDATION - ); - } - - $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); - if (!$model) { - throw new Errors\NotFoundException; - } - - $result = $this->model->edit($id, $postData); - - return $response->withJson($result->toArray(), SUCCESS_OK); - } - - public function delete(Request $request, Response $response): Response - { - $id = (int)$request->getAttribute('id'); - $model = $this->model->remove($id); + $result = $paginated + ->withPath($basePath) + ->appends($params) + ->toArray(); - $data = $model ? $model->toArray() : ['destroyed' => true]; - return $response->withJson($data, SUCCESS_OK); - } - - public function restore(Request $request, Response $response): Response - { - $id = (int)$request->getAttribute('id'); - $data = $this->model->unremove($id); - - return $response->withJson($data, SUCCESS_OK); - } - - // —————————————————————————————————————————————————————— - // — - // — Utility methods - // — - // —————————————————————————————————————————————————————— - - public function _formatPagination(Paginator $results): array - { - $result = $results->toArray(); $data = $result['data']; - unset($result['data']); + unset( + $result['data'], + $result['links'] + ); return [ 'pagination' => $result, diff --git a/server/src/App/Controllers/BillController.php b/server/src/App/Controllers/BillController.php index 18fda825f..c97eebd5b 100644 --- a/server/src/App/Controllers/BillController.php +++ b/server/src/App/Controllers/BillController.php @@ -3,63 +3,42 @@ namespace Robert2\API\Controllers; +use Robert2\API\Controllers\BaseController; use Robert2\API\Controllers\Traits\WithPdf; -use Robert2\API\Errors; -use Robert2\API\Services\Auth; use Robert2\API\Models\Bill; -use Slim\Http\Request; +use Robert2\API\Services\Auth; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; -class BillController +class BillController extends BaseController { use WithPdf; - protected $container; - - /** @var Bill */ - protected $model; - - public function __construct($container) - { - $this->container = $container; - $this->model = new Bill(); - } - - // —————————————————————————————————————————————————————— - // — - // — Getters - // — - // —————————————————————————————————————————————————————— - public function getOne(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); + + $model = Bill::find($id); if (!$model) { - throw new Errors\NotFoundException; + throw new HttpNotFoundException($request); } return $response->withJson($model->toArray()); } - // —————————————————————————————————————————————————————— - // — - // — Setters - // — - // —————————————————————————————————————————————————————— - public function create(Request $request, Response $response): Response { $eventId = (int)$request->getAttribute('eventId'); $discountRate = (float)$request->getParsedBodyParam('discountRate'); - $result = $this->model->createFromEvent($eventId, Auth::user()->id, $discountRate); + $result = (new Bill())->createFromEvent($eventId, Auth::user()->id, $discountRate); return $response->withJson($result->toArray(), SUCCESS_CREATED); } public function delete(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->remove($id); + $model = Bill::staticRemove($id); $data = $model ? $model->toArray() : ['destroyed' => true]; return $response->withJson($data, SUCCESS_OK); diff --git a/server/src/App/Controllers/CategoryController.php b/server/src/App/Controllers/CategoryController.php index eef0b69e7..015e93f1e 100644 --- a/server/src/App/Controllers/CategoryController.php +++ b/server/src/App/Controllers/CategoryController.php @@ -3,6 +3,9 @@ namespace Robert2\API\Controllers; +use Robert2\API\Controllers\Traits\WithCrud; + class CategoryController extends BaseController { + use WithCrud; } diff --git a/server/src/App/Controllers/CompanyController.php b/server/src/App/Controllers/CompanyController.php index 47bd662c9..030407772 100644 --- a/server/src/App/Controllers/CompanyController.php +++ b/server/src/App/Controllers/CompanyController.php @@ -3,37 +3,28 @@ namespace Robert2\API\Controllers; -use Slim\Http\Request; -use Slim\Http\Response; - -use Robert2\API\Errors; use Robert2\API\Controllers\Traits\Taggable; +use Robert2\API\Controllers\Traits\WithCrud; +use Robert2\API\Models\Company; +use Slim\Exception\HttpNotFoundException; +use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; class CompanyController extends BaseController { - use Taggable; - - // —————————————————————————————————————————————————————— - // — - // — Model dedicated methods - // — - // —————————————————————————————————————————————————————— + use WithCrud, Taggable { + Taggable::getAll insteadof WithCrud; + } - public function getPersons(Request $request, Response $response) + public function getPersons(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - if (!$this->model->exists($id)) { - throw new Errors\NotFoundException; + $company = Company::find($id); + if (!$company) { + throw new HttpNotFoundException($request); } - $Company = $this->model->find($id); - $persons = $Company->Persons()->paginate($this->itemsCount); - - $basePath = $request->getUri()->getPath(); - $persons->withPath($basePath); - - $results = $this->_formatPagination($persons); - + $results = $this->paginate($request, $company->Persons()); return $response->withJson($results); } } diff --git a/server/src/App/Controllers/CountryController.php b/server/src/App/Controllers/CountryController.php index d7ba559c0..9c01f13b9 100644 --- a/server/src/App/Controllers/CountryController.php +++ b/server/src/App/Controllers/CountryController.php @@ -3,14 +3,18 @@ namespace Robert2\API\Controllers; -use Slim\Http\Request; +use Robert2\API\Controllers\Traits\WithCrud; +use Robert2\API\Models\Country; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; class CountryController extends BaseController { + use WithCrud; + public function getAll(Request $request, Response $response): Response { - $data = $this->model + $data = (new Country()) ->setOrderBy('id', true) ->getAll() ->get() diff --git a/server/src/App/Controllers/DocumentController.php b/server/src/App/Controllers/DocumentController.php index 0b2d4acd7..30c6878a9 100644 --- a/server/src/App/Controllers/DocumentController.php +++ b/server/src/App/Controllers/DocumentController.php @@ -3,59 +3,39 @@ namespace Robert2\API\Controllers; -use Robert2\API\Models\Document; -use Robert2\API\Errors; use Robert2\API\Controllers\Traits\FileResponse; -use Slim\Http\Request; +use Robert2\API\Models\Document; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; -class DocumentController +class DocumentController extends BaseController { use FileResponse; - protected $container; - protected $model; - - public function __construct($container) - { - $this->container = $container; - $this->model = new Document(); - } - - // —————————————————————————————————————————————————————— - // — - // — Getters - // — - // —————————————————————————————————————————————————————— - public function getOne(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); + $model = Document::find($id); if (!$model) { - throw new Errors\NotFoundException; + throw new HttpNotFoundException($request); } $filePath = Document::getFilePath((int)$model->material_id, $model->name); $fileContent = file_get_contents($filePath); if (!$fileContent) { - throw new Errors\NotFoundException("The file of the document cannot be found."); + throw new HttpNotFoundException($request, "The file of the document cannot be found."); } return $this->_responseWithFile($response, $model->name, $fileContent); } - // —————————————————————————————————————————————————————— - // — - // — Setters - // — - // —————————————————————————————————————————————————————— - public function delete(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $this->model->remove($id); + Document::staticRemove($id); + return $response->withJson(['destroyed' => true], SUCCESS_OK); } } diff --git a/server/src/App/Controllers/EntryController.php b/server/src/App/Controllers/EntryController.php new file mode 100644 index 000000000..f903763d4 --- /dev/null +++ b/server/src/App/Controllers/EntryController.php @@ -0,0 +1,79 @@ +view = $view; + $this->settings = $container->get('settings'); + } + + public function index(Request $request, Response $response) + { + if (!Config::customConfigExists()) { + return $response->withRedirect('/install', 302); // 302 Redirect + } + + $serverConfig = $this->getServerConfig(); + return $this->view->render($response, 'webclient.twig', \compact('serverConfig')); + } + + // ------------------------------------------------------ + // - + // - Internal methods + // - + // ------------------------------------------------------ + + protected function getServerConfig(): string + { + $rawConfig = $this->settings; + $baseUrl = preg_replace('/\/$/', '', $rawConfig['apiUrl']); + + $config = [ + 'baseUrl' => $baseUrl, + 'api' => [ + 'url' => $baseUrl . '/api', + 'headers' => $rawConfig['apiHeaders'], + 'version' => Config::getVersion(), + ], + 'auth' => [ + 'cookie' => $rawConfig['auth']['cookie'], + 'timeout' => $rawConfig['sessionExpireHours'], + ], + 'defaultPaginationLimit' => $rawConfig['maxItemsPerPage'], + 'defaultLang' => $rawConfig['defaultLang'], + 'currency' => $rawConfig['currency'], + 'beneficiaryTagName' => $rawConfig['defaultTags']['beneficiary'], + 'technicianTagName' => $rawConfig['defaultTags']['technician'], + 'billingMode' => $rawConfig['billingMode'], + 'degressiveRate' => sprintf( + 'function (daysCount) { return %s; }', + $rawConfig['degressiveRateFunction'] + ), + ]; + + // TODO: Passer la formule `$rawConfig['degressiveRateFunction']` directement au js + // sans la wrappée dans une fonction et utiliser le filtre twig + // `| json_encode(constant('Robert2\\API\\Config\\Config::JSON_OPTIONS'))` + $jsonConfig = json_encode($config, Config::JSON_OPTIONS); + $jsonConfig = preg_replace('/"degressiveRate": "/', '"degressiveRate": ', $jsonConfig); + return preg_replace('/}"/', '}', $jsonConfig); + } +} diff --git a/server/src/App/Controllers/EstimateController.php b/server/src/App/Controllers/EstimateController.php index 3c3b5a03e..e7c644626 100644 --- a/server/src/App/Controllers/EstimateController.php +++ b/server/src/App/Controllers/EstimateController.php @@ -4,62 +4,41 @@ namespace Robert2\API\Controllers; use Robert2\API\Controllers\Traits\WithPdf; -use Robert2\API\Errors; -use Robert2\API\Services\Auth; use Robert2\API\Models\Estimate; -use Slim\Http\Request; +use Robert2\API\Services\Auth; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; -class EstimateController +class EstimateController extends BaseController { use WithPdf; - protected $container; - - /** @var Estimate */ - protected $model; - - public function __construct($container) - { - $this->container = $container; - $this->model = new Estimate(); - } - - // —————————————————————————————————————————————————————— - // — - // — Getters - // — - // —————————————————————————————————————————————————————— - public function getOne(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); + + $model = Estimate::find($id); if (!$model) { - throw new Errors\NotFoundException; + throw new HttpNotFoundException($request); } return $response->withJson($model->toArray()); } - // —————————————————————————————————————————————————————— - // — - // — Setters - // — - // —————————————————————————————————————————————————————— - public function create(Request $request, Response $response): Response { $eventId = (int)$request->getAttribute('eventId'); $discountRate = (float)$request->getParsedBodyParam('discountRate'); - $result = $this->model->createFromEvent($eventId, Auth::user()->id, $discountRate); + + $result = Estimate::createFromEvent($eventId, Auth::user()->id, $discountRate); return $response->withJson($result->toArray(), SUCCESS_CREATED); } public function delete(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->remove($id); + $model = Estimate::staticRemove($id); $data = $model ? $model->toArray() : ['destroyed' => true]; return $response->withJson($data, SUCCESS_OK); diff --git a/server/src/App/Controllers/EventController.php b/server/src/App/Controllers/EventController.php index 91865821a..826ca1077 100644 --- a/server/src/App/Controllers/EventController.php +++ b/server/src/App/Controllers/EventController.php @@ -3,23 +3,22 @@ namespace Robert2\API\Controllers; -use Robert2\API\Errors; +use Robert2\API\Controllers\Traits\WithCrud; use Robert2\API\Controllers\Traits\WithPdf; use Robert2\API\Models\Park; use Robert2\API\Models\Event; -use Slim\Http\Request; +use Slim\Exception\HttpNotFoundException; +use Slim\Http\ServerRequest as Request; use Slim\Http\Response; class EventController extends BaseController { + use WithCrud; use WithPdf; - /** @var Event */ - protected $model; - // —————————————————————————————————————————————————————— // — - // — Getters + // — Actions // — // —————————————————————————————————————————————————————— @@ -29,7 +28,7 @@ public function getAll(Request $request, Response $response): Response $endDate = $request->getQueryParam('end', null); $deleted = (bool)$request->getQueryParam('deleted', false); - $results = $this->model + $results = (new Event) ->setPeriod($startDate, $endDate) ->getAll($deleted) ->with('Beneficiaries:persons.id,first_name,last_name') @@ -38,9 +37,9 @@ public function getAll(Request $request, Response $response): Response $data = $results->get()->toArray(); $useMultipleParks = Park::count() > 1; foreach ($data as $index => $event) { - $eventMissingMaterials = $this->model->getMissingMaterials($event['id']); + $eventMissingMaterials = Event::getMissingMaterials($event['id']); $data[$index]['has_missing_materials'] = !empty($eventMissingMaterials); - $data[$index]['parks'] = $useMultipleParks ? $this->model->getParks($event['id']) : null; + $data[$index]['parks'] = $useMultipleParks ? Event::getParks($event['id']) : null; } return $response->withJson([ 'data' => $data ]); @@ -49,8 +48,8 @@ public function getAll(Request $request, Response $response): Response public function getOne(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - if (!$this->model->exists($id)) { - throw new Errors\NotFoundException; + if (!Event::staticExists($id)) { + throw new HttpNotFoundException($request); } return $response->withJson($this->_getFormattedEvent($id)); } @@ -58,11 +57,11 @@ public function getOne(Request $request, Response $response): Response public function getMissingMaterials(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - if (!$this->model->exists($id)) { - throw new Errors\NotFoundException; + if (!Event::staticExists($id)) { + throw new HttpNotFoundException($request); } - $eventMissingMaterials = $this->model->getMissingMaterials($id); + $eventMissingMaterials = Event::getMissingMaterials($id); if (empty($eventMissingMaterials)) { return $response->withJson([]); } @@ -70,15 +69,9 @@ public function getMissingMaterials(Request $request, Response $response): Respo return $response->withJson($eventMissingMaterials); } - // ------------------------------------------------------ - // - - // - Setters - // - - // ------------------------------------------------------ - public function create(Request $request, Response $response): Response { - $postData = $request->getParsedBody(); + $postData = (array)$request->getParsedBody(); $id = $this->_saveEvent(null, $postData); return $response->withJson($this->_getFormattedEvent($id), SUCCESS_CREATED); @@ -87,12 +80,11 @@ public function create(Request $request, Response $response): Response public function update(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); - if (!$model) { - throw new Errors\NotFoundException; + if (!Event::staticExists($id)) { + throw new HttpNotFoundException($request); } - $postData = $request->getParsedBody(); + $postData = (array)$request->getParsedBody(); $id = $this->_saveEvent($id, $postData); return $response->withJson($this->_getFormattedEvent($id), SUCCESS_OK); @@ -113,7 +105,7 @@ protected function _saveEvent(?int $id, array $postData): int ); } - $event = $this->model->edit($id, $postData); + $event = Event::staticEdit($id, $postData); if (isset($postData['beneficiaries'])) { $event->Beneficiaries()->sync($postData['beneficiaries']); @@ -142,7 +134,7 @@ protected function _saveEvent(?int $id, array $postData): int protected function _getFormattedEvent(int $id): array { - $model = $this->model + $model = (new Event) ->with('User') ->with('Assignees') ->with('Beneficiaries') diff --git a/server/src/App/Controllers/HomeController.php b/server/src/App/Controllers/HomeController.php deleted file mode 100644 index 7ff639f7b..000000000 --- a/server/src/App/Controllers/HomeController.php +++ /dev/null @@ -1,306 +0,0 @@ -_bindTwig($container); - - $this->router = $container->get('router'); - $this->config = $container->get('settings'); - } - - // —————————————————————————————————————————————————————— - // — - // — Main routes methods - // — - // —————————————————————————————————————————————————————— - - public function webclient(Request $request, Response $response) - { - if (!Config::customConfigExists()) { - return $response->withRedirect('/install', 302); // 302 Redirect - } - return $this->view->render($response, 'webclient.twig'); - } - - public function install(Request $request, Response $response) - { - $installProgress = Install\Install::getInstallProgress(); - $currentStep = $installProgress['step']; - $stepData = []; - $error = false; - $validationErrors = null; - - $i18n = new I18n(); - $lang = $i18n->getCurrentLocale(); - $allCurrencies = Install\Install::getAllCurrencies(); - - if ($request->isGet() && $currentStep === 'welcome') { - return $this->view->render( - $response, - 'install.twig', - $this->_getCheckRequirementsData() + ['lang' => $lang] - ); - } - - if ($request->isPost()) { - $installData = $request->getParsedBody(); - - if ($currentStep === 'settings') { - $installData['currency'] = $allCurrencies[$installData['currency']]; - } - - try { - $installProgress = Install\Install::setInstallProgress($currentStep, $installData); - - if ($currentStep === 'company') { - $this->_validateCompanyData($installData); - } - - if ($currentStep === 'database') { - $settings = Install\Install::getSettingsFromInstallData(); - Config::saveCustomConfig($settings, true); - Config::getPDO(); // - Try to connect to DB - } - - if ($currentStep === 'dbStructure') { - Install\Install::createMissingTables(); - Install\Install::insertInitialDataIntoDB(); - } - - if ($currentStep === 'adminUser') { - $user = new Models\User(); - if (array_key_exists('skipUserCreation', $installData) - && $installData['skipUserCreation'] === 'yes' - ) { - $existingAdmins = $user->getAll()->where('group_id', 'admin')->get()->toArray(); - if (empty($existingAdmins)) { - throw new \InvalidArgumentException( - "At least one user must exists. Please create an admin user." - ); - } - } else { - $installData['user']['group_id'] = 'admin'; - $user->edit(null, $installData['user']); - } - } - - if ($currentStep === 'categories') { - $categories = explode(',', $installData['categories']); - $Category = new Models\Category(); - $Category->bulkAdd(array_unique($categories)); - } - } catch (\Exception $e) { - $installProgress = Install\Install::setInstallProgress($currentStep); - - $stepData = $installData; - $error = $e->getMessage(); - if (method_exists($e, 'getValidationErrors')) { - $error = "validationErrors"; - $validationErrors = $e->getValidationErrors(); - } - - if ($currentStep === 'database') { - Config::deleteCustomConfig(); - } - } - } - - if ($installProgress['step'] === 'coreSettings' && empty($stepData['JWTSecret'])) { - $stepData['JWTSecret'] = md5(uniqid('Robert2', true)); - } - - if ($installProgress['step'] === 'settings') { - $stepData['currencies'] = $allCurrencies; - } - - if ($installProgress['step'] === 'dbStructure') { - try { - $stepData = [ - 'migrationStatus' => Install\Install::getMigrationsStatus(), - 'canProcess' => true - ]; - } catch (\Exception $e) { - $stepData = [ - 'migrationStatus' => [], - 'errorCode' => $e->getCode(), - 'error' => $e->getMessage(), - 'canProcess' => false - ]; - } - } - - if ($installProgress['step'] === 'adminUser') { - $user = new Models\User(); - $stepData['existingAdmins'] = $user->getAll()->where('group_id', 'admin')->get()->toArray(); - } - - return $this->view->render($response, 'install.twig', [ - 'lang' => $lang, - 'step' => $installProgress['step'], - 'stepNumber' => array_search($installProgress['step'], Install\Install::INSTALL_STEPS), - 'error' => $error, - 'validationErrors' => $validationErrors, - 'stepData' => $stepData, - 'config' => $this->config, - ]); - } - - // —————————————————————————————————————————————————————— - // — - // — Private methods - // — - // —————————————————————————————————————————————————————— - - private function _bindTwig($container): void - { - $this->view = new \Slim\Views\Twig(VIEWS_FOLDER, [ - 'cache' => false, - ]); - - // - // - Global variables - // - - $this->view->getEnvironment()->addGlobal('env', Config::getEnv()); - - // - // - Extensions - // - - $this->view->addExtension(new \Slim\Views\TwigExtension( - $container->router, - $container->request->getUri() - )); - - // - // - Functions - // - - $i18n = new I18n(); - $translate = new \Twig\TwigFunction('translate', [$i18n, 'translate']); - $version = new \Twig\TwigFunction('version', $this->getVersion()); - $serverConfig = new \Twig\TwigFunction('serverConfig', $this->getServerConfig()); - $clientAssetFunction = new \Twig\TwigFunction('client_asset', $this->getClientAsset()); - - $this->view->getEnvironment()->addFunction($translate); - $this->view->getEnvironment()->addFunction($version); - $this->view->getEnvironment()->addFunction($serverConfig); - $this->view->getEnvironment()->addFunction($clientAssetFunction); - } - - private function _getCheckRequirementsData(): array - { - $phpVersion = PHP_VERSION; - if (strpos(PHP_VERSION, '+')) { - $phpVersion = substr(PHP_VERSION, 0, strpos(PHP_VERSION, '+')); - } - - $phpversionOK = version_compare(PHP_VERSION, '7.1.0') >= 0; - $loadedExtensions = get_loaded_extensions(); - $neededExstensions = Install\Install::REQUIRED_EXTENSIONS; - $missingExtensions = array_diff($neededExstensions, $loadedExtensions); - - return [ - 'step' => 'welcome', - 'phpVersion' => $phpVersion, - 'phpVersionOK' => $phpversionOK, - 'missingExtensions' => $missingExtensions, - 'requirementsOK' => $phpversionOK && empty($missingExtensions), - ]; - } - - private function _validateCompanyData($companyData): void - { - $errors = []; - foreach (['name', 'street', 'zipCode', 'locality'] as $mandatoryfield) { - if (empty($companyData[$mandatoryfield])) { - $errors[$mandatoryfield] = "Missing value"; - continue; - } - } - - if (empty($errors)) { - return; - } - - $error = new Errors\ValidationException(); - $error->setValidationErrors($errors); - throw $error; - } - - // —————————————————————————————————————————————————————— - // — - // — Custom twig functions methods - // — - // —————————————————————————————————————————————————————— - - protected function getVersion(): callable - { - return function (): string { - return trim(file_get_contents(SRC_FOLDER . DS . 'VERSION')); - }; - } - - public function getClientAsset(): callable - { - return function ($path) { - $host = Config::getSettings(); - - $host = Config::getEnv() === 'development' - ? 'http://localhost:8081' - : ''; - - return vsprintf('%s/webclient/%s?v=%s', [ - rtrim($host, '/'), - ltrim($path, '/'), - $this->getVersion()(), - ]); - }; - } - - protected function getServerConfig(): callable - { - $rawConfig = Config::getSettings(); - $config = [ - 'baseUrl' => $rawConfig['apiUrl'], - 'api' => [ - 'url' => $rawConfig['apiUrl'] . '/api', - 'headers' => $rawConfig['apiHeaders'], - 'version' => $this->getVersion()() - ], - 'auth' => [ - 'cookie' => $rawConfig['auth']['cookie'], - 'timeout' => $rawConfig['sessionExpireHours'], - ], - 'defaultPaginationLimit' => $rawConfig['maxItemsPerPage'], - 'defaultLang' => $rawConfig['defaultLang'], - 'currency' => $rawConfig['currency'], - 'beneficiaryTagName' => $rawConfig['defaultTags']['beneficiary'], - 'technicianTagName' => $rawConfig['defaultTags']['technician'], - 'billingMode' => $rawConfig['billingMode'], - 'degressiveRate' => sprintf( - 'function (daysCount) { return %s; }', - $rawConfig['degressiveRateFunction'] - ), - ]; - - return function () use ($config): string { - $jsonConfig = json_encode($config, Config::JSON_OPTIONS); - $jsonConfig = preg_replace('/"degressiveRate": "/', '"degressiveRate": ', $jsonConfig); - return preg_replace('/}"/', '}', $jsonConfig); - }; - } -} diff --git a/server/src/App/Controllers/MaterialController.php b/server/src/App/Controllers/MaterialController.php index 3a82acc90..4751e3c82 100644 --- a/server/src/App/Controllers/MaterialController.php +++ b/server/src/App/Controllers/MaterialController.php @@ -3,21 +3,21 @@ namespace Robert2\API\Controllers; -use Robert2\API\Errors; use Robert2\API\Config\Config; -use Robert2\API\Models\Event; +use Robert2\API\Controllers\Traits\Taggable; +use Robert2\API\Controllers\Traits\WithCrud; use Robert2\API\Models\Document; +use Robert2\API\Models\Event; use Robert2\API\Models\Material; -use Robert2\API\Controllers\Traits\Taggable; -use Slim\Http\Request; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; class MaterialController extends BaseController { - use Taggable; - - /** @var Material */ - protected $model; + use WithCrud, Taggable { + Taggable::getAll insteadof WithCrud; + } // —————————————————————————————————————————————————————— // — @@ -50,7 +50,7 @@ public function getAll(Request $request, Response $response): Response $orderBy = $request->getQueryParam('orderBy', null); $ascending = (bool)$request->getQueryParam('ascending', true); - $model = $this->model + $model = (new Material) ->setOrderBy($orderBy, $ascending) ->setSearch($searchTerm, $searchField); @@ -60,15 +60,9 @@ public function getAll(Request $request, Response $response): Response $model = $model->getAllFilteredOrTagged($options, $tags, $withDeleted); } - $results = $model->paginate($this->itemsCount); - - $basePath = $request->getUri()->getPath(); - $params = $request->getQueryParams(); - $results = $results->withPath($basePath)->appends($params); - $results = $this->_formatPagination($results); - + $results = $this->paginate($request, $model); if ($dateForQuantities) { - $results['data'] = $this->model->recalcQuantitiesForPeriod( + $results['data'] = Material::recalcQuantitiesForPeriod( $results['data'], $dateForQuantities, $dateForQuantities, @@ -83,22 +77,19 @@ public function getAllWhileEvent(Request $request, Response $response): Response { $eventId = (int)$request->getAttribute('eventId'); - $Event = new Event(); - $currentEvent = $Event->find($eventId); + $currentEvent = Event::find($eventId); if (!$currentEvent) { - throw new Errors\NotFoundException( - sprintf("Event #%d was not found.", $eventId) - ); + throw new HttpNotFoundException($request); } - $results = $this->model + $results = (new Material) ->setOrderBy('reference', true) ->getAll() ->get() ->toArray(); if ($results && count($results) > 0) { - $results = $this->model->recalcQuantitiesForPeriod( + $results = Material::recalcQuantitiesForPeriod( $results, $currentEvent->start_date, $currentEvent->end_date, @@ -117,8 +108,7 @@ public function getAllWhileEvent(Request $request, Response $response): Response public function create(Request $request, Response $response): Response { - $postData = $request->getParsedBody(); - + $postData = (array)$request->getParsedBody(); $result = $this->_saveMaterial(null, $postData); return $response->withJson($result, SUCCESS_CREATED); } @@ -126,13 +116,11 @@ public function create(Request $request, Response $response): Response public function update(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); - if (!$model) { - throw new Errors\NotFoundException; + if (!Material::staticExists($id)) { + throw new HttpNotFoundException($request); } - $postData = $request->getParsedBody(); - + $postData = (array)$request->getParsedBody(); $result = $this->_saveMaterial($id, $postData); return $response->withJson($result, SUCCESS_OK); } @@ -145,7 +133,7 @@ public function update(Request $request, Response $response): Response protected function _saveMaterial(?int $id, array $postData): array { - if (!is_array($postData) || empty($postData)) { + if (empty($postData)) { throw new \InvalidArgumentException( "Missing request data to process validation", ERROR_VALIDATION @@ -171,7 +159,7 @@ protected function _saveMaterial(?int $id, array $postData): array } } - $result = $this->model->edit($id, $postData); + $result = Material::staticEdit($id, $postData); if (isset($postData['attributes'])) { $attributes = []; @@ -187,16 +175,15 @@ protected function _saveMaterial(?int $id, array $postData): array $result->Attributes()->sync($attributes); } - $model = $this->model->find($result->id); - return $model->toArray(); + return Material::find($result->id)->toArray(); } public function getAllDocuments(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); + $model = Material::find($id); if (!$model) { - throw new Errors\NotFoundException; + throw new HttpNotFoundException($request); } return $response->withJson($model->documents, SUCCESS_OK); @@ -205,9 +192,8 @@ public function getAllDocuments(Request $request, Response $response): Response public function handleUploadDocuments(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); - if (!$model) { - throw new Errors\NotFoundException; + if (!Material::staticExists($id)) { + throw new HttpNotFoundException($request); } $uploadedFiles = $request->getUploadedFiles(); diff --git a/server/src/App/Controllers/ParkController.php b/server/src/App/Controllers/ParkController.php index 8fecc9c45..bcdc2b139 100644 --- a/server/src/App/Controllers/ParkController.php +++ b/server/src/App/Controllers/ParkController.php @@ -3,10 +3,9 @@ namespace Robert2\API\Controllers; -use Robert2\API\Models\Park; +use Robert2\API\Controllers\Traits\WithCrud; class ParkController extends BaseController { - /** @var Park */ - protected $model; + use WithCrud; } diff --git a/server/src/App/Controllers/PersonController.php b/server/src/App/Controllers/PersonController.php index 8937e7513..b1ad8fe7d 100644 --- a/server/src/App/Controllers/PersonController.php +++ b/server/src/App/Controllers/PersonController.php @@ -4,8 +4,11 @@ namespace Robert2\API\Controllers; use Robert2\API\Controllers\Traits\Taggable; +use Robert2\API\Controllers\Traits\WithCrud; class PersonController extends BaseController { - use Taggable; + use WithCrud, Taggable { + Taggable::getAll insteadof WithCrud; + } } diff --git a/server/src/App/Controllers/SetupController.php b/server/src/App/Controllers/SetupController.php new file mode 100644 index 000000000..507a3088b --- /dev/null +++ b/server/src/App/Controllers/SetupController.php @@ -0,0 +1,203 @@ +view = $view; + $this->i18n = $i18n; + $this->settings = $container->get('settings'); + } + + public function index(Request $request, Response $response) + { + $installProgress = Install::getInstallProgress(); + $currentStep = $installProgress['step']; + $stepData = []; + $error = false; + $validationErrors = null; + + $lang = $this->i18n->getCurrentLocale(); + $allCurrencies = Install::getAllCurrencies(); + + if ($request->isGet() && $currentStep === 'welcome') { + return $this->view->render( + $response, + 'install.twig', + $this->_getCheckRequirementsData() + ['lang' => $lang] + ); + } + + if ($request->isPost()) { + $installData = $request->getParsedBody(); + + if ($currentStep === 'settings') { + $installData['currency'] = $allCurrencies[$installData['currency']]; + } + + try { + $installProgress = Install::setInstallProgress($currentStep, $installData); + + if ($currentStep === 'company') { + $this->_validateCompanyData($installData); + } + + if ($currentStep === 'database') { + $settings = Install::getSettingsFromInstallData(); + Config::saveCustomConfig($settings, true); + Config::getPDO(); // - Try to connect to DB + } + + if ($currentStep === 'dbStructure') { + Install::createMissingTables(); + Install::insertInitialDataIntoDB(); + } + + if ($currentStep === 'adminUser') { + $user = new User(); + if (array_key_exists('skipUserCreation', $installData) + && $installData['skipUserCreation'] === 'yes' + ) { + $existingAdmins = $user->getAll()->where('group_id', 'admin')->get()->toArray(); + if (empty($existingAdmins)) { + throw new \InvalidArgumentException( + "At least one user must exists. Please create an admin user." + ); + } + } else { + $installData['user']['group_id'] = 'admin'; + $user->edit(null, $installData['user']); + } + } + + if ($currentStep === 'categories') { + $categories = explode(',', $installData['categories']); + $Category = new Category(); + $Category->bulkAdd(array_unique($categories)); + } + } catch (\Exception $e) { + $installProgress = Install::setInstallProgress($currentStep); + + $stepData = $installData; + $error = $e->getMessage(); + if ($e instanceof ValidationException) { + $error = "validationErrors"; + $validationErrors = $e->getValidationErrors(); + } + + if ($currentStep === 'database') { + Config::deleteCustomConfig(); + } + } + } + + if ($installProgress['step'] === 'coreSettings' && empty($stepData['JWTSecret'])) { + $stepData['JWTSecret'] = md5(uniqid('Robert2', true)); + } + + if ($installProgress['step'] === 'settings') { + $stepData['currencies'] = $allCurrencies; + } + + if ($installProgress['step'] === 'dbStructure') { + try { + $stepData = [ + 'migrationStatus' => Install::getMigrationsStatus(), + 'canProcess' => true + ]; + } catch (\Exception $e) { + $stepData = [ + 'migrationStatus' => [], + 'errorCode' => $e->getCode(), + 'error' => $e->getMessage(), + 'canProcess' => false + ]; + } + } + + if ($installProgress['step'] === 'adminUser') { + $user = new User(); + $stepData['existingAdmins'] = $user->getAll()->where('group_id', 'admin')->get()->toArray(); + } + + return $this->view->render($response, 'install.twig', [ + 'lang' => $lang, + 'step' => $installProgress['step'], + 'stepNumber' => array_search($installProgress['step'], Install::INSTALL_STEPS), + 'error' => $error, + 'validationErrors' => $validationErrors, + 'stepData' => $stepData, + 'config' => $this->settings, + ]); + } + + // ------------------------------------------------------ + // - + // - Internal methods + // - + // ------------------------------------------------------ + + private function _getCheckRequirementsData(): array + { + $phpVersion = PHP_VERSION; + if (strpos(PHP_VERSION, '+')) { + $phpVersion = substr(PHP_VERSION, 0, strpos(PHP_VERSION, '+')); + } + + $phpversionOK = version_compare(PHP_VERSION, '7.3.0') >= 0; + $loadedExtensions = get_loaded_extensions(); + $neededExstensions = Install::REQUIRED_EXTENSIONS; + $missingExtensions = array_diff($neededExstensions, $loadedExtensions); + + return [ + 'step' => 'welcome', + 'phpVersion' => $phpVersion, + 'phpVersionOK' => $phpversionOK, + 'missingExtensions' => $missingExtensions, + 'requirementsOK' => $phpversionOK && empty($missingExtensions), + ]; + } + + private function _validateCompanyData($companyData): void + { + $errors = []; + foreach (['name', 'street', 'zipCode', 'locality'] as $mandatoryfield) { + if (empty($companyData[$mandatoryfield])) { + $errors[$mandatoryfield] = "Missing value"; + continue; + } + } + + if (empty($errors)) { + return; + } + + throw (new ValidationException) + ->setValidationErrors($errors); + } +} diff --git a/server/src/App/Controllers/SubCategoryController.php b/server/src/App/Controllers/SubCategoryController.php index f06b64820..523a6a33f 100644 --- a/server/src/App/Controllers/SubCategoryController.php +++ b/server/src/App/Controllers/SubCategoryController.php @@ -3,6 +3,9 @@ namespace Robert2\API\Controllers; +use Robert2\API\Controllers\Traits\WithCrud; + class SubCategoryController extends BaseController { + use WithCrud; } diff --git a/server/src/App/Controllers/TagController.php b/server/src/App/Controllers/TagController.php index 616e0643d..af0f26473 100644 --- a/server/src/App/Controllers/TagController.php +++ b/server/src/App/Controllers/TagController.php @@ -3,44 +3,37 @@ namespace Robert2\API\Controllers; -use Robert2\API\Errors; -use Slim\Http\Request; +use Robert2\API\Controllers\Traits\WithCrud; +use Robert2\API\Models\Tag; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; class TagController extends BaseController { + use WithCrud; + public function getPersons(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - if (!$this->model->exists($id)) { - throw new Errors\NotFoundException; + $tag = Tag::find($id); + if (!$tag) { + throw new HttpNotFoundException($request); } - $Tag = $this->model->find($id); - $materials = $Tag->Persons()->paginate($this->itemsCount); - - $basePath = $request->getUri()->getPath(); - $materials->withPath($basePath); - - $results = $this->_formatPagination($materials); - + $results = $this->paginate($request, $tag->Persons()); return $response->withJson($results); } public function getMaterials(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - if (!$this->model->exists($id)) { - throw new Errors\NotFoundException; + $tag = Tag::find($id); + if (!$tag) { + throw new HttpNotFoundException($request); } - $Tag = $this->model->find($id); - $materials = $Tag->Materials()->paginate($this->itemsCount); - - $basePath = $request->getUri()->getPath(); - $materials->withPath($basePath); - - $results = $this->_formatPagination($materials); + $results = $this->paginate($request, $tag->Materials()); return $response->withJson($results); } } diff --git a/server/src/App/Controllers/Traits/FileResponse.php b/server/src/App/Controllers/Traits/FileResponse.php index 299b215df..4ef6ebb1a 100644 --- a/server/src/App/Controllers/Traits/FileResponse.php +++ b/server/src/App/Controllers/Traits/FileResponse.php @@ -4,7 +4,7 @@ namespace Robert2\API\Controllers\Traits; use Slim\Http\Response; -use Slim\Http\Stream; +use Slim\Psr7\Stream; trait FileResponse { diff --git a/server/src/App/Controllers/Traits/Taggable.php b/server/src/App/Controllers/Traits/Taggable.php index 91a136bce..c615a515b 100644 --- a/server/src/App/Controllers/Traits/Taggable.php +++ b/server/src/App/Controllers/Traits/Taggable.php @@ -3,13 +3,16 @@ namespace Robert2\API\Controllers\Traits; -use Slim\Http\Request; +use Illuminate\Database\Eloquent\Builder; +use Robert2\API\Controllers\Traits\WithModel; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; - -use Robert2\API\Errors; +use Slim\Http\ServerRequest as Request; trait Taggable { + use WithModel; + public function getAll(Request $request, Response $response): Response { $searchTerm = $request->getQueryParam('search', null); @@ -20,28 +23,37 @@ public function getAll(Request $request, Response $response): Response $ascending = (bool)$request->getQueryParam('ascending', true); $withDeleted = (bool)$request->getQueryParam('deleted', false); - $results = $this->model + $model = $this->getModel(); + if (!method_exists($model, 'getAllFilteredOrTagged')) { + throw new \LogicException("Missing `getAllFilteredOrTagged` method in model."); + } + + /** @var Builder $query */ + $query = $model ->setOrderBy($orderBy, $ascending) ->setSearch($searchTerm, $searchField) - ->getAllFilteredOrTagged([], $tags, $withDeleted) - ->paginate($limit ? (int)$limit : $this->itemsCount); + ->getAllFilteredOrTagged([], $tags, $withDeleted); - $basePath = $request->getUri()->getPath(); - $params = $request->getQueryParams(); - $results = $results->withPath($basePath)->appends($params); - $results = $this->_formatPagination($results); - - return $response->withJson($results); + $paginated = $this->paginate($request, $query, $limit ? (int)$limit : null); + return $response->withJson($paginated); } public function getTags(Request $request, Response $response): Response { - $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); + $id = (int)$request->getAttribute('id'); + $model = $this->getModelClass()::find($id); if (!$model) { - throw new Errors\NotFoundException; + throw new HttpNotFoundException($request); } return $response->withJson($model->tags); } + + // ------------------------------------------------------ + // - + // - Abstract methods + // - + // ------------------------------------------------------ + + abstract protected function paginate(Request $request, $query, ?int $limit = null): array; } diff --git a/server/src/App/Controllers/Traits/WithCrud.php b/server/src/App/Controllers/Traits/WithCrud.php new file mode 100644 index 000000000..58c7e4f00 --- /dev/null +++ b/server/src/App/Controllers/Traits/WithCrud.php @@ -0,0 +1,114 @@ +getQueryParam('search', null); + $searchField = $request->getQueryParam('searchBy', null); + $orderBy = $request->getQueryParam('orderBy', null); + $limit = $request->getQueryParam('limit', null); + $ascending = (bool)$request->getQueryParam('ascending', true); + $withDeleted = (bool)$request->getQueryParam('deleted', false); + + $query = $this->getModel() + ->setOrderBy($orderBy, $ascending) + ->setSearch($searchTerm, $searchField) + ->getAll($withDeleted); + + $paginated = $this->paginate($request, $query, $limit ? (int)$limit : null); + return $response->withJson($paginated); + } + + public function getOne(Request $request, Response $response): Response + { + $id = (int)$request->getAttribute('id'); + $model = $this->getModelClass()::find($id); + if (!$model) { + throw new HttpNotFoundException($request); + } + + return $response->withJson($model->toArray()); + } + + // —————————————————————————————————————————————————————— + // — + // — Setters + // — + // —————————————————————————————————————————————————————— + + public function create(Request $request, Response $response): Response + { + $postData = (array)$request->getParsedBody(); + if (empty($postData)) { + throw new \InvalidArgumentException( + "Missing request data to process validation", + ERROR_VALIDATION + ); + } + + $result = $this->getModelClass()::new($postData); + return $response->withJson($result->toArray(), SUCCESS_CREATED); + } + + public function update(Request $request, Response $response): Response + { + $postData = (array)$request->getParsedBody(); + if (empty($postData)) { + throw new \InvalidArgumentException( + "Missing request data to process validation", + ERROR_VALIDATION + ); + } + + $id = (int)$request->getAttribute('id'); + $model = $this->getModelClass()::find($id); + if (!$model) { + throw new HttpNotFoundException($request); + } + + $result = $this->getModelClass()::staticEdit($id, $postData); + return $response->withJson($result->toArray(), SUCCESS_OK); + } + + public function delete(Request $request, Response $response): Response + { + $id = (int)$request->getAttribute('id'); + $model = $this->getModelClass()::staticRemove($id); + + $data = $model ? $model->toArray() : ['destroyed' => true]; + return $response->withJson($data, SUCCESS_OK); + } + + public function restore(Request $request, Response $response): Response + { + $id = (int)$request->getAttribute('id'); + $data = $this->getModelClass()::staticUnremove($id); + + return $response->withJson($data, SUCCESS_OK); + } + + // ------------------------------------------------------ + // - + // - Abstract methods + // - + // ------------------------------------------------------ + + abstract protected function paginate(Request $request, $query, ?int $limit = null): array; +} diff --git a/server/src/App/Controllers/Traits/WithModel.php b/server/src/App/Controllers/Traits/WithModel.php new file mode 100644 index 000000000..49bfa513a --- /dev/null +++ b/server/src/App/Controllers/Traits/WithModel.php @@ -0,0 +1,45 @@ +getModelClass(); + return new $modelClass; + } + + protected function getModelClass(): string + { + if (static::$modelClass !== null) { + if (!is_subclass_of(static::$modelClass, BaseModel::class)) { + throw new \LogicException("Missing or invalid `modelClass` static property in controller."); + } + return static::$modelClass; + } + + // - Détection automatique du modèle. + $controllerName = class_basename($this); + $modelName = preg_replace('/Controller$/', '', $controllerName); + $modelFullName = sprintf('\\Robert2\\API\\Models\\%s', $modelName); + if (!class_exists($modelFullName, true) || !is_subclass_of($modelFullName, BaseModel::class)) { + throw new \RuntimeException( + sprintf("Unable to retrieves the associated model class for controller `%s`.", $controllerName) + ); + } + + return static::$modelClass = $modelFullName; + } +} diff --git a/server/src/App/Controllers/Traits/WithPdf.php b/server/src/App/Controllers/Traits/WithPdf.php index 1dde059f7..43fcbb5c4 100644 --- a/server/src/App/Controllers/Traits/WithPdf.php +++ b/server/src/App/Controllers/Traits/WithPdf.php @@ -3,32 +3,33 @@ namespace Robert2\API\Controllers\Traits; -use Slim\Http\Request; +use Robert2\API\Controllers\Traits\WithModel; +use Slim\Exception\HttpNotFoundException; use Slim\Http\Response; - -use Robert2\API\Errors; +use Slim\Http\ServerRequest as Request; trait WithPdf { + use WithModel; use FileResponse; public function getOnePdf(Request $request, Response $response): Response { $id = (int)$request->getAttribute('id'); - $model = $this->model->find($id); + $model = $this->getModelClass()::find($id); if (!$model) { - throw new Errors\NotFoundException(); + throw new HttpNotFoundException($request); } - if (!method_exists($this->model, 'getPdfName')) { + if (!method_exists($model, 'getPdfName')) { throw new \RuntimeException("Model used for PDF must implement getPdfName() method."); } - $fileName = $this->model->getPdfName($id); + $fileName = $model->getPdfName($id); - if (!method_exists($this->model, 'getPdfContent')) { + if (!method_exists($model, 'getPdfContent')) { throw new \RuntimeException("Model used for PDF must implement getPdfContent() method."); } - $fileContent = $this->model->getPdfContent($id); + $fileContent = $model->getPdfContent($id); return $this->_responseWithFile($response, $fileName, $fileContent); } diff --git a/server/src/App/Controllers/UserController.php b/server/src/App/Controllers/UserController.php index be5388769..705b43249 100644 --- a/server/src/App/Controllers/UserController.php +++ b/server/src/App/Controllers/UserController.php @@ -3,57 +3,47 @@ namespace Robert2\API\Controllers; -use Slim\Http\Request; -use Slim\Http\Response; -use Robert2\API\Errors; -use Robert2\API\Services\Auth; +use Robert2\API\Controllers\Traits\WithCrud; use Robert2\API\Models\User; +use Robert2\API\Models\UserSetting; +use Robert2\API\Services\Auth; +use Slim\Exception\HttpNotFoundException; +use Slim\Http\Response; +use Slim\Http\ServerRequest as Request; class UserController extends BaseController { - /** @var User */ - protected $model; - - // —————————————————————————————————————————————————————— - // — - // — Model dedicated methods - // — - // —————————————————————————————————————————————————————— + use WithCrud { + delete as originalDelete; + } public function getOne(Request $request, Response $response): Response { - $id = (int)$request->getAttribute('id'); - $user = $this->model->find($id); + $id = (int)$request->getAttribute('id'); + $user = User::find($id); if (!$user) { - throw new Errors\NotFoundException; + throw new HttpNotFoundException($request); } - unset($user->password); - return $response->withJson($user->toArray()); } public function getSettings(Request $request, Response $response): Response { - $id = (int)$request->getAttribute('id'); - $user = $this->model->find($id); - - if (!$user) { - throw new Errors\NotFoundException; - } + $id = (int)$request->getAttribute('id'); + $user = User::find($id); - $settings = $user->settings; - if (!$settings) { - throw new Errors\NotFoundException; + if (!$user || !$user->settings) { + throw new HttpNotFoundException($request); } - return $response->withJson($settings); + return $response->withJson($user->settings); } public function updateSettings(Request $request, Response $response): Response { - $postData = $request->getParsedBody(); + $postData = (array)$request->getParsedBody(); if (empty($postData)) { throw new \InvalidArgumentException( "Missing request data to process validation", @@ -62,12 +52,13 @@ public function updateSettings(Request $request, Response $response): Response } $id = (int)$request->getAttribute('id'); - if (!$this->model->exists($id)) { - throw new Errors\NotFoundException; + $user = User::find($id); + if (!$user) { + throw new HttpNotFoundException($request); } - $result = $this->model->setSettings($id, $postData); - return $response->withJson($result, SUCCESS_OK); + $result = UserSetting::editByUser($user, $postData); + return $response->withJson($result->toArray(), SUCCESS_OK); } public function delete(Request $request, Response $response): Response @@ -79,6 +70,6 @@ public function delete(Request $request, Response $response): Response ERROR_VALIDATION ); } - return parent::delete($request, $response); + return $this->originalDelete($request, $response); } } diff --git a/server/src/App/Errors/ErrorHandler.php b/server/src/App/Errors/ErrorHandler.php index 7e684fc50..5239be1bd 100644 --- a/server/src/App/Errors/ErrorHandler.php +++ b/server/src/App/Errors/ErrorHandler.php @@ -1,95 +1,54 @@ container = $container; - } + protected $defaultErrorRendererContentType = 'application/json'; + protected $defaultErrorRenderer = JsonErrorRenderer::class; - public function __invoke($request, $response, $exception) - { - if ($this->container->settings["displayErrorDetails"] === true) { - return $this->developpementResponse($request, $response, $exception); - } - // @codeCoverageIgnoreStart - return $this->productionResponse($request, $response, $exception); - // @codeCoverageIgnoreEnd - } + protected $errorRenderers = [ + 'application/json' => JsonErrorRenderer::class, + ]; - /** - * @codeCoverageIgnore - */ - private function productionResponse($request, $response, $exception) + protected function determineStatusCode(): int { - $errorCode = $exception->getCode() ?: ERROR_SERVER; - $output = [ - 'success' => false, - 'error' => [ - 'code' => $errorCode, - 'message' => $exception->getMessage(), - ] - ]; + if ($this->method === 'OPTIONS') { + return 200; + } - if (method_exists($exception, 'getValidationErrors')) { - $output['error']['details'] = $exception->getValidationErrors(); + if ($this->exception instanceof ModelNotFoundException) { + return 404; } - $this->container->logger->error($exception->getMessage()); - $this->container->logger->error($exception->getTraceAsString() . "\n"); + if ($this->exception instanceof HttpException) { + return $this->exception->getCode(); + } + $errorCode = $this->exception->getCode() ?: ERROR_SERVER; if ($errorCode >= 100 and $errorCode <= 599) { - return $response->withJson($output, $errorCode); + return $errorCode; } - return $response->withJson($output, ERROR_SERVER); + return ERROR_SERVER; } - private function developpementResponse($request, $response, $exception) + protected function writeToErrorLog(): void { - $requested = sprintf( - '(%s) %s', - $request->getMethod(), - $request->getUri() + $isIgnoredException = ( + $this->exception instanceof HttpException || + $this->exception instanceof ModelNotFoundException || + $this->exception instanceof ValidationException ); - - $file = sprintf( - '%s, line %s.', - $exception->getFile(), - $exception->getLine() - ); - - $errorCode = $exception->getCode() ?: ERROR_SERVER; - - $output = [ - 'success' => false, - 'error' => [ - 'requested' => $requested, - 'code' => $errorCode, - 'message' => $exception->getMessage(), - 'file' => $file, - 'stackTrace' => $exception->getTrace() - ] - ]; - - if (method_exists($exception, 'getValidationErrors')) { - $output['error'] = [ - 'code' => $errorCode, - 'message' => $exception->getMessage(), - 'details' => $exception->getValidationErrors() - ]; + if ($isIgnoredException) { + return; } - - if ($errorCode < 100 || $errorCode > 599) { - $errorCode = ERROR_SERVER; - } - - if ($errorCode === ERROR_NOT_FOUND) { - unset($output['error']['details']); - unset($output['error']['stackTrace']); - } - - return $response->withJson($output, $errorCode); + parent::writeToErrorLog(); } } diff --git a/server/src/App/Errors/MethodNotAllowedHandler.php b/server/src/App/Errors/MethodNotAllowedHandler.php deleted file mode 100644 index 487fe7627..000000000 --- a/server/src/App/Errors/MethodNotAllowedHandler.php +++ /dev/null @@ -1,24 +0,0 @@ - false, - 'error' => [ - 'code' => 405, - 'message' => 'Method not allowed', - 'requested' => sprintf( - 'Method must be one of: [%s]. You asked: (%s) %s', - implode(', ', $methods), - $request->getMethod(), - $request->getUri() - ) - ] - ]; - - return $response->withJson($output, 405); - } -} diff --git a/server/src/App/Errors/NotFoundException.php b/server/src/App/Errors/NotFoundException.php deleted file mode 100644 index fad310e1f..000000000 --- a/server/src/App/Errors/NotFoundException.php +++ /dev/null @@ -1,11 +0,0 @@ - false, - 'error' => [ - 'requested' => sprintf( - '(%s) %s', - $request->getMethod(), - $request->getUri() - ), - 'code' => ERROR_NOT_FOUND, - 'message' => 'Route Not found' - ] - ]; - - return $response->withJson($output, ERROR_NOT_FOUND); - } -} diff --git a/server/src/App/Errors/Renderer/JsonErrorRenderer.php b/server/src/App/Errors/Renderer/JsonErrorRenderer.php new file mode 100644 index 000000000..497e62c44 --- /dev/null +++ b/server/src/App/Errors/Renderer/JsonErrorRenderer.php @@ -0,0 +1,76 @@ + false, + 'error' => static::format($exception, $displayErrorDetails), + ]; + return (string) \json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + } + + // ------------------------------------------------------ + // - + // - Internal methods + // - + // ------------------------------------------------------ + + private static function format(\Throwable $exception, bool $displayErrorDetails): array + { + $output = [ + 'code' => $exception->getCode() ?: ERROR_SERVER, + 'message' => $exception->getMessage(), + ]; + + if ($exception instanceof ModelNotFoundException) { + $output['code'] = ERROR_NOT_FOUND; + $output['message'] = "Not found."; // "Entity not found." + return $output; + } + + if ($exception instanceof ValidationException) { + $output['details'] = $exception->getValidationErrors(); + return $output; + } + + if ($exception instanceof HttpException) { + if ($displayErrorDetails) { + $request = $exception->getRequest(); + $requestDetail = sprintf("(%s) %s", $request->getMethod(), $request->getUri()); + if ($exception instanceof HttpMethodNotAllowedException) { + $requestDetail = sprintf( + "Method must be one of: [%s]. You asked: %s", + implode(', ', $exception->getAllowedMethods()), + $requestDetail + ); + } + $output['debug'] = ['requested' => $requestDetail]; + } + return $output; + } + + if ($output['code'] === ERROR_NOT_FOUND) { + return $output; + } + + if ($displayErrorDetails) { + $output['debug'] = [ + 'file' => sprintf('%s, line %s.', $exception->getFile(), $exception->getLine()), + 'stackTrace' => $exception->getTrace(), + ]; + } + + return $output; + } +} diff --git a/server/src/App/Errors/UnauthorizedException.php b/server/src/App/Errors/UnauthorizedException.php deleted file mode 100644 index 6434a4a29..000000000 --- a/server/src/App/Errors/UnauthorizedException.php +++ /dev/null @@ -1,11 +0,0 @@ -_validation = (array)$validation; - } - public function getValidationErrors() - { - return $this->_validation; + return $this; } /** * Parses PDO error codes to translate them into error exceptions */ - public function setPDOValidationException(QueryException $e): void + public function setPDOValidationException(QueryException $e): self { $message = ""; $details = $e->getMessage(); @@ -41,7 +44,19 @@ public function setPDOValidationException(QueryException $e): void $keyNameWithoutUnique = substr($details, $offsetKeyName, - strlen("_UNIQUE'")); $message = "Duplicate entry: index $keyNameWithoutUnique must be unique"; } - $this->setValidationErrors([$message, $details]); + + return $this; + } + + // ------------------------------------------------------ + // - + // - Getters + // - + // ------------------------------------------------------ + + public function getValidationErrors() + { + return $this->_validation; } } diff --git a/server/src/App/Lib/Pdf/Pdf.php b/server/src/App/Lib/Pdf/Pdf.php index 2a0f663fc..015235200 100644 --- a/server/src/App/Lib/Pdf/Pdf.php +++ b/server/src/App/Lib/Pdf/Pdf.php @@ -4,10 +4,9 @@ namespace Robert2\Lib\Pdf; use Dompdf\Dompdf; -use Twig\Extra\Intl\IntlExtension; - -use Robert2\API\I18n\I18n; use Robert2\API\Config\Config; +use Robert2\API\Services\I18n; +use Robert2\API\Services\View; class Pdf { @@ -50,25 +49,9 @@ public static function createFromTemplate(string $templateName, array $data): st 'grouping_used' => false ]; - $baseTemplateDir = VIEWS_FOLDER . DS . 'pdf'; - $loader = new \Twig\Loader\FilesystemLoader($baseTemplateDir); - - $twig = new \Twig\Environment($loader, ['cache' => false]); - $twig->addExtension(new IntlExtension()); - $twig->addExtension(new \Twig_Extensions_Extension_Text()); - $i18n = new I18n(Config::getSettings('defaultLang')); - $translateFunction = new \Twig\TwigFunction('translate', [$i18n, 'translate']); - $twig->addFunction($translateFunction); - $pluralFunction = new \Twig\TwigFunction('plural', [$i18n, 'plural']); - $twig->addFunction($pluralFunction); - - $template = $twig->load(sprintf('%s.twig', $templateName)); - $html = $template->render($data); - - // debug($html); exit; - - $pdf = new Pdf($html); - return $pdf->getResult(); + $template = sprintf('pdf/%s.twig', $templateName); + $html = (new View($i18n))->fetch($template, $data); + return (new static($html))->getResult(); } } diff --git a/server/src/App/Middlewares/Acl.php b/server/src/App/Middlewares/Acl.php index 6cb5bacba..092d6d3ce 100644 --- a/server/src/App/Middlewares/Acl.php +++ b/server/src/App/Middlewares/Acl.php @@ -3,38 +3,38 @@ namespace Robert2\API\Middlewares; -use Robert2\API\ApiRouter; use Robert2\API\Config; use Robert2\API\Services\Auth; -use Slim\Http\Request; -use Slim\Http\Response; +use Psr\Http\Server\RequestHandlerInterface as RequestHandler; +use Slim\Exception\HttpUnauthorizedException; +use Slim\Http\ServerRequest as Request; class Acl { - public function __invoke(Request $request, Response $response, callable $next) + public function __invoke(Request $request, RequestHandler $handler) { + if ($request->isOptions()) { + return $handler->handle($request); + } + $currentRoute = $this->_getCurrentRoute($request); if (!preg_match('/^\/?api\//', $currentRoute)) { - return $next($request, $response); + return $handler->handle($request); } $groupId = Auth::isAuthenticated() ? Auth::user()->group_id : 'visitor'; $method = strtolower($request->getMethod()); $deniedRoutes = $this->_getDeniedRoutes($groupId, $method); if (empty($deniedRoutes)) { - return $next($request, $response); + return $handler->handle($request); } $currentRoute = preg_replace('/^\/api/', '', $currentRoute) . '[/]'; if (in_array($currentRoute, $deniedRoutes)) { - $oldResponse = $response->withStatus(ERROR_UNAUTHORIZED); - return $oldResponse->withJson([ - 'error' => "Unauthorized by ACL: users of group '$groupId' " - . "are not allowed to access this API endpoint." - ]); + throw new HttpUnauthorizedException($request); } - return $next($request, $response); + return $handler->handle($request); } protected function _getCurrentRoute(Request $request): string @@ -61,7 +61,7 @@ protected function _getDeniedRoutes(string $groupId, string $method): array return []; } - $routesByMethod = (new ApiRouter())->getRoutes(); + $routesByMethod = include CONFIG_FOLDER . DS . 'routes.php'; if (!array_key_exists($method, $routesByMethod)) { return []; } diff --git a/server/src/App/Middlewares/Pagination.php b/server/src/App/Middlewares/Pagination.php index ddd76597c..d539534cb 100644 --- a/server/src/App/Middlewares/Pagination.php +++ b/server/src/App/Middlewares/Pagination.php @@ -4,19 +4,22 @@ namespace Robert2\API\Middlewares; use Illuminate\Pagination\Paginator; -use Slim\Http\Request; -use Slim\Http\Response; +use Psr\Http\Server\RequestHandlerInterface as RequestHandler; +use Slim\Http\ServerRequest as Request; class Pagination { - public function __invoke(Request $request, Response $response, callable $next) + public function __invoke(Request $request, RequestHandler $handler) { + /** @var \Slim\Http\Response */ + $response = $handler->handle($request); + Paginator::currentPageResolver( function () use ($request) { return $request->getParam('page'); } ); - return $next($request, $response); + return $response; } } diff --git a/server/src/App/Models/Attribute.php b/server/src/App/Models/Attribute.php index 845f8bd23..af34bfc11 100644 --- a/server/src/App/Models/Attribute.php +++ b/server/src/App/Models/Attribute.php @@ -4,7 +4,6 @@ namespace Robert2\API\Models; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Model; use Robert2\API\Validation\Validator as V; class Attribute extends BaseModel @@ -16,7 +15,7 @@ public function __construct(array $attributes = []) parent::__construct($attributes); $this->validation = [ - 'name' => V::notEmpty()->alnum(self::EXTRA_CHARS)->length(2, 64), + 'name' => V::notEmpty()->alnum(static::EXTRA_CHARS)->length(2, 64), 'type' => V::notEmpty()->oneOf( v::equals('string'), v::equals('integer'), @@ -105,26 +104,20 @@ public function getAll(bool $withDeleted = false): Builder // — // —————————————————————————————————————————————————————— - public function edit(?int $id = null, array $data = []): Model + public function edit(?int $id = null, array $data = []): BaseModel { if ($id) { $data = ['name' => $data['name']]; } - return parent::edit($id, $data); } - public function remove(int $id, array $options = []): ?Model + public function remove(int $id, array $options = []): ?BaseModel { - $model = self::find($id); - if (empty($model)) { - throw new Errors\NotFoundException; - } - - if (!$model->delete()) { + $attribute = static::findOrFail($id); + if (!$attribute->delete()) { throw new \RuntimeException(sprintf("Unable to delete the attribute %d.", $id)); } - return null; } } diff --git a/server/src/App/Models/BaseModel.php b/server/src/App/Models/BaseModel.php index b4335721e..96314da36 100644 --- a/server/src/App/Models/BaseModel.php +++ b/server/src/App/Models/BaseModel.php @@ -3,13 +3,13 @@ namespace Robert2\API\Models; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\QueryException; -use Illuminate\Database\Eloquent\Builder; use Respect\Validation\Exceptions\NestedValidationException; -use InvalidArgumentException; use Robert2\API\Config\Config; -use Robert2\API\Errors; +use Robert2\API\Errors\ValidationException; abstract class BaseModel extends Model { @@ -64,7 +64,7 @@ public function getAll(bool $withDeleted = false): Builder public function getAllFiltered(array $conditions, bool $withDeleted = false): Builder { - $builder = self::where($conditions); + $builder = static::where($conditions); if (!empty($this->searchTerm)) { $builder = $this->_setSearchConditions($builder); @@ -102,7 +102,7 @@ public function setSearch(?string $term = null, $fields = null): BaseModel $fields = !is_array($fields) ? explode('|', $fields) : $fields; foreach ($fields as $field) { if (!in_array($field, $this->getAllowedSearchFields())) { - throw new InvalidArgumentException("Search field « $field » not allowed."); + throw new \InvalidArgumentException("Search field « $field » not allowed."); } $this->searchField = $field; } @@ -118,64 +118,84 @@ public function setSearch(?string $term = null, $fields = null): BaseModel // — // —————————————————————————————————————————————————————— - public function edit(?int $id = null, array $data = []): Model + public static function new(array $data = []): BaseModel { - if ($id && !$this->exists($id)) { - throw new Errors\NotFoundException(sprintf("Edit failed, record %d not found.", $id)); + // TODO: Migrer les éventuels overwrites de la méthode legacy dans les modèles. + // puis déplacer l'implémentation depuis la methode legacy vers cette méthode. + return static::staticEdit(null, $data); + } + + public static function staticEdit(?int $id = null, array $data = []): BaseModel + { + // TODO: Migrer les éventuels overwrites de la méthode legacy dans les modèles. + // puis déplacer l'implémentation depuis la methode legacy vers cette méthode. + return (new static)->edit($id, $data); + } + + public static function staticRemove(int $id, array $options = []): ?BaseModel + { + // TODO: Migrer les éventuels overwrites de la méthode legacy dans les modèles. + // puis déplacer l'implémentation depuis la methode legacy vers cette méthode. + return (new static)->remove($id, $options); + } + + public static function staticUnremove(int $id): BaseModel + { + // TODO: Migrer les éventuels overwrites de la méthode legacy dans les modèles. + // puis déplacer l'implémentation depuis la methode legacy vers cette méthode. + return (new static)->unremove($id); + } + + /** @deprecated Veuillez utiliser `new` ou `staticEdit`. */ + public function edit(?int $id = null, array $data = []): BaseModel + { + if ($id && !static::staticExists($id)) { + throw (new ModelNotFoundException) + ->setModel(get_class($this), $id); } $data = cleanEmptyFields($data); $data = $this->_trimStringFields($data); try { - $model = self::firstOrNew(compact('id')); + $model = static::firstOrNew(compact('id')); $model->fill($data)->validate()->save(); } catch (QueryException $e) { - $error = new Errors\ValidationException(); - $error->setPDOValidationException($e); - throw $error; + throw (new ValidationException) + ->setPDOValidationException($e); } return $model->refresh(); } - public function remove(int $id, array $options = []): ?Model + /** @deprecated Veuillez utiliser `staticRemove`. */ + public function remove(int $id, array $options = []): ?BaseModel { - $options = array_merge([ - 'force' => false - ], $options); - - $model = self::withTrashed()->find($id); - if (empty($model)) { - throw new Errors\NotFoundException; - } + $options = array_merge(['force' => false], $options); - if ($model->trashed() || $options['force'] === true) { - if (!$model->forceDelete()) { + $entity = static::withTrashed()->findOrFail($id); + if ($entity->trashed() || $options['force'] === true) { + if (!$entity->forceDelete()) { throw new \RuntimeException(sprintf("Unable to destroy the record %d.", $id)); } return null; } - if (!$model->delete()) { + if (!$entity->delete()) { throw new \RuntimeException(sprintf("Unable to delete the record %d.", $id)); } - return $model; + return $entity; } - public function unremove(int $id): Model + /** @deprecated Veuillez utiliser `staticUnremove`. */ + public function unremove(int $id): BaseModel { - $model = self::onlyTrashed()->find($id); - if (empty($model)) { - throw new Errors\NotFoundException; - } - - if (!$model->restore()) { + $entity = static::onlyTrashed()->findOrFail($id); + if (!$entity->restore()) { throw new \RuntimeException(sprintf("Unable to restore the record %d.", $id)); } - - return $model; + return $entity; } // —————————————————————————————————————————————————————— @@ -184,9 +204,17 @@ public function unremove(int $id): Model // — // —————————————————————————————————————————————————————— + public static function staticExists(int $id): bool + { + // TODO: Migrer les éventuels overwrites de la méthode legacy dans les modèles. + // puis déplacer l'implémentation depuis la methode legacy vers cette méthode. + return (new static)->exists($id); + } + + /** @deprecated Veuillez utiliser `staticExists`. */ public function exists(int $id): bool { - return self::where('id', $id)->exists(); + return static::where('id', $id)->exists(); } public function validate(): self @@ -219,9 +247,8 @@ public function validate(): self } if (count($errors) > 0) { - $ex = new Errors\ValidationException(); - $ex->setValidationErrors($errors); - throw $ex; + throw (new ValidationException) + ->setValidationErrors($errors); } return $this; @@ -264,7 +291,7 @@ protected function _getOrderBy(?Builder $builder = null): Builder return $builder->orderBy($order, $direction); } - return self::orderBy($order, $direction); + return static::orderBy($order, $direction); } protected function _setSearchConditions(Builder $builder): Builder @@ -296,4 +323,10 @@ protected function _trimStringFields(array $data): array } return $trimmedData; } + + // @see https://laravel.com/docs/8.x/eloquent-serialization#customizing-the-default-date-format + protected function serializeDate(\DateTimeInterface $date) + { + return $date->format('Y-m-d H:i:s'); + } } diff --git a/server/src/App/Models/Bill.php b/server/src/App/Models/Bill.php index 88fe616a1..3e5d16b76 100644 --- a/server/src/App/Models/Bill.php +++ b/server/src/App/Models/Bill.php @@ -3,14 +3,11 @@ namespace Robert2\API\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use Robert2\API\Validation\Validator as V; - -use Robert2\API\Errors; use Robert2\API\Config\Config; -use Robert2\API\I18n\I18n; use Robert2\API\Models\Traits\WithPdf; +use Robert2\API\Services\I18n; +use Robert2\API\Validation\Validator as V; use Robert2\Lib\Domain\EventBill; class Bill extends BaseModel @@ -112,17 +109,12 @@ public function User() 'user_id', ]; - public function createFromEvent(int $eventId, int $userId, float $discountRate = 0.0): Model + public function createFromEvent(int $eventId, int $userId, float $discountRate = 0.0): Bill { - $Event = new Event(); - $billEvent = $Event + $billEvent = (new Event) ->with('Beneficiaries') ->with('Materials') - ->find($eventId); - - if (!$billEvent) { - throw new Errors\NotFoundException("Event not found."); - } + ->findOrFail($eventId); $date = new \DateTime(); $eventData = $billEvent->toArray(); @@ -147,11 +139,7 @@ public function createFromEvent(int $eventId, int $userId, float $discountRate = public function getPdfName(int $id): string { - $model = $this->withTrashed()->find($id); - if (!$model) { - throw new NotFoundException(sprintf('Record %d not found.', $id)); - } - + $bill = $this->withTrashed()->findOrFail($id); $company = Config::getSettings('companyData'); $i18n = new I18n(Config::getSettings('defaultLang')); @@ -159,8 +147,8 @@ public function getPdfName(int $id): string '%s-%s-%s-%s.pdf', $i18n->translate('Bill'), slugify($company['name']), - $model->number, - slugify($model->Beneficiary->full_name) + $bill->number, + slugify($bill->Beneficiary->full_name) ); if (isTestMode()) { $fileName = sprintf('TEST-%s', $fileName); @@ -171,16 +159,10 @@ public function getPdfName(int $id): string public function getPdfContent(int $id): string { - if (!$this->exists($id)) { - throw new Errors\NotFoundException; - } - - $bill = self::find($id); - + $bill = static::findOrFail($id); $date = new \DateTime($bill->date); - $Event = new Event(); - $eventData = $Event + $eventData = (new Event) ->with('Beneficiaries') ->with('Materials') ->find($bill->event_id) @@ -211,7 +193,7 @@ public function getPdfContent(int $id): string public function deleteByNumber(string $number): void { - $bill = self::where('number', $number); + $bill = static::where('number', $number); if (!$bill) { return; } @@ -221,7 +203,7 @@ public function deleteByNumber(string $number): void public function getLastBillNumber(): int { - $allBills = self::selectRaw('number') + $allBills = static::selectRaw('number') ->whereRaw(sprintf('YEAR(date) = %s', date('Y'))) ->get(); diff --git a/server/src/App/Models/Category.php b/server/src/App/Models/Category.php index 177dc0ff4..d35741280 100644 --- a/server/src/App/Models/Category.php +++ b/server/src/App/Models/Category.php @@ -88,7 +88,7 @@ public function getMaterialsAttribute() public function getIdsByNames(array $names): array { - $categories = self::whereIn('name', $names)->get(); + $categories = static::whereIn('name', $names)->get(); $ids = []; foreach ($categories as $category) { $ids[] = $category->id; @@ -108,7 +108,7 @@ public function bulkAdd(array $categoriesNames = []): array { $categories = array_map( function ($categoryName) { - $existingCategory = self::where('name', $categoryName)->first(); + $existingCategory = static::where('name', $categoryName)->first(); if ($existingCategory) { return $existingCategory; } @@ -129,9 +129,8 @@ function ($categoryName) { } } } catch (QueryException $e) { - $error = new ValidationException(); - $error->setPDOValidationException($e); - throw $error; + throw (new ValidationException) + ->setPDOValidationException($e); } }); diff --git a/server/src/App/Models/Company.php b/server/src/App/Models/Company.php index 906648fb2..e98cd72a6 100644 --- a/server/src/App/Models/Company.php +++ b/server/src/App/Models/Company.php @@ -3,12 +3,8 @@ namespace Robert2\API\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Robert2\API\Validation\Validator as V; - -use Robert2\API\Errors; use Robert2\API\Models\Traits\Taggable; class Company extends BaseModel @@ -102,7 +98,7 @@ public function getTagsAttribute() 'note', ]; - public function edit(?int $id = null, array $data = []): Model + public function edit(?int $id = null, array $data = []): BaseModel { if (!empty($data['phone'])) { $data['phone'] = normalizePhone($data['phone']); @@ -127,12 +123,7 @@ public function addPersons(int $id, array $persons) throw new \InvalidArgumentException("Missing persons data to add to company."); } - try { - $Company = self::findOrFail($id); - } catch (ModelNotFoundException $e) { - throw new Errors\NotFoundException; - } - + $Company = static::findOrFail($id); foreach ($persons as $person) { $person['company_id'] = $Company->id; $Person = new Person; diff --git a/server/src/App/Models/Country.php b/server/src/App/Models/Country.php index 97f9e92e0..ae6051b3f 100644 --- a/server/src/App/Models/Country.php +++ b/server/src/App/Models/Country.php @@ -18,7 +18,7 @@ public function __construct(array $attributes = []) parent::__construct($attributes); $this->validation = [ - 'name' => V::notEmpty()->alpha(self::EXTRA_CHARS)->length(4, 96), + 'name' => V::notEmpty()->alpha(static::EXTRA_CHARS)->length(4, 96), 'code' => V::notEmpty()->alpha()->length(4, 4), ]; } diff --git a/server/src/App/Models/Document.php b/server/src/App/Models/Document.php index fd3b29092..643e2d468 100644 --- a/server/src/App/Models/Document.php +++ b/server/src/App/Models/Document.php @@ -3,8 +3,6 @@ namespace Robert2\API\Models; -use Illuminate\Database\Eloquent\Model; -use Robert2\API\Config\Config; use Robert2\API\Errors; use Robert2\API\Validation\Validator as V; @@ -17,8 +15,6 @@ class Document extends BaseModel public function __construct(array $attributes = []) { - Config::getCapsule(); - parent::__construct($attributes); $this->validation = [ @@ -63,7 +59,7 @@ public function getMaterialAttribute() public function getFilePathAttribute() { - return self::getFilePath($this->material_id, $this->name); + return static::getFilePath($this->material_id, $this->name); } // ------------------------------------------------------ @@ -85,28 +81,23 @@ public function getFilePathAttribute() // - // ------------------------------------------------------ - public function remove(int $id, array $options = []): ?Model + public function remove(int $id, array $options = []): ?BaseModel { - $model = self::find($id); - if (empty($model)) { - throw new Errors\NotFoundException; - } - - $filePath = self::getFilePath((int)$model->material_id, $model->name); - - if (!$model->forceDelete()) { + $document = static::findOrFail($id); + if (!$document->forceDelete()) { throw new \RuntimeException( sprintf("Unable to delete document %d.", $id) ); } + $filePath = static::getFilePath((int)$document->material_id, $document->name); if (!unlink($filePath)) { throw new \RuntimeException( - sprintf("Unable to delete file '%s' from data folder: %s", $model->name, $filePath) + sprintf("Unable to delete file '%s' from data folder: %s", $document->name, $filePath) ); }; - return $model; + return $document; } public static function getFilePath(int $materialId, ?string $name = null): string diff --git a/server/src/App/Models/Estimate.php b/server/src/App/Models/Estimate.php index fec699419..14e923cb9 100644 --- a/server/src/App/Models/Estimate.php +++ b/server/src/App/Models/Estimate.php @@ -3,14 +3,11 @@ namespace Robert2\API\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use Robert2\API\Validation\Validator as V; - -use Robert2\API\Errors; use Robert2\API\Config\Config; -use Robert2\API\I18n\I18n; use Robert2\API\Models\Traits\WithPdf; +use Robert2\API\Services\I18n; +use Robert2\API\Validation\Validator as V; use Robert2\Lib\Domain\EventEstimate; class Estimate extends BaseModel @@ -109,17 +106,12 @@ public function User() 'user_id', ]; - public function createFromEvent(int $eventId, int $userId, float $discountRate = 0.0): Model + public static function createFromEvent(int $eventId, int $userId, float $discountRate = 0.0): Estimate { - $Event = new Event(); - $estimateEvent = $Event + $estimateEvent = (new Event) ->with('Beneficiaries') ->with('Materials') - ->find($eventId); - - if (!$estimateEvent) { - throw new Errors\NotFoundException("Event not found."); - } + ->findOrFail($eventId); $date = new \DateTime(); $eventData = $estimateEvent->toArray(); @@ -141,13 +133,9 @@ public function createFromEvent(int $eventId, int $userId, float $discountRate = public function getPdfName(int $id): string { - $model = $this->withTrashed()->find($id); - if (!$model) { - throw new NotFoundException(sprintf('Record %d not found.', $id)); - } - + $estimate = $this->withTrashed()->findOrFail($id); $company = Config::getSettings('companyData'); - $date = new \DateTime($model->date); + $date = new \DateTime($estimate->date); $i18n = new I18n(Config::getSettings('defaultLang')); $fileName = sprintf( @@ -155,7 +143,7 @@ public function getPdfName(int $id): string $i18n->translate('Estimate'), slugify($company['name']), $date->format('Ymd-Hi'), - slugify($model->Beneficiary->full_name) + slugify($estimate->Beneficiary->full_name) ); if (isTestMode()) { $fileName = sprintf('TEST-%s', $fileName); @@ -166,16 +154,10 @@ public function getPdfName(int $id): string public function getPdfContent(int $id): string { - if (!$this->exists($id)) { - throw new Errors\NotFoundException; - } - - $estimate = self::find($id); - + $estimate = static::findOrFail($id); $date = new \DateTime($estimate->date); - $Event = new Event(); - $eventData = $Event + $eventData = (new Event) ->with('Beneficiaries') ->with('Materials') ->find($estimate->event_id) diff --git a/server/src/App/Models/Event.php b/server/src/App/Models/Event.php index 352f0a567..962fc70f4 100644 --- a/server/src/App/Models/Event.php +++ b/server/src/App/Models/Event.php @@ -210,7 +210,7 @@ public function getAll(bool $withDeleted = false): Builder ]); }; - $builder = self::orderBy('start_date', 'asc') + $builder = static::orderBy('start_date', 'asc') ->where($conditions); if ($withDeleted) { @@ -220,14 +220,14 @@ public function getAll(bool $withDeleted = false): Builder return $builder; } - public function getMissingMaterials(int $id): ?array + public static function getMissingMaterials(int $id): ?array { - $event = $this->with('Materials')->find($id); + $event = static::with('Materials')->find($id); if (!$event || empty($event->materials)) { return null; } - $eventMaterials = (new Material())->recalcQuantitiesForPeriod( + $eventMaterials = Material::recalcQuantitiesForPeriod( $event->materials, $event->start_date, $event->end_date, @@ -248,9 +248,9 @@ public function getMissingMaterials(int $id): ?array return empty($missingMaterials) ? null : array_values($missingMaterials); } - public function getParks(int $id): ?array + public static function getParks(int $id): ?array { - $event = $this->with('Materials')->find($id); + $event = static::with('Materials')->find($id); if (!$event) { return null; } @@ -264,15 +264,11 @@ public function getParks(int $id): ?array public function getPdfContent(int $id): string { - if (!$this->exists($id)) { - throw new Errors\NotFoundException; - } - $event = $this ->with('Assignees') ->with('Beneficiaries') ->with('Materials') - ->find($id) + ->findOrFail($id) ->toArray(); $date = new \DateTime(); @@ -308,6 +304,18 @@ public function getPdfContent(int $id): string // — // —————————————————————————————————————————————————————— + protected $fillable = [ + 'user_id', + 'reference', + 'title', + 'description', + 'start_date', + 'end_date', + 'is_confirmed', + 'location', + 'is_billable', + ]; + public function setPeriod(?string $start, ?string $end): Event { $thisYear = date('Y'); @@ -323,16 +331,4 @@ public function setPeriod(?string $start, ?string $end): Event return $this; } - - protected $fillable = [ - 'user_id', - 'reference', - 'title', - 'description', - 'start_date', - 'end_date', - 'is_confirmed', - 'location', - 'is_billable', - ]; } diff --git a/server/src/App/Models/Material.php b/server/src/App/Models/Material.php index 48b190a7e..90961eae1 100644 --- a/server/src/App/Models/Material.php +++ b/server/src/App/Models/Material.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Robert2\API\Validation\Validator as V; - use Robert2\API\Models\Traits\Taggable; class Material extends BaseModel @@ -235,7 +234,7 @@ public function getDocumentsAttribute() // - // ------------------------------------------------------ - public function recalcQuantitiesForPeriod( + public static function recalcQuantitiesForPeriod( array $data, string $start, string $end, @@ -245,7 +244,7 @@ public function recalcQuantitiesForPeriod( return []; } - $events = (new Event())->setPeriod($start, $end)->getAll(); + $events = (new Event)->setPeriod($start, $end)->getAll(); if ($exceptEventId) { $events = $events->where('id', '!=', $exceptEventId); } diff --git a/server/src/App/Models/Person.php b/server/src/App/Models/Person.php index 0ee25c001..16e7822e3 100644 --- a/server/src/App/Models/Person.php +++ b/server/src/App/Models/Person.php @@ -3,15 +3,15 @@ namespace Robert2\API\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\DB; use Robert2\API\Config\Config; +use Robert2\API\Errors\ValidationException; use Robert2\API\Models\Traits\Taggable; use Robert2\API\Validation\Validator as V; -use Robert2\API\Errors; class Person extends BaseModel { @@ -38,8 +38,8 @@ public function __construct(array $attributes = []) $this->validation = [ 'user_id' => V::optional(V::numeric()), - 'first_name' => V::notEmpty()->alpha(self::EXTRA_CHARS)->length(2, 96), - 'last_name' => V::notEmpty()->alpha(self::EXTRA_CHARS)->length(2, 96), + 'first_name' => V::notEmpty()->alpha(static::EXTRA_CHARS)->length(2, 96), + 'last_name' => V::notEmpty()->alpha(static::EXTRA_CHARS)->length(2, 96), 'email' => V::optional(V::email()->length(null, 191)), 'phone' => V::optional(V::phone()), 'street' => V::optional(V::length(null, 191)), @@ -146,7 +146,7 @@ protected function _getOrderBy(?Builder $builder = null): Builder if ($builder) { $builder = $builder->orderBy($order, $direction); } else { - $builder = self::orderBy($order, $direction); + $builder = static::orderBy($order, $direction); } if ($order === 'companies.legal_name') { @@ -177,10 +177,11 @@ protected function _getOrderBy(?Builder $builder = null): Builder 'note', ]; - public function edit(?int $id = null, array $data = []): Model + public function edit(?int $id = null, array $data = []): BaseModel { - if ($id && !$this->exists($id)) { - throw new Errors\NotFoundException(sprintf("Edit failed, entity %d not found.", $id)); + if ($id && !static::staticExists($id)) { + throw (new ModelNotFoundException) + ->setModel(get_class($this), $id); } $data = cleanEmptyFields($data); @@ -191,7 +192,7 @@ public function edit(?int $id = null, array $data = []): Model } try { - $person = self::firstOrNew(compact('id')); + $person = static::firstOrNew(compact('id')); $person->fill($data)->validate()->save(); if (!empty($data['tags'])) { @@ -199,12 +200,11 @@ public function edit(?int $id = null, array $data = []): Model } } catch (QueryException $e) { if (!isDuplicateException($e)) { - $error = new Errors\ValidationException(); - $error->setPDOValidationException($e); - throw $error; + throw (new ValidationException) + ->setPDOValidationException($e); } - $person = self::where('email', $data['email'])->first(); + $person = static::where('email', $data['email'])->first(); $this->_setOtherTag($person); } diff --git a/server/src/App/Models/Tag.php b/server/src/App/Models/Tag.php index b6c7bcc93..1f6ad04aa 100644 --- a/server/src/App/Models/Tag.php +++ b/server/src/App/Models/Tag.php @@ -82,7 +82,7 @@ public function getMaterialsAttribute() public function getIdsByNames(array $names): array { - $tags = self::whereIn('name', $names)->get(); + $tags = static::whereIn('name', $names)->get(); $ids = []; foreach ($tags as $tag) { $ids[] = $tag->id; @@ -102,7 +102,7 @@ public function bulkAdd(array $tagNames = []): array { $tags = array_map( function ($tagName) { - $existingTag = self::where('name', $tagName)->first(); + $existingTag = static::where('name', $tagName)->first(); if ($existingTag) { return $existingTag; } @@ -123,9 +123,8 @@ function ($tagName) { } } } catch (QueryException $e) { - $error = new ValidationException(); - $error->setPDOValidationException($e); - throw $error; + throw (new ValidationException) + ->setPDOValidationException($e); } }); diff --git a/server/src/App/Models/Traits/Taggable.php b/server/src/App/Models/Traits/Taggable.php index 4324ea0c1..49f3fae83 100644 --- a/server/src/App/Models/Traits/Taggable.php +++ b/server/src/App/Models/Traits/Taggable.php @@ -3,17 +3,15 @@ namespace Robert2\API\Models\Traits; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; - -use Robert2\API\Errors; +use Robert2\API\Models\BaseModel; use Robert2\API\Models\Tag; trait Taggable { public function Tags() { - return $this->morphToMany('Robert2\API\Models\Tag', 'taggable') + return $this->morphToMany(Tag::class, 'taggable') ->select(['id', 'name']); } @@ -48,27 +46,24 @@ public function getAllFilteredOrTagged(array $conditions, array $tags = [], bool // — // —————————————————————————————————————————————————————— - public function edit(?int $id = null, array $data = []): Model + public function edit(?int $id = null, array $data = []): BaseModel { - $Model = parent::edit($id, $data); + $entity = parent::edit($id, $data); if (array_key_exists('tags', $data)) { - $this->setTags($Model['id'], $data['tags']); + $this->setTags($entity['id'], $data['tags']); } - return $Model; + return $entity; } public function setTags(int $id, ?array $tagNames): array { - $Model = self::find($id); - if (!$Model) { - throw new Errors\NotFoundException; - } + $entity = static::findOrFail($id); if (empty($tagNames)) { - $Model->Tags()->sync([]); - return $Model->tags; + $entity->Tags()->sync([]); + return $entity->tags; } // - Filter list to keep only names @@ -84,22 +79,22 @@ public function setTags(int $id, ?array $tagNames): array $tagsIds[] = $Tag->id; } - $Model->Tags()->sync($tagsIds); - return $Model->tags; + $entity->Tags()->sync($tagsIds); + return $entity->tags; } public function addTag(int $id, string $tagName): array { - $Model = self::find($id); - $tagName = trim($tagName); + $entity = static::findOrFail($id); - if (!$Model || empty($tagName)) { - throw new Errors\NotFoundException; + $tagName = trim($tagName); + if (empty($tagName)) { + throw new \InvalidArgumentException("The new tag should not be empty."); } $Tag = Tag::firstOrCreate(['name' => $tagName]); - $Model->Tags()->attach($Tag->id); - return $Model->tags; + $entity->Tags()->attach($Tag->id); + return $entity->tags; } } diff --git a/server/src/App/Models/Traits/WithPdf.php b/server/src/App/Models/Traits/WithPdf.php index f0b70095a..7c7fc12aa 100644 --- a/server/src/App/Models/Traits/WithPdf.php +++ b/server/src/App/Models/Traits/WithPdf.php @@ -3,10 +3,9 @@ namespace Robert2\API\Models\Traits; -use Robert2\Lib\Pdf\Pdf; -use Robert2\API\I18n\I18n; use Robert2\API\Config\Config; -use Robert2\API\Errors\NotFoundException; +use Robert2\API\Services\I18n; +use Robert2\Lib\Pdf\Pdf; trait WithPdf { @@ -20,13 +19,9 @@ trait WithPdf public function getPdfName(int $id): string { - $model = $this->withTrashed()->find($id); - if (!$model) { - throw new NotFoundException(sprintf('Record %d not found.', $id)); - } + $model = static::withTrashed()->findOrFail($id); $company = Config::getSettings('companyData'); - $i18n = new I18n(Config::getSettings('defaultLang')); $fileName = sprintf( '%s-%s-%s.pdf', @@ -52,7 +47,6 @@ protected function _getPdfAsString(array $data): string if (empty($this->pdfTemplate)) { throw new \RuntimeException("Missing model's PDF template name"); } - return Pdf::createFromTemplate($this->pdfTemplate, $data); } } diff --git a/server/src/App/Models/User.php b/server/src/App/Models/User.php index 478bc0d23..10a932260 100644 --- a/server/src/App/Models/User.php +++ b/server/src/App/Models/User.php @@ -3,13 +3,12 @@ namespace Robert2\API\Models; -use Robert2\API\Config; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\SoftDeletes; -use Robert2\API\Validation\Validator as V; use Illuminate\Database\Eloquent\Builder; - +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Database\Eloquent\SoftDeletes; +use Robert2\API\Config; use Robert2\API\Errors; +use Robert2\API\Validation\Validator as V; class User extends BaseModel { @@ -108,23 +107,17 @@ public function getAll(bool $softDeleted = false): Builder return parent::getAll($softDeleted)->select($fields); } - public function getLogin(string $identifier, string $password): User + public static function fromLogin(string $identifier, string $password): User { - $user = self::where('email', $identifier) + $user = static::where('email', $identifier) ->orWhere('pseudo', $identifier) ->with('settings') - ->first(); - - if (!$user) { - throw new Errors\NotFoundException; - } + ->firstOrFail(); if (!password_verify($password, $user->password)) { - throw new Errors\NotFoundException; + throw new ModelNotFoundException(static::class); } - unset($user->password); - return $user; } @@ -141,24 +134,7 @@ public function getLogin(string $identifier, string $password): User 'password', ]; - public function setSettings(int $userId, array $data = []): array - { - if (!$this->exists($userId)) { - throw new Errors\NotFoundException("User not found, cannot modify settings."); - } - - $UserSetting = new UserSetting(); - $settings = $UserSetting->edit($userId, $data); - - return $settings->toArray(); - } - - public static function new(array $data = []): User - { - return (new static())->edit(null, $data); - } - - public function edit(?int $id = null, array $data = []): Model + public function edit(?int $id = null, array $data = []): BaseModel { if (isset($data['password']) && !empty($data['password'])) { $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT); @@ -168,7 +144,7 @@ public function edit(?int $id = null, array $data = []): Model $user = parent::edit($id, $data); $userId = (int)$user['id']; - $User = self::find($userId); + $User = static::find($userId); if (!$id) { $settings = Config\Config::getSettings(); diff --git a/server/src/App/Models/UserSetting.php b/server/src/App/Models/UserSetting.php index 5bf3e0266..1b7838adb 100644 --- a/server/src/App/Models/UserSetting.php +++ b/server/src/App/Models/UserSetting.php @@ -3,11 +3,9 @@ namespace Robert2\API\Models; -use Illuminate\Database\Eloquent\Model; -use Robert2\API\Validation\Validator as V; use Illuminate\Database\Eloquent\Builder; - -use Robert2\API\Errors; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Robert2\API\Validation\Validator as V; class UserSetting extends BaseModel { @@ -68,22 +66,19 @@ public function getAll(bool $softDeleted = false): Builder 'auth_token_validity_duration' ]; - public function edit(?int $userId = null, array $data = []): Model + public static function editByUser(User $user, array $data = []): UserSetting { - if (!$userId) { - throw new Errors\NotFoundException("Cannot edit settings of an unknown user, please provide user id."); - } - - $model = $this->where('user_id', $userId)->first(); - if (!$model) { - throw new Errors\NotFoundException("User not found to edit settings of."); + if (!$user->exists) { + throw (new ModelNotFoundException) + ->setModel(static::class); } - return parent::edit($model->id, $data); + $settings = static::where('user_id', $user->id)->firstOrFail(); + return static::staticEdit($settings->id, $data); } // - Prevents the deletion of a user's settings - public function remove(int $id, array $options = []): ?Model + public function remove(int $id, array $options = []): ?BaseModel { return null; } diff --git a/server/src/App/Services/Auth.php b/server/src/App/Services/Auth.php index 2988af9ff..d6e645b16 100644 --- a/server/src/App/Services/Auth.php +++ b/server/src/App/Services/Auth.php @@ -3,11 +3,13 @@ namespace Robert2\API\Services; +use Psr\Http\Server\RequestHandlerInterface as RequestHandler; use Robert2\API\Config\Acl; -use Robert2\API\Services\Auth\AuthenticatorInterface; use Robert2\API\Models\User; -use Slim\Http\Request; -use Slim\Http\Response; +use Robert2\API\Services\Auth\AuthenticatorInterface; +use Slim\Exception\HttpUnauthorizedException; +use Slim\Http\ServerRequest as Request; +use Slim\Psr7\Response; final class Auth { @@ -27,18 +29,18 @@ public function __construct(array $authenticators = []) $this->authenticators = $authenticators; } - public function middleware(Request $request, Response $response, callable $next) + public function middleware(Request $request, RequestHandler $handler) { if (!$this->needsAuthentication($request)) { $this->retrieveUser($request); - return $next($request, $response); + return $handler->handle($request); } if (!$this->retrieveUser($request)) { - return $this->unauthenticated($request, $response); + return $this->unauthenticated($request, $handler); } - return $next($request, $response); + return $handler->handle($request); } public function logout() @@ -92,7 +94,7 @@ public static function isApiRequest(Request $request): bool protected function needsAuthentication(Request $request): bool { // - HTTP Method: OPTIONS => On laisse passer. - if ($request->getMethod() === 'OPTIONS') { + if ($request->isOptions()) { return false; } @@ -130,32 +132,20 @@ protected function retrieveUser(Request $request): bool return false; } - protected function unauthenticated(Request $request, Response $response): Response + protected function unauthenticated(Request $request, RequestHandler $handler): Response { if (static::isLoginRequest($request)) { - return $response; + return $handler->handle($request); } $isApiRequest = static::isApiRequest($request); $isNormalRequest = !$request->isXhr() && !$isApiRequest; if ($isNormalRequest) { // TODO: globalConfig['client_url'] . '/login' à la place de '/login' ? - return $response->withRedirect('/login'); - } - - $errorResponse = buildResponse(401); - if (!$isApiRequest) { - return $errorResponse; + return (new Response(302))->withHeader('Location', '/login'); } - $data = [ - 'success' => false, - 'error' => [ - 'message' => 'Unauthorized', - 'details' => null, - ], - ]; - return $errorResponse->withJson($data, ERROR_UNAUTHORIZED); + throw new HttpUnauthorizedException($request); } protected static function requestMatch(Request $request, $paths): bool diff --git a/server/src/App/Services/Auth/AuthenticatorInterface.php b/server/src/App/Services/Auth/AuthenticatorInterface.php index e55bca4a8..e687994fa 100644 --- a/server/src/App/Services/Auth/AuthenticatorInterface.php +++ b/server/src/App/Services/Auth/AuthenticatorInterface.php @@ -4,7 +4,7 @@ namespace Robert2\API\Services\Auth; use Robert2\API\Models\User; -use Slim\Http\Request; +use Slim\Http\ServerRequest as Request; interface AuthenticatorInterface { diff --git a/server/src/App/Services/Auth/JWT.php b/server/src/App/Services/Auth/JWT.php index 417a1a9e6..d48375c2e 100644 --- a/server/src/App/Services/Auth/JWT.php +++ b/server/src/App/Services/Auth/JWT.php @@ -7,7 +7,7 @@ use Robert2\API\Config\Config; use Robert2\API\Services\Auth; use Robert2\API\Models\User; -use Slim\Http\Request; +use Slim\Http\ServerRequest as Request; final class JWT implements AuthenticatorInterface { @@ -50,7 +50,7 @@ private function fetchToken(Request $request): string { // - Tente de récupérer le token dans les headers HTTP. $headerName = $this->settings['httpAuthHeader']; - $header = $request->getHeaderLine(sprintf('HTTP_%s', strtoupper(snake_case($headerName)))); + $header = $request->getHeaderLine(sprintf('HTTP_%s', strtoupper(\snakeCase($headerName)))); if (!empty($header)) { if (preg_match('/Bearer\s+(.*)$/i', $header, $matches)) { return $matches[1]; diff --git a/server/src/App/I18n/I18n.php b/server/src/App/Services/I18n.php similarity index 54% rename from server/src/App/I18n/I18n.php rename to server/src/App/Services/I18n.php index f851f03df..5f0ecc7bc 100644 --- a/server/src/App/I18n/I18n.php +++ b/server/src/App/Services/I18n.php @@ -1,43 +1,43 @@ "en", - "available" => ["en", "fr"], + 'default' => 'en', + 'available' => ['en', 'fr'], ]; public function __construct(?string $lang = null) { - $fileLoader = new PhpFilesLoader(__DIR__ . '/locales'); - - $this->translation = new Translate($fileLoader, $this->config); + $translationsPath = new PhpFilesLoader(LOCALES_FOLDER); + $this->translator = new Translate($translationsPath, $this->config); // - Overwrite Accept-Language header if needed if ($lang && in_array($lang, $this->config['available'])) { - $this->translation->setLanguage($lang); + $this->translator->setLanguage($lang); } } public function translate(string $message, $args = null): string { - return $this->translation->t($message, $args); + return $this->translator->t($message, $args); } public function plural(string $message, $num, $args = null): string { - return $this->translation->plural($message, $num, $args); + return $this->translator->plural($message, $num, $args); } public function getCurrentLocale(): string { - return $this->translation->getLanguage(); + return $this->translator->getLanguage(); } } diff --git a/server/src/App/Services/Logger.php b/server/src/App/Services/Logger.php new file mode 100644 index 000000000..3be1e7d05 --- /dev/null +++ b/server/src/App/Services/Logger.php @@ -0,0 +1,92 @@ + null, + 'max_files' => 5, + 'level' => \Monolog\Logger::DEBUG, + ]; + + /** + * Constructeur. + * + * @param array $settings La configuration du logger. + */ + public function __construct($settings = []) + { + $this->settings = array_replace($this->settings, $settings); + + if (null !== $this->settings['timezone']) { + if (is_string($this->settings['timezone'])) { + $this->settings['timezone'] = new \DateTimeZone($this->settings['timezone']); + } + } + + $levels = array_keys(\Monolog\Logger::getLevels()); + if (!in_array($this->settings['level'], $levels)) { + $this->settings['level'] = \Monolog\Logger::DEBUG; + } + + $this->globalLogger = $this->createLogger('app'); + } + + // ------------------------------------------------------ + // - + // - Public methods + // - + // ------------------------------------------------------ + + /** + * Permet de créer un logger. + * + * @param string $name Le nom (unique) du logger. + */ + public function createLogger(string $name) + { + $logger = new \Monolog\Logger($name); + + // - Timezone + if (!empty($this->settings['timezone'])) { + $logger->setTimezone($this->settings['timezone']); + } + + // - Handler + $path = VAR_FOLDER . DS . 'logs' . DS . \slugify($name) . '.log'; + $handler = new Handler\RotatingFileHandler( + $path, + $this->settings['max_files'], + $this->settings['level'] + ); + $handler->setFormatter(new LineFormatter(null, null, true, true)); + $logger->pushHandler($handler); + + return $logger; + } + + /** + * Ajoute un message de log. + * + * @param integer $level Le niveau de log. + * @param string $message Le message à logger. + * @param array $context Le contexte du log (si utile). + * + * @return void + */ + public function log($level, $message, array $context = []) + { + $this->globalLogger->log($level, $message, $context); + } +} diff --git a/server/src/App/Services/View.php b/server/src/App/Services/View.php new file mode 100644 index 000000000..047bc2d47 --- /dev/null +++ b/server/src/App/Services/View.php @@ -0,0 +1,108 @@ +view = Twig::create(VIEWS_FOLDER, [ + 'debug' => isTestMode() || Config::getEnv() !== 'production', + 'cache' => $cachePath, + ]); + + // + // - Global variables + // + + $this->view->getEnvironment()->addGlobal('env', Config::getEnv()); + + // + // - Extensions + // + + $this->view->addExtension(new IntlExtension()); + $this->view->addExtension(new StringExtension()); + $this->view->addExtension(new DebugExtension()); + + // + // - Functions + // + + $translate = new TwigFunction('translate', [$i18n, 'translate']); + $plural = new TwigFunction('plural', [$i18n, 'plural']); + $version = new TwigFunction('version', $this->getVersion()); + $clientAssetFunction = new TwigFunction('client_asset', $this->getClientAsset()); + + $this->view->getEnvironment()->addFunction($translate); + $this->view->getEnvironment()->addFunction($plural); + $this->view->getEnvironment()->addFunction($version); + $this->view->getEnvironment()->addFunction($clientAssetFunction); + } + + // ------------------------------------------------------ + // - + // - Public methods + // - + // ------------------------------------------------------ + + public function fetch(string $template, array $data = []): string + { + return $this->view->fetch($template, $data); + } + + public function render(Response $response, string $template, array $data = []): Response + { + $response->getBody()->write($this->fetch($template, $data)); + return $response; + } + + // —————————————————————————————————————————————————————— + // — + // — Custom twig functions methods + // — + // —————————————————————————————————————————————————————— + + private function getVersion(): callable + { + return function (): string { + return Config::getVersion(); + }; + } + + private function getClientAsset(): callable + { + $host = Config::getEnv() === 'development' + ? 'http://localhost:8081' + : ''; + + $basePath = sprintf('%s/webclient', rtrim($host, '/')); + return function ($path) use ($basePath) { + return vsprintf('%s/%s?v=%s', [ + $basePath, + ltrim($path, '/'), + Config::getVersion(), + ]); + }; + } +} diff --git a/server/src/App/Validation/Validator.php b/server/src/App/Validation/Validator.php index 411226fde..08ef0c0a1 100644 --- a/server/src/App/Validation/Validator.php +++ b/server/src/App/Validation/Validator.php @@ -2,10 +2,10 @@ namespace Robert2\API\Validation; -use Robert2\API\I18n\I18n; +use Respect\Validation\Exceptions\NestedValidationException; use Respect\Validation\Factory; use Respect\Validation\Validator as CoreValidator; -use Respect\Validation\Exceptions\NestedValidationException; +use Robert2\API\Services\I18n; class Validator extends CoreValidator { @@ -14,6 +14,7 @@ public function assert($input) try { parent::assert($input); } catch (NestedValidationException $e) { + // TODO: Laisser le front gérer le traduction des messages de validation. $e->setParam('translator', [new I18n, 'translate']); throw $e; } diff --git a/server/src/database/phinx.php b/server/src/database/phinx.php index 3db6ac6f5..1c892bcf0 100644 --- a/server/src/database/phinx.php +++ b/server/src/database/phinx.php @@ -1,11 +1,14 @@ [ diff --git a/server/src/install/Install.php b/server/src/install/Install.php index 816630733..65d3b7baf 100644 --- a/server/src/install/Install.php +++ b/server/src/install/Install.php @@ -7,7 +7,7 @@ class Install { - // See in HomeController for steps execution + // See in SetupController for steps execution const INSTALL_STEPS = [ 'welcome', 'coreSettings', diff --git a/server/src/App/I18n/locales/en/messages.php b/server/src/locales/en/messages.php similarity index 99% rename from server/src/App/I18n/locales/en/messages.php rename to server/src/locales/en/messages.php index 35822b7f7..68668be4d 100644 --- a/server/src/App/I18n/locales/en/messages.php +++ b/server/src/locales/en/messages.php @@ -2698,4 +2698,7 @@ "label" => "Label", + + "enable-javascript-message" + => "Please enable JavaScript to use this application.", ]; diff --git a/server/src/App/I18n/locales/fr/messages.php b/server/src/locales/fr/messages.php similarity index 99% rename from server/src/App/I18n/locales/fr/messages.php rename to server/src/locales/fr/messages.php index 7dc62d2d6..294dbd2df 100644 --- a/server/src/App/I18n/locales/fr/messages.php +++ b/server/src/locales/fr/messages.php @@ -2700,4 +2700,7 @@ "label" => "Étiquette", + + "enable-javascript-message" + => "Veuillez activer JavaScript pour utiliser l'application.", ]; diff --git a/server/src/public/index.php b/server/src/public/index.php index 54031758f..fb18f0c4a 100644 --- a/server/src/public/index.php +++ b/server/src/public/index.php @@ -1,4 +1,6 @@ safeLoad(); -// - Init App -$app = (new Robert2\API\App())->configureAndGet(); +// - Let's go ! +$app = new App(); $app->add(new \Slim\HttpCache\Cache('private', 0)); - $app->run(); diff --git a/server/src/views/install/en/welcome.twig b/server/src/views/install/en/welcome.twig index 3ccdb5736..6aa7c3b12 100644 --- a/server/src/views/install/en/welcome.twig +++ b/server/src/views/install/en/welcome.twig @@ -9,7 +9,7 @@ But first, let's check if the minimal requirements are met on your server.

- Robert2 API is compatible with PHP version 7.1.0 and earlier. + Robert2 API is compatible with PHP version 7.3.0 and earlier.
{% if phpVersionOK %}
OK, PHP version: {{phpVersion}}
diff --git a/server/src/views/install/fr/welcome.twig b/server/src/views/install/fr/welcome.twig index 6560732eb..063af4513 100644 --- a/server/src/views/install/fr/welcome.twig +++ b/server/src/views/install/fr/welcome.twig @@ -9,7 +9,7 @@ Mais avant, vérifions si votre serveur dispose du minimum requis pour que Robert2 fonctionne.

- L'API de Robert2 est compatible avec la version de PHP 7.1.0 et plus. + L'API de Robert2 est compatible avec la version de PHP 7.3.0 et plus.
{% if phpVersionOK %}
OK, version de PHP utilisée : {{phpVersion}}
diff --git a/server/src/views/pdf/bill-default.twig b/server/src/views/pdf/bill-default.twig index b28ed5cdd..d902f9d4f 100644 --- a/server/src/views/pdf/bill-default.twig +++ b/server/src/views/pdf/bill-default.twig @@ -1,4 +1,4 @@ -{% extends "base.twig" %} +{% extends "pdf/base.twig" %} {% block title %}{{ translate("Bill") }} n°{{ number }}{% endblock %} diff --git a/server/src/views/pdf/estimate-default.twig b/server/src/views/pdf/estimate-default.twig index e24a3f72b..13ee68554 100644 --- a/server/src/views/pdf/estimate-default.twig +++ b/server/src/views/pdf/estimate-default.twig @@ -1,4 +1,4 @@ -{% extends "base.twig" %} +{% extends "pdf/base.twig" %} {% block title %}{{ translate("Estimate") }}{% endblock %} diff --git a/server/src/views/pdf/event-summary-default.twig b/server/src/views/pdf/event-summary-default.twig index de2008e99..6f1efa072 100644 --- a/server/src/views/pdf/event-summary-default.twig +++ b/server/src/views/pdf/event-summary-default.twig @@ -1,4 +1,4 @@ -{% extends "base.twig" %} +{% extends "pdf/base.twig" %} {% block title %}{{ translate("releaseSheet") }}{% endblock %} diff --git a/server/src/views/webclient.twig b/server/src/views/webclient.twig index f4afc7cfd..28f217ab8 100644 --- a/server/src/views/webclient.twig +++ b/server/src/views/webclient.twig @@ -37,13 +37,13 @@ - +
{{ include('blocks/loading.twig') }}
diff --git a/server/tests/ApiTestCase.php b/server/tests/ApiTestCase.php index 74e8528f3..90e3da3f2 100644 --- a/server/tests/ApiTestCase.php +++ b/server/tests/ApiTestCase.php @@ -3,18 +3,30 @@ namespace Robert2\Tests; -use There4\Slim\Test\WebTestCase; -use Robert2\API; +use Robert2\API\App; +use PHPUnit\Framework\TestCase; -class ApiTestCase extends WebTestCase +class ApiTestCase extends TestCase { - use SettingsTrait; + use SettingsTrait { + setUp as baseSetUp; + } + + /** @var App */ + protected $app; + + /** @var ApiTestClient */ + protected $client; - public function getSlimInstance(): \Slim\App + protected function setUp(): void { - $app = (new API\App())->configureAndGet(); - $app->add(new \Slim\HttpCache\Cache('private', 0)); - return $app; + $this->baseSetUp(); + + $this->app = new App(); + $this->client = new ApiTestClient($this->app); + + // - Test specific configuration + $this->app->add(new \Slim\HttpCache\Cache('private', 0)); } // —————————————————————————————————————————————————————— @@ -29,10 +41,10 @@ public function assertStatusCode(int $expectedCode): void if ($expectedCode !== 500 && $actualCode === 500) { $response = $this->_getResponseAsArray(); - $message = sprintf( + $message = sprintf( "%s, in %s\n", $response['error']['message'], - $response['error']['file'] + $response['error']['debug']['file'] ); throw new \Exception($message, (int)$response['error']['code']); } @@ -52,9 +64,10 @@ public function assertErrorMessage(string $message): void $this->assertEquals($message, $result['error']['message']); } - public function assertNotFoundErrorMessage(): void + public function assertNotFound(): void { - $this->assertErrorMessage("The required resource was not found."); + $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertErrorMessage("Not found."); } public function assertValidationErrorMessage(): void diff --git a/server/tests/ApiTestClient.php b/server/tests/ApiTestClient.php new file mode 100644 index 000000000..0df5f8520 --- /dev/null +++ b/server/tests/ApiTestClient.php @@ -0,0 +1,72 @@ +app = $app; + } + + public function __call($method, $arguments) + { + $methods = [ 'get', 'post', 'patch', 'put', 'delete', 'head', 'options']; + if (!in_array($method, $methods, true)) { + throw new \BadMethodCallException(sprintf("%s is not supported", strtoupper($method))); + } + return call_user_func_array([$this, 'request'], array_merge([$method], $arguments)); + } + + // ------------------------------------------------------ + // - + // - Internal methods + // - + // ------------------------------------------------------ + + protected function request(string $method, string $uri, ?array $data = null): Body + { + // - Request + $method = strtoupper($method); + $request = new ServerRequest((new ServerRequestFactory())->createServerRequest($method, $uri)); + if ($data !== null) { + if ($method === 'GET') { + $request = $request->withQueryParams($data); + } else { + $request = $request->withParsedBody($data); + $request = $request->withHeader('Content-Type', 'application/json'); + } + } + $this->request = $request; + + // - Response + $this->response = $this->app->handle($this->request); + return $this->response->getBody(); + } +} diff --git a/server/tests/SettingsTrait.php b/server/tests/SettingsTrait.php index 9b4292edf..e4f4bf581 100644 --- a/server/tests/SettingsTrait.php +++ b/server/tests/SettingsTrait.php @@ -10,7 +10,7 @@ trait SettingsTrait protected $settings = []; protected $Fixtures = null; - public function setup(): void + protected function setUp(): void { parent::setUp(); diff --git a/server/tests/endpoints/BillsTest.php b/server/tests/endpoints/BillsTest.php index 010dde523..b1619a71c 100644 --- a/server/tests/endpoints/BillsTest.php +++ b/server/tests/endpoints/BillsTest.php @@ -179,9 +179,8 @@ public function testDownloadPdf() $this->assertStatusCode(404); // - Download bill n°1 PDF file - $this->client->get('/bills/1/pdf'); + $responseStream = $this->client->get('/bills/1/pdf'); $this->assertStatusCode(200); - $responseStream = $this->client->response->getBody(); $this->assertTrue($responseStream->isReadable()); } } diff --git a/server/tests/endpoints/CategoriesTest.php b/server/tests/endpoints/CategoriesTest.php index a1e44b3f9..c237c3fc7 100644 --- a/server/tests/endpoints/CategoriesTest.php +++ b/server/tests/endpoints/CategoriesTest.php @@ -79,8 +79,7 @@ public function testGetCategories() public function testGetCategorieNotFound() { $this->client->get('/api/categories/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetCategory() @@ -132,7 +131,7 @@ public function testUpdateCategoryNoData() public function testUpdateCategoryNotFound() { $this->client->put('/api/categories/999', ['name' => '__inexistant__']); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testUpdateCategory() @@ -177,7 +176,7 @@ public function testDeleteAndDestroyCategory() public function testRestoreCategoryNotFound() { $this->client->put('/api/categories/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreCategory() diff --git a/server/tests/endpoints/CompaniesTest.php b/server/tests/endpoints/CompaniesTest.php index b60b64441..57158577d 100644 --- a/server/tests/endpoints/CompaniesTest.php +++ b/server/tests/endpoints/CompaniesTest.php @@ -65,8 +65,7 @@ public function testGetCompanies() public function testGetCompanyNotFound() { $this->client->get('/api/companies/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetCompany() @@ -137,8 +136,7 @@ public function testGetCompanySearchByLegalName() public function testGetPersonsNotFound() { $this->client->get('/api/companies/999/persons'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetPersons() @@ -259,7 +257,7 @@ public function testDeleteAndDestroyCompany() public function testRestoreCompanyNotFound() { $this->client->put('/api/companies/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreCompany() diff --git a/server/tests/endpoints/CountriesTest.php b/server/tests/endpoints/CountriesTest.php index 7daba2f8f..630a8d937 100644 --- a/server/tests/endpoints/CountriesTest.php +++ b/server/tests/endpoints/CountriesTest.php @@ -40,8 +40,7 @@ public function testGetCountries() public function testGetCountryNotFound() { $this->client->get('/api/countries/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetCountry() diff --git a/server/tests/endpoints/DocumentsTest.php b/server/tests/endpoints/DocumentsTest.php index 2d044c510..dbeecc1fe 100644 --- a/server/tests/endpoints/DocumentsTest.php +++ b/server/tests/endpoints/DocumentsTest.php @@ -12,9 +12,8 @@ public function testGetDocumentFile() $this->assertStatusCode(404); // - Téléchargement du document #1 - $this->client->get('/documents/1/download'); + $responseStream = $this->client->get('/documents/1/download'); $this->assertStatusCode(200); - $responseStream = $this->client->response->getBody(); $this->assertTrue($responseStream->isReadable()); } diff --git a/server/tests/endpoints/ErrorsTest.php b/server/tests/endpoints/ErrorsTest.php index c2d6b8bb4..22643b8ae 100644 --- a/server/tests/endpoints/ErrorsTest.php +++ b/server/tests/endpoints/ErrorsTest.php @@ -6,9 +6,7 @@ final class ErrorsTest extends ApiTestCase public function testRouteNotFound() { $this->client->get('/api/inexistant-resource'); - - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertErrorMessage("The required resource was not found."); + $this->assertNotFound(); } public function testMethodNotAllowed() @@ -16,6 +14,6 @@ public function testMethodNotAllowed() $this->client->put('/not-a-get-route'); $this->assertStatusCode(ERROR_NOT_ALLOWED); - $this->assertErrorMessage("Method not allowed"); + $this->assertErrorMessage("Method not allowed. Must be one of: GET"); } } diff --git a/server/tests/endpoints/EstimatesTest.php b/server/tests/endpoints/EstimatesTest.php index b50005d15..7d2bc1411 100644 --- a/server/tests/endpoints/EstimatesTest.php +++ b/server/tests/endpoints/EstimatesTest.php @@ -174,9 +174,8 @@ public function testDownloadPdf() $this->assertStatusCode(404); // - Download bill n°1 PDF file - $this->client->get('/estimates/1/pdf'); + $responseStream = $this->client->get('/estimates/1/pdf'); $this->assertStatusCode(200); - $responseStream = $this->client->response->getBody(); $this->assertTrue($responseStream->isReadable()); } } diff --git a/server/tests/endpoints/EventsTest.php b/server/tests/endpoints/EventsTest.php index 1b5454216..574a8badd 100644 --- a/server/tests/endpoints/EventsTest.php +++ b/server/tests/endpoints/EventsTest.php @@ -128,8 +128,7 @@ public function testGetEvents() public function testGetEventNotFound() { $this->client->get('/api/events/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetOneEvent() @@ -445,7 +444,7 @@ public function testUpdateEventNoData() public function testUpdateEventNotFound() { $this->client->put('/api/events/999', ['name' => '__inexistant__']); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testUpdateEvent() @@ -763,7 +762,7 @@ public function testDeleteAndDestroyEvent() public function testRestoreEventNotFound() { $this->client->put('/api/events/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreEvent() @@ -837,8 +836,7 @@ public function testGetMissingMaterials() // - Event not found $this->client->get('/api/events/999/missing-materials'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testDownloadPdf() @@ -848,9 +846,8 @@ public function testDownloadPdf() $this->assertStatusCode(404); // - Download event n°1 PDF file - $this->client->get('/events/1/pdf'); + $responseStream = $this->client->get('/events/1/pdf'); $this->assertStatusCode(200); - $responseStream = $this->client->response->getBody(); $this->assertTrue($responseStream->isReadable()); } } diff --git a/server/tests/endpoints/MainEntryPointTest.php b/server/tests/endpoints/MainEntryPointTest.php index 06d4044b8..630eef2ab 100644 --- a/server/tests/endpoints/MainEntryPointTest.php +++ b/server/tests/endpoints/MainEntryPointTest.php @@ -5,10 +5,8 @@ final class MainEntryPointTest extends ApiTestCase { public function testMainEntryPoint() { - $this->client->get('/'); + $response = (string)$this->client->get('/'); $this->assertStatusCode(200); - - $response = (string)$this->client->response->getBody(); $this->assertNotEmpty($response); $expectedFirstLine = ''; diff --git a/server/tests/endpoints/MaterialsTest.php b/server/tests/endpoints/MaterialsTest.php index 112180bab..e42027684 100644 --- a/server/tests/endpoints/MaterialsTest.php +++ b/server/tests/endpoints/MaterialsTest.php @@ -312,8 +312,7 @@ public function testGetMaterialsSearchByReference() public function testGetMaterialNotFound() { $this->client->get('/api/materials/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetMaterialsWhileEvent() @@ -628,8 +627,7 @@ public function testGetMaterial() public function testGetTagsNotFound() { $this->client->get('/api/materials/999/tags'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetTags() @@ -941,7 +939,7 @@ public function testDeleteAndDestroyMaterial() public function testRestoreMaterialNotFound() { $this->client->put('/api/materials/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreMaterial() diff --git a/server/tests/endpoints/ParksTest.php b/server/tests/endpoints/ParksTest.php index 93ff9315c..7ba3326a8 100644 --- a/server/tests/endpoints/ParksTest.php +++ b/server/tests/endpoints/ParksTest.php @@ -69,8 +69,7 @@ public function testGetParks() public function testGetParkNotFound() { $this->client->get('/api/parks/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetPark() diff --git a/server/tests/endpoints/PersonsTest.php b/server/tests/endpoints/PersonsTest.php index e261a868c..952da17ad 100644 --- a/server/tests/endpoints/PersonsTest.php +++ b/server/tests/endpoints/PersonsTest.php @@ -207,8 +207,7 @@ public function testGetPersonsWithLimit() public function testGetPersonNotFound() { $this->client->get('/api/persons/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetPerson() @@ -262,8 +261,7 @@ public function testGetPerson() public function testGetTagsNotFound() { $this->client->get('/api/persons/999/tags'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetTags() @@ -491,7 +489,7 @@ public function testDeleteAndDestroyEvent() public function testRestorePersonNotFound() { $this->client->put('/api/persons/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreEvent() diff --git a/server/tests/endpoints/SubCategoriesTest.php b/server/tests/endpoints/SubCategoriesTest.php index 4963f372e..964a95578 100644 --- a/server/tests/endpoints/SubCategoriesTest.php +++ b/server/tests/endpoints/SubCategoriesTest.php @@ -43,7 +43,7 @@ public function testUpdateSubCategoryNoData() public function testUpdateSubCategoryNotFound() { $this->client->put('/api/subcategories/999', ['something' => '__inexistant__']); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testUpdateSubCategory() @@ -77,7 +77,7 @@ public function testDeleteAndDestroySubCategory() public function testRestoreSubCategoryNotFound() { $this->client->put('/api/subcategories/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreSubCategory() diff --git a/server/tests/endpoints/TagsTest.php b/server/tests/endpoints/TagsTest.php index aea108719..65c93004a 100644 --- a/server/tests/endpoints/TagsTest.php +++ b/server/tests/endpoints/TagsTest.php @@ -94,8 +94,7 @@ public function testCreateTag() public function testGetPersonsNotFound() { $this->client->get('/api/tags/999/persons'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetPersons() @@ -108,8 +107,7 @@ public function testGetPersons() public function testGetMaterialsNotFound() { $this->client->get('/api/tags/999/materials'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetMaterials() @@ -136,7 +134,7 @@ public function testDeleteAndDestroyTag() public function testRestoreTagNotFound() { $this->client->put('/api/tags/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreTag() diff --git a/server/tests/endpoints/TokenTest.php b/server/tests/endpoints/TokenTest.php index d25152a78..1d7cedf70 100644 --- a/server/tests/endpoints/TokenTest.php +++ b/server/tests/endpoints/TokenTest.php @@ -40,8 +40,7 @@ public function testTokenInexistantUser() 'identifier' => 'nobody@test.org', 'password' => 'testing', ]); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testTokenWrongPassword() @@ -50,8 +49,7 @@ public function testTokenWrongPassword() 'identifier' => 'tester@robertmanager.net', 'password' => 'wrongPassword', ]); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testTokenAuthOK() diff --git a/server/tests/endpoints/UsersTest.php b/server/tests/endpoints/UsersTest.php index 1fc5710a9..2e27eb447 100644 --- a/server/tests/endpoints/UsersTest.php +++ b/server/tests/endpoints/UsersTest.php @@ -124,8 +124,7 @@ public function testGetUsers() public function testGetUserNotFound() { $this->client->get('/api/users/9999'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetUser() @@ -189,15 +188,13 @@ public function testGetUser() public function testGetUserSettingsNotFound() { $this->client->get('/api/users/9999/settings'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetUserSettingsNoSettingFound() { $this->client->get('/api/users/3/settings'); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testGetUserSettings() @@ -224,8 +221,7 @@ public function testSetUserSettingsNoData() public function testSetUserSettingsNoUser() { $this->client->put('/api/users/999/settings', ['language' => 'FR']); - $this->assertStatusCode(ERROR_NOT_FOUND); - $this->assertNotFoundErrorMessage(); + $this->assertNotFound(); } public function testSetUserSettings() @@ -355,7 +351,7 @@ public function testUpdateCategoryNoData() public function testUpdateUserNotFound() { $this->client->put('/api/users/999', ['pseudo' => '__inexistant__']); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testUpdateUser() @@ -392,7 +388,7 @@ public function testDeleteAndDestroyUser() public function testRestoreUserNotFound() { $this->client->put('/api/users/restore/999'); - $this->assertStatusCode(ERROR_NOT_FOUND); + $this->assertNotFound(); } public function testRestoreUser() diff --git a/server/tests/libs/domain/EventBillTest.php b/server/tests/libs/domain/EventBillTest.php index 38724d953..bc7e956b9 100644 --- a/server/tests/libs/domain/EventBillTest.php +++ b/server/tests/libs/domain/EventBillTest.php @@ -60,9 +60,8 @@ public function setUp(): void public function testEmptyEvent() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(0); $this->expectExceptionMessage("Cannot create EventBill value-object without complete event's data."); - $empty = new EventBill($this->_date, [], $this->_number); + new EventBill($this->_date, [], $this->_number); } public function testNoBeneficiary() @@ -78,9 +77,8 @@ public function testNoBeneficiary() ], ]; $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(0); $this->expectExceptionMessage("Cannot create EventBill value-object without complete event's data."); - $noBeneficiaries = new EventBill($this->_date, $event, $this->_number); + new EventBill($this->_date, $event, $this->_number); } public function testNoMaterials() @@ -96,9 +94,8 @@ public function testNoMaterials() 'materials' => [], ]; $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(0); $this->expectExceptionMessage("Cannot create EventBill value-object without complete event's data."); - $noMaterials = new EventBill($this->_date, $event, $this->_number); + new EventBill($this->_date, $event, $this->_number); } // ------------------------------------------------------ diff --git a/server/tests/libs/domain/EventEstimateTest.php b/server/tests/libs/domain/EventEstimateTest.php index 7d58cc709..13dc290fd 100644 --- a/server/tests/libs/domain/EventEstimateTest.php +++ b/server/tests/libs/domain/EventEstimateTest.php @@ -57,9 +57,8 @@ public function setUp(): void public function testEmptyEvent() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(0); $this->expectExceptionMessage("Cannot create EventEstimate value-object without complete event's data."); - $empty = new EventEstimate($this->_date, []); + new EventEstimate($this->_date, []); } public function testNoBeneficiary() @@ -75,9 +74,8 @@ public function testNoBeneficiary() ], ]; $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(0); $this->expectExceptionMessage("Cannot create EventEstimate value-object without complete event's data."); - $noBeneficiaries = new EventEstimate($this->_date, $event); + new EventEstimate($this->_date, $event); } public function testNoMaterials() @@ -93,9 +91,8 @@ public function testNoMaterials() 'materials' => [], ]; $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionCode(0); $this->expectExceptionMessage("Cannot create EventEstimate value-object without complete event's data."); - $noMaterials = new EventEstimate($this->_date, $event); + new EventEstimate($this->_date, $event); } // ------------------------------------------------------ diff --git a/server/tests/libs/pdf/PdfTest.php b/server/tests/libs/pdf/PdfTest.php index e92cca3de..7b58f1248 100644 --- a/server/tests/libs/pdf/PdfTest.php +++ b/server/tests/libs/pdf/PdfTest.php @@ -47,8 +47,7 @@ public function testCreateFromTemplateNotFoundError(): void { // - Template doesn't exist $this->expectException(\Twig\Error\LoaderError::class); - $this->expectExceptionCode(0); - $this->expectExceptionMessage("Unable to find template \"_inexistant-template_.twig\""); + $this->expectExceptionMessage("Unable to find template \"pdf/_inexistant-template_.twig\""); Pdf::createFromTemplate('_inexistant-template_', [], $this->_pdfResultFile); } diff --git a/server/tests/models/AttributeTest.php b/server/tests/models/AttributeTest.php index 00bbd8f22..3a05a2084 100644 --- a/server/tests/models/AttributeTest.php +++ b/server/tests/models/AttributeTest.php @@ -3,7 +3,7 @@ namespace Robert2\Tests; -use Robert2\API\Models; +use Robert2\API\Models\Attribute; final class AttributeTest extends ModelTestCase { @@ -11,7 +11,7 @@ public function setup(): void { parent::setUp(); - $this->model = new Models\Attribute(); + $this->model = new Attribute(); } public function testTableName(): void @@ -113,7 +113,7 @@ public function testGetAll(): void public function testGetMaterials(): void { - $Event = $this->model::find(4); + $Event = Attribute::find(4); $results = $Event->materials; $expected = [ [ @@ -191,6 +191,6 @@ public function testRemove(): void // - Supprime une caractéristique spéciale $this->model->remove(3); // - Vérifie qu'elle a bien été supprimée - $this->assertEmpty(Models\Attribute::find(3)); + $this->assertEmpty(Attribute::find(3)); } } diff --git a/server/tests/models/BillTest.php b/server/tests/models/BillTest.php index 2778d41a4..188e08a58 100644 --- a/server/tests/models/BillTest.php +++ b/server/tests/models/BillTest.php @@ -3,8 +3,8 @@ namespace Robert2\Tests; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Robert2\API\Models; -use Robert2\API\Errors; final class BillTest extends ModelTestCase { @@ -149,8 +149,7 @@ public function testGetMaterials() public function testCreateFromEventNotFound() { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionMessage("Event not found."); + $this->expectException(ModelNotFoundException::class); $this->model->createFromEvent(999, 1, 25); } diff --git a/server/tests/models/CompanyTest.php b/server/tests/models/CompanyTest.php index b30c6eb96..f52bb35b9 100644 --- a/server/tests/models/CompanyTest.php +++ b/server/tests/models/CompanyTest.php @@ -3,8 +3,9 @@ namespace Robert2\Tests; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Robert2\API\Models; -use Robert2\API\Errors; +use Robert2\API\Errors\ValidationException; final class CompanyTest extends ModelTestCase { @@ -142,21 +143,21 @@ public function testGetCountry(): void public function testCreateCompanyWithoutData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $this->model->edit(null, []); } public function testCreateCompanyBadData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $this->model->edit(null, ['foo' => 'bar']); } public function testCreateCompanyDuplicate(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_DUPLICATE); $this->model->edit(null, ['legal_name' => 'Testing, Inc']); } @@ -248,7 +249,7 @@ public function testAddPersonsWithoutData(): void public function testAddPersonsToInexistant(): void { - $this->expectException(Errors\NotFoundException::class); + $this->expectException(ModelNotFoundException::class); $persons = [['first_name' => 'Laurent', 'last_name' => 'Bigboss']]; $this->model->addPersons(999, $persons); } diff --git a/server/tests/models/DocumentTest.php b/server/tests/models/DocumentTest.php index 144e2c1b1..4fb5a6819 100644 --- a/server/tests/models/DocumentTest.php +++ b/server/tests/models/DocumentTest.php @@ -3,8 +3,8 @@ namespace Robert2\Tests; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Robert2\API\Models; -use Robert2\API\Errors; final class DocumentTest extends ModelTestCase { @@ -68,7 +68,7 @@ public function testGetFilePathAttribute() public function testRemoveNotExists() { - $this->expectException(Errors\NotFoundException::class); + $this->expectException(ModelNotFoundException::class); $this->model->remove(9999); } diff --git a/server/tests/models/EstimateTest.php b/server/tests/models/EstimateTest.php index 94fbfe5aa..96c163d6a 100644 --- a/server/tests/models/EstimateTest.php +++ b/server/tests/models/EstimateTest.php @@ -3,8 +3,8 @@ namespace Robert2\Tests; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Robert2\API\Models; -use Robert2\API\Errors; final class EstimateTest extends ModelTestCase { @@ -148,8 +148,7 @@ public function testGetMaterials() public function testCreateFromEventNotFound() { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionMessage("Event not found."); + $this->expectException(ModelNotFoundException::class); $this->model->createFromEvent(999, 1, 25); } diff --git a/server/tests/models/EventTest.php b/server/tests/models/EventTest.php index f4278f90a..7b55101bd 100644 --- a/server/tests/models/EventTest.php +++ b/server/tests/models/EventTest.php @@ -3,8 +3,8 @@ namespace Robert2\Tests; -use Robert2\API\Models; use Robert2\API\Errors; +use Robert2\API\Models\Event; final class EventTest extends ModelTestCase { @@ -12,7 +12,7 @@ public function setup(): void { parent::setUp(); - $this->model = new Models\Event(); + $this->model = new Event(); } public function testTableName(): void @@ -76,11 +76,11 @@ public function testGetAll(): void public function testGetMissingMaterials(): void { // - No missing materials for event #3 - $result = $this->model->getMissingMaterials(3); + $result = Event::getMissingMaterials(3); $this->assertNull($result); // - Get missing materials of event #1 - $result = $this->model->getMissingMaterials(1); + $result = Event::getMissingMaterials(1); $this->assertNotNull($result); $this->assertCount(1, $result); $this->assertEquals('DBXPA2', $result[0]['reference']); @@ -89,10 +89,10 @@ public function testGetMissingMaterials(): void public function testGetParks(): void { - $result = $this->model->getParks(1); + $result = Event::getParks(1); $this->assertEquals([1], $result); - $result = $this->model->getParks(4); + $result = Event::getParks(4); $this->assertEquals([null, 1], $result); } @@ -298,7 +298,7 @@ public function testValidate(): void $data, ['end_date' => '2020-03-03 23:59:59'] ); - (new Models\Event($testData))->validate(); + (new Event($testData))->validate(); // - Validation fail: end date is after start date $this->expectException(Errors\ValidationException::class); @@ -307,7 +307,7 @@ public function testValidate(): void $data, ['end_date' => '2020-02-20 23:59:59'] ); - (new Models\Event($testData))->validate(); + (new Event($testData))->validate(); } public function testValidateReference(): void @@ -322,14 +322,14 @@ public function testValidateReference(): void foreach (['REF1', null] as $testValue) { $testData = array_merge($data, ['reference' => $testValue]); - (new Models\Event($testData))->validate(); + (new Event($testData))->validate(); } // - Validation fail: Reference is an empty string $this->expectException(Errors\ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $testData = array_merge($data, ['reference' => '']); - (new Models\Event($testData))->validate(); + (new Event($testData))->validate(); } public function testGetPdfContent() diff --git a/server/tests/models/MaterialTest.php b/server/tests/models/MaterialTest.php index 7057d34bd..fd51bbcd6 100644 --- a/server/tests/models/MaterialTest.php +++ b/server/tests/models/MaterialTest.php @@ -3,8 +3,9 @@ namespace Robert2\Tests; -use Robert2\API\Errors; -use Robert2\API\Models; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Robert2\API\Errors\ValidationException; +use Robert2\API\Models\Material; final class MaterialTest extends ModelTestCase { @@ -12,7 +13,7 @@ public function setup(): void { parent::setUp(); - $this->model = new Models\Material(); + $this->model = new Material(); } public function testTableName(): void @@ -72,7 +73,7 @@ public function testRecalcQuantitiesForPeriod(): void // - Calcul des quantités restantes de chaque matériel pour une période sans événement $data = $getData(); - $result = $this->model->recalcQuantitiesForPeriod($data, '2018-12-01', '2018-12-02'); + $result = Material::recalcQuantitiesForPeriod($data, '2018-12-01', '2018-12-02'); $this->assertCount(7, $result); foreach ([4, 2, 30, 2, 32] as $index => $expected) { $this->assertEquals($expected, $result[$index]['remaining_quantity']); @@ -80,7 +81,7 @@ public function testRecalcQuantitiesForPeriod(): void // - Calcul des quantités restantes de chaque matériel pour une période avec trois événements $data = $getData(); - $result = $this->model->recalcQuantitiesForPeriod($data, '2018-12-15', '2018-12-20'); + $result = Material::recalcQuantitiesForPeriod($data, '2018-12-15', '2018-12-20'); $this->assertCount(7, $result); foreach ([0, 0, 20, 1, 20] as $index => $expected) { $this->assertEquals($expected, $result[$index]['remaining_quantity']); @@ -88,7 +89,7 @@ public function testRecalcQuantitiesForPeriod(): void // - Calcul des quantités restantes de chaque matériel pour une période avec un seul événement $data = $getData(); - $result = $this->model->recalcQuantitiesForPeriod($data, '2018-12-19', '2018-12-20'); + $result = Material::recalcQuantitiesForPeriod($data, '2018-12-19', '2018-12-20'); $this->assertCount(7, $result); foreach ([1, 0, 30, 2, 32] as $index => $expected) { $this->assertEquals($expected, $result[$index]['remaining_quantity']); @@ -97,7 +98,7 @@ public function testRecalcQuantitiesForPeriod(): void // - Calcul des quantités restantes de chaque matériel pour une période avec trois événements // - en excluant l'événement n°2 $data = $getData(); - $result = $this->model->recalcQuantitiesForPeriod($data, '2018-12-15', '2018-12-20', 2); + $result = Material::recalcQuantitiesForPeriod($data, '2018-12-15', '2018-12-20', 2); $this->assertCount(7, $result); foreach ([3, 1, 20, 1, 20] as $index => $expected) { $this->assertEquals($expected, $result[$index]['remaining_quantity']); @@ -270,7 +271,7 @@ public function testSetTagsNoData(): void public function testSetTagsNotFound(): void { - $this->expectException(Errors\NotFoundException::class); + $this->expectException(ModelNotFoundException::class); $this->model->setTags(999, ['notFoundTag']); } @@ -293,21 +294,21 @@ public function testSetTags(): void public function testCreateMaterialWithoutData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $this->model->edit(null, []); } public function testCreateMaterialBadData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $this->model->edit(null, ['foo' => 'bar']); } public function testCreateMaterialDuplicate(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_DUPLICATE); $this->model->edit(null, [ 'name' => 'Test duplicate ref. CL3', diff --git a/server/tests/models/PersonTest.php b/server/tests/models/PersonTest.php index 07c50f156..8b8b3c8f4 100644 --- a/server/tests/models/PersonTest.php +++ b/server/tests/models/PersonTest.php @@ -3,8 +3,9 @@ namespace Robert2\Tests; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Robert2\API\Models; -use Robert2\API\Errors; +use Robert2\API\Errors\ValidationException; final class PersonTest extends ModelTestCase { @@ -155,7 +156,7 @@ public function testSetTagsNoData(): void public function testSetTagsNotFound(): void { - $this->expectException(Errors\NotFoundException::class); + $this->expectException(ModelNotFoundException::class); $this->model->setTags(999, ['notFoundTag']); } @@ -178,14 +179,14 @@ public function testSetTags(): void public function testCreatePersonWithoutData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $this->model->edit(null, []); } public function testCreatePersonBadData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); $this->model->edit(null, ['foo' => 'bar']); } diff --git a/server/tests/models/UserSettingTest.php b/server/tests/models/UserSettingTest.php index ab7216560..34ae62ff7 100644 --- a/server/tests/models/UserSettingTest.php +++ b/server/tests/models/UserSettingTest.php @@ -3,8 +3,9 @@ namespace Robert2\Tests; -use Robert2\API\Models; -use Robert2\API\Errors; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Robert2\API\Models\User; +use Robert2\API\Models\UserSetting; final class UserSettingTest extends ModelTestCase { @@ -12,7 +13,7 @@ public function setup(): void { parent::setUp(); - $this->model = new Models\UserSetting(); + $this->model = new UserSetting(); } public function testTableName(): void @@ -27,23 +28,15 @@ public function testGetAll(): void $this->model->getAll(); } - public function testEditNoUserId(): void + public function testEditByUserWithNewUser(): void { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionCode(ERROR_NOT_FOUND); - $this->model->edit(null, []); + $this->expectException(ModelNotFoundException::class); + UserSetting::editByUser(new User(), []); } - public function testEditNotFound(): void + public function testEditByUser(): void { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionCode(ERROR_NOT_FOUND); - $this->model->edit(999, []); - } - - public function testEdit(): void - { - $result = $this->model->edit(1, [ + $result = UserSetting::editByUser(User::find(1), [ 'language' => 'FR', 'auth_token_validity_duration' => 133, ]); diff --git a/server/tests/models/UserTest.php b/server/tests/models/UserTest.php index 678a8abf5..e4a2e9edf 100644 --- a/server/tests/models/UserTest.php +++ b/server/tests/models/UserTest.php @@ -3,8 +3,10 @@ namespace Robert2\Tests; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Robert2\API\Errors\ValidationException; use Robert2\API\Models; -use Robert2\API\Errors; +use Robert2\API\Models\User; final class UserTest extends ModelTestCase { @@ -12,7 +14,7 @@ public function setup(): void { parent::setUp(); - $this->model = new Models\User(); + $this->model = new User(); } public function testTableName(): void @@ -122,14 +124,13 @@ public function testGetAll(): void $this->assertEquals($expected, $result); } - public function testgetLoginNotFound(): void + public function testFromLoginNotFound(): void { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionCode(ERROR_NOT_FOUND); - $this->model->getLogin('foo', 'bar'); + $this->expectException(ModelNotFoundException::class); + User::fromLogin('foo', 'bar'); } - public function testGetLogin(): void + public function testFromLogin(): void { $expectedUserData = array_merge($this->expectedDataUser1, [ 'cas_identifier' => null, @@ -144,32 +145,31 @@ public function testGetLogin(): void ]); // - Retourne l'utilisateur n°1 et sa personne associée en utilisant l'e-mail - $result = $this->model->getLogin('tester@robertmanager.net', 'testing-pw')->toArray(); + $result = User::fromLogin('tester@robertmanager.net', 'testing-pw')->toArray(); $this->assertEquals($expectedUserData, $result); // - Retourne l'utilisateur n°1 et sa personne associée en utilisant le pseudo - $result = $this->model->getLogin('test1', 'testing-pw')->toArray(); + $result = User::fromLogin('test1', 'testing-pw')->toArray(); $this->assertEquals($expectedUserData, $result); } public function testCreateWithoutData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); Models\User::new([]); } public function testCreateBadData(): void { - $this->expectException(Errors\ValidationException::class); + $this->expectException(ValidationException::class); $this->expectExceptionCode(ERROR_VALIDATION); Models\User::new(['foo' => 'bar']); } public function testUpdateNotFound(): void { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionCode(ERROR_NOT_FOUND); + $this->expectException(ModelNotFoundException::class); $this->model->edit(999, []); } @@ -251,8 +251,7 @@ public function testUpdate(): void public function testRemoveNotFound(): void { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionCode(ERROR_NOT_FOUND); + $this->expectException(ModelNotFoundException::class); $this->model->remove(999); } @@ -291,37 +290,6 @@ public function testGetSettings(): void ], $result); } - public function testSetSettingsNotFound(): void - { - $this->expectException(Errors\NotFoundException::class); - $this->expectExceptionCode(ERROR_NOT_FOUND); - $this->model->setSettings(999, ['language' => 'FR']); - } - - public function testSetSettingsBadData(): void - { - $this->expectException(Errors\ValidationException::class); - $this->expectExceptionCode(ERROR_VALIDATION); - $this->model->setSettings(1, ['language' => '__invalid__']); - } - - public function testSetSettings(): void - { - $result = $this->model->setSettings(1, [ - 'language' => 'FR', - 'auth_token_validity_duration' => 33, - ]); - unset($result['created_at']); - unset($result['updated_at']); - - $this->assertEquals([ - 'id' => 1, - 'user_id' => 1, - 'language' => 'FR', - 'auth_token_validity_duration' => 33, - ], $result); - } - public function testGetEvents(): void { $User = $this->model::find(1); diff --git a/server/tests/other/FunctionsTest.php b/server/tests/other/FunctionsTest.php index 76d6461aa..9b72d6367 100644 --- a/server/tests/other/FunctionsTest.php +++ b/server/tests/other/FunctionsTest.php @@ -4,7 +4,7 @@ namespace Robert2\Tests; use PHPUnit\Framework\TestCase; -use Slim\Http\UploadedFile; +use Slim\Psr7\UploadedFile; final class FunctionsTest extends TestCase { @@ -24,6 +24,14 @@ public function testSnakeToCamelCase(): void $this->assertEquals('SecondTest', snakeToCamelCase('second_test', true)); } + public function testSnakeCase(): void + { + $this->assertEquals('un_test', snakeCase("un_test")); + $this->assertEquals('un_test', snakeCase("Un test")); + $this->assertEquals('un_test', snakeCase("UnTest")); + $this->assertEquals('un_test', snakeCase("unTest")); + } + public function testSlugify(): void { $this->assertEquals('un_test', slugify("un test")); diff --git a/server/tests/other/I18nTest.php b/server/tests/other/I18nTest.php index 52b7dd2b2..640250d07 100644 --- a/server/tests/other/I18nTest.php +++ b/server/tests/other/I18nTest.php @@ -3,28 +3,28 @@ namespace Robert2\Tests; -use Robert2\API\I18n\I18n; +use Robert2\API\Services\I18n; final class I18nTest extends ModelTestCase { public function testTranslate() { - $i18n = new I18n(); + $i18n = new I18n(); $result = $i18n->translate("{{name}} must be iterable"); $this->assertEquals("{{name}} must be iterable", $result); - $i18n = new I18n('fr'); + $i18n = new I18n('fr'); $result = $i18n->translate("{{name}} must be iterable"); $this->assertEquals("{{name}} doit être itérable", $result); } public function testTranslatePlural() { - $i18n = new I18n(); + $i18n = new I18n(); $result = $i18n->plural("There is %d event in this period", 5); $this->assertEquals("There are 5 events in this period", $result); - $i18n = new I18n('fr'); + $i18n = new I18n('fr'); $result = $i18n->plural("There is %d event in this period", 5); $this->assertEquals("Il y a 5 événements dans cette période", $result); }