Sfoglia il codice sorgente

Merge branch 'master' into beta

beta
Daniel 1 anno fa
parent
commit
47578d3cb9
100 ha cambiato i file con 7928 aggiunte e 443 eliminazioni
  1. +10
    -0
      .ddev/addon-metadata/phpmyadmin/manifest.yaml
  2. +14
    -0
      .ddev/commands/host/phpmyadmin
  3. +279
    -0
      .ddev/config.yaml
  4. +30
    -0
      .ddev/docker-compose.phpmyadmin.yaml
  5. +4
    -0
      .ddev/docker-compose.phpmyadmin_norouter.yaml
  6. +8
    -0
      .ddev/php/xdebug.ini
  7. +3
    -0
      1-deployBetaProBuddy.sh
  8. +55
    -0
      1-deployLiveProBuddy.sh
  9. +0
    -0
      1addActiveStateToProfile.php
  10. +7
    -0
      README.md
  11. +5
    -0
      composer.json
  12. +260
    -0
      composer.lock
  13. +52
    -3
      src/client/app/css/app.css
  14. +1
    -2
      src/client/app/index.php
  15. +4
    -0
      src/client/app/js/app/core/Dict.js
  16. +6
    -0
      src/client/app/js/app/model/Appointment.js
  17. +5
    -0
      src/client/app/js/app/model/Profile.js
  18. +5
    -0
      src/client/app/js/app/state/AppointmentCreate.js
  19. +1
    -0
      src/client/app/js/app/state/AppointmentEdit.js
  20. +1
    -1
      src/client/app/js/app/state/AppointmentEditAttendee.js
  21. +1
    -1
      src/client/app/js/app/state/ConfigurationAttendanceLog.js
  22. +6
    -3
      src/client/app/js/app/state/ConfigurationProfileDelete.js
  23. +3
    -1
      src/client/app/js/app/state/CourseCategories.js
  24. +7
    -0
      src/client/app/js/app/state/GroupMemberManagement.js
  25. +6
    -1
      src/client/app/js/app/state/GroupMemberManagementMember.js
  26. +35
    -32
      src/client/app/js/app/state/Home.js
  27. +2
    -1
      src/client/app/js/app/state/StatsExport.js
  28. +1
    -1
      src/client/app/tmpl/appointment-detail.html
  29. +39
    -15
      src/client/app/tmpl/appointment-form-edit.html
  30. +38
    -14
      src/client/app/tmpl/appointment-form.html
  31. +17
    -0
      src/client/app/tmpl/configuration-profile-delete.html
  32. +277
    -278
      src/client/app/tmpl/group-member-management-member-body.html
  33. +18
    -0
      src/client/app/tmpl/group-member-management.html
  34. +1
    -1
      src/client/app/tmpl/gui-navbar.html
  35. +3
    -2
      src/client/app/tmpl/home-appointment-item.html
  36. +1
    -1
      src/client/app/tmpl/home-modal-appointment-filter.html
  37. +11
    -7
      src/client/app/tmpl/home.html
  38. +2
    -1
      src/client/manager/js/app/components/appointments/appointment-select-table.html
  39. +2
    -0
      src/client/manager/js/app/components/members/member-data-table.html
  40. +24
    -0
      src/client/manager/js/app/model/UserProfile.js
  41. +2
    -1
      src/client/manager/js/app/views/appointments/AppointmentCalendar.js
  42. +2
    -1
      src/client/manager/js/app/views/appointments/AppointmentList.js
  43. +2
    -1
      src/client/manager/js/app/views/contract/ContractCharging.js
  44. +2
    -1
      src/client/manager/js/app/views/contract/ContractCreate.js
  45. +2
    -1
      src/client/manager/js/app/views/corona/CoronaSpecial.js
  46. +2
    -1
      src/client/manager/js/app/views/kkspecial/EmployeeList.js
  47. +4
    -2
      src/client/manager/js/app/views/members/MemberList.js
  48. +2
    -1
      src/client/manager/js/app/views/settings/SettingsAccess.js
  49. +38
    -0
      src/server/patches/1addActiveStateToProfile.php
  50. +2
    -2
      src/server/patches/2addDbTermsConditionsToTeam.php
  51. +2
    -2
      src/server/patches/3addTermsAcceptedToProfile.php
  52. +3
    -3
      src/server/patches/4addDbNewUsersInactive.php
  53. +18
    -0
      src/server/patches/5addIconsToAppointments.php
  54. +0
    -0
      src/server/patches/old/addCategoryIdsJsToAppointments.php
  55. +0
    -0
      src/server/patches/old/addDateTimeRejectToExistingAppointments.php
  56. +0
    -0
      src/server/patches/old/addGeneralCourseCategoryToTeam.php
  57. +0
    -0
      src/server/patches/old/addPublicIdToProfile.php
  58. +0
    -0
      src/server/patches/old/addTeamCategoriesToAdminProfiles.php
  59. +0
    -0
      src/server/patches/old/replaceCategoryByCategoryIdInAppointment.php
  60. +14
    -1
      src/server/server/config/boot_global.php
  61. +15
    -4
      src/server/server/config/boot_local.php
  62. +41
    -32
      src/server/server/control/TB_Server_Control_Account.php
  63. +23
    -15
      src/server/server/control/TB_Server_Control_Appointment.php
  64. +1
    -1
      src/server/server/control/TB_Server_Control_Auth.php
  65. +28
    -3
      src/server/server/control/TB_Server_Control_Team.php
  66. +14
    -3
      src/server/shared/ent/teamdata/TB_Shared_Ent_TeamData_Appointment.php
  67. +10
    -3
      src/server/shared/ent/teamdata/TB_Shared_Ent_TeamData_Profile.php
  68. +25
    -0
      vendor/autoload.php
  69. +463
    -0
      vendor/brick/math/CHANGELOG.md
  70. +20
    -0
      vendor/brick/math/LICENSE
  71. +39
    -0
      vendor/brick/math/composer.json
  72. +754
    -0
      vendor/brick/math/src/BigDecimal.php
  73. +1051
    -0
      vendor/brick/math/src/BigInteger.php
  74. +509
    -0
      vendor/brick/math/src/BigNumber.php
  75. +413
    -0
      vendor/brick/math/src/BigRational.php
  76. +35
    -0
      vendor/brick/math/src/Exception/DivisionByZeroException.php
  77. +23
    -0
      vendor/brick/math/src/Exception/IntegerOverflowException.php
  78. +12
    -0
      vendor/brick/math/src/Exception/MathException.php
  79. +12
    -0
      vendor/brick/math/src/Exception/NegativeNumberException.php
  80. +41
    -0
      vendor/brick/math/src/Exception/NumberFormatException.php
  81. +19
    -0
      vendor/brick/math/src/Exception/RoundingNecessaryException.php
  82. +668
    -0
      vendor/brick/math/src/Internal/Calculator.php
  83. +65
    -0
      vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
  84. +108
    -0
      vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
  85. +572
    -0
      vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
  86. +98
    -0
      vendor/brick/math/src/RoundingMode.php
  87. +579
    -0
      vendor/composer/ClassLoader.php
  88. +359
    -0
      vendor/composer/InstalledVersions.php
  89. +21
    -0
      vendor/composer/LICENSE
  90. +10
    -0
      vendor/composer/autoload_classmap.php
  91. +10
    -0
      vendor/composer/autoload_files.php
  92. +9
    -0
      vendor/composer/autoload_namespaces.php
  93. +12
    -0
      vendor/composer/autoload_psr4.php
  94. +50
    -0
      vendor/composer/autoload_real.php
  95. +53
    -0
      vendor/composer/autoload_static.php
  96. +256
    -0
      vendor/composer/installed.json
  97. +56
    -0
      vendor/composer/installed.php
  98. +26
    -0
      vendor/composer/platform_check.php
  99. +19
    -0
      vendor/ramsey/collection/LICENSE
  100. +70
    -0
      vendor/ramsey/collection/README.md

+ 10
- 0
.ddev/addon-metadata/phpmyadmin/manifest.yaml Vedi File

@@ -0,0 +1,10 @@
name: phpmyadmin
repository: ddev/ddev-phpmyadmin
version: v0.3.8
install_date: "2024-08-29T14:51:22+02:00"
project_files:
- docker-compose.phpmyadmin.yaml
- docker-compose.phpmyadmin_norouter.yaml
- commands/host/phpmyadmin
global_files: []
removal_actions: []

+ 14
- 0
.ddev/commands/host/phpmyadmin Vedi File

@@ -0,0 +1,14 @@
#!/bin/bash

## #ddev-generated: If you want to edit and own this file, remove this line.
## Description: Launch a browser with PhpMyAdmin
## Usage: phpmyadmin
## Example: "ddev phpmyadmin"

DDEV_PHPMYADMIN_PORT=8036
DDEV_PHPMYADMIN_HTTPS_PORT=8037
if [ ${DDEV_PRIMARY_URL%://*} = "http" ] || [ -n "${GITPOD_WORKSPACE_ID:-}" ] || [ "${CODESPACES:-}" = "true" ]; then
ddev launch :$DDEV_PHPMYADMIN_PORT
else
ddev launch :$DDEV_PHPMYADMIN_HTTPS_PORT
fi

+ 279
- 0
.ddev/config.yaml Vedi File

@@ -0,0 +1,279 @@
name: probuddy-master
type: php
docroot: /src/client
#docroot: /src/client/manager #manager console
#docroot: /src/client/app #app
php_version: "8.1"
webserver_type: nginx-fpm
xdebug_enabled: false
additional_hostnames: []
additional_fqdns: []
database:
type: mariadb
version: "10.4"
use_dns_when_possible: true
composer_version: "2"
web_environment: []
router_http_port: 8091
router_https_port: 8463

# Key features of DDEV's config.yaml:

# name: <projectname> # Name of the project, automatically provides
# http://projectname.ddev.site and https://projectname.ddev.site

# type: <projecttype> # backdrop, craftcms, django4, drupal6/7/8/9/10, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress
# See https://ddev.readthedocs.io/en/latest/users/quickstart/ for more
# information on the different project types

# docroot: <relative_path> # Relative path to the directory containing index.php.

# php_version: "8.1" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3"

# You can explicitly specify the webimage but this
# is not recommended, as the images are often closely tied to DDEV's' behavior,
# so this can break upgrades.

# webimage: <docker_image> # nginx/php docker image.

# database:
# type: <dbtype> # mysql, mariadb, postgres
# version: <version> # database version, like "10.4" or "8.0"
# MariaDB versions can be 5.5-10.8 and 10.11, MySQL versions can be 5.5-8.0
# PostgreSQL versions can be 9-16.

# router_http_port: <port> # Port to be used for http (defaults to global configuration, usually 80)
# router_https_port: <port> # Port for https (defaults to global configuration, usually 443)

# xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart"
# Note that for most people the commands
# "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better,
# as leaving Xdebug enabled all the time is a big performance hit.

# xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart"
# Note that for most people the commands
# "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better,
# as leaving Xhprof enabled all the time is a big performance hit.

# webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn

# timezone: Europe/Berlin
# This is the timezone used in the containers and by PHP;
# it can be set to any valid timezone,
# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# For example Europe/Dublin or MST7MDT

# composer_root: <relative_path>
# Relative path to the Composer root directory from the project root. This is
# the directory which contains the composer.json and where all Composer related
# commands are executed.

# composer_version: "2"
# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1
# to use the latest major version available at the time your container is built.
# It is also possible to use each other Composer version channel. This includes:
# - 2.2 (latest Composer LTS version)
# - stable
# - preview
# - snapshot
# Alternatively, an explicit Composer version may be specified, for example "2.2.18".
# To reinstall Composer after the image was built, run "ddev debug refresh".

# nodejs_version: "18"
# change from the default system Node.js version to any other version.
# Numeric version numbers can be complete (i.e. 18.15.0) or
# incomplete (18, 17.2, 16). 'lts' and 'latest' can be used as well along with
# other named releases.
# see https://www.npmjs.com/package/n#specifying-nodejs-versions
# Note that you can continue using 'ddev nvm' or nvm inside the web container
# to change the project's installed node version if you need to.

# additional_hostnames:
# - somename
# - someothername
# would provide http and https URLs for "somename.ddev.site"
# and "someothername.ddev.site".

# additional_fqdns:
# - example.com
# - sub1.example.com
# would provide http and https URLs for "example.com" and "sub1.example.com"
# Please take care with this because it can cause great confusion.

# upload_dirs: "custom/upload/dir"
#
# upload_dirs:
# - custom/upload/dir
# - ../private
#
# would set the destination paths for ddev import-files to <docroot>/custom/upload/dir
# When Mutagen is enabled this path is bind-mounted so that all the files
# in the upload_dirs don't have to be synced into Mutagen.

# disable_upload_dirs_warning: false
# If true, turns off the normal warning that says
# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set"

# ddev_version_constraint: ""
# Example:
# ddev_version_constraint: ">= 1.22.4"
# This will enforce that the running ddev version is within this constraint.
# See https://github.com/Masterminds/semver#checking-version-constraints for
# supported constraint formats

# working_dir:
# web: /var/www/html
# db: /home
# would set the default working directory for the web and db services.
# These values specify the destination directory for ddev ssh and the
# directory in which commands passed into ddev exec are run.

# omit_containers: [db, ddev-ssh-agent]
# Currently only these containers are supported. Some containers can also be
# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit
# the "db" container, several standard features of DDEV that access the
# database container will be unusable. In the global configuration it is also
# possible to omit ddev-router, but not here.

# performance_mode: "global"
# DDEV offers performance optimization strategies to improve the filesystem
# performance depending on your host system. Should be configured globally.
#
# If set, will override the global config. Possible values are:
# - "global": uses the value from the global config.
# - "none": disables performance optimization for this project.
# - "mutagen": enables Mutagen for this project.
# - "nfs": enables NFS for this project.
#
# See https://ddev.readthedocs.io/en/latest/users/install/performance/#nfs
# See https://ddev.readthedocs.io/en/latest/users/install/performance/#mutagen

# fail_on_hook_fail: False
# Decide whether 'ddev start' should be interrupted by a failing hook

# host_https_port: "59002"
# The host port binding for https can be explicitly specified. It is
# dynamic unless otherwise specified.
# This is not used by most people, most people use the *router* instead
# of the localhost port.

# host_webserver_port: "59001"
# The host port binding for the ddev-webserver can be explicitly specified. It is
# dynamic unless otherwise specified.
# This is not used by most people, most people use the *router* instead
# of the localhost port.

# host_db_port: "59002"
# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic
# unless explicitly specified.

# mailpit_http_port: "8025"
# mailpit_https_port: "8026"
# The Mailpit ports can be changed from the default 8025 and 8026

# host_mailpit_port: "8025"
# The mailpit port is not normally bound on the host at all, instead being routed
# through ddev-router, but it can be bound directly to localhost if specified here.

# webimage_extra_packages: [php7.4-tidy, php-bcmath]
# Extra Debian packages that are needed in the webimage can be added here

# dbimage_extra_packages: [telnet,netcat]
# Extra Debian packages that are needed in the dbimage can be added here

# use_dns_when_possible: true
# If the host has internet access and the domain configured can
# successfully be looked up, DNS will be used for hostname resolution
# instead of editing /etc/hosts
# Defaults to true

# project_tld: ddev.site
# The top-level domain used for project URLs
# The default "ddev.site" allows DNS lookup via a wildcard
# If you prefer you can change this to "ddev.local" to preserve
# pre-v1.9 behavior.

# ngrok_args: --basic-auth username:pass1234
# Provide extra flags to the "ngrok http" command, see
# https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h"

# disable_settings_management: false
# If true, DDEV will not create CMS-specific settings files like
# Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php
# In this case the user must provide all such settings.

# You can inject environment variables into the web container with:
# web_environment:
# - SOMEENV=somevalue
# - SOMEOTHERENV=someothervalue

# no_project_mount: false
# (Experimental) If true, DDEV will not mount the project into the web container;
# the user is responsible for mounting it manually or via a script.
# This is to enable experimentation with alternate file mounting strategies.
# For advanced users only!

# bind_all_interfaces: false
# If true, host ports will be bound on all network interfaces,
# not the localhost interface only. This means that ports
# will be available on the local network if the host firewall
# allows it.

# default_container_timeout: 120
# The default time that DDEV waits for all containers to become ready can be increased from
# the default 120. This helps in importing huge databases, for example.

#web_extra_exposed_ports:
#- name: nodejs
# container_port: 3000
# http_port: 2999
# https_port: 3000
#- name: something
# container_port: 4000
# https_port: 4000
# http_port: 3999
# Allows a set of extra ports to be exposed via ddev-router
# Fill in all three fields even if you don’t intend to use the https_port!
# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start.
#
# The port behavior on the ddev-webserver must be arranged separately, for example
# using web_extra_daemons.
# For example, with a web app on port 3000 inside the container, this config would
# expose that web app on https://<project>.ddev.site:9999 and http://<project>.ddev.site:9998
# web_extra_exposed_ports:
# - name: myapp
# container_port: 3000
# http_port: 9998
# https_port: 9999

#web_extra_daemons:
#- name: "http-1"
# command: "/var/www/html/node_modules/.bin/http-server -p 3000"
# directory: /var/www/html
#- name: "http-2"
# command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000"
# directory: /var/www/html

# override_config: false
# By default, config.*.yaml files are *merged* into the configuration
# But this means that some things can't be overridden
# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge
# and you can't erase existing hooks or all environment variables.
# However, with "override_config: true" in a particular config.*.yaml file,
# 'use_dns_when_possible: false' can override the existing values, and
# hooks:
# post-start: []
# or
# web_environment: []
# or
# additional_hostnames: []
# can have their intended affect. 'override_config' affects only behavior of the
# config.*.yaml file it exists in.

# Many DDEV commands can be extended to run tasks before or after the
# DDEV command is executed, for example "post-start", "post-import-db",
# "pre-composer", "post-composer"
# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more
# information on the commands that can be extended and the tasks you can define
# for them. Example:
#hooks:

+ 30
- 0
.ddev/docker-compose.phpmyadmin.yaml Vedi File

@@ -0,0 +1,30 @@
#ddev-generated
services:
phpmyadmin:
container_name: ddev-${DDEV_SITENAME}-phpmyadmin
image: phpmyadmin:5.2.0
working_dir: "/root"
restart: "no"
labels:
com.ddev.site-name: ${DDEV_SITENAME}
com.ddev.approot: $DDEV_APPROOT
volumes:
- ".:/mnt/ddev_config"
- "ddev-global-cache:/mnt/ddev-global-cache"
expose:
- "80"
environment:
- PMA_USER=root
- PMA_PASSWORD=root
- PMA_HOST=db
- PMA_PORT=3306
- VIRTUAL_HOST=$DDEV_HOSTNAME
- UPLOAD_LIMIT=4000M
- HTTP_EXPOSE=8036:80
- HTTPS_EXPOSE=8037:80
healthcheck:
interval: 120s
timeout: 2s
retries: 1
depends_on:
- db

+ 4
- 0
.ddev/docker-compose.phpmyadmin_norouter.yaml Vedi File

@@ -0,0 +1,4 @@
#ddev-generated
# If omit_containers[ddev-router] then this file will be replaced
# with another with a `ports` statement to directly expose port 80 to 8036
services: {}

+ 8
- 0
.ddev/php/xdebug.ini Vedi File

@@ -0,0 +1,8 @@
;xdebug.log = /home/danielknudsen/xdebug.log
;xdebug.client_port = 9003
;xdebug.client_host=host.docker.internal
xdebug.client_host=docker.for.mac.localhost
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.log = /tmp/xdebug.log
xdebug.log_level = 7

+ 3
- 0
1-deployBetaProBuddy.sh Vedi File

@@ -28,6 +28,9 @@ cp -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/git_repositories/beta-
rm -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/src/server/dependencies
cp -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/git_repositories/beta-probuddy/src/server/dependencies /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/src/server

rm -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/src/server/patches
cp -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/git_repositories/beta-probuddy/src/server/patches /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/src/server

rm -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/src/server/server/cli
cp -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/git_repositories/beta-probuddy/src/server/server/cli /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/src/server/server



+ 55
- 0
1-deployLiveProBuddy.sh Vedi File

@@ -0,0 +1,55 @@
#!/bin/bash

cd /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master
git pull

echo "$(tput setab 2)pro-buddy has been PULLED$(tput sgr 0)"

rm -rf /www/htdocs/v034011/projects/probuddy/client
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/client /www/htdocs/v034011/projects/probuddy

rm -rf /www/htdocs/v034011/projects/probuddy/server/admin/AHDMN
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/admin/AHDMN /www/htdocs/v034011/projects/probuddy/server/admin

rm -rf /www/htdocs/v034011/projects/probuddy/server/admin/libs
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/admin/libs /www/htdocs/v034011/projects/probuddy/server/admin

rm -rf /www/htdocs/v034011/projects/probuddy/server/admin/services
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/admin/services /www/htdocs/v034011/projects/probuddy/server/admin

rm -rf /www/htdocs/v034011/projects/probuddy/server/admin/boot.php
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/admin/boot.php /www/htdocs/v034011/projects/probuddy/server/admin

rm -rf /www/htdocs/v034011/projects/probuddy/server/dependencies
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/dependencies /www/htdocs/v034011/projects/probuddy/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/patches
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/patches /www/htdocs/v034011/projects/probuddy/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/server/cli
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/server/cli /www/htdocs/v034011/projects/probuddy/server/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/server/control
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/server/control /www/htdocs/v034011/projects/probuddy/server/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/server/core
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/server/core /www/htdocs/v034011/projects/probuddy/server/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/server/job
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/server/job /www/htdocs/v034011/projects/probuddy/server/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/server/template
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/server/template /www/htdocs/v034011/projects/probuddy/server/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/server/utils
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/server/utils /www/htdocs/v034011/projects/probuddy/server/server

rm -rf /www/htdocs/v034011/projects/probuddy/server/shared
cp -rf /www/htdocs/v034011/projects/probuddy/git_repository/probuddy-master/src/server/shared /www/htdocs/v034011/projects/probuddy/server

echo "$(tput setab 2)Files have been copied$(tput sgr 0)"


echo "$(tput setab 7)$(tput setaf 1)THINK ABOUT POSSIBLE PATCHES!"

echo "You have updated probuddy live!$(tput sgr 0)"

tools/patches/1addActiveStateToProfile.php → 1addActiveStateToProfile.php Vedi File


+ 7
- 0
README.md Vedi File

@@ -1,3 +1,8 @@
DDEV
- Um zwischen app und manager zu wechseln in die .ddev/config.yaml gucken, dort dann entsprechend einstellen :)


DOCKER
- Client: http://localhost:8097/client/app/#/auth/start
- Database: http://localhost:8096
- Template-Engine: https://github.com/cho45/micro-template.js
@@ -28,3 +33,5 @@ Neuinstallation:
- php pw_gen.php - Erzeugt Passwort "test"
- In phpmyadmin pb_core - account: SQL Statement: UPDATE `account` SET `pass`='aa47377bfef0917b6ff2e73ece5a6952d7763664' WHERE 1

Update client:
- um Client Caching zu umgehen bei Frontend Changes -> versions nummer erhöhen in src/client/app/index.php

+ 5
- 0
composer.json Vedi File

@@ -0,0 +1,5 @@
{
"require": {
"ramsey/uuid": "^4.7"
}
}

+ 260
- 0
composer.lock Vedi File

@@ -0,0 +1,260 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "311c7a785a2af4ab4dae0f24542d289d",
"packages": [
{
"name": "brick/math",
"version": "0.12.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1",
"vimeo/psalm": "5.16.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Brick\\Math\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Arbitrary-precision arithmetic library",
"keywords": [
"Arbitrary-precision",
"BigInteger",
"BigRational",
"arithmetic",
"bigdecimal",
"bignum",
"bignumber",
"brick",
"decimal",
"integer",
"math",
"mathematics",
"rational"
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.12.1"
},
"funding": [
{
"url": "https://github.com/BenMorel",
"type": "github"
}
],
"time": "2023-11-29T23:19:16+00:00"
},
{
"name": "ramsey/collection",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
},
"type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
}
},
"autoload": {
"psr-4": {
"Ramsey\\Collection\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ben Ramsey",
"email": "ben@benramsey.com",
"homepage": "https://benramsey.com"
}
],
"description": "A PHP library for representing and manipulating collections.",
"keywords": [
"array",
"collection",
"hash",
"map",
"queue",
"set"
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/2.0.0"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
"type": "tidelift"
}
],
"time": "2022-12-31T21:50:55+00:00"
},
{
"name": "ramsey/uuid",
"version": "4.7.6",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
},
"replace": {
"rhumsaa/uuid": "self.version"
},
"require-dev": {
"captainhook/captainhook": "^5.10",
"captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.8",
"ergebnis/composer-normalize": "^2.15",
"mockery/mockery": "^1.3",
"paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2",
"php-mock/php-mock-mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.1",
"phpbench/phpbench": "^1.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9",
"ramsey/composer-repl": "^1.4",
"slevomat/coding-standard": "^8.4",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.9"
},
"suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
"ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
"ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
"paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
"ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
},
"type": "library",
"extra": {
"captainhook": {
"force-install": true
}
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Ramsey\\Uuid\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
"keywords": [
"guid",
"identifier",
"uuid"
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.6"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
"type": "tidelift"
}
],
"time": "2024-04-27T21:32:50+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

+ 52
- 3
src/client/app/css/app.css Vedi File

@@ -81,7 +81,13 @@ body.body-content {
}
.content {
margin-top: 72px;
/*margin-top: 72px;*/
margin-top: 20px;
margin-bottom: 20px;
}
.dropdown-menu {
top: auto;
bottom: 100%;
}
.c-offcanvas--right {
@@ -92,6 +98,8 @@ body.body-content {
{
position: absolute;
min-width: 280px;
max-height: calc(100vh - 60px);
overflow: auto;
}
.ul-offcanvas-nav
@@ -457,7 +465,8 @@ body.body-auth .action-button {
position: fixed;
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
bottom: 20px;
/*bottom: 20px;*/
bottom: 76px;
right: 20px;
text-align: center;
font-size: 20px;
@@ -471,6 +480,8 @@ body.body-auth .action-button {
overflow: hidden;
z-index: 100;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
overflow: auto;
max-height: calc(100% - 85px);
}
.action-list {
@@ -482,7 +493,7 @@ body.body-auth .action-button {
bottom: 0;
right: 0;
opacity: 0;
background-color: white;
background-color: #fff;
}
.action-list-item {
@@ -1125,6 +1136,44 @@ h6.in-card {
font-size: 0.75rem;
}
.search-box {
display: flex;
position: relative;
margin-right: 20px;
}
.search-box .fa {
font-size: 16px;
color: #ccc;
position: absolute;
right: 8px;
top: 50%;
transform: translate(0,-50%);
}
.search-box input {
width: 200px;
padding-right: 25px;
}
.right-content {
display: flex;
justify-content: right;
align-items: center;
}
@media only screen and (max-width: 500px) {
.float-right.search-content {
float: none !important;
margin-right: 0 !important;
}
.right-content {
justify-content: left;
padding-top: 5px;
}
}
h6.calendar-week {
margin-top: 0;
margin-bottom: 0;


+ 1
- 2
src/client/app/index.php Vedi File

@@ -1,8 +1,7 @@
<?php
require_once __DIR__ . '/../../server/server/config/boot_global.php';
require_once __DIR__ . '/../../server/server/config/boot_local.php';
$version = 'v=2.0.1';//time();//Francis_Utils_Config::get( 'version' );
$version = 'v=2.1.2';//time();//Francis_Utils_Config::get( 'version' );
?>
<!DOCTYPE html>
<html lang="en">


+ 4
- 0
src/client/app/js/app/core/Dict.js Vedi File

@@ -111,6 +111,7 @@ app.core.Dict = {
"CLOSE" : "Schließen",
"CREATE_APPOINTMENT" : "Termin erstellen",
"APPOINTMENT_SUBJECT" : "Terminname",
"APPOINTMENT_ICON" : "Icon",
"APPOINTMENT_START_DATE" : "Startdatum",
"APPOINTMENT_START_TIME" : "Startzeit",
"APPOINTMENT_END_DATE" : "Enddatum",
@@ -431,6 +432,8 @@ app.core.Dict = {
"ACCOUNT_NOT_VALIDATED_DESCRIPTION" : "Bitte validiere zunächst deine Email Adresse. Wir haben dir gerade einen Validierungslink per Mail zugeschickt. Bitte schau in deinem Postfach nach (ggf. auch im Spam-Ordner) und klicke auf den Button.",
"LOGIN_FIRSTNAME" : "Vorname",
"LOGIN_LASTNAME" : "Nachname",
"DELETION_NOT_POSSIBLE_INFO_HEADLINE" : "Informationen zur Löschung deines Profils",
"DELETION_NOT_POSSIBLE_INFO" : "Du hast noch <strong>{0}</strong> Teilnahme(n) für zukünftige Termine, bei denen die Absagefrist noch nicht verstrichen ist. Bitte sage alle Teilnahmen ab, bevor Du Deinen Account löschen kannst.",
"DELETION_GROUP_OWNER_INFO_HEADLINE" : "Informationen zur Löschung deines Profils",
"DELETION_GROUP_OWNER_INFO" : "Bitte schreibe eine Mail an support@probuddy.de um die Löschung deines Profile bei Probuddy zu veranlassen. Da du Gruppeninhaber mindestens einer Gruppe bei ProBuddy bist, wird diese Gruppe/Gruppen automatisch mitgelöscht.",
"BTN_BACK" : "Zurück",
@@ -571,6 +574,7 @@ app.core.Dict = {
"GROUP_MANAGEMENT_MEMBERS_ACTIVE" : "Aktiv",
"GROUP_MANAGEMENT_MEMBERS_INACTIVE" : "Inaktiv",
"GROUP_MANAGEMENT_MEMBERS_NOT_APPROVED" : "Unbestätigt",
"GROUP_MANAGEMENT_MEMBERS_DELETED" : "Gelöscht",
"GROUP_MANAGEMENT_MEMBERS_CHANGE_STATUS" : "Gruppenstatus ändern",
"GROUP_MANAGEMENT_MEMBERS_CHANGE_STATUS_INFO" : "Du kannst deinen eigenen Gruppenstatus nicht verändern.",
"BTN_GROUP_MANAGEMENT_MEMBERS_SAVE_STATUS" : "Status speichern",


+ 6
- 0
src/client/app/js/app/model/Appointment.js Vedi File

@@ -15,6 +15,7 @@ app.model.Appointment = function( appData, attendees, attendeeProfiles )
attendees = attendees || [],
attendeeProfiles = attendeeProfiles || [],
id = data.id,
icon = data.icon,
category = data.category || null,
categoryIds = typeof( data.category_ids_js ) === 'string' ? JSON.parse( data.category_ids_js ) : data.category_ids_js,
visibility = data.visibility,
@@ -65,6 +66,11 @@ app.model.Appointment = function( appData, attendees, attendeeProfiles )
return teamId;
};
this.getIcon = function()
{
return icon;
};
this.getSubject = function()
{
return subject;


+ 5
- 0
src/client/app/js/app/model/Profile.js Vedi File

@@ -223,6 +223,11 @@ app.model.Profile = function( data, groupsData )
teamData = gd;
};
this.getGroupStatus = function( groupId )
{
return this.getGroupData(groupId).status;
}
this.isOwn = function()
{
return ( this.getId() == app.model.SessionUser.getUserProfile().getId() );


+ 5
- 0
src/client/app/js/app/state/AppointmentCreate.js Vedi File

@@ -184,6 +184,7 @@ app.state.AppointmentCreate = function()
var $form = $content.find( '[data-id="form-appointment"]' ).first(),
teamId,
icon,
appointmentState = $form.find( '[data-id="checkbox-appointment-state"]' ).first().is( ':checked' ) ? 'open' : null,
isValid = app.util.Form.bootstrapValidate( $form );
@@ -191,11 +192,15 @@ app.state.AppointmentCreate = function()
{
app.gui.PageLoader.show();
teamId = $form.find( '[data-id="select-team-id"]' ).first().val();
icon = $('#input-subject-icon option:selected').val();
//console.log(icon);
app.core.Rpc.call(
'Appointment',
'create',
{
teamId : teamId,
icon: icon,
categoryIds : $form.find( '[data-id="select-category-' + teamId + '"]' ).first().val(),
visibility : ( 'visibility-category-only' === $form.find( '[name="input-visibility"]:checked' ).first().val() ) ? app.model.Appointment.ENUM_VISIBLE_FOR_CATEGORIES : app.model.Appointment.ENUM_VISIBLE_FOR_ALL,
subject : $form.find( '[data-id="input-subject"]' ).first().val(),


+ 1
- 0
src/client/app/js/app/state/AppointmentEdit.js Vedi File

@@ -168,6 +168,7 @@ app.state.AppointmentEdit= function()
'Appointment',
'update',
{
icon: $('#input-subject-icon option:selected').val(),
processSerial : isSerial,
appointmentId : appointmentId,
categoryIds : $form.find( '[data-id="select-category-' + appointment.getTeamId() + '"]' ).first().val(),


+ 1
- 1
src/client/app/js/app/state/AppointmentEditAttendee.js Vedi File

@@ -31,7 +31,7 @@ app.state.AppointmentEditAttendee = function()
app.core.Rpc.call(
'Team',
'getMembers',
{ teamId : appointment.getTeamId() },
{ teamId : appointment.getTeamId(), activeOnly: true },
function( res2 )
{
let childs, m, notDecided = [];


+ 1
- 1
src/client/app/js/app/state/ConfigurationAttendanceLog.js Vedi File

@@ -40,7 +40,7 @@ app.state.ConfigurationAttendanceLog = function()
{
$content.find( '[data-id="container-appointment-log"]' ).first().html(
app.core.View.getTemplate(
'group-member-management-body-appointment-log',
'group-member-management-member-body-appointment-log',
{
logs : res.appointmentLog
}


+ 6
- 3
src/client/app/js/app/state/ConfigurationProfileDelete.js Vedi File

@@ -18,15 +18,18 @@ app.state.ConfigurationProfileDelete = function()
app.core.Rpc.call(
'Account',
'getAccountRelatedData',
null,
{
includeNumFutureAttendances : true
},
function( res ) {
app.gui.PageLoader.hide();
console.log(res);
app.core.View.setContent(
app.core.View.getTemplate(
'configuration-profile-delete',
{
profile: new app.model.Profile(res.profile)
profile: new app.model.Profile(res.profile),
numFutureAttendances: res.numFutureAttendances
}
)
);


+ 3
- 1
src/client/app/js/app/state/CourseCategories.js Vedi File

@@ -30,7 +30,9 @@ app.state.CourseCategories = function()
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true,
activeOnly: true
},
function( res )
{


+ 7
- 0
src/client/app/js/app/state/GroupMemberManagement.js Vedi File

@@ -13,11 +13,13 @@ app.state.GroupMemberManagement = function()
{
let $content = app.core.View.getContent(),
group = null,
currentProfile = null,
memberToEdit = null,
members = [],
membersActive = [],
membersInactive = [],
membersNotApproved = [],
membersDeleted = [],
groupId = p.groupId,
memberId = p.hasOwnProperty( 'memberId' ) ? p.memberId : null,
activeTab = 'active';
@@ -65,6 +67,10 @@ app.state.GroupMemberManagement = function()
case 'not_approved':
membersNotApproved.push(member);
break;
case 'deleted':
membersDeleted.push(member);
console.log(member.isAccessible())
break;
}
}
@@ -76,6 +82,7 @@ app.state.GroupMemberManagement = function()
membersActive: membersActive,
membersInactive: membersInactive,
membersNotApproved: membersNotApproved,
membersDeleted: membersDeleted,
group : group,
groups : app.model.SessionUser.getAdminGroups(),
currentProfile : currentProfile,


+ 6
- 1
src/client/app/js/app/state/GroupMemberManagementMember.js Vedi File

@@ -11,7 +11,6 @@ app.state.GroupMemberManagementMember = function()
state.onEnter = function( p )
{
console.log(p);
let $content = app.core.View.getContent(),
fnRenderMemberForm = null,
fnGetMemberById = null,
@@ -27,6 +26,11 @@ app.state.GroupMemberManagementMember = function()
fnRenderMemberForm = function( profile )
{
let status = '';
if (profile) {
status = profile.getGroupData(groupId).status;
}
$memberContainer = $content.find( '[data-id="member-container"]' ).first();
$memberContainer.html(
app.core.View.getTemplate(
@@ -34,6 +38,7 @@ app.state.GroupMemberManagementMember = function()
{
p : profile,
g : group,
status: status
}
)
);


+ 35
- 32
src/client/app/js/app/state/Home.js Vedi File

@@ -452,34 +452,6 @@ app.state.Home = function()
}
}
/**
* Render appointments according to pager setting
* @param pageNo
*/
function updatePaging( pageNo )
{
var pager = self.createPager(
appointments,
+pageNo
),
$content = app.core.View.getContent();
$content.html(
app.core.View.getTemplate(
'home',
{
appointments : pager.pageElements,
pager : pager,
filter : filter,
groupsNotActiveString: groupsNotActiveString,
}
)
);
// Animate scroll to top
$("html, body").animate({ scrollTop: 0 });
}
// Note
// This needs to be called once at the beginning to trigger correct handlers and body classes
app.core.View.setContent( 'Loading...' );
@@ -511,14 +483,45 @@ app.state.Home = function()
)
);
}
self.appointments = appointments;
updatePaging();
$content = app.core.View.getContent();
$content.html(
app.core.View.getTemplate(
'home',
{
appointments: appointments,
filter : filter,
groupsNotActiveString: groupsNotActiveString,
}
)
);
// Animate scroll to top
$("html, body").animate({ scrollTop: 0 });
$content.on( 'change', '[data-id="pager"]', function( e )
$content.on('input', '[data-id="appointment-search-filter"]', function(e)
{
updatePaging( +$( this ).val() );
var searchTerm = $(this).val().toLowerCase();
// Filter elements
$('[data-type="appointment-item-container"]').each(function() {
var $div = $(this);
let appCat = $div.find('.appointment-category').text().toLowerCase();
let appSub = $div.find('.appointment-subject').text().toLowerCase();
let appDate = $div.find('.appointment-datetime').text().toLowerCase();
if (appCat.includes(searchTerm) ||
appSub.includes(searchTerm) ||
appDate.includes(searchTerm) ||
searchTerm === '') {
$div.show();
} else {
$div.hide();
}
});
});
$content.on( 'click', '[data-type="appointment-short-info"]', function( e )


+ 2
- 1
src/client/app/js/app/state/StatsExport.js Vedi File

@@ -104,7 +104,8 @@ app.state.StatsExport = function()
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true
},
function( res2 )
{


+ 1
- 1
src/client/app/tmpl/appointment-detail.html Vedi File

@@ -44,7 +44,7 @@
<% } %>
</div>
<div class="appointment-detail-appointment-name">
<%= a.getSubject() %>
<%= a.getIcon() %> <%= a.getSubject() %>
</div>
</div>
</div>


+ 39
- 15
src/client/app/tmpl/appointment-form-edit.html Vedi File

@@ -24,22 +24,46 @@
value="<%= mTeam.getId() %>" />
<div class="form-group">
<label for="input-subject">
<%= _lc( 'APPOINTMENT_SUBJECT' ) %>
</label>
<input type="text"
minlength="3"
maxlength="255"
name="subject"
data-id="input-subject"
id="input-subject"
class="form-control"
value="<%= a.getSubject() %>"
autocomplete="off"
required />
<div class="invalid-feedback">
<%= _lc( 'VALIDATION_INPUT_REQUIRED' ) %>
<div class="form-row">
<div class="col col-2">
<label for="input-subject-icon">
<%= _lc( 'APPOINTMENT_ICON' ) %>
</label>
<select class="form-control" name="categoryicon" id="input-subject-icon">
<option value="" <%= a.getIcon() === "" ? 'selected' : '' %>>- kein -</option>
<option value="&#9880;" <%= a.getIcon() === "❀" ? 'selected' : '' %>>&#9880;</option>
<option value="&#9873;" <%= a.getIcon() === "⚑" ? 'selected' : '' %>>&#9873;</option>
<option value="&cross;" <%= a.getIcon() === "✗" ? 'selected' : '' %>>&cross;</option>
<option value="&#10025;" <%= a.getIcon() === "✩" ? 'selected' : '' %>>&#10025;</option>
<option value="&#10045;" <%= a.getIcon() === "✽" ? 'selected' : '' %>>&#10045;</option>
<option value="&#10084;" <%= a.getIcon() === "❤" ? 'selected' : '' %>>&#10084;</option>
<option value="&#10132;" <%= a.getIcon() === "➔" ? 'selected' : '' %>>&#10132;</option>
<option value="&#9881;" <%= a.getIcon() === "⚙" ? 'selected' : '' %>>&#9881;</option>
<option value="&#9863;" <%= a.getIcon() === "⚇" ? 'selected' : '' %>>&#9863;</option>
<option value="&#9788;" <%= a.getIcon() === "☼" ? 'selected' : '' %>>&#9788;</option>
<option value="&#9787;" <%= a.getIcon() === "☻" ? 'selected' : '' %>>&#9787;</option>
</select>
</div>
<div class="col col-10">
<label for="input-subject">
<%= _lc( 'APPOINTMENT_SUBJECT' ) %>
</label>
<input type="text"
minlength="3"
maxlength="255"
name="subject"
data-id="input-subject"
id="input-subject"
class="form-control"
value="<%= a.getSubject() %>"
autocomplete="off"
required />
<div class="invalid-feedback">
<%= _lc( 'VALIDATION_INPUT_REQUIRED' ) %>
</div>
</div>
</div>
</div>
<% var categories = mTeam.getAdminCourseCategoriesForProfile( currentProfile ); %>


+ 38
- 14
src/client/app/tmpl/appointment-form.html Vedi File

@@ -38,20 +38,44 @@
</div>
<div class="form-group">
<label for="input-subject">
<%= _lc( 'APPOINTMENT_SUBJECT' ) %>
</label>
<input type="text"
minlength="3"
maxlength="255"
name="subject"
data-id="input-subject"
id="input-subject"
class="form-control"
autocomplete="off"
required />
<div class="invalid-feedback">
<%= _lc( 'VALIDATION_INPUT_REQUIRED' ) %>
<div class="form-row">
<div class="col col-2">
<label for="input-subject-icon">
<%= _lc( 'APPOINTMENT_ICON' ) %>
</label>
<select class="form-control"
name="categoryicon" id="input-subject-icon">
<option value="">- kein -</option>
<option value="&#9880;">&#9880;</option>
<option value="&#9873;">&#9873;</option>
<option value="&cross;">&cross;</option>
<option value="&#10025;">&#10025;</option>
<option value="&#10045;">&#10045;</option>
<option value="&#10084;">&#10084;</option>
<option value="&#10132;">&#10132;</option>
<option value="&#9881;">&#9881;</option>
<option value="&#9863;">&#9863;</option>
<option value="&#9788;">&#9788;</option>
<option value="&#9787;">&#9787;</option>
</select>
</div>
<div class="col col-10">
<label for="input-subject">
<%= _lc( 'APPOINTMENT_SUBJECT' ) %>
</label>
<input type="text"
minlength="3"
maxlength="255"
name="subject"
data-id="input-subject"
id="input-subject"
class="form-control"
autocomplete="off"
required />
<div class="invalid-feedback">
<%= _lc( 'VALIDATION_INPUT_REQUIRED' ) %>
</div>
</div>
</div>
</div>


+ 17
- 0
src/client/app/tmpl/configuration-profile-delete.html Vedi File

@@ -19,6 +19,23 @@
</a>
</div>
<% } else if (numFutureAttendances > 0) { %>
<div class="card-header text-white bg-danger">
<i class="fas fa-info-circle"></i> <%= _lc( 'DELETION_NOT_POSSIBLE_INFO_HEADLINE' ) %>
</div>
<div class="card-body">
<p>
<strong><%=raw _lc( 'DELETION_NOT_POSSIBLE_INFO', [numFutureAttendances] ) %></strong>
</p>
</div>
<div class="card-footer">
<a href="#/configuration/profile"
class="btn btn-sm btn-secondary">
<%= _lc( 'BTN_BACK' ) %>
</a>
</div>
<% } else { %>
<form data-id="form-profile-deletion"


+ 277
- 278
src/client/app/tmpl/group-member-management-member-body.html Vedi File

@@ -1,289 +1,288 @@
<% if ( p ) { %>
<% var currentUser = app.model.SessionUser.getUserProfile(); %>
<div class="row">
<div class="col text-center">
<img class="img-fluid rounded-circle profile-image-big img-thumbnail"
style="margin-bottom: 12px; padding: 6px"
src="<%= p.getProfileImg() %>" />
</div>
</div>
<div class="row">
<div class="col text-center">
<div style="font-size: 1.4rem">
<%=raw p.getName() %>
</div>
<% if ( p.isInGroupCategory( 'DOGSCHOOL' ) ) { %>
<div class="profile-header">
<%= _lc( 'PROFILE_DOGNAME' ) %>
</div>
<div class="profile-content">
<%= p.getCustomGroupProperty( 'DOGSCHOOL', 'dogname' ) ? p.getCustomGroupProperty( 'DOGSCHOOL', 'dogname' ) : '---' %>
</div>
<% } %>
<div class="profile-header">
<%= _lc( 'PROFILE_STATUS' ) %>
</div>
<div class="profile-content">
<%= p.getStatus() %>
<% var currentUser = app.model.SessionUser.getUserProfile(); %>
<div class="row">
<div class="col text-center">
<img class="img-fluid rounded-circle profile-image-big img-thumbnail"
style="margin-bottom: 12px; padding: 6px"
src="<%= p.getProfileImg() %>" />
</div>
<div class="profile-header">
<%= _lc( 'ADDRESS' ) %>
</div>
<div class="profile-content">
<%= p.getStreet() ? p.getStreet() : '---' %><br />
<%= p.getZipCode() ? p.getZipCode() : '' %>&nbsp;<%= p.getCity() ? p.getCity() : '' %>
</div>
<div class="profile-header">
<%= _lc( 'PHONE' ) %>
</div>
<div class="profile-content">
<% if ( p.getMobile() ) { %>
<a href="tel:<%= p.getMobile() %>"><%= p.getMobile() %></a>
<% } else { %>
---
<% } %>
<br />
<% if ( p.getPhone() ) { %>
<a href="tel:<%= p.getPhone() %>"><%= p.getPhone() %></a>
<% } else { %>
---
</div>
<div class="row">
<div class="col text-center">
<div style="font-size: 1.4rem">
<%=raw p.getName() %>
</div>
<% if ( p.isInGroupCategory( 'DOGSCHOOL' ) ) { %>
<div class="profile-header">
<%= _lc( 'PROFILE_DOGNAME' ) %>
</div>
<div class="profile-content">
<%= p.getCustomGroupProperty( 'DOGSCHOOL', 'dogname' ) ? p.getCustomGroupProperty( 'DOGSCHOOL', 'dogname' ) : '---' %>
</div>
<% } %>
</div>
<div class="profile-header">
<%= _lc( 'EMAIL' ) %>
</div>
<div class="profile-content">
<% if ( p.getEmail() ) { %>
<a href="mailto:<%= p.getEmail() %>"><%= p.getEmail() %></a>
<% if ( p.getEmailValidated() === true ) { %>
<div> ( <i class="far fa-check-circle text-success"></i> <%= _lc( 'GROUP_MANAGEMENT_MEMBERS_EMAIL_VALIDATED' ) %> )</div>
<div class="profile-header">
<%= _lc( 'PROFILE_STATUS' ) %>
</div>
<div class="profile-content">
<%= p.getStatus() %>
</div>
<div class="profile-header">
<%= _lc( 'ADDRESS' ) %>
</div>
<div class="profile-content">
<%= p.getStreet() ? p.getStreet() : '---' %><br />
<%= p.getZipCode() ? p.getZipCode() : '' %>&nbsp;<%= p.getCity() ? p.getCity() : '' %>
</div>
<div class="profile-header">
<%= _lc( 'PHONE' ) %>
</div>
<div class="profile-content">
<% if ( p.getMobile() ) { %>
<a href="tel:<%= p.getMobile() %>"><%= p.getMobile() %></a>
<% } else { %>
---
<% } %>
<br />
<% if ( p.getPhone() ) { %>
<a href="tel:<%= p.getPhone() %>"><%= p.getPhone() %></a>
<% } else { %>
---
<% } %>
</div>
<div class="profile-header">
<%= _lc( 'EMAIL' ) %>
</div>
<div class="profile-content">
<% if ( p.getEmail() ) { %>
<a href="mailto:<%= p.getEmail() %>"><%= p.getEmail() %></a>
<% if ( p.getEmailValidated() === true ) { %>
<div> ( <i class="far fa-check-circle text-success"></i> <%= _lc( 'GROUP_MANAGEMENT_MEMBERS_EMAIL_VALIDATED' ) %> )</div>
<% } else { %>
<div> ( <i class="far fa-window-close text-danger"></i> <%= _lc( 'GROUP_MANAGEMENT_MEMBERS_EMAIL_NOT_VALIDATED' ) %> )</div>
<% } %>
<% } else { %>
<div> ( <i class="far fa-window-close text-danger"></i> <%= _lc( 'GROUP_MANAGEMENT_MEMBERS_EMAIL_NOT_VALIDATED' ) %> )</div>
---
<% } %>
<% } else { %>
---
<% } %>
</div>
<div class="profile-header">
<%= _lc( 'BIRTHDAY' ) %>
</div>
<div class="profile-content">
<%= ( null != p.getMomentBirthday() ) ? p.getMomentBirthday().format( 'DD.MM.YYYY' ) : '---' %>
</div>
<div class="profile-header">
<%= _lc( 'JOIN_DT' ) %>
</div>
<div class="profile-content">
<% var momentJoin = p.getMomentJoinInGroup( g.getId() ); %>
<%= ( null != momentJoin ) ? momentJoin.format( 'DD.MM.YYYY' ) : '---' %>
</div>
<div class="profile-header">
<%= _lc( 'BIRTHDAY' ) %>
</div>
<div class="profile-content">
<%= ( null != p.getMomentBirthday() ) ? p.getMomentBirthday().format( 'DD.MM.YYYY' ) : '---' %>
</div>
<div class="profile-header">
<%= _lc( 'JOIN_DT' ) %>
</div>
<div class="profile-content">
<% var momentJoin = p.getMomentJoinInGroup( g.getId() ); %>
<%= ( null != momentJoin ) ? momentJoin.format( 'DD.MM.YYYY' ) : '---' %>
</div>
</div>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'CHANGE_ROLE' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-6">
<select class="form-control form-control-sm"
<%= ( 'trainer' === p.getRoleInGroup( g.getId() ) ) ? 'disabled="disabled"' : '' %>
data-id="select-member-role">
<option value="player" <%= ( 'player' === p.getRoleInGroup( g.getId() ) ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MEMBER' ) %>
</option>
<option value="cotrainer" <%= ( 'cotrainer' === p.getRoleInGroup( g.getId() ) ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_ADMIN' ) %>
</option>
<% if ( 'trainer' === p.getRoleInGroup( g.getId() ) ) { %>
<option value="trainer" <%= ( 'trainer' === p.getRoleInGroup( g.getId() ) ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_OWNER' ) %>
</option>
<% } %>
</select>
</div>
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-update-role">
<%= _lc( 'BTN_SAVE_NEW_ROLE' ) %>
</button>
</div>
<% if ( 'trainer' === p.getRoleInGroup( g.getId() ) ) { %>
<div class="col-sm-12">
<small><i><%= _lc( 'CANNOT_CHANGE_GROUP_OWNER_ROLE_INFO' ) %></i></small>
</div>
<% } %>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'GROUP_MANAGEMENT_MEMBERS_CHANGE_STATUS' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-6">
<select class="form-control form-control-sm" data-id="select-member-status"
<%= ( p.getId() === currentUser.getId() ) ? 'disabled="disabled"' : '' %>
>
<option value="active" <%= ( 'active' === p.getGroupData( g.getId() ).status ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_ACTIVE' ) %>
</option>
<option value="inactive" <%= ( 'inactive' === p.getGroupData( g.getId() ).status ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_INACTIVE' ) %>
</option>
<option value="not_approved" <%= ( 'not_approved' === p.getGroupData( g.getId() ).status ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_NOT_APPROVED' ) %>
</option>
</select>
</div>
<div class="col">
<button class="btn btn-primary btn-sm" data-id="btn-update-status">
<%= _lc( 'BTN_GROUP_MANAGEMENT_MEMBERS_SAVE_STATUS' ) %>
</button>
</div>
<% if ( p.getId() === currentUser.getId() ) { %>
<div class="col-sm-12">
<small><i><%= _lc( 'GROUP_MANAGEMENT_MEMBERS_CHANGE_STATUS_INFO' ) %></i></small>
</div>
<% } %>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'ASSIGNED_MEMBER_GROUP_CATEGORIES' ) %></strong>
</div>
</div>
<div class="row">
<div class="col">
<% var cgs = g.getCourseCategoriesForProfile( p ); %>
<% for ( var cgsi = 0; cgsi < cgs.length; cgsi++ ) { %>
<% if ( cgsi > 0 ) { %>
<%= ' ' %>
<% } %>
<span class="badge badge-pill badge-primary"><%= cgs[ cgsi ].name %></span>
<hr />
<% if ( status !== 'deleted' ) { %>
<div class="row">
<div class="col">
<strong><%= _lc( 'CHANGE_ROLE' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-6">
<select class="form-control form-control-sm"
<%= ( 'trainer' === p.getRoleInGroup( g.getId() ) ) ? 'disabled="disabled"' : '' %>
data-id="select-member-role">
<option value="player" <%= ( 'player' === p.getRoleInGroup( g.getId() ) ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MEMBER' ) %>
</option>
<option value="cotrainer" <%= ( 'cotrainer' === p.getRoleInGroup( g.getId() ) ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_ADMIN' ) %>
</option>
<% if ( 'trainer' === p.getRoleInGroup( g.getId() ) ) { %>
<option value="trainer" <%= ( 'trainer' === p.getRoleInGroup( g.getId() ) ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_OWNER' ) %>
</option>
<% } %>
</select>
</div>
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-update-role">
<%= _lc( 'BTN_SAVE_NEW_ROLE' ) %>
</button>
</div>
<% if ( 'trainer' === p.getRoleInGroup( g.getId() ) ) { %>
<div class="col-sm-12">
<small><i><%= _lc( 'CANNOT_CHANGE_GROUP_OWNER_ROLE_INFO' ) %></i></small>
</div>
<% } %>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'GROUP_MANAGEMENT_MEMBERS_CHANGE_STATUS' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-6">
<select class="form-control form-control-sm" data-id="select-member-status"
<%= ( p.getId() === currentUser.getId() ) ? 'disabled="disabled"' : '' %>
>
<option value="active" <%= ( 'active' === p.getGroupData( g.getId() ).status ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_ACTIVE' ) %>
</option>
<option value="inactive" <%= ( 'inactive' === p.getGroupData( g.getId() ).status ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_INACTIVE' ) %>
</option>
<option value="not_approved" <%= ( 'not_approved' === p.getGroupData( g.getId() ).status ) ? 'selected="selected"' : '' %> >
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_NOT_APPROVED' ) %>
</option>
</select>
</div>
<div class="col">
<button class="btn btn-primary btn-sm" data-id="btn-update-status">
<%= _lc( 'BTN_GROUP_MANAGEMENT_MEMBERS_SAVE_STATUS' ) %>
</button>
</div>
<% if ( p.getId() === currentUser.getId() ) { %>
<div class="col-sm-12">
<small><i><%= _lc( 'GROUP_MANAGEMENT_MEMBERS_CHANGE_STATUS_INFO' ) %></i></small>
</div>
<% } %>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'ASSIGNED_MEMBER_GROUP_CATEGORIES' ) %></strong>
</div>
</div>
<div class="row">
<div class="col">
<% var cgs = g.getCourseCategoriesForProfile( p ); %>
<% for ( var cgsi = 0; cgsi < cgs.length; cgsi++ ) { %>
<% if ( cgsi > 0 ) { %>
<%= ' ' %>
<% } %>
<span class="badge badge-pill badge-primary"><%= cgs[ cgsi ].name %></span>
<% } %>
</div>
<div class="col-sm-12">
<small><i><%= _lc( 'ASSIGNED_MEMBER_CATEOGORY_INFO' ) %></i></small>
</div>
<div class="col">
<a href="#/course/categories/<%= g.getId() %>"
class="btn btn-primary btn-sm">
<%= _lc( 'BTN_NAVIGATE_TO_CATEGORY_MANAGEMENT' ) %>
</a>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'MEMBER_CONTRACT' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group" style="margin-bottom:0;">
<label for="appointment-log-until">
<%= _lc( 'CONTRACT_NAME' ) %>
</label>
<input type="text"
class="form-control"
maxlength="128"
value="<%= p.getContractInGroup( g.getId() ) ? p.getContractInGroup( g.getId() ) : '' %>"
data-id="input-contract" />
</div>
</div>
<div class="col-6">
<div class="form-group" style="margin-bottom:0;">
<label for="contract-date">
<%= _lc( 'CONTRACT_DATE' ) %>
</label>
<input type="date"
id="contract-date"
value="<%= p.getContractMomentInGroup( g.getId() ) ? p.getContractMomentInGroup( g.getId() ).format( 'YYYY-MM-DD' ) : '' %>"
data-id="input-contract-moment"
class="form-control" />
</div>
</div>
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-update-contract">
<%= _lc( 'BTN_SAVE_MEMBER_CONTRACT' ) %>
</button>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'APPOINTMENT_LOG' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12">
<div class="form-group" style="margin-bottom:0;">
<label for="appointment-log-from">
<%= _lc( 'FROM' ) %>
</label>
<input type="date"
id="appointment-log-from"
value="<%= p.getContractMomentInGroup( g.getId() ) ? p.getContractMomentInGroup( g.getId() ).format( 'YYYY-MM-DD' ) : moment().subtract( 30, 'days' ).format( 'YYYY-MM-DD' ) %>"
data-id="input-appointment-log-from"
class="form-control" />
</div>
</div>
<div class="col-lg-6 col-md-12">
<div class="form-group" style="margin-bottom:0;">
<label for="appointment-log-until">
<%= _lc( 'UNTIL' ) %>
</label>
<input type="date"
id="appointment-log-until"
value="<%= moment().format( 'YYYY-MM-DD' ) %>"
data-id="input-appointment-log-until"
class="form-control" />
</div>
</div>
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-update-appointment-log">
<%= _lc( 'BTN_UPDATE_APPOINTMENT_LOG' ) %>
</button>
</div>
</div>
<div class="row">
<div class="col"
data-id="container-appointment-log">
<i class="fas fa-spinner fa-spin"></i>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'ADMIN_NOTES' ) %></strong><small> - <%= _lc( 'ADMIN_NOTES_INFO' ) %></small>
</div>
</div>
<div class="row">
<div class="col"
data-id="admin-note">
<i class="fas fa-spinner fa-spin"></i>
</div>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-save-admin-note">
<%= _lc( 'BTN_SAVE_ADMIN_NOTE' ) %>
</button>
</div>
</div>
<% } %>
</div>
<div class="col-sm-12">
<small><i><%= _lc( 'ASSIGNED_MEMBER_CATEOGORY_INFO' ) %></i></small>
</div>
<div class="col">
<a href="#/course/categories/<%= g.getId() %>"
class="btn btn-primary btn-sm">
<%= _lc( 'BTN_NAVIGATE_TO_CATEGORY_MANAGEMENT' ) %>
</a>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'MEMBER_CONTRACT' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group" style="margin-bottom:0;">
<label for="appointment-log-until">
<%= _lc( 'CONTRACT_NAME' ) %>
</label>
<input type="text"
class="form-control"
maxlength="128"
value="<%= p.getContractInGroup( g.getId() ) ? p.getContractInGroup( g.getId() ) : '' %>"
data-id="input-contract" />
</div>
</div>
<div class="col-6">
<div class="form-group" style="margin-bottom:0;">
<label for="contract-date">
<%= _lc( 'CONTRACT_DATE' ) %>
</label>
<input type="date"
id="contract-date"
value="<%= p.getContractMomentInGroup( g.getId() ) ? p.getContractMomentInGroup( g.getId() ).format( 'YYYY-MM-DD' ) : '' %>"
data-id="input-contract-moment"
class="form-control" />
</div>
</div>
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-update-contract">
<%= _lc( 'BTN_SAVE_MEMBER_CONTRACT' ) %>
</button>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'APPOINTMENT_LOG' ) %></strong>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12">
<div class="form-group" style="margin-bottom:0;">
<label for="appointment-log-from">
<%= _lc( 'FROM' ) %>
</label>
<input type="date"
id="appointment-log-from"
value="<%= p.getContractMomentInGroup( g.getId() ) ? p.getContractMomentInGroup( g.getId() ).format( 'YYYY-MM-DD' ) : moment().subtract( 30, 'days' ).format( 'YYYY-MM-DD' ) %>"
data-id="input-appointment-log-from"
class="form-control" />
</div>
</div>
<div class="col-lg-6 col-md-12">
<div class="form-group" style="margin-bottom:0;">
<label for="appointment-log-until">
<%= _lc( 'UNTIL' ) %>
</label>
<input type="date"
id="appointment-log-until"
value="<%= moment().format( 'YYYY-MM-DD' ) %>"
data-id="input-appointment-log-until"
class="form-control" />
</div>
</div>
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-update-appointment-log">
<%= _lc( 'BTN_UPDATE_APPOINTMENT_LOG' ) %>
</button>
</div>
</div>
<div class="row">
<div class="col"
data-id="container-appointment-log">
<i class="fas fa-spinner fa-spin"></i>
</div>
</div>
<hr />
<div class="row">
<div class="col">
<strong><%= _lc( 'ADMIN_NOTES' ) %></strong><small> - <%= _lc( 'ADMIN_NOTES_INFO' ) %></small>
</div>
</div>
<div class="row">
<div class="col"
data-id="admin-note">
<i class="fas fa-spinner fa-spin"></i>
</div>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary btn-sm"
data-id="btn-save-admin-note">
<%= _lc( 'BTN_SAVE_ADMIN_NOTE' ) %>
</button>
</div>
</div>
<% } else { %>
<div class="row">
<div class="col">
<%= _lc( 'SELECT_MEMBER_TO_EDIT' ) %>
<div class="row">
<div class="col">
<%= _lc( 'SELECT_MEMBER_TO_EDIT' ) %>
</div>
</div>
</div>
<% } %>

+ 18
- 0
src/client/app/tmpl/group-member-management.html Vedi File

@@ -46,6 +46,12 @@
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_NOT_APPROVED' ) %> (<%= membersNotApproved.length %>)
</a>
</li>
<li class="nav-item">
<a class="nav-link <%= ( activeTab == 'deleted' ) ? 'active' : '' %>" data-content="group-members-deleted">
<i class="fas fa-users"></i>
<%= _lc( 'GROUP_MANAGEMENT_MEMBERS_DELETED' ) %> (<%= membersDeleted.length %>)
</a>
</li>
</ul>
</div>
<div class="card-body" data-id="member-container">
@@ -86,6 +92,18 @@
</div>
</div>
<div id="group-members-deleted" class="group-members-content group-members-tab-content <%= ( activeTab == 'deleted' ) ? 'active' : '' %>">
<div class="table-responsive">
<table class="table table-profiles">
<tbody>
<% for ( var mi = 0; mi < membersDeleted.length; mi++ ) { %>
<%=raw app.core.View.getTemplate( 'group-member-management-row', { m : membersDeleted[ mi ], g : group } ) %>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
<div class="card-footer">
</div>


+ 1
- 1
src/client/app/tmpl/gui-navbar.html Vedi File

@@ -1,4 +1,4 @@
<nav class="navbar fixed-top bg-gradient">
<nav class="navbar fixed-bottom bg-gradient">
<div class="container-fluid">
<a class="navbar-brand" href="#/home">
<!--


+ 3
- 2
src/client/app/tmpl/home-appointment-item.html Vedi File

@@ -46,7 +46,7 @@
<% } %>
</div>
<div class="appointment-subject">
<%= a.getSubject() %>
<%= a.getIcon() %> <%= a.getSubject() %>
<% if ( a.isDraft() ) { %>
<span class="badge badge-warning">Entwurf</span>
<% } %>
@@ -58,7 +58,8 @@
<% displayedDateStart = mStart.format( 'ddd' ) + ' ' + mStart.format( 'D.M.' ) + ' ' + mStart.format( 'HH:mm' ) %>
<% displayedDateEnd = mEnd.format( 'ddd' ) + ' ' + mEnd.format( 'D.M.' ) + ' ' + mEnd.format( 'HH:mm' ) %>
<% } %>
<i class="far fa-clock"></i> <%= displayedDateStart %> - <%= displayedDateEnd %> | <i class="far fa-thumbs-up"></i> <%= a.getNumAttendeesAccepted() %> <% if ( 0 < a.getMaxAttendees() ) { %> | <%= a.getMaxAttendees() - a.getNumAttendeesAccepted() %> verfügbar <% } %> | <%= group ? group.getName() : '' %>
<% var available = a.getMaxAttendees() - a.getNumAttendeesAccepted() >= 0 ? a.getMaxAttendees() - a.getNumAttendeesAccepted() : 0 %>
<i class="far fa-clock"></i> <%= displayedDateStart %> - <%= displayedDateEnd %> | <i class="far fa-thumbs-up"></i> <%= a.getNumAttendeesAccepted() %> <% if ( 0 < a.getMaxAttendees() ) { %> | <%= available %> verfügbar <% } %> | <%= group ? group.getName() : '' %>
</div>
</div>
<% if ( a.getVProfileCanAttend() ) { %>


+ 1
- 1
src/client/app/tmpl/home-modal-appointment-filter.html Vedi File

@@ -1,4 +1,4 @@
<form>
<form style="overflow: auto; overflow-x: hidden; max-height: 60vh">
<div class="row">
<div class="col">
<div class="form-group">


+ 11
- 7
src/client/app/tmpl/home.html Vedi File

@@ -79,11 +79,17 @@
<h4>
<%= currentMonth %>
<% if ( monthHeader === null ) { %>
<span class="float-right clickable" style="margin-right: 12px">
<div data-type="btn-filter"
class="<%= ( false === filter.isModified ) ? 'text-primary' : 'text-danger' %>"
style="font-size: smaller">
<i class="fas fa-filter"></i>
<span class="float-right clickable search-content" style="margin-right: 12px">
<div class="right-content">
<div class="search-box">
<i class="fa fa-search"></i>
<input data-id="appointment-search-filter" type="text" class="form-control form-control-sm">
</div>
<div data-type="btn-filter"
class="<%= ( false === filter.isModified ) ? 'text-primary' : 'text-danger' %>"
style="font-size: smaller">
<i class="fas fa-filter"></i>
</div>
</div>
</span>
<% } %>
@@ -110,6 +116,4 @@
</div>
<% } %>
<%=raw app.core.View.getTemplate( 'home-pager', { pager : pager } ) %>
<% } %>

+ 2
- 1
src/client/manager/js/app/components/appointments/appointment-select-table.html Vedi File

@@ -85,8 +85,9 @@
</table>
</div>
/* todo: refactoring into css file */
<style>
/* todo: refactoring into css file */
/* spawntree */
.button-group {
display: flex;


+ 2
- 0
src/client/manager/js/app/components/members/member-data-table.html Vedi File

@@ -9,6 +9,7 @@
<th>Postleitzahl</th>
<th>Stadt</th>
<th>Telefon/Mobile</th>
<th>Status</th>
<th></th>
</tr>
</thead>
@@ -22,6 +23,7 @@
<td><%= members[ mi ].zip ? members[ mi ].zip : '' %></td>
<td><%= members[ mi ].city ? members[ mi ].city : '' %></td>
<td><%= members[ mi ].phone ? members[ mi ].phone : '---' %> / <%= members[ mi ].mobile ? members[ mi ].mobile : '---' %></td>
<td><%= members[ mi ].getGroupStatusText() %></td>
<td class="text-center">
<button type="button"
data-type="btn-edit-memeber"


+ 24
- 0
src/client/manager/js/app/model/UserProfile.js Vedi File

@@ -102,6 +102,30 @@ const UserProfile = {
}
return gd;
},
getGroupStatus : function( groupId )
{
if ( !groupId ) {
groupId = this.contextGroupId;
}
return this.getGroupData(groupId).status;
},
getGroupStatusText : function( groupId )
{
if ( !groupId ) {
groupId = this.contextGroupId;
}
switch (this.getGroupData(groupId).status) {
case 'active':
return 'aktiv';
case 'inactive':
return 'inaktiv';
case 'not_approved':
return 'unbestätigt';
case 'deleted':
return 'gelöscht';
}
return '';
},
getRoleName : function()
{
var gd = this.getGroupData( this.contextGroupId ),


+ 2
- 1
src/client/manager/js/app/views/appointments/AppointmentCalendar.js Vedi File

@@ -23,7 +23,8 @@ const AppointmentCalendar = {
'Team',
'getDetails',
{
teamId : p.groupId
teamId : p.groupId,
includeMembers: true
},
function( res )
{


+ 2
- 1
src/client/manager/js/app/views/appointments/AppointmentList.js Vedi File

@@ -25,7 +25,8 @@ const AppointmentList = {
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true
},
function( res )
{


+ 2
- 1
src/client/manager/js/app/views/contract/ContractCharging.js Vedi File

@@ -40,7 +40,8 @@ const ContractCharging = {
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true
},
function( res )
{


+ 2
- 1
src/client/manager/js/app/views/contract/ContractCreate.js Vedi File

@@ -25,7 +25,8 @@ const ContractCreate = {
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true
},
function( res )
{


+ 2
- 1
src/client/manager/js/app/views/corona/CoronaSpecial.js Vedi File

@@ -27,7 +27,8 @@ const CoronaSpecial = {
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true
},
function( res )
{


+ 2
- 1
src/client/manager/js/app/views/kkspecial/EmployeeList.js Vedi File

@@ -44,7 +44,8 @@ const EmployeeList = {
'Team',
'getDetails',
{
teamId : p.get( 'groupId' )
teamId : p.get( 'groupId' ),
includeMembers: true
},
function( res )
{


+ 4
- 2
src/client/manager/js/app/views/members/MemberList.js Vedi File

@@ -22,7 +22,9 @@ const MemberList = {
'Team',
'getDetails',
{
teamId : p.get( 'groupId' )
teamId : p.get( 'groupId' ),
includeMembers: true
},
function( res )
{
@@ -39,7 +41,7 @@ const MemberList = {
this.createComponent(
'member-data-table',
$container.find( '[f-id="container-member-data-table"]' ).first().get( 0 ),
members
members,
);
}
$container.find( '.sk-loading' ).toggleClass( 'sk-loading' );


+ 2
- 1
src/client/manager/js/app/views/settings/SettingsAccess.js Vedi File

@@ -26,7 +26,8 @@ const SettingsAccess = {
'Team',
'getDetails',
{
teamId : groupId
teamId : groupId,
includeMembers: true
},
function( res )
{


+ 38
- 0
src/server/patches/1addActiveStateToProfile.php Vedi File

@@ -0,0 +1,38 @@
<?php

require_once __DIR__ . '/../server/config/boot_global.php';
require_once __DIR__ . '/../server/config/boot_local.php';

function patch_addActiveStateToProfile()
{

$db = TB_Shared_Db_TeamData::get();
$sql = 'SELECT * FROM profile';
$stmt = $db->query( $sql );
$res = $stmt->fetchAll();

$db->beginTransaction();
foreach( $res as $row )
{
if ( is_array( $row ) && isset( $row[ 'id' ] ) )
{

$entProfile = TB_Shared_Ent_TeamData_Profile::get( $row[ 'id' ] );
$resTeam = [];
foreach ($entProfile->teams_js as $team) {
$tmpTeam = $team;
$tmpTeam['status'] = 'active';
$resTeam[] = $tmpTeam;
}
$entProfile->teams_js = $resTeam;
$entProfile->save();
unset($team);
unset( $entProfile );
}
}
$db->commit();

echo "DONE...";
}

patch_addActiveStateToProfile();

tools/patches/2addDbTermsConditionsToTeam.php → src/server/patches/2addDbTermsConditionsToTeam.php Vedi File

@@ -1,7 +1,7 @@
<?php

require_once __DIR__ . '/../../src/server/server/config/boot_global.php';
require_once __DIR__ . '/../../src/server/server/config/boot_local.php';
require_once __DIR__ . '/../server/config/boot_global.php';
require_once __DIR__ . '/../server/config/boot_local.php';

function patch()
{

tools/patches/3addTermsAcceptedToProfile.php → src/server/patches/3addTermsAcceptedToProfile.php Vedi File

@@ -1,7 +1,7 @@
<?php

require_once __DIR__ . '/../../src/server/server/config/boot_global.php';
require_once __DIR__ . '/../../src/server/server/config/boot_local.php';
require_once __DIR__ . '/../server/config/boot_global.php';
require_once __DIR__ . '/../server/config/boot_local.php';

function patch_addTermsAcceptedToProfile()
{

tools/patches/4addDbNewUsersInactive.php → src/server/patches/4addDbNewUsersInactive.php Vedi File

@@ -1,7 +1,7 @@
<?php

require_once __DIR__ . '/../../src/server/server/config/boot_global.php';
require_once __DIR__ . '/../../src/server/server/config/boot_local.php';
require_once __DIR__ . '/../server/config/boot_global.php';
require_once __DIR__ . '/../server/config/boot_local.php';

function patch()
{
@@ -9,7 +9,7 @@ function patch()
$db = TB_Shared_Db_TeamData::get();

$sql = "ALTER TABLE team
ADD COLUMN new_users_inactive TINYINT(1) NOT NULL DEFAULT 1 AFTER terms_conditions_active;";
ADD COLUMN new_users_inactive TINYINT(1) NOT NULL DEFAULT 0 AFTER terms_conditions_active;";
$stmt = $db->query( $sql );

$sql = "UPDATE team SET new_users_inactive = 0 WHERE 1";

+ 18
- 0
src/server/patches/5addIconsToAppointments.php Vedi File

@@ -0,0 +1,18 @@
<?php

require_once __DIR__ . '/../server/config/boot_global.php';
require_once __DIR__ . '/../server/config/boot_local.php';

function patch()
{

$db = TB_Shared_Db_TeamData::get();

$sql = "ALTER TABLE appointment
ADD COLUMN icon varchar(50) DEFAULT '' AFTER visibility;";
$stmt = $db->query( $sql );

echo "DONE...";
}

patch();

tools/patches/old/addCategoryIdsJsToAppointments.php → src/server/patches/old/addCategoryIdsJsToAppointments.php Vedi File


tools/patches/old/addDateTimeRejectToExistingAppointments.php → src/server/patches/old/addDateTimeRejectToExistingAppointments.php Vedi File


tools/patches/old/addGeneralCourseCategoryToTeam.php → src/server/patches/old/addGeneralCourseCategoryToTeam.php Vedi File


tools/patches/old/addPublicIdToProfile.php → src/server/patches/old/addPublicIdToProfile.php Vedi File


tools/patches/old/addTeamCategoriesToAdminProfiles.php → src/server/patches/old/addTeamCategoriesToAdminProfiles.php Vedi File


tools/patches/old/replaceCategoryByCategoryIdInAppointment.php → src/server/patches/old/replaceCategoryByCategoryIdInAppointment.php Vedi File


+ 14
- 1
src/server/server/config/boot_global.php Vedi File

@@ -42,5 +42,18 @@ function _lc( $txt, array $params = NULL ) {
*
*/
function _xss( $txt ) {
return ( is_string( $txt ) ) ? filter_var( $txt, FILTER_SANITIZE_STRING ) : $txt;
if (!is_string($txt)) {
return $txt;
}
// Entfernt alle HTML- und PHP-Tags
$txt = strip_tags($txt);
// Konvertiert spezielle Zeichen in HTML-Entitäten
$txt = htmlspecialchars($txt, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// Entfernt Steuerzeichen
$txt = preg_replace('/[\x00-\x1F\x7F]/u', '', $txt);
return $txt;
}

+ 15
- 4
src/server/server/config/boot_local.php Vedi File

@@ -6,13 +6,24 @@
// General
Francis_Utils_Config::set( 'url.client', 'src/client/app' );
// DB settings
Francis_Utils_Config::set( 'db.tbcore.host', 'database' );
// DB settings (docker)
//Francis_Utils_Config::set( 'db.tbcore.host', 'database' );
//Francis_Utils_Config::set( 'db.tbcore.name', 'pb_core' ); // probudy core
//Francis_Utils_Config::set( 'db.tbcore.user', 'root' );
//Francis_Utils_Config::set( 'db.tbcore.pass', 'root' );
//
//Francis_Utils_Config::set( 'db.tbteamdata.host', 'database' );
//Francis_Utils_Config::set( 'db.tbteamdata.name', 'pb_teamdata' );
//Francis_Utils_Config::set( 'db.tbteamdata.user', 'root' );
//Francis_Utils_Config::set( 'db.tbteamdata.pass', 'root' );
// DB settings (ddev)
Francis_Utils_Config::set( 'db.tbcore.host', 'db' );
Francis_Utils_Config::set( 'db.tbcore.name', 'pb_core' ); // probudy core
Francis_Utils_Config::set( 'db.tbcore.user', 'root' );
Francis_Utils_Config::set( 'db.tbcore.pass', 'root' );
Francis_Utils_Config::set( 'db.tbteamdata.host', 'database' );
Francis_Utils_Config::set( 'db.tbteamdata.host', 'db' );
Francis_Utils_Config::set( 'db.tbteamdata.name', 'pb_teamdata' );
Francis_Utils_Config::set( 'db.tbteamdata.user', 'root' );
Francis_Utils_Config::set( 'db.tbteamdata.pass', 'root' );
@@ -39,4 +50,4 @@ Francis_Utils_Config::set( 'slack.logWebhook', "https://hooks.slack.com/services
Francis_Utils_Config::set( 'analytics.trackingId', 'UA-139730172-1' );
// Manager console
Francis_Utils_Config::set( 'url.manager', 'https://manager.probuddy.de/' );
Francis_Utils_Config::set( 'url.manager', '/manager' );

+ 41
- 32
src/server/server/control/TB_Server_Control_Account.php Vedi File

@@ -3,6 +3,11 @@
* (c)1337 aheadware.com - All rights reserved
********************************************************************************/
require Francis_Utils_Config::get( 'path.root' ).'/vendor/autoload.php';
use Ramsey\Uuid\Uuid;
/**
* Class TB_Server_Control_Account
*/
@@ -23,63 +28,67 @@ class TB_Server_Control_Account
$resp->addData( 'profile', $session->getProfile() );
$resp->addData( 'own_teams', TB_Shared_Ent_TeamData_Team::getByProfile( $session->getProfile() ) );
if ($params->get('includeNumFutureAttendances')) {
$appointmentsAccepted = TB_Shared_Ent_TeamData_Appointment::getAcceptedByProfile(
$session->getProfile()->id,
false,
true
);
$resp->addData( 'numFutureAttendances', count($appointmentsAccepted) );
}
return $resp;
}
public static function delete( TB_Server_Core_RequestData $params )
{
throw new \Exception( 'Does not work anymore.' );
/*
$resp = new TB_Server_Core_Response();
$session = TB_Server_Core_Session::get();
if ( is_null( $session ) ) {
throw new Exception( "No session found. Deletion failed." );
}
$account = $session->getAccount();
if ( is_null( $account ) ) throw new Exception( "No account in session. Deletion failed." );
if ( is_null( $account ) ) {
throw new Exception( "No account in session. Deletion failed." );
}
$profile = $session->getProfile();
$appointmentsToUpdate = TB_Shared_Ent_TeamData_Appointment::getAcceptedByProfile( $profile->id );
$dbCore = TB_Shared_Db_Core::get();
$dbTeam = TB_Shared_Db_TeamData::get();
if ( is_null( $profile ) ) {
throw new Exception( "No profile. Deletion failed." );
}
if ( $profile->ownsATeam() )
{
throw new \Exception( 'Cannot delete a profile who owns a team' );
}
try
{
$dbCore->beginTransaction();
$dbTeam->beginTransaction();
$account->delete();
$profile->delete();
$dbCore->commit();
$dbTeam->commit();
}
catch( Exception $e )
$appointmentsAccepted =
TB_Shared_Ent_TeamData_Appointment::getAcceptedByProfile(
$profile->id, false, true
);
if ( $appointmentsAccepted && count( $appointmentsAccepted ) > 0 )
{
$dbCore->rollBack();
$dbTeam->rollBack();
throw $e;
throw new \Exception( 'User has accepted appointments in the future' );
}
if ( $appointmentsToUpdate && count( $appointmentsToUpdate ) > 0 )
{
$a = $profile->teams_js;
foreach( $appointmentsToUpdate as $app )
{
TB_Server_Control_Appointment::processWaitingList( $app );
}
$resTeam = [];
foreach ($profile->teams_js as $team) {
$tmpTeam = $team;
$tmpTeam['status'] = TB_Shared_Ent_TeamData_Profile::STATUS_DELETED;
$resTeam[] = $tmpTeam;
}
$profile->teams_js = $resTeam;
$uuid = \Ramsey\Uuid\Uuid::uuid4()->toString();
$account->email = $account->email . $uuid;
$account->save();
$profile->save();
return $resp;
*/
}
/**


+ 23
- 15
src/server/server/control/TB_Server_Control_Appointment.php Vedi File

@@ -25,7 +25,7 @@ class TB_Server_Control_Appointment {
{
throw new \Exception( 'No team id specified.' );
}
$icon = _xss( $params->get( 'icon' ) );
$sessionProfile = TB_Server_Core_Session::get()->getProfile();
if ( false === $sessionProfile->isAdminOfTeam( $forTeamId ) )
{
@@ -68,11 +68,15 @@ class TB_Server_Control_Appointment {
}
// Link replacements
if ( 0 === mb_strlen( $comment ) )
{
$comment = NULL;
if (empty($comment) || !is_string($comment)) {
$comment = null;
} else {
$comment = TB_Server_Utils_Helper::makeUrltoLink($comment);
$comment = trim($comment);
if ($comment === '') {
$comment = null;
} else {
$comment = TB_Server_Utils_Helper::makeUrltoLink($comment);
}
}
$visibility = $params->get( 'visibility', TB_Shared_Ent_TeamData_Appointment::VISIBLE_ALL );
@@ -121,6 +125,7 @@ class TB_Server_Control_Appointment {
$appointment = new TB_Shared_Ent_TeamData_Appointment();
$appointment->team_id = $forTeamId;
$appointment->icon = $icon;
$appointment->category_ids_js = $categoryIds;
$appointment->visibility = $visibility;
$appointment->subject = _xss( $params->get( 'subject' ) );
@@ -409,19 +414,19 @@ class TB_Server_Control_Appointment {
$comment = _xss( $params->get( 'comment' ) );
if ( 0 === mb_strlen( $comment ) )
{
$comment = NULL;
}
// Link replacements
if ( 0 === mb_strlen( $comment ) )
{
$comment = NULL;
if (empty($comment) || !is_string($comment)) {
$comment = null;
} else {
$comment = TB_Server_Utils_Helper::makeUrltoLink($comment);
$comment = trim($comment);
if ($comment === '') {
$comment = null;
} else {
$comment = TB_Server_Utils_Helper::makeUrltoLink($comment);
}
}
$visibility = $params->get( 'visibility', TB_Shared_Ent_TeamData_Appointment::VISIBLE_ALL );
if ( $visibility !== TB_Shared_Ent_TeamData_Appointment::VISIBLE_ALL && $visibility !== TB_Shared_Ent_TeamData_Appointment::VISIBLE_CATEGORIES )
@@ -497,6 +502,7 @@ class TB_Server_Control_Appointment {
$appointment->visibility = $visibility;
$appointment->category_ids_js = $categoryIds;
$appointment->subject = _xss( $params->get( 'subject' ) );
$appointment->icon = _xss( $params->get( 'icon' ) );
$appointment->start_dt->setTimezone( $defaultDtz );
$appointment->end_dt->setTimezone( $defaultDtz );
@@ -862,10 +868,12 @@ class TB_Server_Control_Appointment {
throw new \Exception( 'Not visible for current session profile.' );
}
if ( false === $appointment->isAttendableByProfile( $sessionProfile ) )
$isAttendableByProfile = $appointment->isAttendableByProfile( $sessionProfile );
if ( false === $isAttendableByProfile )
{
throw new \Exception( 'Not possible to attend by current profile.' );
}
$appointment->v_profile_can_attend = $isAttendableByProfile;
//$attendeeData = TB_Shared_Ent_TeamData_Attendee::getWithProfileDataByAppointmentId( $appointment->id );
$attendeeData = TB_Shared_Ent_TeamData_Attendee::getByAppointmentId( $appointment->id );


+ 1
- 1
src/server/server/control/TB_Server_Control_Auth.php Vedi File

@@ -691,7 +691,7 @@ class TB_Server_Control_Auth
$team->category = _xss( $teamCategory );
$team->affiliate_id = is_string( $affiliateId ) ? _xss( $affiliateId ) : NULL;
$team->terms_conditions_active = 0;
$team->new_users_inactive = 1;
$team->new_users_inactive = 0;
$team->save();
// Set default course categories


+ 28
- 3
src/server/server/control/TB_Server_Control_Team.php Vedi File

@@ -83,6 +83,7 @@ class TB_Server_Control_Team {
$includeEmail = $params->get( 'includeEmails' );
$adminsOnly = $params->get( 'adminsOnly' );
$includeMembers = $params->get( 'includeMembers' );
$activeOnly = $params->get( 'activeOnly');
$members = array();
$sessionProfile = TB_Server_Core_Session::get()->getProfile();
@@ -113,7 +114,27 @@ class TB_Server_Control_Team {
$filteredMembers[] = $member;
}
} else {
$filteredMembers[] = $member;
$teamData = $member->getTeamsData($teamId);
if ($activeOnly === true) {
if ($teamData['status'] === TB_Shared_Ent_TeamData_Profile::STATUS_ACTIVE) {
$filteredMembers[] = $member;
}
} else {
if ($teamData['status'] === TB_Shared_Ent_TeamData_Profile::STATUS_DELETED) {
$member->street = "";
$member->zip_code = "";
$member->city = "";
$member->country = "";
$member->mobile = "";
$member->phone = "";
$member->gender = "";
$member->pic_url = "";
$member->birthday_dt = "";
$member->misc = "";
$member->status = "";
}
$filteredMembers[] = $member;
}
}
}
}
@@ -127,7 +148,10 @@ class TB_Server_Control_Team {
/** @var TB_Shared_Ent_TeamData_Profile $member */
foreach ( $members as $member )
{
array_push( $accountIds, $member->account_id );
$teamData = $member->getTeamsData($teamId);
if ($teamData['status'] !== TB_Shared_Ent_TeamData_Profile::STATUS_DELETED) {
array_push( $accountIds, $member->account_id );
}
}
$emails = TB_Shared_Ent_Core_Account::getEmailsForAccountIds( $accountIds );
@@ -161,6 +185,7 @@ class TB_Server_Control_Team {
$resp = new TB_Server_Core_Response();
$teamId = $params->get( 'teamId' );
$activeOnly = $params->get( 'activeOnly');
$sessionProfile = TB_Server_Core_Session::get()->getProfile();
if ( false === $sessionProfile->isInTeam( $teamId ) )
@@ -176,7 +201,7 @@ class TB_Server_Control_Team {
return $resp;
}
$resp->addData( 'members', TB_Shared_Ent_TeamData_Profile::getProfilesByTeamId( $team->id ) );
$resp->addData( 'members', TB_Shared_Ent_TeamData_Profile::getProfilesByTeamId( $team->id, $activeOnly ) );
return $resp;
}


+ 14
- 3
src/server/shared/ent/teamdata/TB_Shared_Ent_TeamData_Appointment.php Vedi File

@@ -8,6 +8,7 @@
*
* @property integer $id
* @property integer $team_id
* @property string $icon
* @property string $category_id
* @property array $category_ids_js
* @property string $visibility
@@ -139,8 +140,11 @@ class TB_Shared_Ent_TeamData_Appointment extends Francis_Db_Row
* @param $profileId
* @param bool $includePastAppointments
*/
public static function getAcceptedByProfile( $profileId, $includePastAppointments = false )
{
public static function getAcceptedByProfile(
$profileId,
$includePastAppointments = false,
$excludeRejectDeadlineExpired = false
) {
$sql = 'SELECT appointment.* FROM appointment ';
$sql .= 'LEFT JOIN attendee ON appointment.id = attendee.appointment_id ';
$sql .= 'WHERE ';
@@ -149,9 +153,16 @@ class TB_Shared_Ent_TeamData_Appointment extends Francis_Db_Row
$sql .= 'attendee.status = "' . TB_Shared_Ent_TeamData_Attendee::STATUS_ACCEPTED . '" ';
if ( false === $includePastAppointments )
{
$sql .= 'AND appointment.start_dt < UTC_TIMESTAMP()';
$sql .= 'AND appointment.start_dt > UTC_TIMESTAMP()';
}
if ( true === $excludeRejectDeadlineExpired ) {
// By this we do not include appointments where reject deadline has passed expired
$sql .= 'AND appointment.deadline_reject_dt > UTC_TIMESTAMP()';
}
//SELECT appointment.* FROM appointment LEFT JOIN attendee ON appointment.id = attendee.appointment_id WHERE appointment.state = "open" AND attendee.profile_id = 16772 AND attendee.status = "accepted";
return self::findMany( $sql, array( ':profile_id' => $profileId ) );
}


+ 10
- 3
src/server/shared/ent/teamdata/TB_Shared_Ent_TeamData_Profile.php Vedi File

@@ -40,6 +40,7 @@ class TB_Shared_Ent_TeamData_Profile extends Francis_Db_Row {
const STATUS_NOT_APPROVED = 'not_approved';
const STATUS_ACTIVE = 'active';
const STATUS_INACTIVE = 'inactive';
const STATUS_DELETED = 'deleted';
// Enums
const GENDER_MALE = 'male';
@@ -74,7 +75,8 @@ class TB_Shared_Ent_TeamData_Profile extends Francis_Db_Row {
return array(
self::STATUS_ACTIVE,
self::STATUS_INACTIVE,
self::STATUS_NOT_APPROVED
self::STATUS_NOT_APPROVED,
self::STATUS_DELETED
);
}
@@ -754,9 +756,14 @@ class TB_Shared_Ent_TeamData_Profile extends Francis_Db_Row {
* @param $teamId
* @return array
*/
public static function getProfilesByTeamId( $teamId )
public static function getProfilesByTeamId( $teamId, $activeOnly = null )
{
$whereJson = json_encode( array( 'team_id' => $teamId ) );
$params = array('team_id' => "$teamId");
if ($activeOnly === true ) {
$params['status'] = self::STATUS_ACTIVE;
}
$whereJson = json_encode( $params );
$sql = 'SELECT * FROM ' . self::getTable() . ' WHERE ';
$sql .= 'JSON_CONTAINS( `teams_js`, :where_json ) ';
$sql .= 'ORDER BY last_name ASC';


+ 25
- 0
vendor/autoload.php Vedi File

@@ -0,0 +1,25 @@
<?php

// autoload.php @generated by Composer

if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4::getLoader();

+ 463
- 0
vendor/brick/math/CHANGELOG.md Vedi File

@@ -0,0 +1,463 @@
# Changelog

All notable changes to this project will be documented in this file.

## [0.12.1](https://github.com/brick/math/releases/tag/0.12.1) - 2023-11-29

⚡️ **Performance improvements**

- `BigNumber::of()` is now faster, thanks to [@SebastienDug](https://github.com/SebastienDug) in [#77](https://github.com/brick/math/pull/77).

## [0.12.0](https://github.com/brick/math/releases/tag/0.12.0) - 2023-11-26

💥 **Breaking changes**

- Minimum PHP version is now 8.1
- `RoundingMode` is now an `enum`; if you're type-hinting rounding modes, you need to type-hint against `RoundingMode` instead of `int` now
- `BigNumber` classes do not implement the `Serializable` interface anymore (they use the [new custom object serialization mechanism](https://wiki.php.net/rfc/custom_object_serialization))
- The following breaking changes only affect you if you're creating your own `BigNumber` subclasses:
- the return type of `BigNumber::of()` is now `static`
- `BigNumber` has a new abstract method `from()`
- all `public` and `protected` functions of `BigNumber` are now `final`

## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16

💥 **Breaking changes**

- Minimum PHP version is now 8.0
- Methods accepting a union of types are now strongly typed<sup>*</sup>
- `MathException` now extends `Exception` instead of `RuntimeException`

<sup>* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods
internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string`
first.</sup>

## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11

👌 **Improvements**

- `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic

## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02

✨ **New features**

- `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers

## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18

💥 **Breaking changes**

- Minimum PHP version is now 7.4

## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15

🚀 **Compatibility with PHP 8.1**

- Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham

## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20

🐛 **Bug fix**

- Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55).

## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19

✨ **New features**

- `BigInteger::not()` returns the bitwise `NOT` value

🐛 **Bug fixes**

- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available

## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18

👌 **Improvements**

- `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal`

💥 **Breaking changes**

- Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead
- Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead

## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19

🐛 **Bug fix**

- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available

## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18

🚑 **Critical fix**

- This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle.

✨ **New features**

- `BigInteger::modInverse()` calculates a modular multiplicative inverse
- `BigInteger::fromBytes()` creates a `BigInteger` from a byte string
- `BigInteger::toBytes()` converts a `BigInteger` to a byte string
- `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length
- `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds

💩 **Deprecations**

- `BigInteger::powerMod()` is now deprecated in favour of `modPow()`

## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15

🐛 **Fixes**

- added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable`

⚡️ **Optimizations**

- additional optimization in `BigInteger::remainder()`

## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18

✨ **New features**

- `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit

## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16

✨ **New features**

- `BigInteger::isEven()` tests whether the number is even
- `BigInteger::isOdd()` tests whether the number is odd
- `BigInteger::testBit()` tests if a bit is set
- `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number

## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03

🛠️ **Maintenance release**

Classes are now annotated for better static analysis with [psalm](https://psalm.dev/).

This is a maintenance release: no bug fixes, no new features, no breaking changes.

## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23

✨ **New feature**

`BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto.

## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21

✨ **New feature**

`BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different.

## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08

⚡️ **Performance improvements**

A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24.

## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25

🐛 **Bug fixes**

- `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected)

✨ **New features**

- `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet
- `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number

These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation.

💩 **Deprecations**

- `BigInteger::parse()` is now deprecated in favour of `fromBase()`

`BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences:

- the `$base` parameter is required, it does not default to `10`
- it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed

## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20

**Improvements**

- Safer conversion from `float` when using custom locales
- **Much faster** `NativeCalculator` implementation 🚀

You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before.

## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11

**New method**

`BigNumber::sum()` returns the sum of one or more numbers.

## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12

**Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20).

Thanks @manowark 👍

## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07

**New method**

`BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale.

## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06

**New method**

`BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k).

**New exception**

`NegativeNumberException` is thrown when calling `sqrt()` on a negative number.

## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08

**Performance update**

- Further improvement of `toInt()` performance
- `NativeCalculator` can now perform some multiplications more efficiently

## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07

Performance optimization of `toInt()` methods.

## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13

**Breaking changes**

The following deprecated methods have been removed. Use the new method name instead:

| Method removed | Replacement method |
| --- | --- |
| `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` |
| `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` |

---

**New features**

`BigInteger` has been augmented with 5 new methods for bitwise operations:

| New method | Description |
| --- | --- |
| `and()` | performs a bitwise `AND` operation on two numbers |
| `or()` | performs a bitwise `OR` operation on two numbers |
| `xor()` | performs a bitwise `XOR` operation on two numbers |
| `shiftedLeft()` | returns the number shifted left by a number of bits |
| `shiftedRight()` | returns the number shifted right by a number of bits |

Thanks to @DASPRiD 👍

## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20

**New method:** `BigDecimal::hasNonZeroFractionalPart()`

**Renamed/deprecated methods:**

- `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated
- `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated

## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21

**Performance update**

`BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available.

## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01

This is a maintenance release, no code has been changed.

- When installed with `--no-dev`, the autoloader does not autoload tests anymore
- Tests and other files unnecessary for production are excluded from the dist package

This will help make installations more compact.

## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02

Methods renamed:

- `BigNumber:sign()` has been renamed to `getSign()`
- `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()`
- `BigDecimal::scale()` has been renamed to `getScale()`
- `BigDecimal::integral()` has been renamed to `getIntegral()`
- `BigDecimal::fraction()` has been renamed to `getFraction()`
- `BigRational::numerator()` has been renamed to `getNumerator()`
- `BigRational::denominator()` has been renamed to `getDenominator()`

Classes renamed:

- `ArithmeticException` has been renamed to `MathException`

## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02

The base class for all exceptions is now `MathException`.
`ArithmeticException` has been deprecated, and will be removed in 0.7.0.

## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02

A number of methods have been renamed:

- `BigNumber:sign()` is deprecated; use `getSign()` instead
- `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead
- `BigDecimal::scale()` is deprecated; use `getScale()` instead
- `BigDecimal::integral()` is deprecated; use `getIntegral()` instead
- `BigDecimal::fraction()` is deprecated; use `getFraction()` instead
- `BigRational::numerator()` is deprecated; use `getNumerator()` instead
- `BigRational::denominator()` is deprecated; use `getDenominator()` instead

The old methods will be removed in version 0.7.0.

## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25

- Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5`
- Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead
- Method `BigNumber::toInteger()` has been renamed to `toInt()`

## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17

`BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php).
The JSON output is always a string.

## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31

This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.

## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06

The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again.

## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05

**New method: `BigNumber::toScale()`**

This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary.

## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04

**New features**
- Common `BigNumber` interface for all classes, with the following methods:
- `sign()` and derived methods (`isZero()`, `isPositive()`, ...)
- `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types
- `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods
- `toInteger()` and `toFloat()` conversion methods to native types
- Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type
- New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits
- New methods: `BigRational::quotient()` and `remainder()`
- Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException`
- Factory methods `zero()`, `one()` and `ten()` available in all classes
- Rounding mode reintroduced in `BigInteger::dividedBy()`

This release also comes with many performance improvements.

---

**Breaking changes**
- `BigInteger`:
- `getSign()` is renamed to `sign()`
- `toString()` is renamed to `toBase()`
- `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour
- `BigDecimal`:
- `getSign()` is renamed to `sign()`
- `getUnscaledValue()` is renamed to `unscaledValue()`
- `getScale()` is renamed to `scale()`
- `getIntegral()` is renamed to `integral()`
- `getFraction()` is renamed to `fraction()`
- `divideAndRemainder()` is renamed to `quotientAndRemainder()`
- `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode
- `toBigInteger()` does not accept a `$roundingMode` parameter anymore
- `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour
- `BigRational`:
- `getSign()` is renamed to `sign()`
- `getNumerator()` is renamed to `numerator()`
- `getDenominator()` is renamed to `denominator()`
- `of()` is renamed to `nd()`, while `parse()` is renamed to `of()`
- Miscellaneous:
- `ArithmeticException` is moved to an `Exception\` sub-namespace
- `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException`

## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31

Backport of two bug fixes from the 0.5 branch:
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.

## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16

New method: `BigDecimal::stripTrailingZeros()`

## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12

Introducing a `BigRational` class, to perform calculations on fractions of any size.

## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12

Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`.

`BigInteger::dividedBy()` now always returns the quotient of the division.

## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31

Backport of two bug fixes from the 0.5 branch:

- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.

## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11

New methods:
- `BigInteger::remainder()` returns the remainder of a division only
- `BigInteger::gcd()` returns the greatest common divisor of two numbers

## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07

Fix `toString()` not handling negative numbers.

## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07

`BigInteger` and `BigDecimal` now have a `getSign()` method that returns:
- `-1` if the number is negative
- `0` if the number is zero
- `1` if the number is positive

## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05

Minor performance improvements

## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04

The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`.

## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04

Stronger immutability guarantee for `BigInteger` and `BigDecimal`.

So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that.

## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02

Added `BigDecimal::divideAndRemainder()`

## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22

- `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters
- **minimum PHP version is now 5.6**
- continuous integration with PHP 7

## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01

- Added `BigInteger::power()`
- Added HHVM support

## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31

First beta release.


+ 20
- 0
vendor/brick/math/LICENSE Vedi File

@@ -0,0 +1,20 @@
The MIT License (MIT)

Copyright (c) 2013-present Benjamin Morel

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 39
- 0
vendor/brick/math/composer.json Vedi File

@@ -0,0 +1,39 @@
{
"name": "brick/math",
"description": "Arbitrary-precision arithmetic library",
"type": "library",
"keywords": [
"Brick",
"Math",
"Mathematics",
"Arbitrary-precision",
"Arithmetic",
"BigInteger",
"BigDecimal",
"BigRational",
"BigNumber",
"Bignum",
"Decimal",
"Rational",
"Integer"
],
"license": "MIT",
"require": {
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^10.1",
"php-coveralls/php-coveralls": "^2.2",
"vimeo/psalm": "5.16.0"
},
"autoload": {
"psr-4": {
"Brick\\Math\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Brick\\Math\\Tests\\": "tests/"
}
}
}

+ 754
- 0
vendor/brick/math/src/BigDecimal.php Vedi File

@@ -0,0 +1,754 @@
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Internal\Calculator;

/**
* Immutable, arbitrary-precision signed decimal numbers.
*
* @psalm-immutable
*/
final class BigDecimal extends BigNumber
{
/**
* The unscaled value of this decimal number.
*
* This is a string of digits with an optional leading minus sign.
* No leading zero must be present.
* No leading minus sign must be present if the value is 0.
*/
private readonly string $value;

/**
* The scale (number of digits after the decimal point) of this decimal number.
*
* This must be zero or more.
*/
private readonly int $scale;

/**
* Protected constructor. Use a factory method to obtain an instance.
*
* @param string $value The unscaled value, validated.
* @param int $scale The scale, validated.
*/
protected function __construct(string $value, int $scale = 0)
{
$this->value = $value;
$this->scale = $scale;
}

/**
* @psalm-pure
*/
protected static function from(BigNumber $number): static
{
return $number->toBigDecimal();
}

/**
* Creates a BigDecimal from an unscaled value and a scale.
*
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
*
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
* @param int $scale The scale of the number, positive or zero.
*
* @throws \InvalidArgumentException If the scale is negative.
*
* @psalm-pure
*/
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('The scale cannot be negative.');
}

return new BigDecimal((string) BigInteger::of($value), $scale);
}

/**
* Returns a BigDecimal representing zero, with a scale of zero.
*
* @psalm-pure
*/
public static function zero() : BigDecimal
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigDecimal|null $zero
*/
static $zero;

if ($zero === null) {
$zero = new BigDecimal('0');
}

return $zero;
}

/**
* Returns a BigDecimal representing one, with a scale of zero.
*
* @psalm-pure
*/
public static function one() : BigDecimal
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigDecimal|null $one
*/
static $one;

if ($one === null) {
$one = new BigDecimal('1');
}

return $one;
}

/**
* Returns a BigDecimal representing ten, with a scale of zero.
*
* @psalm-pure
*/
public static function ten() : BigDecimal
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigDecimal|null $ten
*/
static $ten;

if ($ten === null) {
$ten = new BigDecimal('10');
}

return $ten;
}

/**
* Returns the sum of this number and the given one.
*
* The result has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
*
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
*/
public function plus(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->value === '0' && $that->scale <= $this->scale) {
return $this;
}

if ($this->value === '0' && $this->scale <= $that->scale) {
return $that;
}

[$a, $b] = $this->scaleValues($this, $that);

$value = Calculator::get()->add($a, $b);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;

return new BigDecimal($value, $scale);
}

/**
* Returns the difference of this number and the given one.
*
* The result has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
*
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
*/
public function minus(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->value === '0' && $that->scale <= $this->scale) {
return $this;
}

[$a, $b] = $this->scaleValues($this, $that);

$value = Calculator::get()->sub($a, $b);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;

return new BigDecimal($value, $scale);
}

/**
* Returns the product of this number and the given one.
*
* The result has a scale of `$this->scale + $that->scale`.
*
* @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
*
* @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
*/
public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->value === '1' && $that->scale === 0) {
return $this;
}

if ($this->value === '1' && $this->scale === 0) {
return $that;
}

$value = Calculator::get()->mul($this->value, $that->value);
$scale = $this->scale + $that->scale;

return new BigDecimal($value, $scale);
}

/**
* Returns the result of the division of this number by the given one, at the given scale.
*
* @param BigNumber|int|float|string $that The divisor.
* @param int|null $scale The desired scale, or null to use the scale of this number.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @throws \InvalidArgumentException If the scale or rounding mode is invalid.
* @throws MathException If the number is invalid, is zero, or rounding was necessary.
*/
public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}

if ($scale === null) {
$scale = $this->scale;
} elseif ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}

if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
return $this;
}

$p = $this->valueWithMinScale($that->scale + $scale);
$q = $that->valueWithMinScale($this->scale - $scale);

$result = Calculator::get()->divRound($p, $q, $roundingMode);

return new BigDecimal($result, $scale);
}

/**
* Returns the exact result of the division of this number by the given one.
*
* The scale of the result is automatically calculated to fit all the fraction digits.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
* or the result yields an infinite number of digits.
*/
public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->value === '0') {
throw DivisionByZeroException::divisionByZero();
}

[, $b] = $this->scaleValues($this, $that);

$d = \rtrim($b, '0');
$scale = \strlen($b) - \strlen($d);

$calculator = Calculator::get();

foreach ([5, 2] as $prime) {
for (;;) {
$lastDigit = (int) $d[-1];

if ($lastDigit % $prime !== 0) {
break;
}

$d = $calculator->divQ($d, (string) $prime);
$scale++;
}
}

return $this->dividedBy($that, $scale)->stripTrailingZeros();
}

/**
* Returns this number exponentiated to the given value.
*
* The result has a scale of `$this->scale * $exponent`.
*
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
*/
public function power(int $exponent) : BigDecimal
{
if ($exponent === 0) {
return BigDecimal::one();
}

if ($exponent === 1) {
return $this;
}

if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
throw new \InvalidArgumentException(\sprintf(
'The exponent %d is not in the range 0 to %d.',
$exponent,
Calculator::MAX_POWER
));
}

return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
}

/**
* Returns the quotient of the division of this number by the given one.
*
* The quotient has a scale of `0`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotient(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}

$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);

$quotient = Calculator::get()->divQ($p, $q);

return new BigDecimal($quotient, 0);
}

/**
* Returns the remainder of the division of this number by the given one.
*
* The remainder has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function remainder(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);

if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}

$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);

$remainder = Calculator::get()->divR($p, $q);

$scale = $this->scale > $that->scale ? $this->scale : $that->scale;

return new BigDecimal($remainder, $scale);
}

/**
* Returns the quotient and remainder of the division of this number by the given one.
*
* The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @return BigDecimal[] An array containing the quotient and the remainder.
*
* @psalm-return array{BigDecimal, BigDecimal}
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
{
$that = BigDecimal::of($that);

if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}

$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);

[$quotient, $remainder] = Calculator::get()->divQR($p, $q);

$scale = $this->scale > $that->scale ? $this->scale : $that->scale;

$quotient = new BigDecimal($quotient, 0);
$remainder = new BigDecimal($remainder, $scale);

return [$quotient, $remainder];
}

/**
* Returns the square root of this number, rounded down to the given number of decimals.
*
* @throws \InvalidArgumentException If the scale is negative.
* @throws NegativeNumberException If this number is negative.
*/
public function sqrt(int $scale) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}

if ($this->value === '0') {
return new BigDecimal('0', $scale);
}

if ($this->value[0] === '-') {
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
}

$value = $this->value;
$addDigits = 2 * $scale - $this->scale;

if ($addDigits > 0) {
// add zeros
$value .= \str_repeat('0', $addDigits);
} elseif ($addDigits < 0) {
// trim digits
if (-$addDigits >= \strlen($this->value)) {
// requesting a scale too low, will always yield a zero result
return new BigDecimal('0', $scale);
}

$value = \substr($value, 0, $addDigits);
}

$value = Calculator::get()->sqrt($value);

return new BigDecimal($value, $scale);
}

/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
*/
public function withPointMovedLeft(int $n) : BigDecimal
{
if ($n === 0) {
return $this;
}

if ($n < 0) {
return $this->withPointMovedRight(-$n);
}

return new BigDecimal($this->value, $this->scale + $n);
}

/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
*/
public function withPointMovedRight(int $n) : BigDecimal
{
if ($n === 0) {
return $this;
}

if ($n < 0) {
return $this->withPointMovedLeft(-$n);
}

$value = $this->value;
$scale = $this->scale - $n;

if ($scale < 0) {
if ($value !== '0') {
$value .= \str_repeat('0', -$scale);
}
$scale = 0;
}

return new BigDecimal($value, $scale);
}

/**
* Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
*/
public function stripTrailingZeros() : BigDecimal
{
if ($this->scale === 0) {
return $this;
}

$trimmedValue = \rtrim($this->value, '0');

if ($trimmedValue === '') {
return BigDecimal::zero();
}

$trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);

if ($trimmableZeros === 0) {
return $this;
}

if ($trimmableZeros > $this->scale) {
$trimmableZeros = $this->scale;
}

$value = \substr($this->value, 0, -$trimmableZeros);
$scale = $this->scale - $trimmableZeros;

return new BigDecimal($value, $scale);
}

/**
* Returns the absolute value of this number.
*/
public function abs() : BigDecimal
{
return $this->isNegative() ? $this->negated() : $this;
}

/**
* Returns the negated value of this number.
*/
public function negated() : BigDecimal
{
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
}

public function compareTo(BigNumber|int|float|string $that) : int
{
$that = BigNumber::of($that);

if ($that instanceof BigInteger) {
$that = $that->toBigDecimal();
}

if ($that instanceof BigDecimal) {
[$a, $b] = $this->scaleValues($this, $that);

return Calculator::get()->cmp($a, $b);
}

return - $that->compareTo($this);
}

public function getSign() : int
{
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
}

public function getUnscaledValue() : BigInteger
{
return self::newBigInteger($this->value);
}

public function getScale() : int
{
return $this->scale;
}

/**
* Returns a string representing the integral part of this decimal number.
*
* Example: `-123.456` => `-123`.
*/
public function getIntegralPart() : string
{
if ($this->scale === 0) {
return $this->value;
}

$value = $this->getUnscaledValueWithLeadingZeros();

return \substr($value, 0, -$this->scale);
}

/**
* Returns a string representing the fractional part of this decimal number.
*
* If the scale is zero, an empty string is returned.
*
* Examples: `-123.456` => '456', `123` => ''.
*/
public function getFractionalPart() : string
{
if ($this->scale === 0) {
return '';
}

$value = $this->getUnscaledValueWithLeadingZeros();

return \substr($value, -$this->scale);
}

/**
* Returns whether this decimal number has a non-zero fractional part.
*/
public function hasNonZeroFractionalPart() : bool
{
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
}

public function toBigInteger() : BigInteger
{
$zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);

return self::newBigInteger($zeroScaleDecimal->value);
}

public function toBigDecimal() : BigDecimal
{
return $this;
}

public function toBigRational() : BigRational
{
$numerator = self::newBigInteger($this->value);
$denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));

return self::newBigRational($numerator, $denominator, false);
}

public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
if ($scale === $this->scale) {
return $this;
}

return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
}

public function toInt() : int
{
return $this->toBigInteger()->toInt();
}

public function toFloat() : float
{
return (float) (string) $this;
}

public function __toString() : string
{
if ($this->scale === 0) {
return $this->value;
}

$value = $this->getUnscaledValueWithLeadingZeros();

return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
}

/**
* This method is required for serializing the object and SHOULD NOT be accessed directly.
*
* @internal
*
* @return array{value: string, scale: int}
*/
public function __serialize(): array
{
return ['value' => $this->value, 'scale' => $this->scale];
}

/**
* This method is only here to allow unserializing the object and cannot be accessed directly.
*
* @internal
* @psalm-suppress RedundantPropertyInitializationCheck
*
* @param array{value: string, scale: int} $data
*
* @throws \LogicException
*/
public function __unserialize(array $data): void
{
if (isset($this->value)) {
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
}

$this->value = $data['value'];
$this->scale = $data['scale'];
}

/**
* Puts the internal values of the given decimal numbers on the same scale.
*
* @return array{string, string} The scaled integer values of $x and $y.
*/
private function scaleValues(BigDecimal $x, BigDecimal $y) : array
{
$a = $x->value;
$b = $y->value;

if ($b !== '0' && $x->scale > $y->scale) {
$b .= \str_repeat('0', $x->scale - $y->scale);
} elseif ($a !== '0' && $x->scale < $y->scale) {
$a .= \str_repeat('0', $y->scale - $x->scale);
}

return [$a, $b];
}

private function valueWithMinScale(int $scale) : string
{
$value = $this->value;

if ($this->value !== '0' && $scale > $this->scale) {
$value .= \str_repeat('0', $scale - $this->scale);
}

return $value;
}

/**
* Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
*/
private function getUnscaledValueWithLeadingZeros() : string
{
$value = $this->value;
$targetLength = $this->scale + 1;
$negative = ($value[0] === '-');
$length = \strlen($value);

if ($negative) {
$length--;
}

if ($length >= $targetLength) {
return $this->value;
}

if ($negative) {
$value = \substr($value, 1);
}

$value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);

if ($negative) {
$value = '-' . $value;
}

return $value;
}
}

+ 1051
- 0
vendor/brick/math/src/BigInteger.php
File diff soppresso perché troppo grande
Vedi File


+ 509
- 0
vendor/brick/math/src/BigNumber.php Vedi File

@@ -0,0 +1,509 @@
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;

/**
* Common interface for arbitrary-precision rational numbers.
*
* @psalm-immutable
*/
abstract class BigNumber implements \JsonSerializable
{
/**
* The regular expression used to parse integer or decimal numbers.
*/
private const PARSE_REGEXP_NUMERICAL =
'/^' .
'(?<sign>[\-\+])?' .
'(?<integral>[0-9]+)?' .
'(?<point>\.)?' .
'(?<fractional>[0-9]+)?' .
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
'$/';

/**
* The regular expression used to parse rational numbers.
*/
private const PARSE_REGEXP_RATIONAL =
'/^' .
'(?<sign>[\-\+])?' .
'(?<numerator>[0-9]+)' .
'\/?' .
'(?<denominator>[0-9]+)' .
'$/';

/**
* Creates a BigNumber of the given value.
*
* The concrete return type is dependent on the given value, with the following rules:
*
* - BigNumber instances are returned as is
* - integer numbers are returned as BigInteger
* - floating point numbers are converted to a string then parsed as such
* - strings containing a `/` character are returned as BigRational
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
*
* @throws NumberFormatException If the format of the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
*
* @psalm-pure
*/
final public static function of(BigNumber|int|float|string $value) : static
{
$value = self::_of($value);

if (static::class === BigNumber::class) {
// https://github.com/vimeo/psalm/issues/10309
assert($value instanceof static);

return $value;
}

return static::from($value);
}

/**
* @psalm-pure
*/
private static function _of(BigNumber|int|float|string $value) : BigNumber
{
if ($value instanceof BigNumber) {
return $value;
}

if (\is_int($value)) {
return new BigInteger((string) $value);
}

if (is_float($value)) {
$value = (string) $value;
}

if (str_contains($value, '/')) {
// Rational number
if (\preg_match(self::PARSE_REGEXP_RATIONAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
throw NumberFormatException::invalidFormat($value);
}

$sign = $matches['sign'];
$numerator = $matches['numerator'];
$denominator = $matches['denominator'];

assert($numerator !== null);
assert($denominator !== null);

$numerator = self::cleanUp($sign, $numerator);
$denominator = self::cleanUp(null, $denominator);

if ($denominator === '0') {
throw DivisionByZeroException::denominatorMustNotBeZero();
}

return new BigRational(
new BigInteger($numerator),
new BigInteger($denominator),
false
);
} else {
// Integer or decimal number
if (\preg_match(self::PARSE_REGEXP_NUMERICAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
throw NumberFormatException::invalidFormat($value);
}

$sign = $matches['sign'];
$point = $matches['point'];
$integral = $matches['integral'];
$fractional = $matches['fractional'];
$exponent = $matches['exponent'];

if ($integral === null && $fractional === null) {
throw NumberFormatException::invalidFormat($value);
}

if ($integral === null) {
$integral = '0';
}

if ($point !== null || $exponent !== null) {
$fractional = ($fractional ?? '');
$exponent = ($exponent !== null) ? (int)$exponent : 0;

if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
throw new NumberFormatException('Exponent too large.');
}

$unscaledValue = self::cleanUp($sign, $integral . $fractional);

$scale = \strlen($fractional) - $exponent;

if ($scale < 0) {
if ($unscaledValue !== '0') {
$unscaledValue .= \str_repeat('0', -$scale);
}
$scale = 0;
}

return new BigDecimal($unscaledValue, $scale);
}

$integral = self::cleanUp($sign, $integral);

return new BigInteger($integral);
}
}

/**
* Overridden by subclasses to convert a BigNumber to an instance of the subclass.
*
* @throws MathException If the value cannot be converted.
*
* @psalm-pure
*/
abstract protected static function from(BigNumber $number): static;

/**
* Proxy method to access BigInteger's protected constructor from sibling classes.
*
* @internal
* @psalm-pure
*/
final protected function newBigInteger(string $value) : BigInteger
{
return new BigInteger($value);
}

/**
* Proxy method to access BigDecimal's protected constructor from sibling classes.
*
* @internal
* @psalm-pure
*/
final protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
{
return new BigDecimal($value, $scale);
}

/**
* Proxy method to access BigRational's protected constructor from sibling classes.
*
* @internal
* @psalm-pure
*/
final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
{
return new BigRational($numerator, $denominator, $checkDenominator);
}

/**
* Returns the minimum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
final public static function min(BigNumber|int|float|string ...$values) : static
{
$min = null;

foreach ($values as $value) {
$value = static::of($value);

if ($min === null || $value->isLessThan($min)) {
$min = $value;
}
}

if ($min === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}

return $min;
}

/**
* Returns the maximum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
final public static function max(BigNumber|int|float|string ...$values) : static
{
$max = null;

foreach ($values as $value) {
$value = static::of($value);

if ($max === null || $value->isGreaterThan($max)) {
$max = $value;
}
}

if ($max === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}

return $max;
}

/**
* Returns the sum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
final public static function sum(BigNumber|int|float|string ...$values) : static
{
/** @var static|null $sum */
$sum = null;

foreach ($values as $value) {
$value = static::of($value);

$sum = $sum === null ? $value : self::add($sum, $value);
}

if ($sum === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}

return $sum;
}

/**
* Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
*
* @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
* concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
* depending on their ability to perform the operation. This will also require a version bump because we're
* potentially breaking custom BigNumber implementations (if any...)
*
* @psalm-pure
*/
private static function add(BigNumber $a, BigNumber $b) : BigNumber
{
if ($a instanceof BigRational) {
return $a->plus($b);
}

if ($b instanceof BigRational) {
return $b->plus($a);
}

if ($a instanceof BigDecimal) {
return $a->plus($b);
}

if ($b instanceof BigDecimal) {
return $b->plus($a);
}

/** @var BigInteger $a */

return $a->plus($b);
}

/**
* Removes optional leading zeros and applies sign.
*
* @param string|null $sign The sign, '+' or '-', optional. Null is allowed for convenience and treated as '+'.
* @param string $number The number, validated as a non-empty string of digits.
*
* @psalm-pure
*/
private static function cleanUp(string|null $sign, string $number) : string
{
$number = \ltrim($number, '0');

if ($number === '') {
return '0';
}

return $sign === '-' ? '-' . $number : $number;
}

/**
* Checks if this number is equal to the given one.
*/
final public function isEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) === 0;
}

/**
* Checks if this number is strictly lower than the given one.
*/
final public function isLessThan(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) < 0;
}

/**
* Checks if this number is lower than or equal to the given one.
*/
final public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) <= 0;
}

/**
* Checks if this number is strictly greater than the given one.
*/
final public function isGreaterThan(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) > 0;
}

/**
* Checks if this number is greater than or equal to the given one.
*/
final public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) >= 0;
}

/**
* Checks if this number equals zero.
*/
final public function isZero() : bool
{
return $this->getSign() === 0;
}

/**
* Checks if this number is strictly negative.
*/
final public function isNegative() : bool
{
return $this->getSign() < 0;
}

/**
* Checks if this number is negative or zero.
*/
final public function isNegativeOrZero() : bool
{
return $this->getSign() <= 0;
}

/**
* Checks if this number is strictly positive.
*/
final public function isPositive() : bool
{
return $this->getSign() > 0;
}

/**
* Checks if this number is positive or zero.
*/
final public function isPositiveOrZero() : bool
{
return $this->getSign() >= 0;
}

/**
* Returns the sign of this number.
*
* @psalm-return -1|0|1
*
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
*/
abstract public function getSign() : int;

/**
* Compares this number to the given one.
*
* @psalm-return -1|0|1
*
* @return int -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`.
*
* @throws MathException If the number is not valid.
*/
abstract public function compareTo(BigNumber|int|float|string $that) : int;

/**
* Converts this number to a BigInteger.
*
* @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
*/
abstract public function toBigInteger() : BigInteger;

/**
* Converts this number to a BigDecimal.
*
* @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
*/
abstract public function toBigDecimal() : BigDecimal;

/**
* Converts this number to a BigRational.
*/
abstract public function toBigRational() : BigRational;

/**
* Converts this number to a BigDecimal with the given scale, using rounding if necessary.
*
* @param int $scale The scale of the resulting `BigDecimal`.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
* This only applies when RoundingMode::UNNECESSARY is used.
*/
abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;

/**
* Returns the exact value of this number as a native integer.
*
* If this number cannot be converted to a native integer without losing precision, an exception is thrown.
* Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
*
* @throws MathException If this number cannot be exactly converted to a native integer.
*/
abstract public function toInt() : int;

/**
* Returns an approximation of this number as a floating-point value.
*
* Note that this method can discard information as the precision of a floating-point value
* is inherently limited.
*
* If the number is greater than the largest representable floating point number, positive infinity is returned.
* If the number is less than the smallest representable floating point number, negative infinity is returned.
*/
abstract public function toFloat() : float;

/**
* Returns a string representation of this number.
*
* The output of this method can be parsed by the `of()` factory method;
* this will yield an object equal to this one, without any information loss.
*/
abstract public function __toString() : string;

final public function jsonSerialize() : string
{
return $this->__toString();
}
}

+ 413
- 0
vendor/brick/math/src/BigRational.php Vedi File

@@ -0,0 +1,413 @@
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;

/**
* An arbitrarily large rational number.
*
* This class is immutable.
*
* @psalm-immutable
*/
final class BigRational extends BigNumber
{
/**
* The numerator.
*/
private readonly BigInteger $numerator;

/**
* The denominator. Always strictly positive.
*/
private readonly BigInteger $denominator;

/**
* Protected constructor. Use a factory method to obtain an instance.
*
* @param BigInteger $numerator The numerator.
* @param BigInteger $denominator The denominator.
* @param bool $checkDenominator Whether to check the denominator for negative and zero.
*
* @throws DivisionByZeroException If the denominator is zero.
*/
protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
{
if ($checkDenominator) {
if ($denominator->isZero()) {
throw DivisionByZeroException::denominatorMustNotBeZero();
}

if ($denominator->isNegative()) {
$numerator = $numerator->negated();
$denominator = $denominator->negated();
}
}

$this->numerator = $numerator;
$this->denominator = $denominator;
}

/**
* @psalm-pure
*/
protected static function from(BigNumber $number): static
{
return $number->toBigRational();
}

/**
* Creates a BigRational out of a numerator and a denominator.
*
* If the denominator is negative, the signs of both the numerator and the denominator
* will be inverted to ensure that the denominator is always positive.
*
* @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger.
* @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
*
* @throws NumberFormatException If an argument does not represent a valid number.
* @throws RoundingNecessaryException If an argument represents a non-integer number.
* @throws DivisionByZeroException If the denominator is zero.
*
* @psalm-pure
*/
public static function nd(
BigNumber|int|float|string $numerator,
BigNumber|int|float|string $denominator,
) : BigRational {
$numerator = BigInteger::of($numerator);
$denominator = BigInteger::of($denominator);

return new BigRational($numerator, $denominator, true);
}

/**
* Returns a BigRational representing zero.
*
* @psalm-pure
*/
public static function zero() : BigRational
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigRational|null $zero
*/
static $zero;

if ($zero === null) {
$zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
}

return $zero;
}

/**
* Returns a BigRational representing one.
*
* @psalm-pure
*/
public static function one() : BigRational
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigRational|null $one
*/
static $one;

if ($one === null) {
$one = new BigRational(BigInteger::one(), BigInteger::one(), false);
}

return $one;
}

/**
* Returns a BigRational representing ten.
*
* @psalm-pure
*/
public static function ten() : BigRational
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigRational|null $ten
*/
static $ten;

if ($ten === null) {
$ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
}

return $ten;
}

public function getNumerator() : BigInteger
{
return $this->numerator;
}

public function getDenominator() : BigInteger
{
return $this->denominator;
}

/**
* Returns the quotient of the division of the numerator by the denominator.
*/
public function quotient() : BigInteger
{
return $this->numerator->quotient($this->denominator);
}

/**
* Returns the remainder of the division of the numerator by the denominator.
*/
public function remainder() : BigInteger
{
return $this->numerator->remainder($this->denominator);
}

/**
* Returns the quotient and remainder of the division of the numerator by the denominator.
*
* @return BigInteger[]
*
* @psalm-return array{BigInteger, BigInteger}
*/
public function quotientAndRemainder() : array
{
return $this->numerator->quotientAndRemainder($this->denominator);
}

/**
* Returns the sum of this number and the given one.
*
* @param BigNumber|int|float|string $that The number to add.
*
* @throws MathException If the number is not valid.
*/
public function plus(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);

$numerator = $this->numerator->multipliedBy($that->denominator);
$numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
$denominator = $this->denominator->multipliedBy($that->denominator);

return new BigRational($numerator, $denominator, false);
}

/**
* Returns the difference of this number and the given one.
*
* @param BigNumber|int|float|string $that The number to subtract.
*
* @throws MathException If the number is not valid.
*/
public function minus(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);

$numerator = $this->numerator->multipliedBy($that->denominator);
$numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
$denominator = $this->denominator->multipliedBy($that->denominator);

return new BigRational($numerator, $denominator, false);
}

/**
* Returns the product of this number and the given one.
*
* @param BigNumber|int|float|string $that The multiplier.
*
* @throws MathException If the multiplier is not a valid number.
*/
public function multipliedBy(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);

$numerator = $this->numerator->multipliedBy($that->numerator);
$denominator = $this->denominator->multipliedBy($that->denominator);

return new BigRational($numerator, $denominator, false);
}

/**
* Returns the result of the division of this number by the given one.
*
* @param BigNumber|int|float|string $that The divisor.
*
* @throws MathException If the divisor is not a valid number, or is zero.
*/
public function dividedBy(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);

$numerator = $this->numerator->multipliedBy($that->denominator);
$denominator = $this->denominator->multipliedBy($that->numerator);

return new BigRational($numerator, $denominator, true);
}

/**
* Returns this number exponentiated to the given value.
*
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
*/
public function power(int $exponent) : BigRational
{
if ($exponent === 0) {
$one = BigInteger::one();

return new BigRational($one, $one, false);
}

if ($exponent === 1) {
return $this;
}

return new BigRational(
$this->numerator->power($exponent),
$this->denominator->power($exponent),
false
);
}

/**
* Returns the reciprocal of this BigRational.
*
* The reciprocal has the numerator and denominator swapped.
*
* @throws DivisionByZeroException If the numerator is zero.
*/
public function reciprocal() : BigRational
{
return new BigRational($this->denominator, $this->numerator, true);
}

/**
* Returns the absolute value of this BigRational.
*/
public function abs() : BigRational
{
return new BigRational($this->numerator->abs(), $this->denominator, false);
}

/**
* Returns the negated value of this BigRational.
*/
public function negated() : BigRational
{
return new BigRational($this->numerator->negated(), $this->denominator, false);
}

/**
* Returns the simplified value of this BigRational.
*/
public function simplified() : BigRational
{
$gcd = $this->numerator->gcd($this->denominator);

$numerator = $this->numerator->quotient($gcd);
$denominator = $this->denominator->quotient($gcd);

return new BigRational($numerator, $denominator, false);
}

public function compareTo(BigNumber|int|float|string $that) : int
{
return $this->minus($that)->getSign();
}

public function getSign() : int
{
return $this->numerator->getSign();
}

public function toBigInteger() : BigInteger
{
$simplified = $this->simplified();

if (! $simplified->denominator->isEqualTo(1)) {
throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
}

return $simplified->numerator;
}

public function toBigDecimal() : BigDecimal
{
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
}

public function toBigRational() : BigRational
{
return $this;
}

public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
}

public function toInt() : int
{
return $this->toBigInteger()->toInt();
}

public function toFloat() : float
{
$simplified = $this->simplified();
return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
}

public function __toString() : string
{
$numerator = (string) $this->numerator;
$denominator = (string) $this->denominator;

if ($denominator === '1') {
return $numerator;
}

return $this->numerator . '/' . $this->denominator;
}

/**
* This method is required for serializing the object and SHOULD NOT be accessed directly.
*
* @internal
*
* @return array{numerator: BigInteger, denominator: BigInteger}
*/
public function __serialize(): array
{
return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
}

/**
* This method is only here to allow unserializing the object and cannot be accessed directly.
*
* @internal
* @psalm-suppress RedundantPropertyInitializationCheck
*
* @param array{numerator: BigInteger, denominator: BigInteger} $data
*
* @throws \LogicException
*/
public function __unserialize(array $data): void
{
if (isset($this->numerator)) {
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
}

$this->numerator = $data['numerator'];
$this->denominator = $data['denominator'];
}
}

+ 35
- 0
vendor/brick/math/src/Exception/DivisionByZeroException.php Vedi File

@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

/**
* Exception thrown when a division by zero occurs.
*/
class DivisionByZeroException extends MathException
{
/**
* @psalm-pure
*/
public static function divisionByZero() : DivisionByZeroException
{
return new self('Division by zero.');
}

/**
* @psalm-pure
*/
public static function modulusMustNotBeZero() : DivisionByZeroException
{
return new self('The modulus must not be zero.');
}

/**
* @psalm-pure
*/
public static function denominatorMustNotBeZero() : DivisionByZeroException
{
return new self('The denominator of a rational number cannot be zero.');
}
}

+ 23
- 0
vendor/brick/math/src/Exception/IntegerOverflowException.php Vedi File

@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use Brick\Math\BigInteger;

/**
* Exception thrown when an integer overflow occurs.
*/
class IntegerOverflowException extends MathException
{
/**
* @psalm-pure
*/
public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
{
$message = '%s is out of range %d to %d and cannot be represented as an integer.';

return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
}
}

+ 12
- 0
vendor/brick/math/src/Exception/MathException.php Vedi File

@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

/**
* Base class for all math exceptions.
*/
class MathException extends \Exception
{
}

+ 12
- 0
vendor/brick/math/src/Exception/NegativeNumberException.php Vedi File

@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

/**
* Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
*/
class NegativeNumberException extends MathException
{
}

+ 41
- 0
vendor/brick/math/src/Exception/NumberFormatException.php Vedi File

@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

/**
* Exception thrown when attempting to create a number from a string with an invalid format.
*/
class NumberFormatException extends MathException
{
public static function invalidFormat(string $value) : self
{
return new self(\sprintf(
'The given value "%s" does not represent a valid number.',
$value,
));
}

/**
* @param string $char The failing character.
*
* @psalm-pure
*/
public static function charNotInAlphabet(string $char) : self
{
$ord = \ord($char);

if ($ord < 32 || $ord > 126) {
$char = \strtoupper(\dechex($ord));

if ($ord < 10) {
$char = '0' . $char;
}
} else {
$char = '"' . $char . '"';
}

return new self(\sprintf('Char %s is not a valid character in the given alphabet.', $char));
}
}

+ 19
- 0
vendor/brick/math/src/Exception/RoundingNecessaryException.php Vedi File

@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

/**
* Exception thrown when a number cannot be represented at the requested scale without rounding.
*/
class RoundingNecessaryException extends MathException
{
/**
* @psalm-pure
*/
public static function roundingNecessary() : RoundingNecessaryException
{
return new self('Rounding is necessary to represent the result of the operation at this scale.');
}
}

+ 668
- 0
vendor/brick/math/src/Internal/Calculator.php Vedi File

@@ -0,0 +1,668 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Internal;

use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\RoundingMode;

/**
* Performs basic operations on arbitrary size integers.
*
* Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
* without leading zero, and with an optional leading minus sign if the number is not zero.
*
* Any other parameter format will lead to undefined behaviour.
* All methods must return strings respecting this format, unless specified otherwise.
*
* @internal
*
* @psalm-immutable
*/
abstract class Calculator
{
/**
* The maximum exponent value allowed for the pow() method.
*/
public const MAX_POWER = 1_000_000;

/**
* The alphabet for converting from and to base 2 to 36, lowercase.
*/
public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';

/**
* The Calculator instance in use.
*/
private static ?Calculator $instance = null;

/**
* Sets the Calculator instance to use.
*
* An instance is typically set only in unit tests: the autodetect is usually the best option.
*
* @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
*/
final public static function set(?Calculator $calculator) : void
{
self::$instance = $calculator;
}

/**
* Returns the Calculator instance to use.
*
* If none has been explicitly set, the fastest available implementation will be returned.
*
* @psalm-pure
* @psalm-suppress ImpureStaticProperty
*/
final public static function get() : Calculator
{
if (self::$instance === null) {
/** @psalm-suppress ImpureMethodCall */
self::$instance = self::detect();
}

return self::$instance;
}

/**
* Returns the fastest available Calculator implementation.
*
* @codeCoverageIgnore
*/
private static function detect() : Calculator
{
if (\extension_loaded('gmp')) {
return new Calculator\GmpCalculator();
}

if (\extension_loaded('bcmath')) {
return new Calculator\BcMathCalculator();
}

return new Calculator\NativeCalculator();
}

/**
* Extracts the sign & digits of the operands.
*
* @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
*/
final protected function init(string $a, string $b) : array
{
return [
$aNeg = ($a[0] === '-'),
$bNeg = ($b[0] === '-'),

$aNeg ? \substr($a, 1) : $a,
$bNeg ? \substr($b, 1) : $b,
];
}

/**
* Returns the absolute value of a number.
*/
final public function abs(string $n) : string
{
return ($n[0] === '-') ? \substr($n, 1) : $n;
}

/**
* Negates a number.
*/
final public function neg(string $n) : string
{
if ($n === '0') {
return '0';
}

if ($n[0] === '-') {
return \substr($n, 1);
}

return '-' . $n;
}

/**
* Compares two numbers.
*
* @psalm-return -1|0|1
*
* @return int -1 if the first number is less than, 0 if equal to, 1 if greater than the second number.
*/
final public function cmp(string $a, string $b) : int
{
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

if ($aNeg && ! $bNeg) {
return -1;
}

if ($bNeg && ! $aNeg) {
return 1;
}

$aLen = \strlen($aDig);
$bLen = \strlen($bDig);

if ($aLen < $bLen) {
$result = -1;
} elseif ($aLen > $bLen) {
$result = 1;
} else {
$result = $aDig <=> $bDig;
}

return $aNeg ? -$result : $result;
}

/**
* Adds two numbers.
*/
abstract public function add(string $a, string $b) : string;

/**
* Subtracts two numbers.
*/
abstract public function sub(string $a, string $b) : string;

/**
* Multiplies two numbers.
*/
abstract public function mul(string $a, string $b) : string;

/**
* Returns the quotient of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string The quotient.
*/
abstract public function divQ(string $a, string $b) : string;

/**
* Returns the remainder of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string The remainder.
*/
abstract public function divR(string $a, string $b) : string;

/**
* Returns the quotient and remainder of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return array{string, string} An array containing the quotient and remainder.
*/
abstract public function divQR(string $a, string $b) : array;

/**
* Exponentiates a number.
*
* @param string $a The base number.
* @param int $e The exponent, validated as an integer between 0 and MAX_POWER.
*
* @return string The power.
*/
abstract public function pow(string $a, int $e) : string;

/**
* @param string $b The modulus; must not be zero.
*/
public function mod(string $a, string $b) : string
{
return $this->divR($this->add($this->divR($a, $b), $b), $b);
}

/**
* Returns the modular multiplicative inverse of $x modulo $m.
*
* If $x has no multiplicative inverse mod m, this method must return null.
*
* This method can be overridden by the concrete implementation if the underlying library has built-in support.
*
* @param string $m The modulus; must not be negative or zero.
*/
public function modInverse(string $x, string $m) : ?string
{
if ($m === '1') {
return '0';
}

$modVal = $x;

if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
$modVal = $this->mod($x, $m);
}

[$g, $x] = $this->gcdExtended($modVal, $m);

if ($g !== '1') {
return null;
}

return $this->mod($this->add($this->mod($x, $m), $m), $m);
}

/**
* Raises a number into power with modulo.
*
* @param string $base The base number; must be positive or zero.
* @param string $exp The exponent; must be positive or zero.
* @param string $mod The modulus; must be strictly positive.
*/
abstract public function modPow(string $base, string $exp, string $mod) : string;

/**
* Returns the greatest common divisor of the two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for GCD calculations.
*
* @return string The GCD, always positive, or zero if both arguments are zero.
*/
public function gcd(string $a, string $b) : string
{
if ($a === '0') {
return $this->abs($b);
}

if ($b === '0') {
return $this->abs($a);
}

return $this->gcd($b, $this->divR($a, $b));
}

/**
* @return array{string, string, string} GCD, X, Y
*/
private function gcdExtended(string $a, string $b) : array
{
if ($a === '0') {
return [$b, '0', '1'];
}

[$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a);

$x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
$y = $x1;

return [$gcd, $x, $y];
}

/**
* Returns the square root of the given number, rounded down.
*
* The result is the largest x such that x² ≤ n.
* The input MUST NOT be negative.
*/
abstract public function sqrt(string $n) : string;

/**
* Converts a number from an arbitrary base.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for base conversion.
*
* @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
* @param int $base The base of the number, validated from 2 to 36.
*
* @return string The converted number, following the Calculator conventions.
*/
public function fromBase(string $number, int $base) : string
{
return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
}

/**
* Converts a number to an arbitrary base.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for base conversion.
*
* @param string $number The number to convert, following the Calculator conventions.
* @param int $base The base to convert to, validated from 2 to 36.
*
* @return string The converted number, lowercase.
*/
public function toBase(string $number, int $base) : string
{
$negative = ($number[0] === '-');

if ($negative) {
$number = \substr($number, 1);
}

$number = $this->toArbitraryBase($number, self::ALPHABET, $base);

if ($negative) {
return '-' . $number;
}

return $number;
}

/**
* Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
*
* @param string $number The number to convert, validated as a non-empty string,
* containing only chars in the given alphabet/base.
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
* @param int $base The base of the number, validated from 2 to alphabet length.
*
* @return string The number in base 10, following the Calculator conventions.
*/
final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
{
// remove leading "zeros"
$number = \ltrim($number, $alphabet[0]);

if ($number === '') {
return '0';
}

// optimize for "one"
if ($number === $alphabet[1]) {
return '1';
}

$result = '0';
$power = '1';

$base = (string) $base;

for ($i = \strlen($number) - 1; $i >= 0; $i--) {
$index = \strpos($alphabet, $number[$i]);

if ($index !== 0) {
$result = $this->add($result, ($index === 1)
? $power
: $this->mul($power, (string) $index)
);
}

if ($i !== 0) {
$power = $this->mul($power, $base);
}
}

return $result;
}

/**
* Converts a non-negative number to an arbitrary base using a custom alphabet.
*
* @param string $number The number to convert, positive or zero, following the Calculator conventions.
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
* @param int $base The base to convert to, validated from 2 to alphabet length.
*
* @return string The converted number in the given alphabet.
*/
final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
{
if ($number === '0') {
return $alphabet[0];
}

$base = (string) $base;
$result = '';

while ($number !== '0') {
[$number, $remainder] = $this->divQR($number, $base);
$remainder = (int) $remainder;

$result .= $alphabet[$remainder];
}

return \strrev($result);
}

/**
* Performs a rounded division.
*
* Rounding is performed when the remainder of the division is not zero.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
* @param RoundingMode $roundingMode The rounding mode.
*
* @throws \InvalidArgumentException If the rounding mode is invalid.
* @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
*
* @psalm-suppress ImpureFunctionCall
*/
final public function divRound(string $a, string $b, RoundingMode $roundingMode) : string
{
[$quotient, $remainder] = $this->divQR($a, $b);

$hasDiscardedFraction = ($remainder !== '0');
$isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');

$discardedFractionSign = function() use ($remainder, $b) : int {
$r = $this->abs($this->mul($remainder, '2'));
$b = $this->abs($b);

return $this->cmp($r, $b);
};

$increment = false;

switch ($roundingMode) {
case RoundingMode::UNNECESSARY:
if ($hasDiscardedFraction) {
throw RoundingNecessaryException::roundingNecessary();
}
break;

case RoundingMode::UP:
$increment = $hasDiscardedFraction;
break;

case RoundingMode::DOWN:
break;

case RoundingMode::CEILING:
$increment = $hasDiscardedFraction && $isPositiveOrZero;
break;

case RoundingMode::FLOOR:
$increment = $hasDiscardedFraction && ! $isPositiveOrZero;
break;

case RoundingMode::HALF_UP:
$increment = $discardedFractionSign() >= 0;
break;

case RoundingMode::HALF_DOWN:
$increment = $discardedFractionSign() > 0;
break;

case RoundingMode::HALF_CEILING:
$increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
break;

case RoundingMode::HALF_FLOOR:
$increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
break;

case RoundingMode::HALF_EVEN:
$lastDigit = (int) $quotient[-1];
$lastDigitIsEven = ($lastDigit % 2 === 0);
$increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
break;

default:
throw new \InvalidArgumentException('Invalid rounding mode.');
}

if ($increment) {
return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
}

return $quotient;
}

/**
* Calculates bitwise AND of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*/
public function and(string $a, string $b) : string
{
return $this->bitwise('and', $a, $b);
}

/**
* Calculates bitwise OR of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*/
public function or(string $a, string $b) : string
{
return $this->bitwise('or', $a, $b);
}

/**
* Calculates bitwise XOR of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*/
public function xor(string $a, string $b) : string
{
return $this->bitwise('xor', $a, $b);
}

/**
* Performs a bitwise operation on a decimal number.
*
* @param 'and'|'or'|'xor' $operator The operator to use.
* @param string $a The left operand.
* @param string $b The right operand.
*/
private function bitwise(string $operator, string $a, string $b) : string
{
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

$aBin = $this->toBinary($aDig);
$bBin = $this->toBinary($bDig);

$aLen = \strlen($aBin);
$bLen = \strlen($bBin);

if ($aLen > $bLen) {
$bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
} elseif ($bLen > $aLen) {
$aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
}

if ($aNeg) {
$aBin = $this->twosComplement($aBin);
}
if ($bNeg) {
$bBin = $this->twosComplement($bBin);
}

$value = match ($operator) {
'and' => $aBin & $bBin,
'or' => $aBin | $bBin,
'xor' => $aBin ^ $bBin,
};

$negative = match ($operator) {
'and' => $aNeg and $bNeg,
'or' => $aNeg or $bNeg,
'xor' => $aNeg xor $bNeg,
};

if ($negative) {
$value = $this->twosComplement($value);
}

$result = $this->toDecimal($value);

return $negative ? $this->neg($result) : $result;
}

/**
* @param string $number A positive, binary number.
*/
private function twosComplement(string $number) : string
{
$xor = \str_repeat("\xff", \strlen($number));

$number ^= $xor;

for ($i = \strlen($number) - 1; $i >= 0; $i--) {
$byte = \ord($number[$i]);

if (++$byte !== 256) {
$number[$i] = \chr($byte);
break;
}

$number[$i] = "\x00";

if ($i === 0) {
$number = "\x01" . $number;
}
}

return $number;
}

/**
* Converts a decimal number to a binary string.
*
* @param string $number The number to convert, positive or zero, only digits.
*/
private function toBinary(string $number) : string
{
$result = '';

while ($number !== '0') {
[$number, $remainder] = $this->divQR($number, '256');
$result .= \chr((int) $remainder);
}

return \strrev($result);
}

/**
* Returns the positive decimal representation of a binary number.
*
* @param string $bytes The bytes representing the number.
*/
private function toDecimal(string $bytes) : string
{
$result = '0';
$power = '1';

for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
$index = \ord($bytes[$i]);

if ($index !== 0) {
$result = $this->add($result, ($index === 1)
? $power
: $this->mul($power, (string) $index)
);
}

if ($i !== 0) {
$power = $this->mul($power, '256');
}
}

return $result;
}
}

+ 65
- 0
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php Vedi File

@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Internal\Calculator;

use Brick\Math\Internal\Calculator;

/**
* Calculator implementation built around the bcmath library.
*
* @internal
*
* @psalm-immutable
*/
class BcMathCalculator extends Calculator
{
public function add(string $a, string $b) : string
{
return \bcadd($a, $b, 0);
}

public function sub(string $a, string $b) : string
{
return \bcsub($a, $b, 0);
}

public function mul(string $a, string $b) : string
{
return \bcmul($a, $b, 0);
}

public function divQ(string $a, string $b) : string
{
return \bcdiv($a, $b, 0);
}

public function divR(string $a, string $b) : string
{
return \bcmod($a, $b, 0);
}

public function divQR(string $a, string $b) : array
{
$q = \bcdiv($a, $b, 0);
$r = \bcmod($a, $b, 0);

return [$q, $r];
}

public function pow(string $a, int $e) : string
{
return \bcpow($a, (string) $e, 0);
}

public function modPow(string $base, string $exp, string $mod) : string
{
return \bcpowmod($base, $exp, $mod, 0);
}

public function sqrt(string $n) : string
{
return \bcsqrt($n, 0);
}
}

+ 108
- 0
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php Vedi File

@@ -0,0 +1,108 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Internal\Calculator;

use Brick\Math\Internal\Calculator;

/**
* Calculator implementation built around the GMP library.
*
* @internal
*
* @psalm-immutable
*/
class GmpCalculator extends Calculator
{
public function add(string $a, string $b) : string
{
return \gmp_strval(\gmp_add($a, $b));
}

public function sub(string $a, string $b) : string
{
return \gmp_strval(\gmp_sub($a, $b));
}

public function mul(string $a, string $b) : string
{
return \gmp_strval(\gmp_mul($a, $b));
}

public function divQ(string $a, string $b) : string
{
return \gmp_strval(\gmp_div_q($a, $b));
}

public function divR(string $a, string $b) : string
{
return \gmp_strval(\gmp_div_r($a, $b));
}

public function divQR(string $a, string $b) : array
{
[$q, $r] = \gmp_div_qr($a, $b);

return [
\gmp_strval($q),
\gmp_strval($r)
];
}

public function pow(string $a, int $e) : string
{
return \gmp_strval(\gmp_pow($a, $e));
}

public function modInverse(string $x, string $m) : ?string
{
$result = \gmp_invert($x, $m);

if ($result === false) {
return null;
}

return \gmp_strval($result);
}

public function modPow(string $base, string $exp, string $mod) : string
{
return \gmp_strval(\gmp_powm($base, $exp, $mod));
}

public function gcd(string $a, string $b) : string
{
return \gmp_strval(\gmp_gcd($a, $b));
}

public function fromBase(string $number, int $base) : string
{
return \gmp_strval(\gmp_init($number, $base));
}

public function toBase(string $number, int $base) : string
{
return \gmp_strval($number, $base);
}

public function and(string $a, string $b) : string
{
return \gmp_strval(\gmp_and($a, $b));
}

public function or(string $a, string $b) : string
{
return \gmp_strval(\gmp_or($a, $b));
}

public function xor(string $a, string $b) : string
{
return \gmp_strval(\gmp_xor($a, $b));
}

public function sqrt(string $n) : string
{
return \gmp_strval(\gmp_sqrt($n));
}
}

+ 572
- 0
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php Vedi File

@@ -0,0 +1,572 @@
<?php

declare(strict_types=1);

namespace Brick\Math\Internal\Calculator;

use Brick\Math\Internal\Calculator;

/**
* Calculator implementation using only native PHP code.
*
* @internal
*
* @psalm-immutable
*/
class NativeCalculator extends Calculator
{
/**
* The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
* For multiplication, this represents the max sum of the lengths of both operands.
*
* In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
* Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
* 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
*/
private readonly int $maxDigits;

/**
* @codeCoverageIgnore
*/
public function __construct()
{
$this->maxDigits = match (PHP_INT_SIZE) {
4 => 9,
8 => 18,
default => throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.')
};
}

public function add(string $a, string $b) : string
{
/**
* @psalm-var numeric-string $a
* @psalm-var numeric-string $b
*/
$result = $a + $b;

if (is_int($result)) {
return (string) $result;
}

if ($a === '0') {
return $b;
}

if ($b === '0') {
return $a;
}

[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

$result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);

if ($aNeg) {
$result = $this->neg($result);
}

return $result;
}

public function sub(string $a, string $b) : string
{
return $this->add($a, $this->neg($b));
}

public function mul(string $a, string $b) : string
{
/**
* @psalm-var numeric-string $a
* @psalm-var numeric-string $b
*/
$result = $a * $b;

if (is_int($result)) {
return (string) $result;
}

if ($a === '0' || $b === '0') {
return '0';
}

if ($a === '1') {
return $b;
}

if ($b === '1') {
return $a;
}

if ($a === '-1') {
return $this->neg($b);
}

if ($b === '-1') {
return $this->neg($a);
}

[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

$result = $this->doMul($aDig, $bDig);

if ($aNeg !== $bNeg) {
$result = $this->neg($result);
}

return $result;
}

public function divQ(string $a, string $b) : string
{
return $this->divQR($a, $b)[0];
}

public function divR(string $a, string $b): string
{
return $this->divQR($a, $b)[1];
}

public function divQR(string $a, string $b) : array
{
if ($a === '0') {
return ['0', '0'];
}

if ($a === $b) {
return ['1', '0'];
}

if ($b === '1') {
return [$a, '0'];
}

if ($b === '-1') {
return [$this->neg($a), '0'];
}

/** @psalm-var numeric-string $a */
$na = $a * 1; // cast to number

if (is_int($na)) {
/** @psalm-var numeric-string $b */
$nb = $b * 1;

if (is_int($nb)) {
// the only division that may overflow is PHP_INT_MIN / -1,
// which cannot happen here as we've already handled a divisor of -1 above.
$q = intdiv($na, $nb);
$r = $na % $nb;

return [
(string) $q,
(string) $r
];
}
}

[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

[$q, $r] = $this->doDiv($aDig, $bDig);

if ($aNeg !== $bNeg) {
$q = $this->neg($q);
}

if ($aNeg) {
$r = $this->neg($r);
}

return [$q, $r];
}

public function pow(string $a, int $e) : string
{
if ($e === 0) {
return '1';
}

if ($e === 1) {
return $a;
}

$odd = $e % 2;
$e -= $odd;

$aa = $this->mul($a, $a);

/** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
$result = $this->pow($aa, $e / 2);

if ($odd === 1) {
$result = $this->mul($result, $a);
}

return $result;
}

/**
* Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
*/
public function modPow(string $base, string $exp, string $mod) : string
{
// special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
if ($base === '0' && $exp === '0' && $mod === '1') {
return '0';
}

// special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
if ($exp === '0' && $mod === '1') {
return '0';
}

$x = $base;

$res = '1';

// numbers are positive, so we can use remainder instead of modulo
$x = $this->divR($x, $mod);

while ($exp !== '0') {
if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
$res = $this->divR($this->mul($res, $x), $mod);
}

$exp = $this->divQ($exp, '2');
$x = $this->divR($this->mul($x, $x), $mod);
}

return $res;
}

/**
* Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
*/
public function sqrt(string $n) : string
{
if ($n === '0') {
return '0';
}

// initial approximation
$x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);

$decreased = false;

for (;;) {
$nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');

if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
break;
}

$decreased = $this->cmp($nx, $x) < 0;
$x = $nx;
}

return $x;
}

/**
* Performs the addition of two non-signed large integers.
*/
private function doAdd(string $a, string $b) : string
{
[$a, $b, $length] = $this->pad($a, $b);

$carry = 0;
$result = '';

for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
$blockLength = $this->maxDigits;

if ($i < 0) {
$blockLength += $i;
/** @psalm-suppress LoopInvalidation */
$i = 0;
}

/** @psalm-var numeric-string $blockA */
$blockA = \substr($a, $i, $blockLength);

/** @psalm-var numeric-string $blockB */
$blockB = \substr($b, $i, $blockLength);

$sum = (string) ($blockA + $blockB + $carry);
$sumLength = \strlen($sum);

if ($sumLength > $blockLength) {
$sum = \substr($sum, 1);
$carry = 1;
} else {
if ($sumLength < $blockLength) {
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
}
$carry = 0;
}

$result = $sum . $result;

if ($i === 0) {
break;
}
}

if ($carry === 1) {
$result = '1' . $result;
}

return $result;
}

/**
* Performs the subtraction of two non-signed large integers.
*/
private function doSub(string $a, string $b) : string
{
if ($a === $b) {
return '0';
}

// Ensure that we always subtract to a positive result: biggest minus smallest.
$cmp = $this->doCmp($a, $b);

$invert = ($cmp === -1);

if ($invert) {
$c = $a;
$a = $b;
$b = $c;
}

[$a, $b, $length] = $this->pad($a, $b);

$carry = 0;
$result = '';

$complement = 10 ** $this->maxDigits;

for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
$blockLength = $this->maxDigits;

if ($i < 0) {
$blockLength += $i;
/** @psalm-suppress LoopInvalidation */
$i = 0;
}

/** @psalm-var numeric-string $blockA */
$blockA = \substr($a, $i, $blockLength);

/** @psalm-var numeric-string $blockB */
$blockB = \substr($b, $i, $blockLength);

$sum = $blockA - $blockB - $carry;

if ($sum < 0) {
$sum += $complement;
$carry = 1;
} else {
$carry = 0;
}

$sum = (string) $sum;
$sumLength = \strlen($sum);

if ($sumLength < $blockLength) {
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
}

$result = $sum . $result;

if ($i === 0) {
break;
}
}

// Carry cannot be 1 when the loop ends, as a > b
assert($carry === 0);

$result = \ltrim($result, '0');

if ($invert) {
$result = $this->neg($result);
}

return $result;
}

/**
* Performs the multiplication of two non-signed large integers.
*/
private function doMul(string $a, string $b) : string
{
$x = \strlen($a);
$y = \strlen($b);

$maxDigits = \intdiv($this->maxDigits, 2);
$complement = 10 ** $maxDigits;

$result = '0';

for ($i = $x - $maxDigits;; $i -= $maxDigits) {
$blockALength = $maxDigits;

if ($i < 0) {
$blockALength += $i;
/** @psalm-suppress LoopInvalidation */
$i = 0;
}

$blockA = (int) \substr($a, $i, $blockALength);

$line = '';
$carry = 0;

for ($j = $y - $maxDigits;; $j -= $maxDigits) {
$blockBLength = $maxDigits;

if ($j < 0) {
$blockBLength += $j;
/** @psalm-suppress LoopInvalidation */
$j = 0;
}

$blockB = (int) \substr($b, $j, $blockBLength);

$mul = $blockA * $blockB + $carry;
$value = $mul % $complement;
$carry = ($mul - $value) / $complement;

$value = (string) $value;
$value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);

$line = $value . $line;

if ($j === 0) {
break;
}
}

if ($carry !== 0) {
$line = $carry . $line;
}

$line = \ltrim($line, '0');

if ($line !== '') {
$line .= \str_repeat('0', $x - $blockALength - $i);
$result = $this->add($result, $line);
}

if ($i === 0) {
break;
}
}

return $result;
}

/**
* Performs the division of two non-signed large integers.
*
* @return string[] The quotient and remainder.
*/
private function doDiv(string $a, string $b) : array
{
$cmp = $this->doCmp($a, $b);

if ($cmp === -1) {
return ['0', $a];
}

$x = \strlen($a);
$y = \strlen($b);

// we now know that a >= b && x >= y

$q = '0'; // quotient
$r = $a; // remainder
$z = $y; // focus length, always $y or $y+1

for (;;) {
$focus = \substr($a, 0, $z);

$cmp = $this->doCmp($focus, $b);

if ($cmp === -1) {
if ($z === $x) { // remainder < dividend
break;
}

$z++;
}

$zeros = \str_repeat('0', $x - $z);

$q = $this->add($q, '1' . $zeros);
$a = $this->sub($a, $b . $zeros);

$r = $a;

if ($r === '0') { // remainder == 0
break;
}

$x = \strlen($a);

if ($x < $y) { // remainder < dividend
break;
}

$z = $y;
}

return [$q, $r];
}

/**
* Compares two non-signed large numbers.
*
* @psalm-return -1|0|1
*/
private function doCmp(string $a, string $b) : int
{
$x = \strlen($a);
$y = \strlen($b);

$cmp = $x <=> $y;

if ($cmp !== 0) {
return $cmp;
}

return \strcmp($a, $b) <=> 0; // enforce -1|0|1
}

/**
* Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
*
* The numbers must only consist of digits, without leading minus sign.
*
* @return array{string, string, int}
*/
private function pad(string $a, string $b) : array
{
$x = \strlen($a);
$y = \strlen($b);

if ($x > $y) {
$b = \str_repeat('0', $x - $y) . $b;

return [$a, $b, $x];
}

if ($x < $y) {
$a = \str_repeat('0', $y - $x) . $a;

return [$a, $b, $y];
}

return [$a, $b, $x];
}
}

+ 98
- 0
vendor/brick/math/src/RoundingMode.php Vedi File

@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Brick\Math;

/**
* Specifies a rounding behavior for numerical operations capable of discarding precision.
*
* Each rounding mode indicates how the least significant returned digit of a rounded result
* is to be calculated. If fewer digits are returned than the digits needed to represent the
* exact numerical result, the discarded digits will be referred to as the discarded fraction
* regardless the digits' contribution to the value of the number. In other words, considered
* as a numerical value, the discarded fraction could have an absolute value greater than one.
*/
enum RoundingMode
{
/**
* Asserts that the requested operation has an exact result, hence no rounding is necessary.
*
* If this rounding mode is specified on an operation that yields a result that
* cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
*/
case UNNECESSARY;

/**
* Rounds away from zero.
*
* Always increments the digit prior to a nonzero discarded fraction.
* Note that this rounding mode never decreases the magnitude of the calculated value.
*/
case UP;

/**
* Rounds towards zero.
*
* Never increments the digit prior to a discarded fraction (i.e., truncates).
* Note that this rounding mode never increases the magnitude of the calculated value.
*/
case DOWN;

/**
* Rounds towards positive infinity.
*
* If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
* Note that this rounding mode never decreases the calculated value.
*/
case CEILING;

/**
* Rounds towards negative infinity.
*
* If the result is positive, behave as for DOWN; if negative, behave as for UP.
* Note that this rounding mode never increases the calculated value.
*/
case FLOOR;

/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
*
* Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
* Note that this is the rounding mode commonly taught at school.
*/
case HALF_UP;

/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
*
* Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
*/
case HALF_DOWN;

/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
*
* If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
*/
case HALF_CEILING;

/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
*
* If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
*/
case HALF_FLOOR;

/**
* Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
*
* Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
* behaves as for HALF_DOWN if it's even.
*
* Note that this is the rounding mode that statistically minimizes
* cumulative error when applied repeatedly over a sequence of calculations.
* It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
*/
case HALF_EVEN;
}

+ 579
- 0
vendor/composer/ClassLoader.php Vedi File

@@ -0,0 +1,579 @@
<?php

/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Composer\Autoload;

/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;

/** @var string|null */
private $vendorDir;

// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();

// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();

/** @var bool */
private $useIncludePath = false;

/**
* @var array<string, string>
*/
private $classMap = array();

/** @var bool */
private $classMapAuthoritative = false;

/**
* @var array<string, bool>
*/
private $missingClasses = array();

/** @var string|null */
private $apcuPrefix;

/**
* @var array<string, self>
*/
private static $registeredLoaders = array();

/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}

/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}

return array();
}

/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}

/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}

/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}

/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}

/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}

/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}

return;
}

$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;

return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}

/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}

/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}

/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}

/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}

/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}

/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}

/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}

/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}

/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}

/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);

if (null === $this->vendorDir) {
return;
}

if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}

/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));

if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}

/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);

return true;
}

return null;
}

/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}

$file = $this->findFileWithExtension($class, '.php');

// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}

if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}

if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}

return $file;
}

/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}

/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}

// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}

// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}

if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}

// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}

// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}

return false;
}

/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}

/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

+ 359
- 0
vendor/composer/InstalledVersions.php Vedi File

@@ -0,0 +1,359 @@
<?php

/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Composer;

use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;

/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;

/**
* @var bool|null
*/
private static $canGetVendors;

/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();

/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}

if (1 === \count($packages)) {
return $packages[0];
}

return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}

/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();

foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}

return $packagesByType;
}

/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}

return false;
}

/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));

return $provided->matches($constraint);
}

/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}

$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}

return implode(' || ', $ranges);
}

throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}

/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}

if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}

return $installed['versions'][$packageName]['version'];
}

throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}

/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}

if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}

return $installed['versions'][$packageName]['pretty_version'];
}

throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}

/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}

if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}

return $installed['versions'][$packageName]['reference'];
}

throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}

/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}

return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}

throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}

/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();

return $installed[0]['root'];
}

/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);

if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}

return self::$installed;
}

/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}

/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}

/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}

$installed = array();

if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}

if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}

if (self::$installed !== array()) {
$installed[] = self::$installed;
}

return $installed;
}
}

+ 21
- 0
vendor/composer/LICENSE Vedi File

@@ -0,0 +1,21 @@

Copyright (c) Nils Adermann, Jordi Boggiano

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


+ 10
- 0
vendor/composer/autoload_classmap.php Vedi File

@@ -0,0 +1,10 @@
<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

+ 10
- 0
vendor/composer/autoload_files.php Vedi File

@@ -0,0 +1,10 @@
<?php

// autoload_files.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
);

+ 9
- 0
vendor/composer/autoload_namespaces.php Vedi File

@@ -0,0 +1,9 @@
<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
);

+ 12
- 0
vendor/composer/autoload_psr4.php Vedi File

@@ -0,0 +1,12 @@
<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
);

+ 50
- 0
vendor/composer/autoload_real.php Vedi File

@@ -0,0 +1,50 @@
<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4
{
private static $loader;

public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}

/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}

require __DIR__ . '/platform_check.php';

spl_autoload_register(array('ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4', 'loadClassLoader'));

require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::getInitializer($loader));

$loader->register(true);

$filesToLoad = \Composer\Autoload\ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}

return $loader;
}
}

+ 53
- 0
vendor/composer/autoload_static.php Vedi File

@@ -0,0 +1,53 @@
<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4
{
public static $files = array (
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
);

public static $prefixLengthsPsr4 = array (
'R' =>
array (
'Ramsey\\Uuid\\' => 12,
'Ramsey\\Collection\\' => 18,
),
'B' =>
array (
'Brick\\Math\\' => 11,
),
);

public static $prefixDirsPsr4 = array (
'Ramsey\\Uuid\\' =>
array (
0 => __DIR__ . '/..' . '/ramsey/uuid/src',
),
'Ramsey\\Collection\\' =>
array (
0 => __DIR__ . '/..' . '/ramsey/collection/src',
),
'Brick\\Math\\' =>
array (
0 => __DIR__ . '/..' . '/brick/math/src',
),
);

public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);

public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$classMap;

}, null, ClassLoader::class);
}
}

+ 256
- 0
vendor/composer/installed.json Vedi File

@@ -0,0 +1,256 @@
{
"packages": [
{
"name": "brick/math",
"version": "0.12.1",
"version_normalized": "0.12.1.0",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1",
"vimeo/psalm": "5.16.0"
},
"time": "2023-11-29T23:19:16+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Brick\\Math\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Arbitrary-precision arithmetic library",
"keywords": [
"Arbitrary-precision",
"BigInteger",
"BigRational",
"arithmetic",
"bigdecimal",
"bignum",
"bignumber",
"brick",
"decimal",
"integer",
"math",
"mathematics",
"rational"
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.12.1"
},
"funding": [
{
"url": "https://github.com/BenMorel",
"type": "github"
}
],
"install-path": "../brick/math"
},
{
"name": "ramsey/collection",
"version": "2.0.0",
"version_normalized": "2.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
},
"time": "2022-12-31T21:50:55+00:00",
"type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Ramsey\\Collection\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ben Ramsey",
"email": "ben@benramsey.com",
"homepage": "https://benramsey.com"
}
],
"description": "A PHP library for representing and manipulating collections.",
"keywords": [
"array",
"collection",
"hash",
"map",
"queue",
"set"
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/2.0.0"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
"type": "tidelift"
}
],
"install-path": "../ramsey/collection"
},
{
"name": "ramsey/uuid",
"version": "4.7.6",
"version_normalized": "4.7.6.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
},
"replace": {
"rhumsaa/uuid": "self.version"
},
"require-dev": {
"captainhook/captainhook": "^5.10",
"captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.8",
"ergebnis/composer-normalize": "^2.15",
"mockery/mockery": "^1.3",
"paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2",
"php-mock/php-mock-mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.1",
"phpbench/phpbench": "^1.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9",
"ramsey/composer-repl": "^1.4",
"slevomat/coding-standard": "^8.4",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.9"
},
"suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
"ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
"ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
"paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
"ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
},
"time": "2024-04-27T21:32:50+00:00",
"type": "library",
"extra": {
"captainhook": {
"force-install": true
}
},
"installation-source": "dist",
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Ramsey\\Uuid\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
"keywords": [
"guid",
"identifier",
"uuid"
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.6"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
"type": "tidelift"
}
],
"install-path": "../ramsey/uuid"
}
],
"dev": true,
"dev-package-names": []
}

+ 56
- 0
vendor/composer/installed.php Vedi File

@@ -0,0 +1,56 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '58250a9b460c2aa8b4e02c2feff7f72cb980a184',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '58250a9b460c2aa8b4e02c2feff7f72cb980a184',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'brick/math' => array(
'pretty_version' => '0.12.1',
'version' => '0.12.1.0',
'reference' => 'f510c0a40911935b77b86859eb5223d58d660df1',
'type' => 'library',
'install_path' => __DIR__ . '/../brick/math',
'aliases' => array(),
'dev_requirement' => false,
),
'ramsey/collection' => array(
'pretty_version' => '2.0.0',
'version' => '2.0.0.0',
'reference' => 'a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5',
'type' => 'library',
'install_path' => __DIR__ . '/../ramsey/collection',
'aliases' => array(),
'dev_requirement' => false,
),
'ramsey/uuid' => array(
'pretty_version' => '4.7.6',
'version' => '4.7.6.0',
'reference' => '91039bc1faa45ba123c4328958e620d382ec7088',
'type' => 'library',
'install_path' => __DIR__ . '/../ramsey/uuid',
'aliases' => array(),
'dev_requirement' => false,
),
'rhumsaa/uuid' => array(
'dev_requirement' => false,
'replaced' => array(
0 => '4.7.6',
),
),
),
);

+ 26
- 0
vendor/composer/platform_check.php Vedi File

@@ -0,0 +1,26 @@
<?php

// platform_check.php @generated by Composer

$issues = array();

if (!(PHP_VERSION_ID >= 80100)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
}

if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

+ 19
- 0
vendor/ramsey/collection/LICENSE Vedi File

@@ -0,0 +1,19 @@
Copyright (c) 2015-2022 Ben Ramsey <ben@benramsey.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 70
- 0
vendor/ramsey/collection/README.md Vedi File

@@ -0,0 +1,70 @@
<h1 align="center">ramsey/collection</h1>

<p align="center">
<strong>A PHP library for representing and manipulating collections.</strong>
</p>

<p align="center">
<a href="https://github.com/ramsey/collection"><img src="http://img.shields.io/badge/source-ramsey/collection-blue.svg?style=flat-square" alt="Source Code"></a>
<a href="https://packagist.org/packages/ramsey/collection"><img src="https://img.shields.io/packagist/v/ramsey/collection.svg?style=flat-square&label=release" alt="Download Package"></a>
<a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/collection.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a>
<a href="https://github.com/ramsey/collection/blob/master/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/collection.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a>
<a href="https://github.com/ramsey/collection/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/collection/continuous-integration.yml?branch=main&logo=github&style=flat-square" alt="Build Status"></a>
<a href="https://codecov.io/gh/ramsey/collection"><img src="https://img.shields.io/codecov/c/gh/ramsey/collection?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a>
<a href="https://shepherd.dev/github/ramsey/collection"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fshepherd.dev%2Fgithub%2Framsey%2Fcollection%2Fcoverage" alt="Psalm Type Coverage"></a>
</p>

## About

ramsey/collection is a PHP library for representing and manipulating collections.

Much inspiration for this library came from the [Java Collections Framework][java].

This project adheres to a [code of conduct](CODE_OF_CONDUCT.md).
By participating in this project and its community, you are expected to
uphold this code.

## Installation

Install this package as a dependency using [Composer](https://getcomposer.org).

``` bash
composer require ramsey/collection
```

## Usage

Examples of how to use this library may be found in the
[Wiki pages](https://github.com/ramsey/collection/wiki/Examples).

## Contributing

Contributions are welcome! To contribute, please familiarize yourself with
[CONTRIBUTING.md](CONTRIBUTING.md).

## Coordinated Disclosure

Keeping user information safe and secure is a top priority, and we welcome the
contribution of external security researchers. If you believe you've found a
security issue in software that is maintained in this repository, please read
[SECURITY.md][] for instructions on submitting a vulnerability report.

## ramsey/collection for Enterprise

Available as part of the Tidelift Subscription.

The maintainers of ramsey/collection and thousands of other packages are working
with Tidelift to deliver commercial support and maintenance for the open source
packages you use to build your applications. Save time, reduce risk, and improve
code health, while paying the maintainers of the exact packages you use.
[Learn more.](https://tidelift.com/subscription/pkg/packagist-ramsey-collection?utm_source=undefined&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

## Copyright and License

The ramsey/collection library is copyright © [Ben Ramsey](https://benramsey.com)
and licensed for use under the terms of the
MIT License (MIT). Please see [LICENSE](LICENSE) for more information.


[java]: http://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html
[security.md]: https://github.com/ramsey/collection/blob/main/SECURITY.md

Dato che sono stati cambiati molti file in questo diff, alcuni di essi non verranno mostrati

Caricamento…
Annulla
Salva